Make Hakuzaru Great Again #22
@@ -22,8 +22,6 @@
|
|||||||
fate_to_erlang/2,
|
fate_to_erlang/2,
|
||||||
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.
|
|
||||||
-export([aaci_from_string/1, annotate_type/2]).
|
|
||||||
|
|
||||||
%%% Types
|
%%% Types
|
||||||
|
|
||||||
|
|||||||
+40
-21
@@ -438,12 +438,33 @@ check_sophia_to_fate(Type, Sophia, Fate) ->
|
|||||||
erlang:error({to_fate_failed, Fate, FateActual})
|
erlang:error({to_fate_failed, Fate, FateActual})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_parser(Sophia, Fate) ->
|
compile_entrypoint_code_and_type(Source, Entrypoint) ->
|
||||||
|
{ok, #{fate_code := FateCode, aci := ACI}} = so_compiler:from_string(Source, [{aci, json}]),
|
||||||
|
|
||||||
|
% Find the fcode for the correct entrypoint.
|
||||||
|
{fcode, Bodies, NamesMap, _} = FateCode,
|
||||||
|
Names = maps:to_list(NamesMap),
|
||||||
|
Name = unicode:characters_to_binary(Entrypoint),
|
||||||
|
{Hash, Name} = lists:keyfind(Name, 2, Names),
|
||||||
|
{_, _, Code} = maps:get(Hash, Bodies),
|
||||||
|
|
||||||
|
% Generate the AACI, and get the AACI type info for the correct entrypoint.
|
||||||
|
AACI = hz_aaci:prepare_aaci(ACI),
|
||||||
|
{ok, {_, Type}} = hz_aaci:get_function_signature(AACI, "f"),
|
||||||
|
|
||||||
|
{Code, Type}.
|
||||||
|
|
||||||
|
extract_return_value(#{0 := [{'RETURNR', {immediate, FATE}}]}) ->
|
||||||
|
FATE;
|
||||||
|
extract_return_value(Code) ->
|
||||||
|
erlang:exit({invalid_literal_fcode, Code}).
|
||||||
|
|
||||||
|
check_parser(Sophia) ->
|
||||||
% Compile the literal using the compiler, to check that it is valid Sophia
|
% Compile the literal using the compiler, to check that it is valid Sophia
|
||||||
% syntax, and to get an AACI object to pass to the parser.
|
% syntax, and to get an AACI object to pass to the parser.
|
||||||
Source = "contract C = entrypoint f() = " ++ Sophia,
|
Source = "contract C = entrypoint f() = " ++ Sophia,
|
||||||
{ok, AACI} = hz_aaci:aaci_from_string(Source),
|
{Code, Type} = compile_entrypoint_code_and_type(Source, "f"),
|
||||||
{ok, {_, Type}} = hz_aaci:get_function_signature(AACI, "f"),
|
Fate = extract_return_value(Code),
|
||||||
|
|
||||||
% Also check that the FATE term is valid, by running it through gmb.
|
% Also check that the FATE term is valid, by running it through gmb.
|
||||||
gmb_fate_encoding:serialize(Fate),
|
gmb_fate_encoding:serialize(Fate),
|
||||||
@@ -453,11 +474,11 @@ check_parser(Sophia, Fate) ->
|
|||||||
% Also check that it can be parsed without type information.
|
% Also check that it can be parsed without type information.
|
||||||
check_sophia_to_fate(unknown_type(), Sophia, Fate).
|
check_sophia_to_fate(unknown_type(), Sophia, Fate).
|
||||||
|
|
||||||
check_parser_with_typedef(Typedef, Sophia, Fate) ->
|
check_parser_with_typedef(Typedef, Sophia) ->
|
||||||
% Compile the type definitions alongside the usual literal expression.
|
% Compile the type definitions alongside the usual literal expression.
|
||||||
Source = "contract C =\n " ++ Typedef ++ "\n entrypoint f() = " ++ Sophia,
|
Source = "contract C =\n " ++ Typedef ++ "\n entrypoint f() = " ++ Sophia,
|
||||||
{ok, AACI} = hz_aaci:aaci_from_string(Source),
|
{Code, Type} = compile_entrypoint_code_and_type(Source, "f"),
|
||||||
{ok, {_, Type}} = hz_aaci:get_function_signature(AACI, "f"),
|
Fate = extract_return_value(Code),
|
||||||
|
|
||||||
% Check the FATE term as usual.
|
% Check the FATE term as usual.
|
||||||
gmb_fate_encoding:serialize(Fate),
|
gmb_fate_encoding:serialize(Fate),
|
||||||
@@ -467,40 +488,38 @@ check_parser_with_typedef(Typedef, Sophia, Fate) ->
|
|||||||
check_sophia_to_fate(Type, Sophia, Fate).
|
check_sophia_to_fate(Type, Sophia, Fate).
|
||||||
|
|
||||||
int_test() ->
|
int_test() ->
|
||||||
check_parser("123", 123).
|
check_parser("123").
|
||||||
|
|
||||||
list_test() ->
|
list_test() ->
|
||||||
check_parser("[1, 2, 3]", [1, 2, 3]).
|
check_parser("[1, 2, 3]").
|
||||||
|
|
||||||
list_of_lists_test() ->
|
list_of_lists_test() ->
|
||||||
check_parser("[[], [1], [2, 3]]", [[], [1], [2, 3]]).
|
check_parser("[[], [1], [2, 3]]").
|
||||||
|
|
||||||
tuple_test() ->
|
tuple_test() ->
|
||||||
check_parser("(1, [2, 3], (4, 5))", {tuple, {1, [2, 3], {tuple, {4, 5}}}}).
|
check_parser("(1, [2, 3], (4, 5))").
|
||||||
|
|
||||||
maps_test() ->
|
maps_test() ->
|
||||||
check_parser("{[1] = 2, [3] = 4}", #{1 => 2, 3 => 4}).
|
check_parser("{[1] = 2, [3] = 4}").
|
||||||
|
|
||||||
records_test() ->
|
records_test() ->
|
||||||
TypeDef = "record pair = {x: int, y: int}",
|
TypeDef = "record pair = {x: int, y: int}",
|
||||||
Sophia = "{x = 1, y = 2}",
|
Sophia = "{x = 1, y = 2}",
|
||||||
Fate = {tuple, {1, 2}},
|
check_parser_with_typedef(TypeDef, Sophia),
|
||||||
check_parser_with_typedef(TypeDef, Sophia, Fate),
|
|
||||||
% The above won't run an untyped parse on the expression, but we can. It
|
% The above won't run an untyped parse on the expression, but we can. It
|
||||||
% will error, though.
|
% will error, though.
|
||||||
{error, {unresolved_record, _, _, _}} = parse_literal(unknown_type(), Sophia).
|
{error, {unresolved_record, _, _, _}} = parse_literal(unknown_type(), Sophia).
|
||||||
|
|
||||||
variant_test() ->
|
variant_test() ->
|
||||||
TypeDef = "datatype multi('a) = Zero | One('a) | Two('a, 'a)",
|
TypeDef = "datatype multi('a) = Zero | One('a) | Two('a, 'a)",
|
||||||
TestFn = fun(Sophia, Fate) ->
|
|
||||||
check_parser_with_typedef(TypeDef, Sophia, Fate),
|
|
||||||
{error, {unresolved_variant, _, _, _}} = parse_literal(unknown_type(), Sophia)
|
|
||||||
end,
|
|
||||||
|
|
||||||
TestFn("Zero", {variant, [0, 1, 2], 0, {}}),
|
check_parser_with_typedef(TypeDef, "Zero"),
|
||||||
TestFn("One(0)", {variant, [0, 1, 2], 1, {0}}),
|
check_parser_with_typedef(TypeDef, "One(0)"),
|
||||||
TestFn("Two(0, 1)", {variant, [0, 1, 2], 2, {0, 1}}),
|
check_parser_with_typedef(TypeDef, "Two(0, 1)"),
|
||||||
TestFn("Two([], [1, 2, 3])", {variant, [0, 1, 2], 2, {[], [1, 2, 3]}}),
|
check_parser_with_typedef(TypeDef, "Two([], [1, 2, 3])"),
|
||||||
|
|
||||||
|
{error, {unresolved_variant, _, _, _}} = parse_literal(unknown_type(), "Zero"),
|
||||||
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user