Skip to main content

src/svg_path@parse.erl

-module(svg_path@parse).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/svg_path/parse.gleam").
-export([path/1]).
-export_type([error/0, token/0, state/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(
    " SVG path data parser.\n"
    "\n"
    " This module parses the `d` attribute syntax used by SVG paths. It supports\n"
    " comma and whitespace separators, compact signed numbers, relative commands,\n"
    " implicit repeated commands, smooth curves, and arcs.\n"
).

-type error() :: {core, svg_path:error()} |
    expected_arc_flag |
    expected_command |
    expected_move |
    expected_number |
    {invalid_number, binary()} |
    {unsupported_command, binary()}.

-type token() :: {command, binary()} | {number, float()}.

-type state() :: {state,
        list(svg_path:subpath()),
        svg_path:subpath(),
        vec@vec2:vec2(float()),
        boolean(),
        boolean(),
        gleam@option:option(vec@vec2:vec2(float())),
        gleam@option:option(vec@vec2:vec2(float()))}.

-file("src/svg_path/parse.gleam", 67).
-spec initial_state() -> state().
initial_state() ->
    {state,
        [],
        svg_path:empty_subpath(svg_path:point(+0.0, +0.0)),
        svg_path:point(+0.0, +0.0),
        false,
        false,
        none,
        none}.

-file("src/svg_path/parse.gleam", 694).
-spec ensure_active(state()) -> {ok, nil} | {error, error()}.
ensure_active(State) ->
    case erlang:element(6, State) andalso erlang:element(5, State) of
        true ->
            {ok, nil};

        false ->
            {error, expected_move}
    end.

-file("src/svg_path/parse.gleam", 653).
-spec clear_curve_controls(state()) -> state().
clear_curve_controls(State) ->
    {state,
        erlang:element(2, State),
        erlang:element(3, State),
        erlang:element(4, State),
        erlang:element(5, State),
        erlang:element(6, State),
        none,
        none}.

-file("src/svg_path/parse.gleam", 637).
-spec append_segment(state(), svg_path:segment(), vec@vec2:vec2(float())) -> {ok,
        state()} |
    {error, error()}.
append_segment(State, Segment, End) ->
    case svg_path:append_segment(erlang:element(3, State), Segment) of
        {error, Error} ->
            {error, {core, Error}};

        {ok, Subpath} ->
            {ok,
                begin
                    _pipe = {state,
                        erlang:element(2, State),
                        Subpath,
                        End,
                        erlang:element(5, State),
                        true,
                        erlang:element(7, State),
                        erlang:element(8, State)},
                    clear_curve_controls(_pipe)
                end}
    end.

-file("src/svg_path/parse.gleam", 626).
-spec append_line_to(state(), vec@vec2:vec2(float())) -> {ok, state()} |
    {error, error()}.
append_line_to(State, Target) ->
    append_segment(State, {line, erlang:element(4, State), Target}, Target).

-file("src/svg_path/parse.gleam", 713).
-spec offset(vec@vec2:vec2(float()), float(), float()) -> vec@vec2:vec2(float()).
offset(Point, X, Y) ->
    svg_path:point(erlang:element(2, Point) + X, erlang:element(3, Point) + Y).

-file("src/svg_path/parse.gleam", 701).
-spec target_point(state(), float(), float(), boolean()) -> vec@vec2:vec2(float()).
target_point(State, X, Y, Relative) ->
    case Relative of
        true ->
            offset(erlang:element(4, State), X, Y);

        false ->
            svg_path:point(X, Y)
    end.

-file("src/svg_path/parse.gleam", 815).
-spec arc_flag(float()) -> {ok, boolean()} | {error, error()}.
arc_flag(Value) ->
    case Value of
        +0.0 ->
            {ok, false};

        1.0 ->
            {ok, true};

        _ ->
            {error, expected_arc_flag}
    end.

-file("src/svg_path/parse.gleam", 773).
-spec take_arc(list(token())) -> {ok,
        {float(),
            float(),
            float(),
            boolean(),
            boolean(),
            float(),
            float(),
            list(token())}} |
    {error, error()}.
take_arc(Tokens) ->
    case Tokens of
        [{number, Radius_x},
            {number, Radius_y},
            {number, X_axis_rotation},
            {number, Large_arc},
            {number, Sweep},
            {number, End_x},
            {number, End_y} |
            Rest] ->
            case arc_flag(Large_arc) of
                {error, Error} ->
                    {error, Error};

                {ok, Large_arc@1} ->
                    case arc_flag(Sweep) of
                        {error, Error@1} ->
                            {error, Error@1};

                        {ok, Sweep@1} ->
                            {ok,
                                {Radius_x,
                                    Radius_y,
                                    X_axis_rotation,
                                    Large_arc@1,
                                    Sweep@1,
                                    End_x,
                                    End_y,
                                    Rest}}
                    end
            end;

        _ ->
            {error, expected_number}
    end.

-file("src/svg_path/parse.gleam", 657).
-spec remember_cubic_control(state(), vec@vec2:vec2(float())) -> state().
remember_cubic_control(State, Control) ->
    {state,
        erlang:element(2, State),
        erlang:element(3, State),
        erlang:element(4, State),
        erlang:element(5, State),
        erlang:element(6, State),
        {some, Control},
        none}.

-file("src/svg_path/parse.gleam", 687).
-spec reflect(vec@vec2:vec2(float()), vec@vec2:vec2(float())) -> vec@vec2:vec2(float()).
reflect(Point, Origin) ->
    svg_path:point(
        (erlang:element(2, Origin) * 2.0) - erlang:element(2, Point),
        (erlang:element(3, Origin) * 2.0) - erlang:element(3, Point)
    ).

-file("src/svg_path/parse.gleam", 673).
-spec reflected_cubic_control(state()) -> vec@vec2:vec2(float()).
reflected_cubic_control(State) ->
    case erlang:element(7, State) of
        {some, Control} ->
            reflect(Control, erlang:element(4, State));

        none ->
            erlang:element(4, State)
    end.

-file("src/svg_path/parse.gleam", 756).
-spec take_smooth_cubic_bezier(list(token())) -> {ok,
        {float(), float(), float(), float(), list(token())}} |
    {error, error()}.
take_smooth_cubic_bezier(Tokens) ->
    case Tokens of
        [{number, Control2_x},
            {number, Control2_y},
            {number, End_x},
            {number, End_y} |
            Rest] ->
            {ok, {Control2_x, Control2_y, End_x, End_y, Rest}};

        _ ->
            {error, expected_number}
    end.

-file("src/svg_path/parse.gleam", 737).
-spec take_cubic_bezier(list(token())) -> {ok,
        {float(), float(), float(), float(), float(), float(), list(token())}} |
    {error, error()}.
take_cubic_bezier(Tokens) ->
    case Tokens of
        [{number, Control1_x},
            {number, Control1_y},
            {number, Control2_x},
            {number, Control2_y},
            {number, End_x},
            {number, End_y} |
            Rest] ->
            {ok,
                {Control1_x,
                    Control1_y,
                    Control2_x,
                    Control2_y,
                    End_x,
                    End_y,
                    Rest}};

        _ ->
            {error, expected_number}
    end.

-file("src/svg_path/parse.gleam", 665).
-spec remember_quadratic_control(state(), vec@vec2:vec2(float())) -> state().
remember_quadratic_control(State, Control) ->
    {state,
        erlang:element(2, State),
        erlang:element(3, State),
        erlang:element(4, State),
        erlang:element(5, State),
        erlang:element(6, State),
        none,
        {some, Control}}.

-file("src/svg_path/parse.gleam", 680).
-spec reflected_quadratic_control(state()) -> vec@vec2:vec2(float()).
reflected_quadratic_control(State) ->
    case erlang:element(8, State) of
        {some, Control} ->
            reflect(Control, erlang:element(4, State));

        none ->
            erlang:element(4, State)
    end.

-file("src/svg_path/parse.gleam", 717).
-spec take_pair(list(token())) -> {ok, {float(), float(), list(token())}} |
    {error, error()}.
take_pair(Tokens) ->
    case Tokens of
        [{number, X}, {number, Y} | Rest] ->
            {ok, {X, Y, Rest}};

        _ ->
            {error, expected_number}
    end.

-file("src/svg_path/parse.gleam", 726).
-spec take_quadratic_bezier(list(token())) -> {ok,
        {float(), float(), float(), float(), list(token())}} |
    {error, error()}.
take_quadratic_bezier(Tokens) ->
    case Tokens of
        [{number, Control_x},
            {number, Control_y},
            {number, End_x},
            {number, End_y} |
            Rest] ->
            {ok, {Control_x, Control_y, End_x, End_y, Rest}};

        _ ->
            {error, expected_number}
    end.

-file("src/svg_path/parse.gleam", 611).
-spec finish_active_subpath(state()) -> {ok, state()} | {error, error()}.
finish_active_subpath(State) ->
    case erlang:element(6, State) of
        false ->
            {ok, State};

        true ->
            {ok,
                {state,
                    [erlang:element(3, State) | erlang:element(2, State)],
                    svg_path:empty_subpath(erlang:element(4, State)),
                    erlang:element(4, State),
                    erlang:element(5, State),
                    false,
                    erlang:element(7, State),
                    erlang:element(8, State)}}
    end.

-file("src/svg_path/parse.gleam", 604).
-spec finish(state()) -> {ok, svg_path:path()} | {error, error()}.
finish(State) ->
    case finish_active_subpath(State) of
        {error, Error} ->
            {error, Error};

        {ok, State@1} ->
            {ok, {path, lists:reverse(erlang:element(2, State@1))}}
    end.

-file("src/svg_path/parse.gleam", 569).
-spec parse_close(list(token()), state()) -> {ok, svg_path:path()} |
    {error, error()}.
parse_close(Tokens, State) ->
    case ensure_active(State) of
        {error, Error} ->
            {error, Error};

        {ok, nil} ->
            Start@1 = case svg_path:start(erlang:element(3, State)) of
                {ok, Start} -> Start;
                _assert_fail ->
                    erlang:error(#{gleam_error => let_assert,
                                message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                file => <<?FILEPATH/utf8>>,
                                module => <<"svg_path/parse"/utf8>>,
                                function => <<"parse_close"/utf8>>,
                                line => 576,
                                value => _assert_fail,
                                start => 15261,
                                'end' => 15313,
                                pattern_start => 15272,
                                pattern_end => 15281})
            end,
            case svg_path:set_closed_with(
                erlang:element(3, State),
                true,
                bridge
            ) of
                {error, Error@1} ->
                    {error, {core, Error@1}};

                {ok, Subpath} ->
                    parse_tokens(
                        Tokens,
                        {state,
                            [Subpath | erlang:element(2, State)],
                            svg_path:empty_subpath(Start@1),
                            Start@1,
                            true,
                            false,
                            none,
                            none}
                    )
            end
    end.

-file("src/svg_path/parse.gleam", 263).
-spec parse_vertical_loop(list(token()), state(), boolean(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_vertical_loop(Tokens, State, Relative, Parsed_any) ->
    case Tokens of
        [{number, Y} | Rest] ->
            Target = case Relative of
                true ->
                    offset(erlang:element(4, State), +0.0, Y);

                false ->
                    svg_path:point(
                        erlang:element(2, erlang:element(4, State)),
                        Y
                    )
            end,
            case append_line_to(State, Target) of
                {error, Error} ->
                    {error, Error};

                {ok, State@1} ->
                    parse_vertical_loop(Rest, State@1, Relative, true)
            end;

        _ ->
            case Parsed_any of
                true ->
                    parse_tokens(Tokens, State);

                false ->
                    {error, expected_number}
            end
    end.

-file("src/svg_path/parse.gleam", 252).
-spec parse_vertical(list(token()), state(), boolean()) -> {ok, svg_path:path()} |
    {error, error()}.
parse_vertical(Tokens, State, Relative) ->
    case ensure_active(State) of
        {error, Error} ->
            {error, Error};

        {ok, nil} ->
            parse_vertical_loop(Tokens, State, Relative, false)
    end.

-file("src/svg_path/parse.gleam", 224).
-spec parse_horizontal_loop(list(token()), state(), boolean(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_horizontal_loop(Tokens, State, Relative, Parsed_any) ->
    case Tokens of
        [{number, X} | Rest] ->
            Target = case Relative of
                true ->
                    offset(erlang:element(4, State), X, +0.0);

                false ->
                    svg_path:point(
                        X,
                        erlang:element(3, erlang:element(4, State))
                    )
            end,
            case append_line_to(State, Target) of
                {error, Error} ->
                    {error, Error};

                {ok, State@1} ->
                    parse_horizontal_loop(Rest, State@1, Relative, true)
            end;

        _ ->
            case Parsed_any of
                true ->
                    parse_tokens(Tokens, State);

                false ->
                    {error, expected_number}
            end
    end.

-file("src/svg_path/parse.gleam", 213).
-spec parse_horizontal(list(token()), state(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_horizontal(Tokens, State, Relative) ->
    case ensure_active(State) of
        {error, Error} ->
            {error, Error};

        {ok, nil} ->
            parse_horizontal_loop(Tokens, State, Relative, false)
    end.

-file("src/svg_path/parse.gleam", 522).
-spec parse_arc_loop(list(token()), state(), boolean(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_arc_loop(Tokens, State, Relative, Parsed_any) ->
    case Tokens of
        [{number, _} | _] ->
            case take_arc(Tokens) of
                {error, Error} ->
                    {error, Error};

                {ok,
                    {Radius_x,
                        Radius_y,
                        X_axis_rotation,
                        Large_arc,
                        Sweep,
                        End_x,
                        End_y,
                        Rest}} ->
                    End = target_point(State, End_x, End_y, Relative),
                    Segment = {arc,
                        erlang:element(4, State),
                        svg_path:point(Radius_x, Radius_y),
                        X_axis_rotation,
                        Large_arc,
                        Sweep,
                        End},
                    case append_segment(State, Segment, End) of
                        {error, Error@1} ->
                            {error, Error@1};

                        {ok, State@1} ->
                            parse_arc_loop(Rest, State@1, Relative, true)
                    end
            end;

        _ ->
            case Parsed_any of
                true ->
                    parse_tokens(Tokens, State);

                false ->
                    {error, expected_number}
            end
    end.

-file("src/svg_path/parse.gleam", 511).
-spec parse_arc(list(token()), state(), boolean()) -> {ok, svg_path:path()} |
    {error, error()}.
parse_arc(Tokens, State, Relative) ->
    case ensure_active(State) of
        {error, Error} ->
            {error, Error};

        {ok, nil} ->
            parse_arc_loop(Tokens, State, Relative, false)
    end.

-file("src/svg_path/parse.gleam", 465).
-spec parse_smooth_cubic_bezier_loop(
    list(token()),
    state(),
    boolean(),
    boolean()
) -> {ok, svg_path:path()} | {error, error()}.
parse_smooth_cubic_bezier_loop(Tokens, State, Relative, Parsed_any) ->
    case Tokens of
        [{number, _} | _] ->
            case take_smooth_cubic_bezier(Tokens) of
                {error, Error} ->
                    {error, Error};

                {ok, {Control2_x, Control2_y, End_x, End_y, Rest}} ->
                    Control1 = reflected_cubic_control(State),
                    Control2 = target_point(
                        State,
                        Control2_x,
                        Control2_y,
                        Relative
                    ),
                    End = target_point(State, End_x, End_y, Relative),
                    Segment = {cubic_bezier,
                        erlang:element(4, State),
                        Control1,
                        Control2,
                        End},
                    case append_segment(State, Segment, End) of
                        {error, Error@1} ->
                            {error, Error@1};

                        {ok, State@1} ->
                            State@2 = remember_cubic_control(State@1, Control2),
                            parse_smooth_cubic_bezier_loop(
                                Rest,
                                State@2,
                                Relative,
                                true
                            )
                    end
            end;

        _ ->
            case Parsed_any of
                true ->
                    parse_tokens(Tokens, State);

                false ->
                    {error, expected_number}
            end
    end.

-file("src/svg_path/parse.gleam", 453).
-spec parse_smooth_cubic_bezier(list(token()), state(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_smooth_cubic_bezier(Tokens, State, Relative) ->
    case ensure_active(State) of
        {error, Error} ->
            {error, Error};

        {ok, nil} ->
            parse_smooth_cubic_bezier_loop(Tokens, State, Relative, false)
    end.

-file("src/svg_path/parse.gleam", 412).
-spec parse_cubic_bezier_loop(list(token()), state(), boolean(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_cubic_bezier_loop(Tokens, State, Relative, Parsed_any) ->
    case Tokens of
        [{number, _} | _] ->
            case take_cubic_bezier(Tokens) of
                {error, Error} ->
                    {error, Error};

                {ok,
                    {Control1_x,
                        Control1_y,
                        Control2_x,
                        Control2_y,
                        End_x,
                        End_y,
                        Rest}} ->
                    Control1 = target_point(
                        State,
                        Control1_x,
                        Control1_y,
                        Relative
                    ),
                    Control2 = target_point(
                        State,
                        Control2_x,
                        Control2_y,
                        Relative
                    ),
                    End = target_point(State, End_x, End_y, Relative),
                    Segment = {cubic_bezier,
                        erlang:element(4, State),
                        Control1,
                        Control2,
                        End},
                    case append_segment(State, Segment, End) of
                        {error, Error@1} ->
                            {error, Error@1};

                        {ok, State@1} ->
                            State@2 = remember_cubic_control(State@1, Control2),
                            parse_cubic_bezier_loop(
                                Rest,
                                State@2,
                                Relative,
                                true
                            )
                    end
            end;

        _ ->
            case Parsed_any of
                true ->
                    parse_tokens(Tokens, State);

                false ->
                    {error, expected_number}
            end
    end.

-file("src/svg_path/parse.gleam", 400).
-spec parse_cubic_bezier(list(token()), state(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_cubic_bezier(Tokens, State, Relative) ->
    case ensure_active(State) of
        {error, Error} ->
            {error, Error};

        {ok, nil} ->
            parse_cubic_bezier_loop(Tokens, State, Relative, false)
    end.

-file("src/svg_path/parse.gleam", 360).
-spec parse_smooth_quadratic_bezier_loop(
    list(token()),
    state(),
    boolean(),
    boolean()
) -> {ok, svg_path:path()} | {error, error()}.
parse_smooth_quadratic_bezier_loop(Tokens, State, Relative, Parsed_any) ->
    case Tokens of
        [{number, _} | _] ->
            case take_pair(Tokens) of
                {error, Error} ->
                    {error, Error};

                {ok, {End_x, End_y, Rest}} ->
                    Control = reflected_quadratic_control(State),
                    End = target_point(State, End_x, End_y, Relative),
                    Segment = {quadratic_bezier,
                        erlang:element(4, State),
                        Control,
                        End},
                    case append_segment(State, Segment, End) of
                        {error, Error@1} ->
                            {error, Error@1};

                        {ok, State@1} ->
                            State@2 = remember_quadratic_control(
                                State@1,
                                Control
                            ),
                            parse_smooth_quadratic_bezier_loop(
                                Rest,
                                State@2,
                                Relative,
                                true
                            )
                    end
            end;

        _ ->
            case Parsed_any of
                true ->
                    parse_tokens(Tokens, State);

                false ->
                    {error, expected_number}
            end
    end.

-file("src/svg_path/parse.gleam", 343).
-spec parse_smooth_quadratic_bezier(list(token()), state(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_smooth_quadratic_bezier(Tokens, State, Relative) ->
    case ensure_active(State) of
        {error, Error} ->
            {error, Error};

        {ok, nil} ->
            parse_smooth_quadratic_bezier_loop(Tokens, State, Relative, false)
    end.

-file("src/svg_path/parse.gleam", 303).
-spec parse_quadratic_bezier_loop(list(token()), state(), boolean(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_quadratic_bezier_loop(Tokens, State, Relative, Parsed_any) ->
    case Tokens of
        [{number, _} | _] ->
            case take_quadratic_bezier(Tokens) of
                {error, Error} ->
                    {error, Error};

                {ok, {Control_x, Control_y, End_x, End_y, Rest}} ->
                    Control = target_point(
                        State,
                        Control_x,
                        Control_y,
                        Relative
                    ),
                    End = target_point(State, End_x, End_y, Relative),
                    Segment = {quadratic_bezier,
                        erlang:element(4, State),
                        Control,
                        End},
                    case append_segment(State, Segment, End) of
                        {error, Error@1} ->
                            {error, Error@1};

                        {ok, State@1} ->
                            State@2 = remember_quadratic_control(
                                State@1,
                                Control
                            ),
                            parse_quadratic_bezier_loop(
                                Rest,
                                State@2,
                                Relative,
                                true
                            )
                    end
            end;

        _ ->
            case Parsed_any of
                true ->
                    parse_tokens(Tokens, State);

                false ->
                    {error, expected_number}
            end
    end.

-file("src/svg_path/parse.gleam", 291).
-spec parse_quadratic_bezier(list(token()), state(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_quadratic_bezier(Tokens, State, Relative) ->
    case ensure_active(State) of
        {error, Error} ->
            {error, Error};

        {ok, nil} ->
            parse_quadratic_bezier_loop(Tokens, State, Relative, false)
    end.

-file("src/svg_path/parse.gleam", 185).
-spec parse_line_loop(list(token()), state(), boolean(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_line_loop(Tokens, State, Relative, Parsed_any) ->
    case Tokens of
        [{number, _} | _] ->
            case take_pair(Tokens) of
                {error, Error} ->
                    {error, Error};

                {ok, {X, Y, Rest}} ->
                    case append_line_to(
                        State,
                        target_point(State, X, Y, Relative)
                    ) of
                        {error, Error@1} ->
                            {error, Error@1};

                        {ok, State@1} ->
                            parse_line_loop(Rest, State@1, Relative, true)
                    end
            end;

        _ ->
            case Parsed_any of
                true ->
                    parse_tokens(Tokens, State);

                false ->
                    {error, expected_number}
            end
    end.

-file("src/svg_path/parse.gleam", 174).
-spec parse_line(list(token()), state(), boolean()) -> {ok, svg_path:path()} |
    {error, error()}.
parse_line(Tokens, State, Relative) ->
    case ensure_active(State) of
        {error, Error} ->
            {error, Error};

        {ok, nil} ->
            parse_line_loop(Tokens, State, Relative, false)
    end.

-file("src/svg_path/parse.gleam", 153).
-spec parse_implicit_lines(list(token()), state(), boolean()) -> {ok,
        svg_path:path()} |
    {error, error()}.
parse_implicit_lines(Tokens, State, Relative) ->
    case Tokens of
        [{number, _} | _] ->
            case take_pair(Tokens) of
                {error, Error} ->
                    {error, Error};

                {ok, {X, Y, Rest}} ->
                    case append_line_to(
                        State,
                        target_point(State, X, Y, Relative)
                    ) of
                        {error, Error@1} ->
                            {error, Error@1};

                        {ok, State@1} ->
                            parse_implicit_lines(Rest, State@1, Relative)
                    end
            end;

        _ ->
            parse_tokens(Tokens, State)
    end.

-file("src/svg_path/parse.gleam", 119).
-spec parse_move(list(token()), state(), boolean()) -> {ok, svg_path:path()} |
    {error, error()}.
parse_move(Tokens, State, Relative) ->
    case take_pair(Tokens) of
        {error, Error} ->
            {error, Error};

        {ok, {X, Y, Rest}} ->
            case finish_active_subpath(State) of
                {error, Error@1} ->
                    {error, Error@1};

                {ok, State@1} ->
                    Base = case Relative andalso erlang:element(5, State@1) of
                        true ->
                            erlang:element(4, State@1);

                        false ->
                            svg_path:point(+0.0, +0.0)
                    end,
                    Target = offset(Base, X, Y),
                    State@2 = {state,
                        erlang:element(2, State@1),
                        svg_path:empty_subpath(Target),
                        Target,
                        true,
                        true,
                        none,
                        none},
                    parse_implicit_lines(Rest, State@2, Relative)
            end
    end.

-file("src/svg_path/parse.gleam", 90).
-spec parse_command(binary(), list(token()), state()) -> {ok, svg_path:path()} |
    {error, error()}.
parse_command(Command, Tokens, State) ->
    case Command of
        <<"M"/utf8>> ->
            parse_move(Tokens, State, false);

        <<"m"/utf8>> ->
            parse_move(Tokens, State, true);

        <<"L"/utf8>> ->
            parse_line(Tokens, State, false);

        <<"l"/utf8>> ->
            parse_line(Tokens, State, true);

        <<"Q"/utf8>> ->
            parse_quadratic_bezier(Tokens, State, false);

        <<"q"/utf8>> ->
            parse_quadratic_bezier(Tokens, State, true);

        <<"T"/utf8>> ->
            parse_smooth_quadratic_bezier(Tokens, State, false);

        <<"t"/utf8>> ->
            parse_smooth_quadratic_bezier(Tokens, State, true);

        <<"C"/utf8>> ->
            parse_cubic_bezier(Tokens, State, false);

        <<"c"/utf8>> ->
            parse_cubic_bezier(Tokens, State, true);

        <<"S"/utf8>> ->
            parse_smooth_cubic_bezier(Tokens, State, false);

        <<"s"/utf8>> ->
            parse_smooth_cubic_bezier(Tokens, State, true);

        <<"A"/utf8>> ->
            parse_arc(Tokens, State, false);

        <<"a"/utf8>> ->
            parse_arc(Tokens, State, true);

        <<"H"/utf8>> ->
            parse_horizontal(Tokens, State, false);

        <<"h"/utf8>> ->
            parse_horizontal(Tokens, State, true);

        <<"V"/utf8>> ->
            parse_vertical(Tokens, State, false);

        <<"v"/utf8>> ->
            parse_vertical(Tokens, State, true);

        <<"Z"/utf8>> ->
            parse_close(Tokens, State);

        <<"z"/utf8>> ->
            parse_close(Tokens, State);

        _ ->
            {error, {unsupported_command, Command}}
    end.

-file("src/svg_path/parse.gleam", 79).
-spec parse_tokens(list(token()), state()) -> {ok, svg_path:path()} |
    {error, error()}.
parse_tokens(Tokens, State) ->
    case Tokens of
        [] ->
            finish(State);

        [{command, Command} | Rest] ->
            parse_command(Command, Rest, State);

        [{number, _} | _] ->
            {error, expected_command}
    end.

-file("src/svg_path/parse.gleam", 988).
-spec strip_leading_plus(binary()) -> binary().
strip_leading_plus(Raw) ->
    case gleam_stdlib:string_starts_with(Raw, <<"+"/utf8>>) of
        true ->
            gleam@string:drop_start(Raw, 1);

        false ->
            Raw
    end.

-file("src/svg_path/parse.gleam", 959).
-spec parse_decimal_number(binary()) -> {ok, float()} | {error, nil}.
parse_decimal_number(Raw) ->
    Raw@1 = strip_leading_plus(Raw),
    case gleam_stdlib:parse_float(Raw@1) of
        {ok, Number} ->
            {ok, Number};

        {error, _} ->
            case gleam_stdlib:parse_int(Raw@1) of
                {ok, Number@1} ->
                    {ok, erlang:float(Number@1)};

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

-file("src/svg_path/parse.gleam", 995).
-spec power_of_ten(integer()) -> float().
power_of_ten(Exponent) ->
    case Exponent of
        0 ->
            1.0;

        _ when Exponent > 0 ->
            10.0 * power_of_ten(Exponent - 1);

        _ ->
            power_of_ten(Exponent + 1) / 10.0
    end.

-file("src/svg_path/parse.gleam", 973).
-spec parse_exponent_number(binary(), binary()) -> {ok, float()} | {error, nil}.
parse_exponent_number(Mantissa, Exponent) ->
    case parse_decimal_number(Mantissa) of
        {error, _} ->
            {error, nil};

        {ok, Mantissa@1} ->
            case begin
                _pipe = Exponent,
                _pipe@1 = strip_leading_plus(_pipe),
                gleam_stdlib:parse_int(_pipe@1)
            end of
                {error, _} ->
                    {error, nil};

                {ok, Exponent@1} ->
                    {ok, Mantissa@1 * power_of_ten(Exponent@1)}
            end
    end.

-file("src/svg_path/parse.gleam", 947).
-spec parse_number(binary()) -> {ok, float()} | {error, nil}.
parse_number(Raw) ->
    case gleam@string:split_once(Raw, <<"e"/utf8>>) of
        {ok, {Mantissa, Exponent}} ->
            parse_exponent_number(Mantissa, Exponent);

        {error, _} ->
            case gleam@string:split_once(Raw, <<"E"/utf8>>) of
                {ok, {Mantissa@1, Exponent@1}} ->
                    parse_exponent_number(Mantissa@1, Exponent@1);

                {error, _} ->
                    parse_decimal_number(Raw)
            end
    end.

-file("src/svg_path/parse.gleam", 943).
-spec is_digit(binary()) -> boolean().
is_digit(Grapheme) ->
    gleam@list:contains(
        [<<"0"/utf8>>,
            <<"1"/utf8>>,
            <<"2"/utf8>>,
            <<"3"/utf8>>,
            <<"4"/utf8>>,
            <<"5"/utf8>>,
            <<"6"/utf8>>,
            <<"7"/utf8>>,
            <<"8"/utf8>>,
            <<"9"/utf8>>],
        Grapheme
    ).

-file("src/svg_path/parse.gleam", 863).
-spec read_number(list(binary()), list(binary()), boolean()) -> {binary(),
    list(binary())}.
read_number(Graphemes, Number, Previous_was_exponent) ->
    case Graphemes of
        [] ->
            {gleam@string:join(lists:reverse(Number), <<""/utf8>>), []};

        [Grapheme | Rest] ->
            case is_digit(Grapheme) orelse (Grapheme =:= <<"."/utf8>>) of
                true ->
                    read_number(Rest, [Grapheme | Number], false);

                false ->
                    case (Grapheme =:= <<"e"/utf8>>) orelse (Grapheme =:= <<"E"/utf8>>) of
                        true ->
                            read_number(Rest, [Grapheme | Number], true);

                        false ->
                            case (Previous_was_exponent orelse gleam@list:is_empty(
                                Number
                            ))
                            andalso ((Grapheme =:= <<"+"/utf8>>) orelse (Grapheme
                            =:= <<"-"/utf8>>)) of
                                true ->
                                    read_number(
                                        Rest,
                                        [Grapheme | Number],
                                        false
                                    );

                                false ->
                                    {gleam@string:join(
                                            lists:reverse(Number),
                                            <<""/utf8>>
                                        ),
                                        Graphemes}
                            end
                    end
            end
    end.

-file("src/svg_path/parse.gleam", 939).
-spec is_number_start(binary()) -> boolean().
is_number_start(Grapheme) ->
    ((is_digit(Grapheme) orelse (Grapheme =:= <<"+"/utf8>>)) orelse (Grapheme
    =:= <<"-"/utf8>>))
    orelse (Grapheme =:= <<"."/utf8>>).

-file("src/svg_path/parse.gleam", 911).
-spec is_command(binary()) -> boolean().
is_command(Grapheme) ->
    gleam@list:contains(
        [<<"M"/utf8>>,
            <<"m"/utf8>>,
            <<"L"/utf8>>,
            <<"l"/utf8>>,
            <<"Q"/utf8>>,
            <<"q"/utf8>>,
            <<"T"/utf8>>,
            <<"t"/utf8>>,
            <<"C"/utf8>>,
            <<"c"/utf8>>,
            <<"S"/utf8>>,
            <<"s"/utf8>>,
            <<"A"/utf8>>,
            <<"a"/utf8>>,
            <<"H"/utf8>>,
            <<"h"/utf8>>,
            <<"V"/utf8>>,
            <<"v"/utf8>>,
            <<"Z"/utf8>>,
            <<"z"/utf8>>],
        Grapheme
    ).

-file("src/svg_path/parse.gleam", 903).
-spec is_separator(binary()) -> boolean().
is_separator(Grapheme) ->
    ((((Grapheme =:= <<" "/utf8>>) orelse (Grapheme =:= <<"\n"/utf8>>)) orelse (Grapheme
    =:= <<"\r"/utf8>>))
    orelse (Grapheme =:= <<"\t"/utf8>>))
    orelse (Grapheme =:= <<","/utf8>>).

-file("src/svg_path/parse.gleam", 829).
-spec tokenize_loop(list(binary()), list(token())) -> {ok, list(token())} |
    {error, error()}.
tokenize_loop(Graphemes, Tokens) ->
    case Graphemes of
        [] ->
            {ok, lists:reverse(Tokens)};

        [Grapheme | Rest] ->
            case is_separator(Grapheme) of
                true ->
                    tokenize_loop(Rest, Tokens);

                false ->
                    case is_command(Grapheme) of
                        true ->
                            tokenize_loop(Rest, [{command, Grapheme} | Tokens]);

                        false ->
                            case is_number_start(Grapheme) of
                                true ->
                                    {Raw, Rest@1} = read_number(
                                        Graphemes,
                                        [],
                                        false
                                    ),
                                    case parse_number(Raw) of
                                        {ok, Number} ->
                                            tokenize_loop(
                                                Rest@1,
                                                [{number, Number} | Tokens]
                                            );

                                        {error, _} ->
                                            {error, {invalid_number, Raw}}
                                    end;

                                false ->
                                    {error, {unsupported_command, Grapheme}}
                            end
                    end
            end
    end.

-file("src/svg_path/parse.gleam", 823).
-spec tokenize(binary()) -> {ok, list(token())} | {error, error()}.
tokenize(Input) ->
    _pipe = Input,
    _pipe@1 = gleam@string:to_graphemes(_pipe),
    tokenize_loop(_pipe@1, []).

-file("src/svg_path/parse.gleam", 60).
?DOC(
    " Parse an SVG path data string into a `Path`.\n"
    "\n"
    " Empty strings parse as an empty path. Move-only subpaths are preserved as\n"
    " empty subpaths with start points. Closepath commands mark subpaths as\n"
    " closed, inserting a straight line back to the subpath start when needed.\n"
).
-spec path(binary()) -> {ok, svg_path:path()} | {error, error()}.
path(Input) ->
    case tokenize(Input) of
        {error, Error} ->
            {error, Error};

        {ok, Tokens} ->
            parse_tokens(Tokens, initial_state())
    end.