Skip to main content

src/swatch@internal@lexer.erl

-module(swatch@internal@lexer).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/swatch/internal/lexer.gleam").
-export([lex_token_to_source/1, lex/1, lex_to_source/1, is_valid_hex_color/1, annotate_nested/1]).
-export_type([lex_token/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 lex_token() :: {lex_whitespace, binary()} |
    {lex_comment, binary()} |
    lex_cdo |
    lex_cdc |
    {lex_string, binary()} |
    {lex_hash, binary()} |
    {lex_at_keyword, binary()} |
    {lex_ident, binary()} |
    {lex_function, binary()} |
    {lex_url_body, binary()} |
    {lex_number, binary()} |
    {lex_dimension, binary(), binary()} |
    {lex_percentage, binary()} |
    {lex_urange, binary()} |
    {lex_delim, binary()} |
    lex_colon |
    lex_semicolon |
    lex_comma |
    lex_open_paren |
    lex_close_paren |
    lex_open_bracket |
    lex_close_bracket |
    lex_open_brace |
    lex_close_brace.

-file("src/swatch/internal/lexer.gleam", 54).
?DOC(false).
-spec lex_token_to_source(lex_token()) -> binary().
lex_token_to_source(Token) ->
    case Token of
        {lex_whitespace, S} ->
            S;

        {lex_comment, S@1} ->
            S@1;

        lex_cdo ->
            <<"<!--"/utf8>>;

        lex_cdc ->
            <<"-->"/utf8>>;

        {lex_string, S@2} ->
            S@2;

        {lex_hash, S@3} ->
            S@3;

        {lex_at_keyword, S@4} ->
            S@4;

        {lex_ident, S@5} ->
            S@5;

        {lex_function, S@6} ->
            S@6;

        {lex_url_body, S@7} ->
            S@7;

        {lex_number, S@8} ->
            S@8;

        {lex_dimension, Number, Unit} ->
            <<Number/binary, Unit/binary>>;

        {lex_percentage, Number@1} ->
            <<Number@1/binary, "%"/utf8>>;

        {lex_urange, S@9} ->
            S@9;

        {lex_delim, S@10} ->
            S@10;

        lex_colon ->
            <<":"/utf8>>;

        lex_semicolon ->
            <<";"/utf8>>;

        lex_comma ->
            <<","/utf8>>;

        lex_open_paren ->
            <<"("/utf8>>;

        lex_close_paren ->
            <<")"/utf8>>;

        lex_open_bracket ->
            <<"["/utf8>>;

        lex_close_bracket ->
            <<"]"/utf8>>;

        lex_open_brace ->
            <<"{"/utf8>>;

        lex_close_brace ->
            <<"}"/utf8>>
    end.

-file("src/swatch/internal/lexer.gleam", 599).
?DOC(false).
-spec consumed(bitstring(), bitstring()) -> binary().
consumed(Input, Here) ->
    Len = erlang:byte_size(Input) - erlang:byte_size(Here),
    Bytes@1 = case gleam_stdlib:bit_array_slice(Input, 0, Len) of
        {ok, Bytes} -> Bytes;
        _assert_fail ->
            erlang:error(#{gleam_error => let_assert,
                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                        file => <<?FILEPATH/utf8>>,
                        module => <<"swatch/internal/lexer"/utf8>>,
                        function => <<"consumed"/utf8>>,
                        line => 601,
                        value => _assert_fail,
                        start => 19218,
                        'end' => 19271,
                        pattern_start => 19229,
                        pattern_end => 19238})
    end,
    Text@1 = case gleam@bit_array:to_string(Bytes@1) of
        {ok, Text} -> Text;
        _assert_fail@1 ->
            erlang:error(#{gleam_error => let_assert,
                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                        file => <<?FILEPATH/utf8>>,
                        module => <<"swatch/internal/lexer"/utf8>>,
                        function => <<"consumed"/utf8>>,
                        line => 602,
                        value => _assert_fail@1,
                        start => 19274,
                        'end' => 19322,
                        pattern_start => 19285,
                        pattern_end => 19293})
    end,
    Text@1.

-file("src/swatch/internal/lexer.gleam", 801).
?DOC(false).
-spec is_whitespace(binary()) -> boolean().
is_whitespace(Char) ->
    case Char of
        <<" "/utf8>> ->
            true;

        <<"\t"/utf8>> ->
            true;

        <<"\n"/utf8>> ->
            true;

        <<"\r"/utf8>> ->
            true;

        <<"\f"/utf8>> ->
            true;

        <<"\r\n"/utf8>> ->
            true;

        _ ->
            false
    end.

-file("src/swatch/internal/lexer.gleam", 403).
?DOC(false).
-spec codepoint_string(bitstring(), bitstring()) -> {ok,
        {binary(), bitstring()}} |
    {error, nil}.
codepoint_string(Cp, Rest) ->
    case gleam@bit_array:to_string(Cp) of
        {ok, Char} ->
            {ok, {Char, Rest}};

        {error, _} ->
            {error, nil}
    end.

-file("src/swatch/internal/lexer.gleam", 391).
?DOC(false).
-spec pop_codepoint(bitstring()) -> {ok, {binary(), bitstring()}} | {error, nil}.
pop_codepoint(Input) ->
    case Input of
        <<16#0D, 16#0A, Rest/bitstring>> ->
            {ok, {<<"\r\n"/utf8>>, Rest}};

        <<B0, Rest@1/bitstring>> when B0 < 16#80 ->
            codepoint_string(<<B0>>, Rest@1);

        <<B0@1, B1, Rest@2/bitstring>> when B0@1 < 16#E0 ->
            codepoint_string(<<B0@1, B1>>, Rest@2);

        <<B0@2, B1@1, B2, Rest@3/bitstring>> when B0@2 < 16#F0 ->
            codepoint_string(<<B0@2, B1@1, B2>>, Rest@3);

        <<B0@3, B1@2, B2@1, B3, Rest@4/bitstring>> ->
            codepoint_string(<<B0@3, B1@2, B2@1, B3>>, Rest@4);

        _ ->
            {error, nil}
    end.

-file("src/swatch/internal/lexer.gleam", 674).
?DOC(false).
-spec escape_trailing_whitespace(bitstring(), binary()) -> {binary(),
    bitstring()}.
escape_trailing_whitespace(Input, Acc) ->
    case pop_codepoint(Input) of
        {ok, {Char, Rest}} ->
            case is_whitespace(Char) of
                true ->
                    {<<Acc/binary, Char/binary>>, Rest};

                false ->
                    {Acc, Input}
            end;

        {error, _} ->
            {Acc, Input}
    end.

-file("src/swatch/internal/lexer.gleam", 815).
?DOC(false).
-spec is_hex_digit(binary()) -> boolean().
is_hex_digit(Char) ->
    case Char of
        <<"0"/utf8>> ->
            true;

        <<"1"/utf8>> ->
            true;

        <<"2"/utf8>> ->
            true;

        <<"3"/utf8>> ->
            true;

        <<"4"/utf8>> ->
            true;

        <<"5"/utf8>> ->
            true;

        <<"6"/utf8>> ->
            true;

        <<"7"/utf8>> ->
            true;

        <<"8"/utf8>> ->
            true;

        <<"9"/utf8>> ->
            true;

        <<"a"/utf8>> ->
            true;

        <<"b"/utf8>> ->
            true;

        <<"c"/utf8>> ->
            true;

        <<"d"/utf8>> ->
            true;

        <<"e"/utf8>> ->
            true;

        <<"f"/utf8>> ->
            true;

        <<"A"/utf8>> ->
            true;

        <<"B"/utf8>> ->
            true;

        <<"C"/utf8>> ->
            true;

        <<"D"/utf8>> ->
            true;

        <<"E"/utf8>> ->
            true;

        <<"F"/utf8>> ->
            true;

        _ ->
            false
    end.

-file("src/swatch/internal/lexer.gleam", 655).
?DOC(false).
-spec take_hex_escape(bitstring(), binary(), integer()) -> {binary(),
    bitstring()}.
take_hex_escape(Input, Acc, Count) ->
    case Count >= 6 of
        true ->
            escape_trailing_whitespace(Input, Acc);

        false ->
            case pop_codepoint(Input) of
                {ok, {Char, Rest}} ->
                    case is_hex_digit(Char) of
                        true ->
                            take_hex_escape(
                                Rest,
                                <<Acc/binary, Char/binary>>,
                                Count + 1
                            );

                        false ->
                            escape_trailing_whitespace(Input, Acc)
                    end;

                {error, _} ->
                    {Acc, Input}
            end
    end.

-file("src/swatch/internal/lexer.gleam", 643).
?DOC(false).
-spec take_identifier_escape(bitstring()) -> {binary(), bitstring()}.
take_identifier_escape(Input) ->
    case pop_codepoint(Input) of
        {error, _} ->
            {<<""/utf8>>, Input};

        {ok, {<<"\n"/utf8>>, _}} ->
            {<<""/utf8>>, Input};

        {ok, {<<"\r"/utf8>>, _}} ->
            {<<""/utf8>>, Input};

        {ok, {<<"\f"/utf8>>, _}} ->
            {<<""/utf8>>, Input};

        {ok, {Char, Rest}} ->
            case is_hex_digit(Char) of
                true ->
                    take_hex_escape(Rest, Char, 1);

                false ->
                    {Char, Rest}
            end
    end.

-file("src/swatch/internal/lexer.gleam", 874).
?DOC(false).
-spec starts_with_valid_escape(bitstring()) -> boolean().
starts_with_valid_escape(Input) ->
    case pop_codepoint(Input) of
        {ok, {<<"\n"/utf8>>, _}} ->
            false;

        {ok, {<<"\r"/utf8>>, _}} ->
            false;

        {ok, {<<"\f"/utf8>>, _}} ->
            false;

        {ok, _} ->
            true;

        {error, _} ->
            false
    end.

-file("src/swatch/internal/lexer.gleam", 419).
?DOC(false).
-spec scan_url_body(bitstring()) -> bitstring().
scan_url_body(Input) ->
    case Input of
        <<16#5C, After/bitstring>> ->
            case starts_with_valid_escape(After) of
                true ->
                    {_, Rest2} = take_identifier_escape(After),
                    scan_url_body(Rest2);

                false ->
                    Input
            end;

        <<B, _/bitstring>> when (((((B =:= 16#29) orelse (B =:= 16#20)) orelse (B =:= 16#09)) orelse (B =:= 16#0A)) orelse (B =:= 16#0D)) orelse (B =:= 16#0C) ->
            Input;

        <<_, Rest/bitstring>> ->
            scan_url_body(Rest);

        _ ->
            <<>>
    end.

-file("src/swatch/internal/lexer.gleam", 413).
?DOC(false).
-spec take_url_body(bitstring()) -> {binary(), bitstring()}.
take_url_body(Input) ->
    Rest = scan_url_body(Input),
    {consumed(Input, Rest), Rest}.

-file("src/swatch/internal/lexer.gleam", 790).
?DOC(false).
-spec scan_whitespace(bitstring()) -> bitstring().
scan_whitespace(Input) ->
    case Input of
        <<B, Rest/bitstring>> when ((((B =:= 16#20) orelse (B =:= 16#09)) orelse (B =:= 16#0A)) orelse (B =:= 16#0D)) orelse (B =:= 16#0C) ->
            scan_whitespace(Rest);

        _ ->
            Input
    end.

-file("src/swatch/internal/lexer.gleam", 785).
?DOC(false).
-spec take_whitespace(bitstring(), binary()) -> {binary(), bitstring()}.
take_whitespace(Input, Acc) ->
    Rest = scan_whitespace(Input),
    {<<Acc/binary, (consumed(Input, Rest))/binary>>, Rest}.

-file("src/swatch/internal/lexer.gleam", 302).
?DOC(false).
-spec lex_url_call(binary(), bitstring(), integer(), list(lex_token())) -> {bitstring(),
    integer(),
    list(lex_token())}.
lex_url_call(Name, After_paren, Bracket_depth, Out) ->
    Out2 = [lex_open_paren, {lex_function, Name} | Out],
    {Whitespace, After_whitespace} = take_whitespace(After_paren, <<""/utf8>>),
    Out3 = case Whitespace of
        <<""/utf8>> ->
            Out2;

        _ ->
            [{lex_whitespace, Whitespace} | Out2]
    end,
    case pop_codepoint(After_whitespace) of
        {ok, {<<"\""/utf8>>, _}} ->
            {After_whitespace, Bracket_depth, Out3};

        {ok, {<<"'"/utf8>>, _}} ->
            {After_whitespace, Bracket_depth, Out3};

        {ok, {<<")"/utf8>>, _}} ->
            {After_whitespace, Bracket_depth, Out3};

        {ok, _} ->
            {Body, After_body} = take_url_body(After_whitespace),
            {After_body, Bracket_depth, [{lex_url_body, Body} | Out3]};

        {error, _} ->
            {After_whitespace, Bracket_depth, Out3}
    end.

-file("src/swatch/internal/lexer.gleam", 278).
?DOC(false).
-spec lex_ident_or_function(binary(), bitstring(), integer(), list(lex_token())) -> {bitstring(),
    integer(),
    list(lex_token())}.
lex_ident_or_function(Name, Rest, Bracket_depth, Out) ->
    case Rest of
        <<"("/utf8, After_paren/bitstring>> ->
            case gleam_stdlib:string_starts_with(Name, <<"--"/utf8>>) of
                true ->
                    {Rest, Bracket_depth, [{lex_ident, Name} | Out]};

                false ->
                    case string:lowercase(Name) =:= <<"url"/utf8>> of
                        true ->
                            lex_url_call(Name, After_paren, Bracket_depth, Out);

                        false ->
                            {After_paren,
                                Bracket_depth,
                                [lex_open_paren, {lex_function, Name} | Out]}
                    end
            end;

        _ ->
            {Rest, Bracket_depth, [{lex_ident, Name} | Out]}
    end.

-file("src/swatch/internal/lexer.gleam", 616).
?DOC(false).
-spec scan_identifier(bitstring()) -> bitstring().
scan_identifier(Input) ->
    case Input of
        <<16#5C, After/bitstring>> ->
            case starts_with_valid_escape(After) of
                true ->
                    {_, Rest2} = take_identifier_escape(After),
                    scan_identifier(Rest2);

                false ->
                    Input
            end;

        <<B, Rest/bitstring>> when (B >= 16#61) andalso (B =< 16#7A) ->
            scan_identifier(Rest);

        <<B@1, Rest@1/bitstring>> when (B@1 >= 16#41) andalso (B@1 =< 16#5A) ->
            scan_identifier(Rest@1);

        <<B@2, Rest@2/bitstring>> when (B@2 >= 16#30) andalso (B@2 =< 16#39) ->
            scan_identifier(Rest@2);

        <<B@3, Rest@3/bitstring>> when (B@3 =:= 16#2D) orelse (B@3 =:= 16#5F) ->
            scan_identifier(Rest@3);

        <<B@4, _/bitstring>> when B@4 >= 16#80 ->
            case pop_codepoint(Input) of
                {ok, {_, Rest@4}} ->
                    scan_identifier(Rest@4);

                {error, _} ->
                    Input
            end;

        _ ->
            Input
    end.

-file("src/swatch/internal/lexer.gleam", 608).
?DOC(false).
-spec take_identifier(bitstring(), binary()) -> {binary(), bitstring()}.
take_identifier(Input, Acc) ->
    Rest = scan_identifier(Input),
    {<<Acc/binary, (consumed(Input, Rest))/binary>>, Rest}.

-file("src/swatch/internal/lexer.gleam", 488).
?DOC(false).
-spec take_range_wildcards(bitstring(), binary(), integer()) -> {binary(),
    bitstring()}.
take_range_wildcards(Input, Acc, Count) ->
    case Count >= 6 of
        true ->
            {Acc, Input};

        false ->
            case pop_codepoint(Input) of
                {ok, {<<"?"/utf8>>, Rest}} ->
                    take_range_wildcards(
                        Rest,
                        <<Acc/binary, "?"/utf8>>,
                        Count + 1
                    );

                _ ->
                    {Acc, Input}
            end
    end.

-file("src/swatch/internal/lexer.gleam", 463).
?DOC(false).
-spec take_range_chars(bitstring(), binary(), integer(), boolean()) -> {binary(),
    bitstring()}.
take_range_chars(Input, Acc, Count, Allow_wildcard) ->
    case Count >= 6 of
        true ->
            {Acc, Input};

        false ->
            case pop_codepoint(Input) of
                {ok, {Char, Rest}} ->
                    case is_hex_digit(Char) of
                        true ->
                            take_range_chars(
                                Rest,
                                <<Acc/binary, Char/binary>>,
                                Count + 1,
                                Allow_wildcard
                            );

                        false ->
                            case Allow_wildcard andalso (Char =:= <<"?"/utf8>>) of
                                true ->
                                    take_range_wildcards(
                                        Rest,
                                        <<Acc/binary, Char/binary>>,
                                        Count + 1
                                    );

                                false ->
                                    {Acc, Input}
                            end
                    end;

                {error, _} ->
                    {Acc, Input}
            end
    end.

-file("src/swatch/internal/lexer.gleam", 446).
?DOC(false).
-spec range_continuation(bitstring()) -> {ok, {binary(), bitstring()}} |
    {error, nil}.
range_continuation(Input) ->
    case pop_codepoint(Input) of
        {ok, {<<"-"/utf8>>, After_dash}} ->
            case pop_codepoint(After_dash) of
                {ok, {Char, _}} ->
                    case is_hex_digit(Char) of
                        true ->
                            {ok,
                                take_range_chars(
                                    After_dash,
                                    <<""/utf8>>,
                                    0,
                                    false
                                )};

                        false ->
                            {error, nil}
                    end;

                {error, _} ->
                    {error, nil}
            end;

        _ ->
            {error, nil}
    end.

-file("src/swatch/internal/lexer.gleam", 363).
?DOC(false).
-spec lex_take_urange(binary(), bitstring()) -> {binary(), bitstring()}.
lex_take_urange(Char, Rest) ->
    After_plus = case Rest of
        <<"+"/utf8, After/bitstring>> ->
            After;

        _ ->
            Rest
    end,
    {First, After_first} = take_range_chars(After_plus, <<""/utf8>>, 0, true),
    Prefix = <<<<Char/binary, "+"/utf8>>/binary, First/binary>>,
    case gleam_stdlib:contains_string(First, <<"?"/utf8>>) of
        true ->
            {Prefix, After_first};

        false ->
            case range_continuation(After_first) of
                {ok, {Second, After_second}} ->
                    {<<<<Prefix/binary, "-"/utf8>>/binary, Second/binary>>,
                        After_second};

                {error, _} ->
                    {Prefix, After_first}
            end
    end.

-file("src/swatch/internal/lexer.gleam", 347).
?DOC(false).
-spec lex_starts_urange(binary(), bitstring()) -> boolean().
lex_starts_urange(Char1, Rest) ->
    case (Char1 =:= <<"u"/utf8>>) orelse (Char1 =:= <<"U"/utf8>>) of
        false ->
            false;

        true ->
            case pop_codepoint(Rest) of
                {ok, {<<"+"/utf8>>, After_plus}} ->
                    case pop_codepoint(After_plus) of
                        {ok, {Char2, _}} ->
                            is_hex_digit(Char2) orelse (Char2 =:= <<"?"/utf8>>);

                        {error, _} ->
                            false
                    end;

                _ ->
                    false
            end
    end.

-file("src/swatch/internal/lexer.gleam", 850).
?DOC(false).
-spec is_non_ascii(binary()) -> boolean().
is_non_ascii(Char) ->
    case gleam@string:to_utf_codepoints(Char) of
        [First | _] ->
            gleam_stdlib:identity(First) >= 16#80;

        [] ->
            false
    end.

-file("src/swatch/internal/lexer.gleam", 826).
?DOC(false).
-spec is_alpha(binary()) -> boolean().
is_alpha(Char) ->
    case Char of
        <<"a"/utf8>> ->
            true;

        <<"b"/utf8>> ->
            true;

        <<"c"/utf8>> ->
            true;

        <<"d"/utf8>> ->
            true;

        <<"e"/utf8>> ->
            true;

        <<"f"/utf8>> ->
            true;

        <<"g"/utf8>> ->
            true;

        <<"h"/utf8>> ->
            true;

        <<"i"/utf8>> ->
            true;

        <<"j"/utf8>> ->
            true;

        <<"k"/utf8>> ->
            true;

        <<"l"/utf8>> ->
            true;

        <<"m"/utf8>> ->
            true;

        <<"n"/utf8>> ->
            true;

        <<"o"/utf8>> ->
            true;

        <<"p"/utf8>> ->
            true;

        <<"q"/utf8>> ->
            true;

        <<"r"/utf8>> ->
            true;

        <<"s"/utf8>> ->
            true;

        <<"t"/utf8>> ->
            true;

        <<"u"/utf8>> ->
            true;

        <<"v"/utf8>> ->
            true;

        <<"w"/utf8>> ->
            true;

        <<"x"/utf8>> ->
            true;

        <<"y"/utf8>> ->
            true;

        <<"z"/utf8>> ->
            true;

        <<"A"/utf8>> ->
            true;

        <<"B"/utf8>> ->
            true;

        <<"C"/utf8>> ->
            true;

        <<"D"/utf8>> ->
            true;

        <<"E"/utf8>> ->
            true;

        <<"F"/utf8>> ->
            true;

        <<"G"/utf8>> ->
            true;

        <<"H"/utf8>> ->
            true;

        <<"I"/utf8>> ->
            true;

        <<"J"/utf8>> ->
            true;

        <<"K"/utf8>> ->
            true;

        <<"L"/utf8>> ->
            true;

        <<"M"/utf8>> ->
            true;

        <<"N"/utf8>> ->
            true;

        <<"O"/utf8>> ->
            true;

        <<"P"/utf8>> ->
            true;

        <<"Q"/utf8>> ->
            true;

        <<"R"/utf8>> ->
            true;

        <<"S"/utf8>> ->
            true;

        <<"T"/utf8>> ->
            true;

        <<"U"/utf8>> ->
            true;

        <<"V"/utf8>> ->
            true;

        <<"W"/utf8>> ->
            true;

        <<"X"/utf8>> ->
            true;

        <<"Y"/utf8>> ->
            true;

        <<"Z"/utf8>> ->
            true;

        _ ->
            case gleam@string:to_utf_codepoints(Char) of
                [_] ->
                    false;

                [] ->
                    false;

                [Codepoint | _] ->
                    N = gleam_stdlib:identity(Codepoint),
                    ((N >= 16#41) andalso (N =< 16#5A)) orelse ((N >= 16#61)
                    andalso (N =< 16#7A))
            end
    end.

-file("src/swatch/internal/lexer.gleam", 845).
?DOC(false).
-spec is_identifier_start(binary()) -> boolean().
is_identifier_start(Char) ->
    (is_alpha(Char) orelse (Char =:= <<"_"/utf8>>)) orelse is_non_ascii(Char).

-file("src/swatch/internal/lexer.gleam", 331).
?DOC(false).
-spec lex_number_token(binary(), bitstring()) -> {lex_token(), bitstring()}.
lex_number_token(Num, Rest) ->
    case pop_codepoint(Rest) of
        {ok, {<<"%"/utf8>>, Rest2}} ->
            {{lex_percentage, Num}, Rest2};

        {ok, {Char, _}} ->
            case is_identifier_start(Char) of
                true ->
                    {Unit, Rest2@1} = take_identifier(Rest, <<""/utf8>>),
                    {{lex_dimension, Num, Unit}, Rest2@1};

                false ->
                    {{lex_number, Num}, Rest}
            end;

        {error, _} ->
            {{lex_number, Num}, Rest}
    end.

-file("src/swatch/internal/lexer.gleam", 776).
?DOC(false).
-spec scan_digits(bitstring()) -> bitstring().
scan_digits(Input) ->
    case Input of
        <<B, Rest/bitstring>> when (B >= 16#30) andalso (B =< 16#39) ->
            scan_digits(Rest);

        _ ->
            Input
    end.

-file("src/swatch/internal/lexer.gleam", 771).
?DOC(false).
-spec take_digits(bitstring(), binary()) -> {binary(), bitstring()}.
take_digits(Input, Acc) ->
    Rest = scan_digits(Input),
    {<<Acc/binary, (consumed(Input, Rest))/binary>>, Rest}.

-file("src/swatch/internal/lexer.gleam", 754).
?DOC(false).
-spec finish_exponent(binary(), bitstring()) -> {ok, {binary(), bitstring()}} |
    {error, nil}.
finish_exponent(Exponent_char, Rest) ->
    {Sign, After_sign} = case pop_codepoint(Rest) of
        {ok, {<<"+"/utf8>>, After}} ->
            {<<"+"/utf8>>, After};

        {ok, {<<"-"/utf8>>, After@1}} ->
            {<<"-"/utf8>>, After@1};

        _ ->
            {<<""/utf8>>, Rest}
    end,
    {Digits, Rest2} = take_digits(After_sign, <<""/utf8>>),
    case Digits of
        <<""/utf8>> ->
            {error, nil};

        _ ->
            {ok,
                {<<<<Exponent_char/binary, Sign/binary>>/binary, Digits/binary>>,
                    Rest2}}
    end.

-file("src/swatch/internal/lexer.gleam", 746).
?DOC(false).
-spec maybe_exponent(bitstring()) -> {ok, {binary(), bitstring()}} |
    {error, nil}.
maybe_exponent(Input) ->
    case pop_codepoint(Input) of
        {ok, {<<"e"/utf8>>, Rest}} ->
            finish_exponent(<<"e"/utf8>>, Rest);

        {ok, {<<"E"/utf8>>, Rest@1}} ->
            finish_exponent(<<"E"/utf8>>, Rest@1);

        _ ->
            {error, nil}
    end.

-file("src/swatch/internal/lexer.gleam", 808).
?DOC(false).
-spec is_digit(binary()) -> boolean().
is_digit(Char) ->
    case Char of
        <<"0"/utf8>> ->
            true;

        <<"1"/utf8>> ->
            true;

        <<"2"/utf8>> ->
            true;

        <<"3"/utf8>> ->
            true;

        <<"4"/utf8>> ->
            true;

        <<"5"/utf8>> ->
            true;

        <<"6"/utf8>> ->
            true;

        <<"7"/utf8>> ->
            true;

        <<"8"/utf8>> ->
            true;

        <<"9"/utf8>> ->
            true;

        _ ->
            false
    end.

-file("src/swatch/internal/lexer.gleam", 728).
?DOC(false).
-spec take_optional_fraction(bitstring(), binary()) -> {binary(), bitstring()}.
take_optional_fraction(Input, Acc) ->
    case pop_codepoint(Input) of
        {ok, {<<"."/utf8>>, After_dot}} ->
            case pop_codepoint(After_dot) of
                {ok, {Char, _}} ->
                    case is_digit(Char) of
                        true ->
                            {Fraction, Rest} = take_digits(
                                After_dot,
                                <<""/utf8>>
                            ),
                            {<<<<Acc/binary, "."/utf8>>/binary,
                                    Fraction/binary>>,
                                Rest};

                        false ->
                            {Acc, Input}
                    end;

                {error, _} ->
                    {Acc, Input}
            end;

        _ ->
            {Acc, Input}
    end.

-file("src/swatch/internal/lexer.gleam", 712).
?DOC(false).
-spec take_number(bitstring(), binary()) -> {binary(), bitstring()}.
take_number(Input, Acc) ->
    Already_has_dot = gleam_stdlib:contains_string(Acc, <<"."/utf8>>),
    {Int_digits, After_int} = take_digits(Input, <<""/utf8>>),
    Mantissa = <<Acc/binary, Int_digits/binary>>,
    {Mantissa2, After_fraction} = case Already_has_dot of
        true ->
            {Mantissa, After_int};

        false ->
            take_optional_fraction(After_int, Mantissa)
    end,
    case maybe_exponent(After_fraction) of
        {ok, {Exponent, Rest}} ->
            {<<Mantissa2/binary, Exponent/binary>>, Rest};

        {error, _} ->
            {Mantissa2, After_fraction}
    end.

-file("src/swatch/internal/lexer.gleam", 693).
?DOC(false).
-spec would_start_number(bitstring()) -> boolean().
would_start_number(Input) ->
    case pop_codepoint(Input) of
        {ok, {Char1, Rest}} ->
            case is_digit(Char1) of
                true ->
                    true;

                false ->
                    case Char1 =:= <<"."/utf8>> of
                        true ->
                            case pop_codepoint(Rest) of
                                {ok, {Char2, _}} ->
                                    is_digit(Char2);

                                {error, _} ->
                                    false
                            end;

                        false ->
                            false
                    end
            end;

        {error, _} ->
            false
    end.

-file("src/swatch/internal/lexer.gleam", 859).
?DOC(false).
-spec starts_identifier_sequence(bitstring()) -> boolean().
starts_identifier_sequence(Input) ->
    case pop_codepoint(Input) of
        {ok, {<<"-"/utf8>>, Rest}} ->
            case pop_codepoint(Rest) of
                {ok, {<<"-"/utf8>>, _}} ->
                    true;

                {ok, {<<"\\"/utf8>>, Escape_rest}} ->
                    starts_with_valid_escape(Escape_rest);

                {ok, {Char, _}} ->
                    is_identifier_start(Char);

                {error, _} ->
                    false
            end;

        {ok, {<<"\\"/utf8>>, Rest@1}} ->
            starts_with_valid_escape(Rest@1);

        {ok, {Char@1, _}} ->
            is_identifier_start(Char@1);

        {error, _} ->
            false
    end.

-file("src/swatch/internal/lexer.gleam", 268).
?DOC(false).
-spec int_max(integer(), integer()) -> integer().
int_max(A, B) ->
    case A > B of
        true ->
            A;

        false ->
            B
    end.

-file("src/swatch/internal/lexer.gleam", 146).
?DOC(false).
-spec lex_step(binary(), bitstring(), integer(), list(lex_token())) -> {bitstring(),
    integer(),
    list(lex_token())}.
lex_step(Char, Rest, Bracket_depth, Out) ->
    case Char of
        <<" "/utf8>> ->
            {Whitespace, Rest2} = take_whitespace(Rest, Char),
            {Rest2, Bracket_depth, [{lex_whitespace, Whitespace} | Out]};

        <<"\t"/utf8>> ->
            {Whitespace, Rest2} = take_whitespace(Rest, Char),
            {Rest2, Bracket_depth, [{lex_whitespace, Whitespace} | Out]};

        <<"\n"/utf8>> ->
            {Whitespace, Rest2} = take_whitespace(Rest, Char),
            {Rest2, Bracket_depth, [{lex_whitespace, Whitespace} | Out]};

        <<"\r"/utf8>> ->
            {Whitespace, Rest2} = take_whitespace(Rest, Char),
            {Rest2, Bracket_depth, [{lex_whitespace, Whitespace} | Out]};

        <<"\f"/utf8>> ->
            {Whitespace, Rest2} = take_whitespace(Rest, Char),
            {Rest2, Bracket_depth, [{lex_whitespace, Whitespace} | Out]};

        <<"\r\n"/utf8>> ->
            {Whitespace, Rest2} = take_whitespace(Rest, Char),
            {Rest2, Bracket_depth, [{lex_whitespace, Whitespace} | Out]};

        <<"{"/utf8>> ->
            {Rest, Bracket_depth, [lex_open_brace | Out]};

        <<"}"/utf8>> ->
            {Rest, Bracket_depth, [lex_close_brace | Out]};

        <<"("/utf8>> ->
            {Rest, Bracket_depth, [lex_open_paren | Out]};

        <<")"/utf8>> ->
            {Rest, Bracket_depth, [lex_close_paren | Out]};

        <<"["/utf8>> ->
            {Rest, Bracket_depth + 1, [lex_open_bracket | Out]};

        <<"]"/utf8>> ->
            {Rest, int_max(Bracket_depth - 1, 0), [lex_close_bracket | Out]};

        <<":"/utf8>> ->
            {Rest, Bracket_depth, [lex_colon | Out]};

        <<";"/utf8>> ->
            {Rest, Bracket_depth, [lex_semicolon | Out]};

        <<","/utf8>> ->
            {Rest, Bracket_depth, [lex_comma | Out]};

        <<"@"/utf8>> ->
            case starts_identifier_sequence(Rest) of
                true ->
                    {Name, Rest2@1} = take_identifier(Rest, <<""/utf8>>),
                    {Rest2@1,
                        Bracket_depth,
                        [{lex_at_keyword, <<"@"/utf8, Name/binary>>} | Out]};

                false ->
                    {Rest, Bracket_depth, [{lex_delim, <<"@"/utf8>>} | Out]}
            end;

        <<"#"/utf8>> ->
            {Name@1, Rest2@2} = take_identifier(Rest, <<""/utf8>>),
            case Name@1 of
                <<""/utf8>> ->
                    {Rest, Bracket_depth, [{lex_delim, <<"#"/utf8>>} | Out]};

                _ ->
                    {Rest2@2,
                        Bracket_depth,
                        [{lex_hash, <<"#"/utf8, Name@1/binary>>} | Out]}
            end;

        <<"."/utf8>> ->
            case pop_codepoint(Rest) of
                {ok, {Next, _}} ->
                    case is_digit(Next) of
                        true ->
                            {Num, Rest2@3} = take_number(Rest, <<"."/utf8>>),
                            {Token, Rest3} = lex_number_token(Num, Rest2@3),
                            {Rest3, Bracket_depth, [Token | Out]};

                        false ->
                            {Rest,
                                Bracket_depth,
                                [{lex_delim, <<"."/utf8>>} | Out]}
                    end;

                {error, _} ->
                    {Rest, Bracket_depth, [{lex_delim, <<"."/utf8>>} | Out]}
            end;

        <<"-"/utf8>> ->
            case would_start_number(Rest) of
                true ->
                    {Num@1, Rest2@4} = take_number(Rest, <<"-"/utf8>>),
                    {Token@1, Rest3@1} = lex_number_token(Num@1, Rest2@4),
                    {Rest3@1, Bracket_depth, [Token@1 | Out]};

                false ->
                    case starts_identifier_sequence(Rest) of
                        true ->
                            {Name@2, Rest2@5} = take_identifier(
                                Rest,
                                <<"-"/utf8>>
                            ),
                            lex_ident_or_function(
                                Name@2,
                                Rest2@5,
                                Bracket_depth,
                                Out
                            );

                        false ->
                            {Rest,
                                Bracket_depth,
                                [{lex_delim, <<"-"/utf8>>} | Out]}
                    end
            end;

        <<"+"/utf8>> ->
            case would_start_number(Rest) of
                true ->
                    {Num@2, Rest2@6} = take_number(Rest, <<"+"/utf8>>),
                    {Token@2, Rest3@2} = lex_number_token(Num@2, Rest2@6),
                    {Rest3@2, Bracket_depth, [Token@2 | Out]};

                false ->
                    {Rest, Bracket_depth, [{lex_delim, <<"+"/utf8>>} | Out]}
            end;

        <<"\\"/utf8>> ->
            case starts_with_valid_escape(Rest) of
                true ->
                    {Escape, Rest2@7} = take_identifier_escape(Rest),
                    {Name@3, Rest3@3} = take_identifier(
                        Rest2@7,
                        <<"\\"/utf8, Escape/binary>>
                    ),
                    lex_ident_or_function(Name@3, Rest3@3, Bracket_depth, Out);

                false ->
                    {Rest, Bracket_depth, [{lex_delim, <<"\\"/utf8>>} | Out]}
            end;

        <<"*"/utf8>> ->
            {Rest, Bracket_depth, [{lex_delim, Char} | Out]};

        <<"/"/utf8>> ->
            {Rest, Bracket_depth, [{lex_delim, Char} | Out]};

        <<"<"/utf8>> ->
            {Rest, Bracket_depth, [{lex_delim, Char} | Out]};

        <<">"/utf8>> ->
            {Rest, Bracket_depth, [{lex_delim, Char} | Out]};

        <<"~"/utf8>> ->
            {Rest, Bracket_depth, [{lex_delim, Char} | Out]};

        <<"|"/utf8>> ->
            {Rest, Bracket_depth, [{lex_delim, Char} | Out]};

        <<"&"/utf8>> ->
            {Rest, Bracket_depth, [{lex_delim, Char} | Out]};

        <<"="/utf8>> ->
            {Rest, Bracket_depth, [{lex_delim, Char} | Out]};

        <<"!"/utf8>> ->
            {Rest, Bracket_depth, [{lex_delim, Char} | Out]};

        _ ->
            case is_digit(Char) of
                true ->
                    {Num@3, Rest2@8} = take_number(Rest, Char),
                    {Token@3, Rest3@4} = lex_number_token(Num@3, Rest2@8),
                    {Rest3@4, Bracket_depth, [Token@3 | Out]};

                false ->
                    case is_identifier_start(Char) of
                        true ->
                            case lex_starts_urange(Char, Rest) of
                                true ->
                                    {Urange, Rest2@9} = lex_take_urange(
                                        Char,
                                        Rest
                                    ),
                                    {Rest2@9,
                                        Bracket_depth,
                                        [{lex_urange, Urange} | Out]};

                                false ->
                                    {Name@4, Rest2@10} = take_identifier(
                                        Rest,
                                        Char
                                    ),
                                    lex_ident_or_function(
                                        Name@4,
                                        Rest2@10,
                                        Bracket_depth,
                                        Out
                                    )
                            end;

                        false ->
                            {Rest, Bracket_depth, [{lex_delim, Char} | Out]}
                    end
            end
    end.

-file("src/swatch/internal/lexer.gleam", 563).
?DOC(false).
-spec quote_byte(binary()) -> integer().
quote_byte(Quote) ->
    case Quote of
        <<"\""/utf8>> ->
            16#22;

        _ ->
            16#27
    end.

-file("src/swatch/internal/lexer.gleam", 574).
?DOC(false).
-spec scan_quoted(bitstring(), integer(), boolean()) -> bitstring().
scan_quoted(Input, Quote, Stop_on_newline) ->
    case Input of
        <<16#5C, After/bitstring>> ->
            case pop_codepoint(After) of
                {ok, {_, Rest2}} ->
                    scan_quoted(Rest2, Quote, Stop_on_newline);

                {error, _} ->
                    <<>>
            end;

        <<B, Rest/bitstring>> ->
            case Stop_on_newline andalso (((B =:= 16#0A) orelse (B =:= 16#0D))
            orelse (B =:= 16#0C)) of
                true ->
                    Input;

                false ->
                    case B =:= Quote of
                        true ->
                            Rest;

                        false ->
                            scan_quoted(Rest, Quote, Stop_on_newline)
                    end
            end;

        _ ->
            <<>>
    end.

-file("src/swatch/internal/lexer.gleam", 550).
?DOC(false).
-spec take_string(bitstring(), binary()) -> {binary(), bitstring()}.
take_string(Input, Quote) ->
    Rest = scan_quoted(Input, quote_byte(Quote), true),
    {consumed(Input, Rest), Rest}.

-file("src/swatch/internal/lexer.gleam", 557).
?DOC(false).
-spec take_quoted(bitstring(), binary()) -> {binary(), bitstring()}.
take_quoted(Input, Quote) ->
    Rest = scan_quoted(Input, quote_byte(Quote), false),
    {consumed(Input, Rest), Rest}.

-file("src/swatch/internal/lexer.gleam", 132).
?DOC(false).
-spec lex_string(bitstring(), binary(), integer()) -> {binary(), bitstring()}.
lex_string(Input, Quote, Bracket_depth) ->
    case Bracket_depth > 0 of
        true ->
            take_quoted(Input, Quote);

        false ->
            take_string(Input, Quote)
    end.

-file("src/swatch/internal/lexer.gleam", 538).
?DOC(false).
-spec scan_comment_body(bitstring()) -> bitstring().
scan_comment_body(Input) ->
    case Input of
        <<"*/"/utf8, Rest/bitstring>> ->
            Rest;

        <<_, Rest@1/bitstring>> ->
            scan_comment_body(Rest@1);

        _ ->
            <<>>
    end.

-file("src/swatch/internal/lexer.gleam", 531).
?DOC(false).
-spec take_until(bitstring()) -> {binary(), bitstring()}.
take_until(Input) ->
    Rest = scan_comment_body(Input),
    {consumed(Input, Rest), Rest}.

-file("src/swatch/internal/lexer.gleam", 95).
?DOC(false).
-spec lex_loop(bitstring(), integer(), list(lex_token())) -> list(lex_token()).
lex_loop(Code, Bracket_depth, Out) ->
    case Code of
        <<>> ->
            lists:reverse(Out);

        <<"/*"/utf8, Rest/bitstring>> ->
            {Body, Rest2} = take_until(Rest),
            lex_loop(
                Rest2,
                Bracket_depth,
                [{lex_comment, <<"/*"/utf8, Body/binary>>} | Out]
            );

        <<"<!--"/utf8, Rest@1/bitstring>> ->
            lex_loop(Rest@1, Bracket_depth, [lex_cdo | Out]);

        <<"-->"/utf8, Rest@2/bitstring>> ->
            lex_loop(Rest@2, Bracket_depth, [lex_cdc | Out]);

        <<"\""/utf8, Rest@3/bitstring>> ->
            {Body@1, Rest2@1} = lex_string(Rest@3, <<"\""/utf8>>, Bracket_depth),
            lex_loop(
                Rest2@1,
                Bracket_depth,
                [{lex_string, <<"\""/utf8, Body@1/binary>>} | Out]
            );

        <<"'"/utf8, Rest@4/bitstring>> ->
            {Body@2, Rest2@2} = lex_string(Rest@4, <<"'"/utf8>>, Bracket_depth),
            lex_loop(
                Rest2@2,
                Bracket_depth,
                [{lex_string, <<"'"/utf8, Body@2/binary>>} | Out]
            );

        _ ->
            case pop_codepoint(Code) of
                {error, _} ->
                    lists:reverse(Out);

                {ok, {Char, Rest@5}} ->
                    {Rest2@3, Depth2, Out2} = lex_step(
                        Char,
                        Rest@5,
                        Bracket_depth,
                        Out
                    ),
                    lex_loop(Rest2@3, Depth2, Out2)
            end
    end.

-file("src/swatch/internal/lexer.gleam", 83).
?DOC(false).
-spec lex(binary()) -> list(lex_token()).
lex(Code) ->
    lex_loop(gleam_stdlib:identity(Code), 0, []).

-file("src/swatch/internal/lexer.gleam", 89).
?DOC(false).
-spec lex_to_source(list(lex_token())) -> binary().
lex_to_source(Tokens) ->
    _pipe = Tokens,
    _pipe@1 = gleam@list:map(_pipe, fun lex_token_to_source/1),
    erlang:list_to_binary(_pipe@1).

-file("src/swatch/internal/lexer.gleam", 512).
?DOC(false).
-spec all_hex_digits(bitstring()) -> boolean().
all_hex_digits(Input) ->
    case pop_codepoint(Input) of
        {error, _} ->
            true;

        {ok, {Char, Rest}} ->
            case is_hex_digit(Char) of
                true ->
                    all_hex_digits(Rest);

                false ->
                    false
            end
    end.

-file("src/swatch/internal/lexer.gleam", 504).
?DOC(false).
-spec is_valid_hex_color(binary()) -> boolean().
is_valid_hex_color(Name) ->
    Bytes = gleam_stdlib:identity(Name),
    case erlang:byte_size(Bytes) of
        3 ->
            all_hex_digits(Bytes);

        4 ->
            all_hex_digits(Bytes);

        6 ->
            all_hex_digits(Bytes);

        8 ->
            all_hex_digits(Bytes);

        _ ->
            false
    end.

-file("src/swatch/internal/lexer.gleam", 911).
?DOC(false).
-spec annotate_step(lex_token(), boolean(), list(boolean())) -> {boolean(),
    boolean(),
    list(boolean())}.
annotate_step(Token, Answer, Stack) ->
    case {Token, Stack} of
        {lex_open_paren, [Saved | Rest]} ->
            {Saved, Saved, Rest};

        {lex_open_bracket, [Saved | Rest]} ->
            {Saved, Saved, Rest};

        {lex_open_paren, []} ->
            {false, false, []};

        {lex_open_bracket, []} ->
            {false, false, []};

        {lex_close_paren, _} ->
            {false, false, [Answer | Stack]};

        {lex_close_bracket, _} ->
            {false, false, [Answer | Stack]};

        {lex_open_brace, _} ->
            {true, true, Stack};

        {lex_close_brace, _} ->
            {false, false, Stack};

        {lex_semicolon, _} ->
            {false, false, Stack};

        {_, _} ->
            {Answer, Answer, Stack}
    end.

-file("src/swatch/internal/lexer.gleam", 896).
?DOC(false).
-spec annotate_loop(
    list(lex_token()),
    boolean(),
    list(boolean()),
    list(boolean())
) -> list(boolean()).
annotate_loop(Rev_tokens, Answer, Stack, Out) ->
    case Rev_tokens of
        [] ->
            Out;

        [Token | Rest] ->
            {Flag, Answer2, Stack2} = annotate_step(Token, Answer, Stack),
            annotate_loop(Rest, Answer2, Stack2, [Flag | Out])
    end.

-file("src/swatch/internal/lexer.gleam", 892).
?DOC(false).
-spec annotate_nested(list(lex_token())) -> list(boolean()).
annotate_nested(Tokens) ->
    annotate_loop(lists:reverse(Tokens), false, [], []).