-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.