sophia/test/aeso_parser_tests.erl
Gaith Hallak e46226a693
Polymorphism support (#357)
* Add polymorphism to syntax tree and parser

* Add polymorphism to infer types

* Fix pretty printing

* Add new tests and fix old tests

* Fix the comparison between unit and empty tuple

* Report undefined interface errors before checking implemented interfaces

* Add test for implementing multiple interfaces

* Add test for implementing two interfaces with entrypoints of same names and different types

* Add tests for interfaces implementing interfaces

* Draft: Add variance switching

* Revert "Draft: Add variance switching"

This reverts commit 92dc6ac169cfbff447ed59de04994f564876b3fb.

* Add variance switching

* Fix broken tests

* Fix broken abi tests

* Add tests for variance switching

* Fix tests after rebase

* Variance switching for custom datatypes

* Fix dialyzer warning

* Add testing for custom types variance switching

* Make opposite_variance a separate function

* Make is_subtype/4 a separate function

* Fix warning

* Mark tvars as invariant

* Add type_vars_uvar ets table to ets_tables()

* Don't destroy and recreate type errors table when not needed

* Fixes from the reviews

* Use is_list to check if a var is a list

* Compare named args in fun_t

* Test only for covariance and contravariance

* Remove arrows_in_type and use infer_type_vars_variance instead

* Add tests for option and type aliases

* Fix previous commit

* Rename check_implemented_interfaces_recursive to check_implemented_interfaces1

* Make interfaces declare functions from extended interfaces

* Restore test.aes

* Add test for variance switching in records

* Enable variance switching for record types

* Handle builtin types type variables separately

* Add tests for oracles and oracle queries

* Replace compare_types with non-throwing version of unify

* Add the context to unification error

* Test variance switching for bivariant records

* Give clear names to the records in records variance switching test

* Handle comments about polymorphism_variance_switching.aes

* Rename datatypes in custom types variance switching test for readability

* Change the variance of the oracle_query type vars

* Add test for accessing maps with the wrong type

* Default to invariant when the variance of the type vars is unknown

* Rename test files to have common prefix

* Rename functions in variance switching tests for readability

* Fix variance inference

* Eliminate redundant tests

* Test all cases for bivariant
2022-06-17 13:09:07 +04:00

117 lines
4.4 KiB
Erlang

-module(aeso_parser_tests).
-export([parse_contract/1]).
-include_lib("eunit/include/eunit.hrl").
id(X) -> X.
simple_contracts_test_() ->
{foreach,
fun() -> ok end,
fun(_) -> ok end,
[{"Parse a contract with an identity function.",
fun() ->
Text = "main contract Identity =\n"
" function id(x) = x\n",
?assertMatch(
[{contract_main, _, {con, _, "Identity"}, _,
[{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"},
[{guarded, _, [], {id, _, "x"}}]}]}], parse_string(Text)),
ok
end},
{"Operator precedence test.",
fun() ->
NoPar = fun NoPar(X) when is_atom(X) -> atom_to_list(X);
NoPar({A, Op, B}) -> lists:concat([NoPar(A), " ", Op, " ", NoPar(B)]);
NoPar({Op, A}) -> lists:concat([Op, " ", NoPar(A)])
end,
Par = fun Par(X) when is_atom(X) -> atom_to_list(X);
Par({A, Op, B}) -> lists:concat(["(", Par(A), " ", Op, " ", Par(B), ")"]);
Par({Op, A}) -> lists:concat(["(", Op, " ", Par(A), ")"])
end,
Parse = fun(S) ->
try remove_line_numbers(parse_expr(S))
catch _:_ -> ?assertMatch(ok, id({parse_fail, S})) end
end,
CheckParens = fun(Expr) ->
?assertEqual(Parse(NoPar(Expr)), Parse(Par(Expr)))
end,
LeftAssoc = fun(Op) -> CheckParens({{a, Op, b}, Op, c}) end,
RightAssoc = fun(Op) -> CheckParens({a, Op, {b, Op, c}}) end,
NonAssoc = fun(Op) ->
?assertThrow({error, [_]},
parse_expr(NoPar({a, Op, {b, Op, c}}))) end,
Stronger = fun(Op1, Op2) ->
CheckParens({{a, Op1, b}, Op2, c}),
CheckParens({a, Op2, {b, Op1, c}})
end,
Tiers = [["||"], ["&&"], ["==", "!=", "<", ">", "=<", ">="], ["::", "++"],
["+", "-"], ["*", "/", "mod"]],
%% associativity
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod"] ],
%% precedence
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
ok
end}
] ++
%% Parse tests of example contracts
[ {lists:concat(["Parse the ", Contract, " contract."]),
fun() -> roundtrip_contract(Contract) end}
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof,
multi_sig, simple_storage, fundme, dutch_auction, utf8] ]
}.
parse_contract(Name) ->
parse_string(aeso_test_utils:read_contract(Name)).
roundtrip_contract(Name) ->
round_trip(aeso_test_utils:read_contract(Name)).
parse_string(Text) -> parse_string(Text, []).
parse_string(Text, Opts) ->
aeso_parser:string(Text, Opts).
parse_expr(Text) ->
[{letval, _, _, Expr}] =
parse_string("let _ = " ++ Text),
Expr.
round_trip(Text) ->
Contract = parse_string(Text),
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))),
Contract1 = parse_string(aeso_scan:utf8_encode(Text1)),
NoSrcLoc = remove_line_numbers(Contract),
NoSrcLoc1 = remove_line_numbers(Contract1),
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
strip_stdlib([{namespace, _, {con, _, "ListInternal"}, _} | Decls]) ->
strip_stdlib(Decls);
strip_stdlib(Decls) -> Decls.
remove_line_numbers({line, _L}) -> {line, 0};
remove_line_numbers({col, _C}) -> {col, 0};
remove_line_numbers([H|T]) ->
[remove_line_numbers(H) | remove_line_numbers(T)];
remove_line_numbers(T) when is_tuple(T) ->
list_to_tuple(remove_line_numbers(tuple_to_list(T)));
remove_line_numbers(M) when is_map(M) ->
maps:from_list(remove_line_numbers(maps:to_list(M)));
remove_line_numbers(X) -> X.
diff(X, X) -> X;
diff([H | T], [H1 | T1]) ->
[diff(H, H1) | diff(T, T1)];
diff(T, T1) when tuple_size(T) == tuple_size(T1) ->
list_to_tuple(diff(tuple_to_list(T), tuple_to_list(T1)));
diff(X, Y) -> {X, '/=', Y}.
tails(Zs) -> lists:foldr(fun(X, [Xs|Xss]) -> [[X|Xs], Xs | Xss] end, [[]], Zs).