Skip to main content

src/shlex.erl

-module(shlex).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/shlex.gleam").
-export([split/1]).
-export_type([lex_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.

-type lex_error() :: unclosed_quotation | no_escaped_character.

-file("src/shlex.gleam", 136).
?DOC(" Merge the character buffer into a token and push it onto the accumulator\n").
-spec push_buffer(list(binary()), list(binary())) -> list(binary()).
push_buffer(Buf, Acc) ->
    case Buf of
        [] ->
            Acc;

        _ ->
            [begin
                    _pipe = Buf,
                    _pipe@1 = lists:reverse(_pipe),
                    gleam@string:join(_pipe@1, <<""/utf8>>)
                end |
                Acc]
    end.

-file("src/shlex.gleam", 108).
-spec double_quote(list(binary()), list(binary()), list(binary())) -> {ok,
        list(binary())} |
    {error, lex_error()}.
double_quote(Input, Acc, Buf) ->
    case Input of
        [] ->
            {error, unclosed_quotation};

        [<<"\\"/utf8>>, C | Rest] ->
            case C of
                <<"\n"/utf8>> ->
                    double_quote(Rest, Acc, Buf);

                <<"$"/utf8>> ->
                    double_quote(Rest, Acc, [C | Buf]);

                <<"`"/utf8>> ->
                    double_quote(Rest, Acc, [C | Buf]);

                <<"\""/utf8>> ->
                    double_quote(Rest, Acc, [C | Buf]);

                <<"\\"/utf8>> ->
                    double_quote(Rest, Acc, [C | Buf]);

                _ ->
                    double_quote(Rest, Acc, [<<"\\"/utf8, C/binary>> | Buf])
            end;

        [<<"\""/utf8>> | Rest@1] ->
            word(Rest@1, Acc, Buf);

        [Hd | Rest@2] ->
            double_quote(Rest@2, Acc, [Hd | Buf])
    end.

-file("src/shlex.gleam", 92).
-spec single_quote(list(binary()), list(binary()), list(binary())) -> {ok,
        list(binary())} |
    {error, lex_error()}.
single_quote(Input, Acc, Buf) ->
    case Input of
        [] ->
            {error, unclosed_quotation};

        [<<"'"/utf8>> | Rest] ->
            word(Rest, Acc, Buf);

        [Hd | Rest@1] ->
            single_quote(Rest@1, Acc, [Hd | Buf])
    end.

-file("src/shlex.gleam", 63).
-spec word(list(binary()), list(binary()), list(binary())) -> {ok,
        list(binary())} |
    {error, lex_error()}.
word(Input, Acc, Buf) ->
    case Input of
        [] ->
            continue([], push_buffer(Buf, Acc));

        [<<"\\"/utf8>>] ->
            {error, no_escaped_character};

        [<<"\\"/utf8>>, <<"\n"/utf8>> | Rest] ->
            word(Rest, Acc, Buf);

        [<<"\\"/utf8>>, Next | Rest@1] ->
            word(Rest@1, Acc, [Next | Buf]);

        [<<"'"/utf8>> | Rest@2] ->
            single_quote(Rest@2, Acc, Buf);

        [<<"\""/utf8>> | Rest@3] ->
            double_quote(Rest@3, Acc, Buf);

        [<<" "/utf8>> | Rest@4] ->
            continue(Rest@4, push_buffer(Buf, Acc));

        [<<"\t"/utf8>> | Rest@4] ->
            continue(Rest@4, push_buffer(Buf, Acc));

        [<<"\n"/utf8>> | Rest@4] ->
            continue(Rest@4, push_buffer(Buf, Acc));

        [Hd | Rest@5] ->
            word(Rest@5, Acc, [Hd | Buf])
    end.

-file("src/shlex.gleam", 52).
-spec comment(list(binary()), list(binary())) -> {ok, list(binary())} |
    {error, lex_error()}.
comment(Input, Acc) ->
    case Input of
        [] ->
            continue([], Acc);

        [<<"\n"/utf8>> | Rest] ->
            continue(Rest, Acc);

        [_ | Rest@1] ->
            comment(Rest@1, Acc)
    end.

-file("src/shlex.gleam", 38).
-spec continue(list(binary()), list(binary())) -> {ok, list(binary())} |
    {error, lex_error()}.
continue(Input, Acc) ->
    case Input of
        [] ->
            _pipe = Acc,
            _pipe@1 = lists:reverse(_pipe),
            {ok, _pipe@1};

        [<<" "/utf8>> | Rest] ->
            continue(Rest, Acc);

        [<<"\t"/utf8>> | Rest] ->
            continue(Rest, Acc);

        [<<"\n"/utf8>> | Rest] ->
            continue(Rest, Acc);

        [<<"#"/utf8>> | Rest@1] ->
            comment(Rest@1, Acc);

        _ ->
            word(Input, Acc, [])
    end.

-file("src/shlex.gleam", 34).
?DOC(
    " Split a shell input into a list of string tokens.\n"
    " \n"
    " This aims to follow the POSIX standard defined by IEEE Std 1003.1-2024.\n"
    " \n"
    " ## Examples\n"
    " \n"
    " ```gleam\n"
    " let assert Ok(tokens) = split(\"git commit -m 'hello world!'\")\n"
    " assert tokens == [\"git\", \"commit\", \"-m\", \"hello worlds!\"]\n"
    " ```\n"
).
-spec split(binary()) -> {ok, list(binary())} | {error, lex_error()}.
split(Input) ->
    _pipe = Input,
    _pipe@1 = gleam@string:to_graphemes(_pipe),
    continue(_pipe@1, []).