Current section
Files
Jump to
Current section
Files
src/kdleam@number.erl
-module(kdleam@number).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/kdleam/number.gleam").
-export([parse_keyword_number/1, parse_number/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(false).
-file("src/kdleam/number.gleam", 13).
?DOC(false).
-spec parse_keyword_number(kdleam@scanner:scanner()) -> {ok,
{gleam@option:option(kdleam@document:kdl_number()),
kdleam@scanner:scanner()}} |
{error, kdleam@error:kdl_error()}.
parse_keyword_number(S) ->
case kdleam@scanner:peek(S) of
{some, <<"#"/utf8>>} ->
Candidates = [{<<"#-inf"/utf8>>, <<"#-inf"/utf8>>},
{<<"#inf"/utf8>>, <<"#inf"/utf8>>},
{<<"#nan"/utf8>>, <<"#nan"/utf8>>}],
case kdleam@scanner:try_tokens(S, Candidates) of
{{some, Keyword}, Next} ->
{ok,
{{some,
kdleam@document:new_number_unchecked(
Keyword,
none,
none
)},
Next}};
{none, Next@1} ->
{ok, {none, Next@1}}
end;
_ ->
{ok, {none, S}}
end.
-file("src/kdleam/number.gleam", 275).
?DOC(false).
-spec power_of_ten(integer()) -> gleam@option:option(float()).
power_of_ten(Exponent) ->
Magnitude = case Exponent < 0 of
true ->
0 - Exponent;
false ->
Exponent
end,
case Magnitude > 308 of
true ->
none;
false ->
case gleam@float:power(10.0, erlang:float(Magnitude)) of
{ok, V} ->
{some, V};
{error, _} ->
none
end
end.
-file("src/kdleam/number.gleam", 268).
?DOC(false).
-spec parse_exponent_int(binary()) -> gleam@option:option(integer()).
parse_exponent_int(Exponent) ->
case gleam_stdlib:parse_int(
gleam_stdlib:string_remove_prefix(Exponent, <<"+"/utf8>>)
) of
{ok, V} ->
{some, V};
{error, _} ->
none
end.
-file("src/kdleam/number.gleam", 257).
?DOC(false).
-spec parse_float_base(binary()) -> gleam@option:option(float()).
parse_float_base(Base) ->
case gleam_stdlib:parse_float(Base) of
{ok, V} ->
{some, V};
{error, _} ->
case gleam_stdlib:parse_int(Base) of
{ok, V@1} ->
{some, erlang:float(V@1)};
{error, _} ->
none
end
end.
-file("src/kdleam/number.gleam", 242).
?DOC(false).
-spec split_exponent(list(binary()), list(binary())) -> gleam@option:option({binary(),
binary()}).
split_exponent(Chars, Rev_base) ->
case Chars of
[] ->
none;
[Ch | Rest] ->
case (Ch =:= <<"e"/utf8>>) orelse (Ch =:= <<"E"/utf8>>) of
true ->
{some,
{begin
_pipe = Rev_base,
_pipe@1 = lists:reverse(_pipe),
kdleam@scanner:join(_pipe@1)
end,
kdleam@scanner:join(Rest)}};
false ->
split_exponent(Rest, [Ch | Rev_base])
end
end.
-file("src/kdleam/number.gleam", 222).
?DOC(false).
-spec parse_exponent_float(binary()) -> gleam@option:option(float()).
parse_exponent_float(Clean) ->
case split_exponent(gleam@string:to_graphemes(Clean), []) of
none ->
none;
{some, {Base, Exponent}} ->
case {parse_float_base(Base), parse_exponent_int(Exponent)} of
{{some, Base_float}, {some, Exp_int}} ->
case power_of_ten(Exp_int) of
{some, Factor} ->
{some, case Exp_int < 0 of
true ->
case Factor of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Base_float / Gleam@denominator
end;
false ->
Base_float * Factor
end};
none ->
none
end;
{_, _} ->
none
end
end.
-file("src/kdleam/number.gleam", 211).
?DOC(false).
-spec parse_float_number(binary(), gleam@option:option(integer())) -> gleam@option:option(float()).
parse_float_number(Clean, As_int) ->
case gleam_stdlib:parse_float(Clean) of
{ok, V} ->
{some, V};
{error, _} ->
case As_int of
{some, V@1} ->
{some, erlang:float(V@1)};
none ->
parse_exponent_float(Clean)
end
end.
-file("src/kdleam/number.gleam", 329).
?DOC(false).
-spec digits_less_or_equal(list(binary()), list(binary())) -> boolean().
digits_less_or_equal(Left, Right) ->
case {Left, Right} of
{[], []} ->
true;
{[L | Ls], [R | Rs]} ->
Lc = kdleam@scanner:codepoint(L),
Rc = kdleam@scanner:codepoint(R),
case Lc < Rc of
true ->
true;
false ->
case Lc > Rc of
true ->
false;
false ->
digits_less_or_equal(Ls, Rs)
end
end;
{_, _} ->
false
end.
-file("src/kdleam/number.gleam", 322).
?DOC(false).
-spec strip_leading_zero_chars(list(binary())) -> list(binary()).
strip_leading_zero_chars(Chars) ->
case Chars of
[<<"0"/utf8>> | Rest] ->
strip_leading_zero_chars(Rest);
_ ->
Chars
end.
-file("src/kdleam/number.gleam", 314).
?DOC(false).
-spec strip_leading_zeros(binary()) -> binary().
strip_leading_zeros(Value) ->
Stripped = strip_leading_zero_chars(gleam@string:to_graphemes(Value)),
case Stripped of
[] ->
<<"0"/utf8>>;
_ ->
kdleam@scanner:join(Stripped)
end.
-file("src/kdleam/number.gleam", 290).
?DOC(false).
-spec decimal_int_fits_i64(binary()) -> boolean().
decimal_int_fits_i64(Clean) ->
Negative = gleam_stdlib:string_starts_with(Clean, <<"-"/utf8>>),
Unsigned = begin
_pipe = Clean,
_pipe@1 = gleam_stdlib:string_remove_prefix(_pipe, <<"-"/utf8>>),
_pipe@2 = gleam_stdlib:string_remove_prefix(_pipe@1, <<"+"/utf8>>),
strip_leading_zeros(_pipe@2)
end,
Digits = gleam@string:to_graphemes(Unsigned),
Len = erlang:length(Digits),
case Len of
0 ->
true;
N when N < 19 ->
true;
N@1 when N@1 > 19 ->
false;
_ ->
digits_less_or_equal(
Digits,
gleam@string:to_graphemes(case Negative of
true ->
<<"9223372036854775808"/utf8>>;
false ->
<<"9223372036854775807"/utf8>>
end)
)
end.
-file("src/kdleam/number.gleam", 393).
?DOC(false).
-spec take_range(list(binary()), integer(), integer()) -> list(binary()).
take_range(Chars, Start, Count) ->
_pipe = Chars,
_pipe@1 = gleam@list:drop(_pipe, Start),
gleam@list:take(_pipe@1, Count).
-file("src/kdleam/number.gleam", 389).
?DOC(false).
-spec raw_between(kdleam@scanner:scanner(), kdleam@scanner:scanner()) -> binary().
raw_between(Start, End) ->
_pipe = take_range(
erlang:element(3, Start),
erlang:element(4, Start),
erlang:element(4, End) - erlang:element(4, Start)
),
kdleam@scanner:join(_pipe).
-file("src/kdleam/number.gleam", 200).
?DOC(false).
-spec reject_identifier_after_number(kdleam@scanner:scanner()) -> {ok,
kdleam@scanner:scanner()} |
{error, kdleam@error:kdl_error()}.
reject_identifier_after_number(S) ->
case kdleam@scanner:peek(S) of
{some, Ch} ->
case kdleam@scanner:is_identifier_char(Ch) andalso (Ch /= <<"."/utf8>>) of
true ->
{error, kdleam@scanner:make_error(S, invalid_number)};
false ->
{ok, S}
end;
none ->
{ok, S}
end.
-file("src/kdleam/number.gleam", 355).
?DOC(false).
-spec collect_dec_part(kdleam@scanner:scanner(), list(binary())) -> {list(binary()),
kdleam@scanner:scanner()}.
collect_dec_part(S, Acc) ->
case kdleam@scanner:peek(S) of
{some, <<"_"/utf8>>} ->
{_, N} = kdleam@scanner:advance(S),
collect_dec_part(N, [<<"_"/utf8>> | Acc]);
{some, Ch} ->
case kdleam@scanner:is_ascii_digit(Ch) of
true ->
{_, N@1} = kdleam@scanner:advance(S),
collect_dec_part(N@1, [Ch | Acc]);
false ->
{lists:reverse(Acc), S}
end;
_ ->
{lists:reverse(Acc), S}
end.
-file("src/kdleam/number.gleam", 373).
?DOC(false).
-spec parse_exponent(kdleam@scanner:scanner()) -> {boolean(),
kdleam@scanner:scanner()}.
parse_exponent(S) ->
{_, S1} = kdleam@scanner:advance(S),
S2 = case kdleam@scanner:peek(S1) of
{some, <<"+"/utf8>>} ->
{_, N} = kdleam@scanner:advance(S1),
N;
{some, <<"-"/utf8>>} ->
{_, N} = kdleam@scanner:advance(S1),
N;
_ ->
S1
end,
{Digits, S3} = collect_dec_part(S2, []),
case Digits of
[] ->
{false, S};
_ ->
{true, S3}
end.
-file("src/kdleam/number.gleam", 348).
?DOC(false).
-spec option_is_digit(gleam@option:option(binary())) -> boolean().
option_is_digit(Ch) ->
case Ch of
{some, C} ->
kdleam@scanner:is_ascii_digit(C);
none ->
false
end.
-file("src/kdleam/number.gleam", 155).
?DOC(false).
-spec parse_decimal(kdleam@scanner:scanner(), kdleam@scanner:scanner()) -> {ok,
{gleam@option:option(kdleam@document:kdl_number()),
kdleam@scanner:scanner()}} |
{error, kdleam@error:kdl_error()}.
parse_decimal(Start, S) ->
{Int_digits, S1} = collect_dec_part(S, []),
case Int_digits of
[] ->
{ok, {none, Start}};
_ ->
{Has_dot, _, S2} = case kdleam@scanner:peek(S1) of
{some, <<"."/utf8>>} ->
case option_is_digit(kdleam@scanner:peek_next(S1)) of
true ->
{_, N} = kdleam@scanner:advance(S1),
{Frac, N2} = collect_dec_part(N, []),
{true, Frac, N2};
false ->
{false, [], S1}
end;
_ ->
{false, [], S1}
end,
{Has_exp, S3} = case kdleam@scanner:peek(S2) of
{some, <<"e"/utf8>>} ->
parse_exponent(S2);
{some, <<"E"/utf8>>} ->
parse_exponent(S2);
_ ->
{false, S2}
end,
gleam@result:'try'(
reject_identifier_after_number(S3),
fun(S4) ->
Raw = raw_between(Start, S3),
Clean = kdleam@scanner:clean_underscores(Raw),
As_int = case (Has_dot orelse Has_exp) orelse gleam_stdlib:contains_string(
Clean,
<<"+"/utf8>>
) of
true ->
none;
false ->
case decimal_int_fits_i64(Clean) of
false ->
none;
true ->
case gleam_stdlib:parse_int(Clean) of
{ok, V} ->
{some, V};
{error, _} ->
none
end
end
end,
As_float = parse_float_number(Clean, As_int),
{ok,
{{some,
kdleam@document:new_number_unchecked(
Raw,
As_int,
As_float
)},
S4}}
end
)
end.
-file("src/kdleam/number.gleam", 97).
?DOC(false).
-spec decimal_int_of_prefixed(
binary(),
gleam@option:option(binary()),
integer()
) -> gleam@option:option(integer()).
decimal_int_of_prefixed(Digits, Sign, Base) ->
case kdleam@bignum:base_to_decimal(
kdleam@scanner:clean_underscores(Digits),
Base
) of
{error, _} ->
none;
{ok, Decimal} ->
Signed = case {Sign, Decimal} of
{{some, <<"-"/utf8>>}, <<"0"/utf8>>} ->
Decimal;
{{some, <<"-"/utf8>>}, _} ->
<<"-"/utf8, Decimal/binary>>;
{_, _} ->
Decimal
end,
case gleam_stdlib:parse_int(Signed) of
{ok, V} ->
case erlang:integer_to_binary(V) =:= Signed of
true ->
{some, V};
false ->
none
end;
{error, _} ->
none
end
end.
-file("src/kdleam/number.gleam", 144).
?DOC(false).
-spec digit_valid(binary(), integer()) -> boolean().
digit_valid(Ch, Base) ->
C = kdleam@scanner:codepoint(Ch),
case Base of
2 ->
(Ch =:= <<"0"/utf8>>) orelse (Ch =:= <<"1"/utf8>>);
8 ->
(C >= 48) andalso (C =< 55);
10 ->
kdleam@scanner:is_ascii_digit(Ch);
16 ->
kdleam@scanner:is_hex_digit(Ch);
_ ->
false
end.
-file("src/kdleam/number.gleam", 122).
?DOC(false).
-spec collect_base_digits(kdleam@scanner:scanner(), integer(), list(binary())) -> {list(binary()),
kdleam@scanner:scanner()}.
collect_base_digits(S, Base, Acc) ->
case kdleam@scanner:peek(S) of
{some, <<"_"/utf8>>} ->
{_, N} = kdleam@scanner:advance(S),
collect_base_digits(N, Base, [<<"_"/utf8>> | Acc]);
{some, Ch} ->
case digit_valid(Ch, Base) of
true ->
{_, N@1} = kdleam@scanner:advance(S),
collect_base_digits(N@1, Base, [Ch | Acc]);
false ->
{lists:reverse(Acc), S}
end;
_ ->
{lists:reverse(Acc), S}
end.
-file("src/kdleam/number.gleam", 68).
?DOC(false).
-spec parse_prefixed(
kdleam@scanner:scanner(),
kdleam@scanner:scanner(),
gleam@option:option(binary()),
integer()
) -> {ok,
{gleam@option:option(kdleam@document:kdl_number()),
kdleam@scanner:scanner()}} |
{error, kdleam@error:kdl_error()}.
parse_prefixed(Start, S, Sign, Base) ->
{_, S1} = kdleam@scanner:advance(S),
{_, S2} = kdleam@scanner:advance(S1),
case kdleam@scanner:peek(S2) of
{some, Ch} ->
case digit_valid(Ch, Base) of
true ->
{_, S3} = collect_base_digits(S2, Base, []),
gleam@result:'try'(
reject_identifier_after_number(S3),
fun(S4) ->
Raw = raw_between(Start, S3),
Digits = raw_between(S2, S3),
As_int = decimal_int_of_prefixed(Digits, Sign, Base),
As_float = case As_int of
{some, V} ->
{some, erlang:float(V)};
none ->
none
end,
{ok,
{{some,
kdleam@document:new_number_unchecked(
Raw,
As_int,
As_float
)},
S4}}
end
);
false ->
{error, kdleam@scanner:make_error(S2, invalid_number)}
end;
_ ->
{error, kdleam@scanner:make_error(S2, invalid_number)}
end.
-file("src/kdleam/number.gleam", 34).
?DOC(false).
-spec parse_number(kdleam@scanner:scanner()) -> {ok,
{gleam@option:option(kdleam@document:kdl_number()),
kdleam@scanner:scanner()}} |
{error, kdleam@error:kdl_error()}.
parse_number(S) ->
Start = S,
{Sign, S1} = case kdleam@scanner:peek(S) of
{some, <<"+"/utf8>>} ->
{_, N} = kdleam@scanner:advance(S),
{{some, <<"+"/utf8>>}, N};
{some, <<"-"/utf8>>} ->
{_, N@1} = kdleam@scanner:advance(S),
{{some, <<"-"/utf8>>}, N@1};
_ ->
{none, S}
end,
case kdleam@scanner:peek(S1) of
{some, <<"0"/utf8>>} ->
case kdleam@scanner:peek_next(S1) of
{some, Marker} ->
case kdleam@scanner:radix_of_marker(Marker) of
{some, Base} ->
parse_prefixed(Start, S1, Sign, Base);
none ->
parse_decimal(Start, S1)
end;
none ->
parse_decimal(Start, S1)
end;
{some, Ch} ->
case kdleam@scanner:is_ascii_digit(Ch) of
true ->
parse_decimal(Start, S1);
false ->
{ok, {none, Start}}
end;
_ ->
{ok, {none, Start}}
end.