-module(molt@internal@parser@scanners). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/molt/internal/parser/scanners.gleam"). -export([scan_basic_string/4, scan_literal_string/4, tokenize_header_line/3, consume_key_and_eq/3]). -export_type([basic_scan_error/0]). -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). -type basic_scan_error() :: unterminated_basic | bad_escape. -file("src/molt/internal/parser/scanners.gleam", 203). ?DOC(false). -spec take_n_hex(binary(), integer(), list(binary())) -> {ok, {binary(), binary()}} | {error, nil}. take_n_hex(Input, N, Acc) -> case N of 0 -> {ok, {molt@internal@utils:reverse_concat(Acc), Input}}; _ -> case Input of <<"0"/utf8, Input@1/binary>> -> take_n_hex(Input@1, N - 1, [<<"0"/utf8>> | Acc]); <<"1"/utf8, Input@2/binary>> -> take_n_hex(Input@2, N - 1, [<<"1"/utf8>> | Acc]); <<"2"/utf8, Input@3/binary>> -> take_n_hex(Input@3, N - 1, [<<"2"/utf8>> | Acc]); <<"3"/utf8, Input@4/binary>> -> take_n_hex(Input@4, N - 1, [<<"3"/utf8>> | Acc]); <<"4"/utf8, Input@5/binary>> -> take_n_hex(Input@5, N - 1, [<<"4"/utf8>> | Acc]); <<"5"/utf8, Input@6/binary>> -> take_n_hex(Input@6, N - 1, [<<"5"/utf8>> | Acc]); <<"6"/utf8, Input@7/binary>> -> take_n_hex(Input@7, N - 1, [<<"6"/utf8>> | Acc]); <<"7"/utf8, Input@8/binary>> -> take_n_hex(Input@8, N - 1, [<<"7"/utf8>> | Acc]); <<"8"/utf8, Input@9/binary>> -> take_n_hex(Input@9, N - 1, [<<"8"/utf8>> | Acc]); <<"9"/utf8, Input@10/binary>> -> take_n_hex(Input@10, N - 1, [<<"9"/utf8>> | Acc]); <<"a"/utf8, Input@11/binary>> -> take_n_hex(Input@11, N - 1, [<<"a"/utf8>> | Acc]); <<"b"/utf8, Input@12/binary>> -> take_n_hex(Input@12, N - 1, [<<"b"/utf8>> | Acc]); <<"c"/utf8, Input@13/binary>> -> take_n_hex(Input@13, N - 1, [<<"c"/utf8>> | Acc]); <<"d"/utf8, Input@14/binary>> -> take_n_hex(Input@14, N - 1, [<<"d"/utf8>> | Acc]); <<"e"/utf8, Input@15/binary>> -> take_n_hex(Input@15, N - 1, [<<"e"/utf8>> | Acc]); <<"f"/utf8, Input@16/binary>> -> take_n_hex(Input@16, N - 1, [<<"f"/utf8>> | Acc]); <<"A"/utf8, Input@17/binary>> -> take_n_hex(Input@17, N - 1, [<<"A"/utf8>> | Acc]); <<"B"/utf8, Input@18/binary>> -> take_n_hex(Input@18, N - 1, [<<"B"/utf8>> | Acc]); <<"C"/utf8, Input@19/binary>> -> take_n_hex(Input@19, N - 1, [<<"C"/utf8>> | Acc]); <<"D"/utf8, Input@20/binary>> -> take_n_hex(Input@20, N - 1, [<<"D"/utf8>> | Acc]); <<"E"/utf8, Input@21/binary>> -> take_n_hex(Input@21, N - 1, [<<"E"/utf8>> | Acc]); <<"F"/utf8, Input@22/binary>> -> take_n_hex(Input@22, N - 1, [<<"F"/utf8>> | Acc]); _ -> {error, nil} end end. -file("src/molt/internal/parser/scanners.gleam", 241). ?DOC(false). -spec parse_valid_scalar(binary()) -> {ok, integer()} | {error, nil}. parse_valid_scalar(Hex) -> case gleam@int:base_parse(Hex, 16) of {ok, Cp} when ((Cp >= 0) andalso (Cp =< 16#10FFFF)) andalso ((Cp < 16#D800) orelse (Cp > 16#DFFF)) -> {ok, Cp}; _ -> {error, nil} end. -file("src/molt/internal/parser/scanners.gleam", 172). ?DOC(false). -spec scan_basic_escape(binary()) -> {ok, {binary(), binary()}} | {error, nil}. scan_basic_escape(Input) -> case Input of <<"\""/utf8, Input@1/binary>> -> {ok, {<<"\\\""/utf8>>, Input@1}}; <<"\\"/utf8, Input@2/binary>> -> {ok, {<<"\\\\"/utf8>>, Input@2}}; <<"b"/utf8, Input@3/binary>> -> {ok, {<<"\\b"/utf8>>, Input@3}}; <<"t"/utf8, Input@4/binary>> -> {ok, {<<"\\t"/utf8>>, Input@4}}; <<"n"/utf8, Input@5/binary>> -> {ok, {<<"\\n"/utf8>>, Input@5}}; <<"f"/utf8, Input@6/binary>> -> {ok, {<<"\\f"/utf8>>, Input@6}}; <<"r"/utf8, Input@7/binary>> -> {ok, {<<"\\r"/utf8>>, Input@7}}; <<"e"/utf8, Input@8/binary>> -> {ok, {<<"\\e"/utf8>>, Input@8}}; <<"u"/utf8, Input@9/binary>> -> gleam@result:'try'( take_n_hex(Input@9, 4, []), fun(_use0) -> {Hex, Input@10} = _use0, gleam@result:'try'( parse_valid_scalar(Hex), fun(_) -> {ok, {<<"\\u"/utf8, Hex/binary>>, Input@10}} end ) end ); <<"U"/utf8, Input@11/binary>> -> gleam@result:'try'( take_n_hex(Input@11, 8, []), fun(_use0@1) -> {Hex@1, Input@12} = _use0@1, gleam@result:'try'( parse_valid_scalar(Hex@1), fun(_) -> {ok, {<<"\\U"/utf8, Hex@1/binary>>, Input@12}} end ) end ); <<"x"/utf8, Input@13/binary>> -> gleam@result:'try'( take_n_hex(Input@13, 2, []), fun(_use0@2) -> {Hex@2, Input@14} = _use0@2, {ok, {<<"\\x"/utf8, Hex@2/binary>>, Input@14}} end ); _ -> {error, nil} end. -file("src/molt/internal/parser/scanners.gleam", 256). ?DOC(false). -spec do_scan_basic(splitter:splitter(), binary(), list(binary())) -> {ok, {list(binary()), binary()}} | {error, basic_scan_error()}. do_scan_basic(Split, Input, Acc) -> {Prefix, Delim, Rest} = splitter_ffi:split(Split, Input), Acc@1 = [Prefix | Acc], case Delim of <<""/utf8>> -> {error, unterminated_basic}; <<"\""/utf8>> -> {ok, {Acc@1, Rest}}; <<"\\"/utf8>> -> case scan_basic_escape(Rest) of {ok, {Fragment, Input@1}} -> do_scan_basic(Split, Input@1, [Fragment | Acc@1]); {error, nil} -> {error, bad_escape} end; _ -> {error, unterminated_basic} end. -file("src/molt/internal/parser/scanners.gleam", 143). ?DOC(false). -spec absorb_trailing_basic(list(binary()), binary()) -> {list(binary()), binary()}. absorb_trailing_basic(Parts_rev, Rest) -> case Rest of <<"\"\""/utf8, After/binary>> -> case gleam_stdlib:string_starts_with(After, <<"\""/utf8>>) of true -> {Parts_rev, Rest}; false -> {[<<"\"\""/utf8>> | Parts_rev], After} end; <<"\""/utf8, After@1/binary>> -> case gleam_stdlib:string_starts_with(After@1, <<"\""/utf8>>) of true -> {Parts_rev, Rest}; false -> {[<<"\""/utf8>> | Parts_rev], After@1} end; _ -> {Parts_rev, Rest} end. -file("src/molt/internal/parser/scanners.gleam", 590). ?DOC(false). -spec maybe_emit_next_part(binary(), list(binary())) -> list(binary()). maybe_emit_next_part(Pending_nl, Parts_rev) -> gleam@bool:guard( Pending_nl =:= <<""/utf8>>, Parts_rev, fun() -> [Pending_nl | Parts_rev] end ). -file("src/molt/internal/parser/scanners.gleam", 276). ?DOC(false). -spec scan_ml_basic( molt@internal@parser@core:splitters(), molt@types:toml_kind(), binary(), list(binary()), binary(), binary() ) -> {greenwood:token(molt@types:toml_kind()), binary(), binary(), binary()}. scan_ml_basic(Sp, Kind, Input, Parts_rev, Pending_nl, Source_rest) -> gleam@bool:guard( (Input =:= <<""/utf8>>) andalso (Source_rest =:= <<""/utf8>>), {{token, invalid_multiline_basic_string, molt@internal@utils:reverse_concat(Parts_rev)}, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>}, fun() -> gleam@bool:lazy_guard( Input =:= <<""/utf8>>, fun() -> Parts_rev@1 = maybe_emit_next_part(Pending_nl, Parts_rev), {Input@1, Pending_nl@1, Source_rest@1} = splitter_ffi:split( erlang:element(2, Sp), Source_rest ), scan_ml_basic( Sp, Kind, Input@1, Parts_rev@1, Pending_nl@1, Source_rest@1 ) end, fun() -> {Prefix, Delim, Input@2} = splitter_ffi:split( erlang:element(7, Sp), Input ), Parts_rev@2 = maybe_emit_next_part(Prefix, Parts_rev), gleam@bool:lazy_guard( Delim =:= <<""/utf8>>, fun() -> scan_ml_basic( Sp, Kind, <<""/utf8>>, Parts_rev@2, Pending_nl, Source_rest ) end, fun() -> gleam@bool:lazy_guard( Delim =:= <<"\"\"\""/utf8>>, fun() -> {Parts_rev@3, Input@3} = absorb_trailing_basic( Parts_rev@2, Input@2 ), {greenwood:token( Kind, molt@internal@utils:reverse_concat( Parts_rev@3 ) ), Input@3, Pending_nl, Source_rest} end, fun() -> gleam@bool:lazy_guard( Delim /= <<"\\"/utf8>>, fun() -> scan_ml_basic( Sp, Kind, Input@2, Parts_rev@2, Pending_nl, Source_rest ) end, fun() -> case scan_basic_escape(Input@2) of {ok, {Fragment, Input@4}} -> scan_ml_basic( Sp, Kind, Input@4, [Fragment | Parts_rev@2], Pending_nl, Source_rest ); {error, nil} -> Parts_rev@4 = [<<"\\"/utf8, Input@2/binary>> | Parts_rev@2], gleam@bool:guard( not casefold:is_blank( Input@2 ), {{token, invalid_value, molt@internal@utils:reverse_concat( Parts_rev@4 )}, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>}, fun() -> scan_ml_basic( Sp, Kind, <<""/utf8>>, Parts_rev@4, Pending_nl, Source_rest ) end ) end end ) end ) end ) end ) end ). -file("src/molt/internal/parser/scanners.gleam", 109). ?DOC(false). -spec scan_multiline_basic( molt@internal@parser@core:splitters(), molt@types:toml_kind(), binary(), binary(), binary() ) -> {greenwood:token(molt@types:toml_kind()), binary(), binary(), binary()}. scan_multiline_basic(Sp, Kind, Input, Pending_nl, Source_rest) -> case Kind of multiline_basic_string_nl -> {Input@1, Pending_nl@1, Source_rest@1} = splitter_ffi:split( erlang:element(2, Sp), Source_rest ), scan_ml_basic(Sp, Kind, Input@1, [], Pending_nl@1, Source_rest@1); _ -> scan_ml_basic(Sp, Kind, Input, [], Pending_nl, Source_rest) end. -file("src/molt/internal/parser/scanners.gleam", 17). ?DOC(false). -spec scan_basic_string( molt@internal@parser@core:splitters(), binary(), binary(), binary() ) -> {greenwood:token(molt@types:toml_kind()), binary(), binary(), binary()}. scan_basic_string(Sp, Input, Pending_nl, Source_rest) -> case Input of <<"\"\""/utf8, After_triple/binary>> -> Kind = case After_triple of <<""/utf8>> -> multiline_basic_string_nl; _ -> multiline_basic_string end, scan_multiline_basic( Sp, Kind, After_triple, Pending_nl, Source_rest ); _ -> case do_scan_basic(erlang:element(5, Sp), Input, []) of {ok, {Parts_rev, Rest}} -> {greenwood:token( basic_string, molt@internal@utils:reverse_concat(Parts_rev) ), Rest, Pending_nl, Source_rest}; {error, unterminated_basic} -> {greenwood:token( invalid_basic_string, <<"\""/utf8, Input/binary>> ), <<""/utf8>>, Pending_nl, Source_rest}; {error, bad_escape} -> {greenwood:token(invalid_value, <<"\""/utf8, Input/binary>>), <<""/utf8>>, Pending_nl, Source_rest} end end. -file("src/molt/internal/parser/scanners.gleam", 411). ?DOC(false). -spec absorb_trailing_literal(list(binary()), binary()) -> {list(binary()), binary()}. absorb_trailing_literal(Parts_rev, Rest) -> case Rest of <<"''"/utf8, After/binary>> -> case gleam_stdlib:string_starts_with(After, <<"'"/utf8>>) of true -> {Parts_rev, Rest}; false -> {[<<"''"/utf8>> | Parts_rev], After} end; <<"'"/utf8, After@1/binary>> -> case gleam_stdlib:string_starts_with(After@1, <<"'"/utf8>>) of true -> {Parts_rev, Rest}; false -> {[<<"'"/utf8>> | Parts_rev], After@1} end; _ -> {Parts_rev, Rest} end. -file("src/molt/internal/parser/scanners.gleam", 356). ?DOC(false). -spec do_scan_ml_literal( molt@internal@parser@core:splitters(), molt@types:toml_kind(), binary(), list(binary()), binary(), binary() ) -> {greenwood:token(molt@types:toml_kind()), binary(), binary(), binary()}. do_scan_ml_literal(Sp, Kind, Input, Parts_rev, Pending_nl, Source_rest) -> gleam@bool:guard( (Input =:= <<""/utf8>>) andalso (Source_rest =:= <<""/utf8>>), {{token, invalid_multiline_literal_string, molt@internal@utils:reverse_concat(Parts_rev)}, <<""/utf8>>, <<""/utf8>>, <<""/utf8>>}, fun() -> gleam@bool:lazy_guard( Input =:= <<""/utf8>>, fun() -> Parts_rev@1 = maybe_emit_next_part(Pending_nl, Parts_rev), {Input@1, Pending_nl@1, Source_rest@1} = splitter_ffi:split( erlang:element(2, Sp), Source_rest ), do_scan_ml_literal( Sp, Kind, Input@1, Parts_rev@1, Pending_nl@1, Source_rest@1 ) end, fun() -> {Prefix, Delim, Input@2} = splitter_ffi:split( erlang:element(8, Sp), Input ), Parts_rev@2 = maybe_emit_next_part(Prefix, Parts_rev), gleam@bool:guard( Delim =:= <<"'''"/utf8>>, begin {Parts_rev@3, Input@3} = absorb_trailing_literal( Parts_rev@2, Input@2 ), {greenwood:token( Kind, molt@internal@utils:reverse_concat( Parts_rev@3 ) ), Input@3, Pending_nl, Source_rest} end, fun() -> gleam@bool:lazy_guard( Delim =:= <<""/utf8>>, fun() -> do_scan_ml_literal( Sp, Kind, <<""/utf8>>, Parts_rev@2, Pending_nl, Source_rest ) end, fun() -> do_scan_ml_literal( Sp, Kind, Input@2, Parts_rev@2, Pending_nl, Source_rest ) end ) end ) end ) end ). -file("src/molt/internal/parser/scanners.gleam", 432). ?DOC(false). -spec scan_multiline_literal( molt@internal@parser@core:splitters(), molt@types:toml_kind(), binary(), binary(), binary() ) -> {greenwood:token(molt@types:toml_kind()), binary(), binary(), binary()}. scan_multiline_literal(Sp, Kind, Input, Pending_nl, Source_rest) -> case Kind of multiline_literal_string_nl -> {Input@1, Pending_nl@1, Source_rest@1} = splitter_ffi:split( erlang:element(2, Sp), Source_rest ), do_scan_ml_literal( Sp, Kind, Input@1, [], Pending_nl@1, Source_rest@1 ); _ -> do_scan_ml_literal(Sp, Kind, Input, [], Pending_nl, Source_rest) end. -file("src/molt/internal/parser/scanners.gleam", 65). ?DOC(false). -spec scan_literal_string( molt@internal@parser@core:splitters(), binary(), binary(), binary() ) -> {greenwood:token(molt@types:toml_kind()), binary(), binary(), binary()}. scan_literal_string(Sp, Input, Pending_nl, Source_rest) -> case Input of <<"''"/utf8, After_triple/binary>> -> Kind = case After_triple of <<""/utf8>> -> multiline_literal_string_nl; _ -> multiline_literal_string end, scan_multiline_literal( Sp, Kind, After_triple, Pending_nl, Source_rest ); _ -> {Prefix, Delim, Rest} = splitter_ffi:split( erlang:element(6, Sp), Input ), case Delim of <<"'"/utf8>> -> {greenwood:token(literal_string, Prefix), Rest, Pending_nl, Source_rest}; _ -> {greenwood:token( invalid_literal_string, <<"'"/utf8, Input/binary>> ), <<""/utf8>>, Pending_nl, Source_rest} end end. -file("src/molt/internal/parser/scanners.gleam", 466). ?DOC(false). -spec tokenize_header_line( molt@internal@parser@core:splitters(), binary(), list(greenwood:token(molt@types:toml_kind())) ) -> list(greenwood:token(molt@types:toml_kind())). tokenize_header_line(Sp, Input, Acc) -> case Input of <<""/utf8>> -> Acc; _ -> {Prefix, Delim, Rest} = splitter_ffi:split( erlang:element(3, Sp), Input ), Acc@1 = case Prefix of <<""/utf8>> -> Acc; _ -> [greenwood:token(bare_key, Prefix) | Acc] end, case Delim of <<""/utf8>> -> Acc@1; <<"["/utf8>> -> tokenize_header_line( Sp, Rest, [greenwood:token(left_bracket, <<""/utf8>>) | Acc@1] ); <<"]"/utf8>> -> tokenize_header_line( Sp, Rest, [greenwood:token(right_bracket, <<""/utf8>>) | Acc@1] ); <<"."/utf8>> -> tokenize_header_line( Sp, Rest, [greenwood:token(dot, <<""/utf8>>) | Acc@1] ); <<"="/utf8>> -> tokenize_header_line( Sp, Rest, [greenwood:token(equals, <<""/utf8>>) | Acc@1] ); <<" "/utf8>> -> {Ws, Rest_after} = molt@internal@parser@core:scan_ws( Delim, Rest ), tokenize_header_line( Sp, Rest_after, [greenwood:token(whitespace, Ws) | Acc@1] ); <<"\t"/utf8>> -> {Ws, Rest_after} = molt@internal@parser@core:scan_ws( Delim, Rest ), tokenize_header_line( Sp, Rest_after, [greenwood:token(whitespace, Ws) | Acc@1] ); <<"\""/utf8>> -> {Str_tok, Rest_after@1, _, _} = scan_basic_string( Sp, Rest, <<""/utf8>>, <<""/utf8>> ), tokenize_header_line(Sp, Rest_after@1, [Str_tok | Acc@1]); <<"'"/utf8>> -> {Str_tok@1, Rest_after@2, _, _} = scan_literal_string( Sp, Rest, <<""/utf8>>, <<""/utf8>> ), tokenize_header_line(Sp, Rest_after@2, [Str_tok@1 | Acc@1]); <<"#"/utf8>> -> [greenwood:token(comment, <<"#"/utf8, Rest/binary>>) | Acc@1]; _ -> tokenize_header_line(Sp, Rest, Acc@1) end end. -file("src/molt/internal/parser/scanners.gleam", 530). ?DOC(false). -spec consume_key_and_eq( molt@internal@parser@core:splitters(), binary(), list(greenwood:element(molt@types:toml_kind())) ) -> {list(greenwood:element(molt@types:toml_kind())), binary()}. consume_key_and_eq(Sp, Input, Acc) -> case Input of <<""/utf8>> -> {Acc, <<""/utf8>>}; _ -> {Prefix, Delim, Rest} = splitter_ffi:split( erlang:element(3, Sp), Input ), Acc@1 = case Prefix of <<""/utf8>> -> Acc; _ -> [{token_element, {token, bare_key, Prefix}} | Acc] end, case Delim of <<""/utf8>> -> {Acc@1, <<""/utf8>>}; <<"="/utf8>> -> {[{token_element, {token, equals, <<""/utf8>>}} | Acc@1], Rest}; <<"."/utf8>> -> consume_key_and_eq( Sp, Rest, [{token_element, {token, dot, <<""/utf8>>}} | Acc@1] ); <<" "/utf8>> -> {Ws, Rest_after} = molt@internal@parser@core:scan_ws( Delim, Rest ), consume_key_and_eq( Sp, Rest_after, [{token_element, {token, whitespace, Ws}} | Acc@1] ); <<"\t"/utf8>> -> {Ws, Rest_after} = molt@internal@parser@core:scan_ws( Delim, Rest ), consume_key_and_eq( Sp, Rest_after, [{token_element, {token, whitespace, Ws}} | Acc@1] ); <<"\""/utf8>> -> {Str_tok, Rest_after@1, _, _} = scan_basic_string( Sp, Rest, <<""/utf8>>, <<""/utf8>> ), consume_key_and_eq( Sp, Rest_after@1, [{token_element, Str_tok} | Acc@1] ); <<"'"/utf8>> -> {Str_tok@1, Rest_after@2, _, _} = scan_literal_string( Sp, Rest, <<""/utf8>>, <<""/utf8>> ), consume_key_and_eq( Sp, Rest_after@2, [{token_element, Str_tok@1} | Acc@1] ); <<"["/utf8>> -> consume_key_and_eq( Sp, Rest, [{token_element, {token, left_bracket, <<""/utf8>>}} | Acc@1] ); <<"]"/utf8>> -> consume_key_and_eq( Sp, Rest, [{token_element, {token, right_bracket, <<""/utf8>>}} | Acc@1] ); <<"#"/utf8>> -> {[{token_element, {token, comment, <<"#"/utf8, Rest/binary>>}} | Acc@1], <<""/utf8>>}; _ -> consume_key_and_eq(Sp, Rest, Acc@1) end end.