Skip to main content

src/molt@internal@parser@scanners.erl

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