List parsing

Slowly chipping away at cases...
This commit is contained in:
Jarvis Carroll 2026-01-15 09:38:04 +00:00
parent 97e32574c4
commit 3f1c9bd626
2 changed files with 64 additions and 17 deletions

View File

@ -23,7 +23,7 @@
erlang_args_to_fate/2, erlang_args_to_fate/2,
get_function_signature/2]). get_function_signature/2]).
% Internal stuff that is useful for writing AACI unit tests. % Internal stuff that is useful for writing AACI unit tests.
-export([annotate_type/2]). -export([aaci_from_string/1, annotate_type/2]).
%%% Types %%% Types

View File

@ -41,8 +41,9 @@ next_token(Tk, [N | _] = String) when N >= $a, N =< $z ->
alphanum_token(Tk, Tk, String, []); alphanum_token(Tk, Tk, String, []);
next_token(Tk, [$_ | _] = String) -> next_token(Tk, [$_ | _] = String) ->
alphanum_token(Tk, Tk, String, []); alphanum_token(Tk, Tk, String, []);
next_token({tk, Row, Col}, [Char | _]) -> next_token({tk, Row, Col}, [Char | Rest]) ->
{error, {unknown_char, Row, Col, [Char]}}. Token = {character, [Char], Row, Col, Col},
{ok, {Token, {tk, Row + 1, Col}, Rest}}.
num_token(Start, {tk, Row, Col}, [N | Rest], Acc) when N >= $0, N =< $9 -> num_token(Start, {tk, Row, Col}, [N | Rest], Acc) when N >= $0, N =< $9 ->
num_token(Start, {tk, Row + 1, Col}, Rest, [N | Acc]); num_token(Start, {tk, Row + 1, Col}, Rest, [N | Acc]);
@ -89,19 +90,58 @@ parse_expression(Type, Tk, String) ->
parse_expression2(Type, Tk, String, {integer, S, Row, Start, End}) -> parse_expression2(Type, Tk, String, {integer, S, Row, Start, End}) ->
Value = list_to_integer(S), Value = list_to_integer(S),
check_type(integer, Type, Row, Start, End, {Value, Tk, String}); case Type of
{_, _, integer} ->
{ok, {Value, Tk, String}};
{_, _, unknown_type} ->
{ok, {Value, Tk, String}};
{O, N, _} ->
{error, {wrong_type, O, N, integer, Row, Start, End}}
end;
parse_expression2(Type, Tk, String, {character, "[", Row, Start, _}) ->
parse_list(Type, Tk, String, Row, Start);
parse_expression2(_, _, _, {_, S, Row, Start, End}) -> parse_expression2(_, _, _, {_, S, Row, Start, End}) ->
{error, {unexpected_token, S, Row, Start, End}}. {error, {unexpected_token, S, Row, Start, End}}.
check_type(Expected, {_, _, Expected}, _, _, _, Result) -> parse_list({_, _, {list, [Inner]}}, Tk, String, Row, Start) ->
{ok, Result}; parse_list_loop(Inner, Tk, String, Row, Start, []);
check_type(_, {_, _, unknown_type}, _, _, _, Result) -> parse_list({_, _, unknown_type}, Tk, String, Row, Start) ->
% We want it to be possible to opt out of type-checking, since FATE is parse_list_loop(unknown_type(), Tk, String, Row, Start, []);
% dynamically typed anyway. parse_list({O, N, _}, _, _, Row, Start) ->
{ok, Result}; {error, {wrong_type, O, N, list, Row, Start, Start}}.
check_type(Expected, {O, N, _}, Row, Start, End, _) ->
{error, {wrong_type, O, N, Expected, Row, Start, End}}.
parse_list_loop(Inner, Tk, String, Row, Start, Acc) ->
case next_token(Tk, String) of
{ok, {{character, "]", _, _, _}, NewTk, NewString}} ->
{ok, {lists:reverse(Acc), NewTk, NewString}};
{ok, {Token, NewTk, NewString}} ->
parse_list_loop2(Inner, NewTk, NewString, Row, Start, Acc, Token)
end.
parse_list_loop2(Inner, Tk, String, Row, Start, Acc, Token) ->
case parse_expression2(Inner, Tk, String, Token) of
{ok, {Value, NewTk, NewString}} ->
parse_list_loop3(Inner, NewTk, NewString, Row, Start, [Value | Acc]);
{error, Reason} ->
Wrapped = wrap_error(Reason, {list_element, length(Acc)}),
{error, Wrapped}
end.
parse_list_loop3(Inner, Tk, String, Row, Start, Acc) ->
case next_token(Tk, String) of
{ok, {{character, "]", _, _, _}, NewTk, NewString}} ->
{ok, {lists:reverse(Acc), NewTk, NewString}};
{ok, {{character, ",", _, _, _}, NewTk, NewString}} ->
parse_list_loop(Inner, NewTk, NewString, Row, Start, Acc);
{error, Reason} ->
{error, Reason}
end.
unknown_type() ->
{unknown_type, already_normalized, unknown_type}.
% TODO
wrap_error(Reason, _) -> Reason.
%%% Tests %%% Tests
@ -115,16 +155,23 @@ check_sophia_to_fate(Type, Sophia, Fate) ->
end. end.
check_parser(Type, Sophia, Fate) -> check_parser(Type, Sophia, Fate) ->
UnknownType = setelement(3, Type, unknown_type),
check_sophia_to_fate(Type, Sophia, Fate), check_sophia_to_fate(Type, Sophia, Fate),
check_sophia_to_fate(UnknownType, Sophia, Fate), check_sophia_to_fate(unknown_type(), Sophia, Fate),
% Finally, check that the FATE result is something that gmb understands. % Finally, check that the FATE result is something that gmb understands.
gmb_fate_encoding:serialize(Fate), gmb_fate_encoding:serialize(Fate),
ok. ok.
int_test() -> check_parser(Sophia, Fate) ->
{ok, Type} = hz_aaci:annotate_type(integer, #{}), Source = "contract C = entrypoint f() = " ++ Sophia,
check_parser(Type, "123", 123). {ok, AACI} = hz_aaci:aaci_from_string(Source),
{ok, {_, Type}} = hz_aaci:get_function_signature(AACI, "f"),
check_parser(Type, Sophia, Fate).
int_test() ->
check_parser("123", 123).
list_test() ->
check_parser("[1, 2, 3]", [1, 2, 3]).