-module(ramble_old). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([get_config/0, go_on_tokens/3, position/0, any_token/0, either/2, choice/1, many/1, many1/1, get_errors/1, seq/2, do_and_recover/2, map/2, 'try'/2, optional/1, 'not'/1, lazy/1, extract_and_recover/3, extract/2, all/1, return/1, sep1/2, sep/2, until/2, recover/0, satisfy/1, 'end'/0, fail/0, peek/1, label/2, token/1, string/1, go/3, lowercase_letter/0, uppercase_letter/0, letter/0, digit/0, alphanum/0, many_as_string/1, many1_as_string/1, digits/0, desperate_custom_parser/1, main/0, whitespace/0, whitespace1/0]). -export_type([position/0, token/1, parse_error/1, parse_result/2, final_parse_result/2, parser_config/1, parser/2]). -type position() :: {position, integer(), integer()}. -type token(GLK) :: {token, GLK} | {labeled_token, binary()}. -type parse_error(GLL) :: {unexpected, position(), token(GLL), list(token(GLL))}. -type parse_result(GLM, GLN) :: {parse_result, {ok, GLN} | {error, parse_error(GLM)}, list(GLM), position(), list(parse_error(GLM))}. -type final_parse_result(GLO, GLP) :: {final_parse_result, {ok, GLP} | {error, parse_error(GLO)}, list(parse_error(GLO))}. -type parser_config(GLQ) :: {parser_config, GLQ, GLQ, binary(), fun((GLQ) -> binary())}. -type parser(GLR, GLS) :: {parser, fun((list(GLR), position(), parser_config(GLR), gleam@option:option(parser(GLR, GLR))) -> parse_result(GLR, GLS))}. -spec get_config() -> parser(GLT, parser_config(GLT)). get_config() -> {parser, fun(Source, Pos, Config, _) -> {parse_result, {ok, Config}, Source, Pos, []} end}. -spec run_on_tokens( parser(GLX, GLY), list(GLX), position(), parser_config(GLX), gleam@option:option(parser(GLX, GLX)) ) -> parse_result(GLX, GLY). run_on_tokens(P, Src, Pos, Config, Recovery) -> (erlang:element(2, P))(Src, Pos, Config, Recovery). -spec go_on_tokens(parser(GMI, GMJ), list(GMI), parser_config(GMI)) -> final_parse_result(GMI, GMJ). go_on_tokens(P, Src, Config) -> Res = run_on_tokens(P, Src, {position, 1, 1}, Config, none), {final_parse_result, erlang:element(2, Res), erlang:element(5, Res)}. -spec position() -> parser(any(), position()). position() -> {parser, fun(Source, P, _, _) -> {parse_result, {ok, P}, Source, P, []} end}. -spec any_token() -> parser(GNF, GNF). any_token() -> {parser, fun(Source, Pos, Config, _) -> {position, Row, Col} = case Pos of {position, _, _} -> Pos; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"ramble_old"/utf8>>, function => <<"any_token"/utf8>>, line => 151}) end, case Source of [H | T] -> New_pos = case H =:= erlang:element(2, Config) of true -> {position, Row + 1, 0}; false -> {position, Row, Col + 1} end, {parse_result, {ok, H}, T, New_pos, []}; [] -> Err = {unexpected, Pos, {token, erlang:element(3, Config)}, []}, {parse_result, {error, Err}, Source, Pos, []} end end}. -spec either(parser(GNV, GNW), parser(GNV, GNW)) -> parser(GNV, GNW). either(P, Q) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(P, Source, Pos, Config, Recovery), case erlang:element(2, Res) of {ok, _} -> Res; {error, E1} -> Res2 = run_on_tokens(Q, Source, Pos, Config, Recovery), case erlang:element(2, Res2) of {ok, _} -> Res2; {error, E2} -> Err = {unexpected, Pos, erlang:element(3, E2), gleam@list:append( erlang:element(4, E1), erlang:element(4, E2) )}, {parse_result, {error, Err}, Source, Pos, []} end end end}. -spec choice_helper(list(parser(GOK, GOL)), list(token(GOK))) -> parser(GOK, GOL). choice_helper(Ps, Expected) -> {parser, fun(Source, Pos, Config, Recovery) -> case Ps of [] -> erlang:error(#{gleam_error => panic, message => <<"`ramble.choice()` doesn't accept an empty list of parsers"/utf8>>, module => <<"ramble_old"/utf8>>, function => <<"choice_helper"/utf8>>, line => 248}); [P] -> Res = run_on_tokens(P, Source, Pos, Config, Recovery), case erlang:element(2, Res) of {ok, _} -> Res; {error, E} -> Err = {unexpected, erlang:element(2, E), erlang:element(3, E), gleam@list:reverse( gleam@list:append( erlang:element(4, E), Expected ) )}, {parse_result, {error, Err}, Source, Pos, []} end; [P@1 | T] -> Res@1 = run_on_tokens(P@1, Source, Pos, Config, Recovery), case erlang:element(2, Res@1) of {ok, V} -> {parse_result, {ok, V}, Source, Pos, []}; {error, E@1} -> run_on_tokens( choice_helper( T, gleam@list:append( erlang:element(4, E@1), Expected ) ), Source, Pos, Config, Recovery ) end end end}. -spec choice(list(parser(GOD, GOE))) -> parser(GOD, GOE). choice(Ps) -> choice_helper(Ps, []). -spec many(parser(GOZ, GPA)) -> parser(GOZ, list(GPA)). many(P) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(P, Source, Pos, Config, Recovery), case erlang:element(2, Res) of {error, _} -> {parse_result, {ok, []}, Source, Pos, []}; {ok, V} -> Res2 = run_on_tokens( many(P), erlang:element(3, Res), erlang:element(4, Res), Config, Recovery ), case erlang:element(2, Res2) of {error, _} -> {parse_result, {ok, []}, Source, Pos, []}; {ok, Vs} -> {parse_result, {ok, [V | Vs]}, erlang:element(3, Res2), erlang:element(4, Res2), gleam@list:append( erlang:element(5, Res), erlang:element(5, Res2) )} end end end}. -spec many1(parser(GPL, GPM)) -> parser(GPL, list(GPM)). many1(P) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(P, Source, Pos, Config, Recovery), case erlang:element(2, Res) of {error, E} -> {parse_result, {error, E}, Source, Pos, erlang:element(5, Res)}; {ok, V} -> Res2 = run_on_tokens( many(P), erlang:element(3, Res), erlang:element(4, Res), Config, Recovery ), case erlang:element(2, Res2) of {error, _} -> {parse_result, {ok, [V]}, erlang:element(3, Res), erlang:element(4, Res), erlang:element(5, Res)}; {ok, Vs} -> {parse_result, {ok, [V | Vs]}, erlang:element(3, Res2), erlang:element(4, Res2), gleam@list:append( erlang:element(5, Res), erlang:element(5, Res2) )} end end end}. -spec get_errors(parse_result(GQG, any())) -> list(parse_error(GQG)). get_errors(Res) -> case erlang:element(2, Res) of {ok, _} -> erlang:element(5, Res); {error, E} -> [E | erlang:element(5, Res)] end. -spec seq(parser(GPX, any()), parser(GPX, GQB)) -> parser(GPX, GQB). seq(P, Q) -> {parser, fun(Source, Pos, Config, Recovery) -> Res1 = run_on_tokens(P, Source, Pos, Config, Recovery), Res1_errors = get_errors(Res1), Res2 = run_on_tokens( Q, erlang:element(3, Res1), erlang:element(4, Res1), Config, Recovery ), {parse_result, erlang:element(2, Res2), erlang:element(3, Res2), erlang:element(4, Res2), gleam@list:append(Res1_errors, erlang:element(5, Res2))} end}. -spec do_and_recover(parser(GQM, GQN), parser(GQM, GQM)) -> parser(GQM, GQN). do_and_recover(P, R) -> {parser, fun(Source, Pos, Config, _) -> run_on_tokens(P, Source, Pos, Config, {some, R}) end}. -spec map(parser(GRO, GRP), fun((GRP) -> GRS)) -> parser(GRO, GRS). map(P, F) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(P, Source, Pos, Config, Recovery), case erlang:element(2, Res) of {ok, V} -> {parse_result, {ok, F(V)}, erlang:element(3, Res), erlang:element(4, Res), erlang:element(5, Res)}; {error, E} -> {parse_result, {error, E}, Source, Pos, erlang:element(5, Res)} end end}. -spec 'try'( parser(GRV, GRW), fun((GRW) -> {ok, GRW} | {error, parse_error(GRV)}) ) -> parser(GRV, GRW). 'try'(P, F) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(P, Source, Pos, Config, Recovery), case erlang:element(2, Res) of {ok, V} -> case F(V) of {ok, A} -> {parse_result, {ok, A}, erlang:element(3, Res), erlang:element(4, Res), erlang:element(5, Res)}; {error, E} -> {parse_result, {error, E}, Source, Pos, []} end; {error, E@1} -> {parse_result, {error, E@1}, Source, Pos, erlang:element(5, Res)} end end}. -spec optional(parser(GSE, GSF)) -> parser(GSE, {ok, GSF} | {error, nil}). optional(P) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(P, Source, Pos, Config, Recovery), case erlang:element(2, Res) of {ok, V} -> {parse_result, {ok, {ok, V}}, erlang:element(3, Res), erlang:element(4, Res), []}; {error, _} -> {parse_result, {ok, {error, nil}}, Source, Pos, []} end end}. -spec 'not'(parser(GSV, any())) -> parser(GSV, nil). 'not'(P) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(P, Source, Pos, Config, Recovery), case erlang:element(2, Res) of {ok, _} -> case Source of [H | _] -> Err = {unexpected, Pos, {token, H}, []}, {parse_result, {error, Err}, Source, Pos, []}; _ -> Err@1 = {unexpected, Pos, {token, erlang:element(3, Config)}, []}, {parse_result, {error, Err@1}, Source, Pos, []} end; {error, _} -> {parse_result, {ok, nil}, Source, Pos, []} end end}. -spec lazy(fun(() -> parser(GTE, GTF))) -> parser(GTE, GTF). lazy(P) -> {parser, fun(Source, Pos, Config, Recovery) -> run_on_tokens(P(), Source, Pos, Config, Recovery) end}. -spec extract_and_recover( parser(GTT, GTU), parser(GTT, GTT), fun((GTU) -> parser(GTT, GTZ)) ) -> parser(GTT, GTZ). extract_and_recover(P, R, F) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(P, Source, Pos, Config, {some, R}), case erlang:element(2, Res) of {ok, V} -> run_on_tokens( F(V), erlang:element(3, Res), erlang:element(4, Res), Config, Recovery ); {error, E} -> {parse_result, {error, E}, Source, Pos, erlang:element(5, Res)} end end}. -spec extract(parser(GTK, GTL), fun((GTL) -> parser(GTK, GTO))) -> parser(GTK, GTO). extract(P, F) -> {parser, fun(Source, Pos, Config, Recovery) -> Recovery_parser = case Recovery of {some, R} -> R; none -> any_token() end, run_on_tokens( extract_and_recover(P, Recovery_parser, F), Source, Pos, Config, Recovery ) end}. -spec all(list(parser(GSM, GSN))) -> parser(GSM, GSN). all(Ps) -> case Ps of [P] -> P; [H | T] -> extract(H, fun(_) -> all(T) end); _ -> erlang:error(#{gleam_error => panic, message => <<"`ramble.all()` doesn't accept an empty list of parsers"/utf8>>, module => <<"ramble_old"/utf8>>, function => <<"all"/utf8>>, line => 526}) end. -spec return(GUE) -> parser(any(), GUE). return(X) -> {parser, fun(Source, Pos, _, _) -> {parse_result, {ok, X}, Source, Pos, []} end}. -spec sep1(parser(GRE, GRF), parser(GRE, any())) -> parser(GRE, list(GRF)). sep1(Parser, S) -> extract( Parser, fun(First) -> extract( many(seq(S, Parser)), fun(Rest) -> return([First | Rest]) end ) end ). -spec sep(parser(GQU, GQV), parser(GQU, any())) -> parser(GQU, list(GQV)). sep(Parser, S) -> extract(optional(sep1(Parser, S)), fun(Res) -> case Res of {ok, Sequence} -> return(Sequence); {error, nil} -> return([]) end end). -spec until(parser(GUM, GUN), parser(GUM, GUM)) -> parser(GUM, list(GUN)). until(P, Terminator) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(Terminator, Source, Pos, Config, Recovery), case erlang:element(2, Res) of {ok, _} -> {parse_result, {ok, []}, Source, Pos, []}; {error, _} -> Parser = (extract( position(), fun(Pos1) -> extract_and_recover( P, Terminator, fun(First) -> extract( position(), fun(Pos2) -> gleam@io:debug({1, Pos1, Pos2}), extract( until(P, Terminator), fun(Rest) -> return([First | Rest]) end ) end ) end ) end )), run_on_tokens(Parser, Source, Pos, Config, Recovery) end end}. -spec recover() -> parser(any(), nil). recover() -> {parser, fun(Source, Pos, Config, Recovery) -> P = case Recovery of {some, R} -> _pipe = until(any_token(), R), map(_pipe, fun(_) -> nil end); none -> _pipe@1 = any_token(), map(_pipe@1, fun(_) -> nil end) end, Res = run_on_tokens(P, Source, Pos, Config, none), {parse_result, {ok, nil}, erlang:element(3, Res), erlang:element(4, Res), []} end}. -spec satisfy(fun((GNC) -> boolean())) -> parser(GNC, GNC). satisfy(Pred) -> {parser, fun(Source, Pos, Config, Recovery) -> {position, Row, Col} = case Pos of {position, _, _} -> Pos; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"ramble_old"/utf8>>, function => <<"satisfy"/utf8>>, line => 118}) end, case Source of [H | T] -> case Pred(H) of true -> New_pos = case H =:= erlang:element(2, Config) of true -> {position, Row + 1, 0}; false -> {position, Row, Col + 1} end, {parse_result, {ok, H}, T, New_pos, []}; false -> Err = {unexpected, Pos, {token, H}, []}, Res = run_on_tokens( recover(), Source, Pos, Config, Recovery ), gleam@io:debug( {Pos, erlang:element(4, Res), Recovery} ), {parse_result, {error, Err}, erlang:element(3, Res), erlang:element(4, Res), []} end; [] -> Err@1 = {unexpected, Pos, {token, erlang:element(3, Config)}, []}, {parse_result, {error, Err@1}, Source, Pos, []} end end}. -spec 'end'() -> parser(any(), nil). 'end'() -> {parser, fun(Source, Pos, Config, Recovery) -> case Source of [] -> {parse_result, {ok, nil}, [], Pos, []}; [H | _] -> Err = {unexpected, Pos, {token, H}, [{token, erlang:element(3, Config)}]}, Res = run_on_tokens( recover(), Source, Pos, Config, Recovery ), {parse_result, {error, Err}, erlang:element(3, Res), erlang:element(4, Res), [Err]} end end}. -spec fail() -> parser(any(), any()). fail() -> {parser, fun(Source, Pos, Config, Recovery) -> Unexpected_token = case Source of [] -> erlang:element(3, Config); [H | _] -> H end, Err = {unexpected, Pos, {token, Unexpected_token}, []}, Res = run_on_tokens(recover(), Source, Pos, Config, Recovery), {parse_result, {error, Err}, erlang:element(3, Res), erlang:element(4, Res), [Err]} end}. -spec peek(parser(GUV, any())) -> parser(GUV, boolean()). peek(P) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(P, Source, Pos, Config, Recovery), case erlang:element(2, Res) of {ok, _} -> {parse_result, {ok, true}, Source, Pos, []}; {error, _} -> {parse_result, {ok, false}, Source, Pos, []} end end}. -spec label(binary(), fun(() -> parser(GVB, GVC))) -> parser(GVB, GVC). label(Label, P) -> {parser, fun(Source, Pos, Config, Recovery) -> Res = run_on_tokens(P(), Source, Pos, Config, Recovery), case erlang:element(2, Res) of {ok, _} -> Res; {error, E} -> E@1 = case E of {unexpected, Pos@1, T, _} -> {unexpected, Pos@1, T, [{labeled_token, Label}]} end, {parse_result, {error, E@1}, Source, Pos, erlang:element(5, Res)} end end}. -spec token(GNO) -> parser(GNO, GNO). token(C) -> extract( get_config(), fun(Config) -> label( <<<<"a '"/utf8, ((erlang:element(5, Config))(C))/binary>>/binary, "'"/utf8>>, fun() -> satisfy(fun(C2) -> C =:= C2 end) end ) end ). -spec string(binary()) -> parser(binary(), binary()). string(S) -> case gleam@string:pop_grapheme(S) of {ok, {H, T}} -> extract( token(H), fun(C) -> extract( string(T), fun(Rest) -> return(<>) end ) end ); {error, _} -> return(<<""/utf8>>) end. -spec go(parser(binary(), GMQ), binary(), parser_config(binary())) -> final_parse_result(binary(), GMQ). go(P, Src, Config) -> go_on_tokens(P, gleam@string:to_graphemes(Src), Config). -spec lowercase_letter() -> parser(binary(), binary()). lowercase_letter() -> label( <<"a lowercase letter"/utf8>>, fun() -> satisfy( fun(_capture) -> gleam_stdlib:contains_string( <<"abcdefghijklmnopqrstuvwxyz"/utf8>>, _capture ) end ) end ). -spec uppercase_letter() -> parser(binary(), binary()). uppercase_letter() -> label( <<"an uppercase letter"/utf8>>, fun() -> satisfy( fun(_capture) -> gleam_stdlib:contains_string( <<"ABCDEFGHIJKLMNOPQRSTUVWXYZ"/utf8>>, _capture ) end ) end ). -spec letter() -> parser(binary(), binary()). letter() -> label( <<"a letter"/utf8>>, fun() -> satisfy( fun(_capture) -> gleam_stdlib:contains_string( <<"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"/utf8>>, _capture ) end ) end ). -spec digit() -> parser(binary(), binary()). digit() -> label( <<"a digit"/utf8>>, fun() -> satisfy( fun(_capture) -> gleam_stdlib:contains_string( <<"0123456789"/utf8>>, _capture ) end ) end ). -spec alphanum() -> parser(binary(), binary()). alphanum() -> label( <<"an alphanumeric character"/utf8>>, fun() -> satisfy( fun(_capture) -> gleam_stdlib:contains_string( <<"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"/utf8>>, _capture ) end ) end ). -spec many_as_string(parser(GPG, binary())) -> parser(GPG, binary()). many_as_string(P) -> _pipe = many(P), map(_pipe, fun gleam@string:concat/1). -spec many1_as_string(parser(GPS, binary())) -> parser(GPS, binary()). many1_as_string(P) -> _pipe = many1(P), map(_pipe, fun gleam@string:concat/1). -spec digits() -> parser(binary(), binary()). digits() -> many1_as_string(digit()). -spec desperate_custom_parser( fun((list(GVH), position(), parser_config(GVH), gleam@option:option(parser(GVH, GVH))) -> parse_result(GVH, GVN)) ) -> parser(GVH, GVN). desperate_custom_parser(Parse) -> {parser, Parse}. -spec pretty_expectations(binary(), list(binary())) -> binary(). pretty_expectations(Head, Tail) -> case Tail of [] -> Head; [X] -> <<<>/binary, X/binary>>; [X@1, Y] -> <<<<<<<>/binary, X@1/binary>>/binary, ", or "/utf8>>/binary, Y/binary>>; [X@2 | Rest] -> <<<>/binary, (pretty_expectations(X@2, Rest))/binary>> end. -spec pretty_err(parse_error(GVT), fun((GVT) -> binary()), binary()) -> binary(). pretty_err(Err, Show_token, Source_name) -> Show = fun(Token) -> case Token of {token, T} -> Show_token(T); {labeled_token, S} -> S end end, case gleam@list:map(erlang:element(4, Err), Show) of [] -> <<<<<<<<<<<<<<<<"I didn't expect the `"/utf8, (Show(erlang:element(3, Err)))/binary>>/binary, "` at position "/utf8>>/binary, (gleam@int:to_string( erlang:element( 2, erlang:element(2, Err) ) ))/binary>>/binary, ":"/utf8>>/binary, (gleam@int:to_string( erlang:element(3, erlang:element(2, Err)) ))/binary>>/binary, " in "/utf8>>/binary, Source_name/binary>>/binary, "."/utf8>>; [Hd | Tl] -> <<<<<<<<<<<<<<<<<<<<"I didn't expect the `"/utf8, (Show( erlang:element(3, Err) ))/binary>>/binary, "` at position "/utf8>>/binary, (gleam@int:to_string( erlang:element( 2, erlang:element(2, Err) ) ))/binary>>/binary, ":"/utf8>>/binary, (gleam@int:to_string( erlang:element( 3, erlang:element(2, Err) ) ))/binary>>/binary, " in "/utf8>>/binary, Source_name/binary>>/binary, ". I expected "/utf8>>/binary, (pretty_expectations(Hd, Tl))/binary>>/binary, "."/utf8>> end. -spec main() -> nil. main() -> Config = {parser_config, <<"\n"/utf8>>, <<"EOF"/utf8>>, <<"DEBUG"/utf8>>, fun(S) -> S end}, {final_parse_result, V, Errs} = go( seq( do_and_recover(token(<<"c"/utf8>>), token(<<";"/utf8>>)), token(<<"a"/utf8>>) ), <<"ab;c"/utf8>>, Config ), gleam@io:debug({V, Errs}), Errs@1 = case V of {error, E} -> [E | Errs]; _ -> Errs end, gleam@list:each( Errs@1, fun(It) -> gleam@io:println( pretty_err( It, erlang:element(5, Config), erlang:element(4, Config) ) ) end ). -spec whitespace() -> parser(binary(), binary()). whitespace() -> label( <<"whitespace"/utf8>>, fun() -> many_as_string( satisfy( fun(_capture) -> gleam_stdlib:contains_string(<<" \t\n"/utf8>>, _capture) end ) ) end ). -spec whitespace1() -> parser(binary(), binary()). whitespace1() -> label( <<"whitespace"/utf8>>, fun() -> many1_as_string( satisfy( fun(_capture) -> gleam_stdlib:contains_string(<<" \t\n"/utf8>>, _capture) end ) ) end ).