Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a14602f36 | |||
| e2ef95d6fd | |||
| 22aaeceba8 | |||
| f1d95484a5 | |||
| 7e65f26211 | |||
| 0b83422189 | |||
| 1a5017ce2b | |||
| 25fa365c29 |
@@ -975,12 +975,20 @@ List.unzip(l : list('a * 'b)) : list('a) * list('b)
|
||||
Opposite to the `zip` operation. Takes a list of pairs and returns pair of lists with respective elements on same indices.
|
||||
|
||||
|
||||
### merge
|
||||
```
|
||||
List.merge(lesser_cmp : ('a, 'a) => bool, l1 : list('a), l2 : list('a)) : list('a)
|
||||
```
|
||||
|
||||
Merges two sorted lists into a single sorted list. O(length(l1) + length(l2))
|
||||
|
||||
|
||||
### sort
|
||||
```
|
||||
List.sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a)
|
||||
```
|
||||
|
||||
Sorts a list using given comparator. `lesser_cmp(x, y)` should return `true` iff `x < y`. If `lesser_cmp` is not transitive or there exists an element `x` such that `lesser_cmp(x, x)` or there exists a pair of elements `x` and `y` such that `lesser_cmp(x, y) && lesser_cmp(y, x)` then the result is undefined. Currently O(n^2).
|
||||
Sorts a list using given comparator. `lesser_cmp(x, y)` should return `true` iff `x < y`. If `lesser_cmp` is not transitive or there exists an element `x` such that `lesser_cmp(x, x)` or there exists a pair of elements `x` and `y` such that `lesser_cmp(x, y) && lesser_cmp(y, x)` then the result is undefined. O(length(l) * log_2(length(l))).
|
||||
|
||||
|
||||
### intersperse
|
||||
|
||||
+61
-5
@@ -227,11 +227,67 @@ namespace List =
|
||||
(left, right)::t => unzip_(t, left::acc_l, right::acc_r)
|
||||
|
||||
|
||||
// TODO: Improve?
|
||||
function sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a) = switch(l)
|
||||
[] => []
|
||||
h::t => switch (partition((x) => lesser_cmp(x, h), t))
|
||||
(lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger)
|
||||
/** Merges two sorted lists using `lt` comparator
|
||||
*/
|
||||
function
|
||||
merge : (('a, 'a) => bool, list('a), list('a)) => list('a)
|
||||
merge(lt, x::xs, y::ys) =
|
||||
if(lt(x, y)) x::merge(lt, xs, y::ys)
|
||||
else y::merge(lt, x::xs, ys)
|
||||
merge(_, [], ys) = ys
|
||||
merge(_, xs, []) = xs
|
||||
|
||||
|
||||
/** Mergesort inspired by
|
||||
* https://hackage.haskell.org/package/base-4.14.1.0/docs/src/Data.OldList.html#sort
|
||||
*/
|
||||
function
|
||||
sort : (('a, 'a) => bool, list('a)) => list('a)
|
||||
sort(_, []) = []
|
||||
sort(lt, l) =
|
||||
merge_all(lt, monotonic_subs(lt, l))
|
||||
|
||||
/** Splits list into compound increasing sublists
|
||||
*/
|
||||
private function
|
||||
monotonic_subs : (('a, 'a) => bool, list('a)) => list(list('a))
|
||||
monotonic_subs(lt, x::y::rest) =
|
||||
if(lt(y, x)) desc(lt, y, [x], rest)
|
||||
else asc(lt, y, [x], rest)
|
||||
monotonic_subs(_, l) = [l]
|
||||
|
||||
/** Extracts the longest descending prefix and proceeds with monotonic split
|
||||
*/
|
||||
private function
|
||||
desc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
|
||||
desc(lt, x, acc, h::t) =
|
||||
if(lt(x, h)) (x::acc) :: monotonic_subs(lt, h::t)
|
||||
else desc(lt, h, x::acc, t)
|
||||
desc(_, x, acc, []) = [x::acc]
|
||||
|
||||
/** Extracts the longest ascending prefix and proceeds with monotonic split
|
||||
*/
|
||||
private function
|
||||
asc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
|
||||
asc(lt, x, acc, h::t) =
|
||||
if(lt(h, x)) List.reverse(x::acc) :: monotonic_subs(lt, h::t)
|
||||
else asc(lt, h, x::acc, t)
|
||||
asc(_, x, acc, []) = [List.reverse(x::acc)]
|
||||
|
||||
/** Merges list of sorted lists
|
||||
*/
|
||||
private function
|
||||
merge_all : (('a, 'a) => bool, list(list('a))) => list('a)
|
||||
merge_all(_, [part]) = part
|
||||
merge_all(lt, parts) = merge_all(lt, merge_pairs(lt, parts))
|
||||
|
||||
/** Single round of `merge_all` – pairs of lists in a list of list
|
||||
*/
|
||||
private function
|
||||
merge_pairs : (('a, 'a) => bool, list(list('a))) => list(list('a))
|
||||
merge_pairs(lt, x::y::rest) = merge(lt, x, y) :: merge_pairs(lt, rest)
|
||||
merge_pairs(_, l) = l
|
||||
|
||||
|
||||
/** Puts `delim` between every two members of the list
|
||||
*/
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ do_contract_interface(Type, ContractString, Options) ->
|
||||
try
|
||||
Ast = aeso_compiler:parse(ContractString, Options),
|
||||
%% io:format("~p\n", [Ast]),
|
||||
{TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold]),
|
||||
{TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]),
|
||||
%% io:format("~p\n", [TypedAst]),
|
||||
from_typed_ast(Type, TypedAst)
|
||||
catch
|
||||
|
||||
@@ -557,7 +557,7 @@ map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
|
||||
infer(Contracts) ->
|
||||
infer(Contracts, []).
|
||||
|
||||
-type option() :: return_env | dont_unfold | no_code | term().
|
||||
-type option() :: return_env | dont_unfold | no_code | debug_mode | term().
|
||||
|
||||
-spec init_env(list(option())) -> env().
|
||||
init_env(_Options) -> global_env().
|
||||
@@ -622,16 +622,22 @@ check_scope_name_clash(Env, Kind, Name) ->
|
||||
|
||||
-spec infer_contract_top(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) ->
|
||||
{env(), [aeso_syntax:decl()]}.
|
||||
infer_contract_top(Env, Kind, Defs0, _Options) ->
|
||||
infer_contract_top(Env, Kind, Defs0, Options) ->
|
||||
create_type_errors(),
|
||||
Defs = desugar(Defs0),
|
||||
infer_contract(Env, Kind, Defs).
|
||||
destroy_and_report_type_errors(Env),
|
||||
infer_contract(Env, Kind, Defs, Options).
|
||||
|
||||
%% infer_contract takes a proplist mapping global names to types, and
|
||||
%% a list of definitions.
|
||||
-spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
|
||||
infer_contract(Env0, What, Defs0) ->
|
||||
-spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) -> {env(), [aeso_syntax:decl()]}.
|
||||
infer_contract(Env0, What, Defs0, Options) ->
|
||||
create_type_errors(),
|
||||
Defs = process_blocks(Defs0),
|
||||
Defs01 = process_blocks(Defs0),
|
||||
Defs = case lists:member(debug_mode, Options) of
|
||||
true -> expose_internals(Defs01, What);
|
||||
false -> Defs01
|
||||
end,
|
||||
destroy_and_report_type_errors(Env0),
|
||||
Env = Env0#env{ what = What },
|
||||
Kind = fun({type_def, _, _, _, _}) -> type;
|
||||
@@ -679,7 +685,7 @@ process_blocks(Decls) ->
|
||||
-spec process_block(aeso_syntax:ann(), [aeso_syntax:decl()]) -> [aeso_syntax:decl()].
|
||||
process_block(_, []) -> [];
|
||||
process_block(_, [Decl]) -> [Decl];
|
||||
process_block(Ann, [Decl | Decls]) ->
|
||||
process_block(_Ann, [Decl | Decls]) ->
|
||||
IsThis = fun(Name) -> fun({letfun, _, {id, _, Name1}, _, _, _}) -> Name == Name1;
|
||||
(_) -> false end end,
|
||||
case Decl of
|
||||
@@ -693,6 +699,25 @@ process_block(Ann, [Decl | Decls]) ->
|
||||
[{fun_clauses, Ann1, Id, {id, [{origin, system} | Ann1], "_"}, Clauses}]
|
||||
end.
|
||||
|
||||
%% Turns private stuff into public stuff
|
||||
expose_internals(Defs, What) ->
|
||||
[ begin
|
||||
Ann = element(2, Def),
|
||||
NewAnn = case What of
|
||||
namespace -> [A ||A <- Ann, A /= {private, true}, A /= private];
|
||||
main_contract -> [{entrypoint, true}|Ann]; % minor duplication
|
||||
contract -> Ann
|
||||
end,
|
||||
Def1 = setelement(2, Def, NewAnn),
|
||||
case Def1 of % fix inner clauses
|
||||
{fun_clauses, Ans, Id, T, Clauses} ->
|
||||
{fun_clauses, Ans, Id, T, expose_internals(Clauses, What)};
|
||||
_ -> Def1
|
||||
end
|
||||
end
|
||||
|| Def <- Defs
|
||||
].
|
||||
|
||||
-spec check_typedefs(env(), [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
|
||||
check_typedefs(Env = #env{ namespace = Ns }, Defs) ->
|
||||
create_type_errors(),
|
||||
@@ -2563,6 +2588,9 @@ mk_error({mixed_record_and_map, Expr}) ->
|
||||
Msg = io_lib:format("Mixed record fields and map keys in\n~s",
|
||||
[pp_expr(" ", Expr)]),
|
||||
mk_t_err(pos(Expr), Msg);
|
||||
mk_error({conflicting_updates_for_field, Upd, Key}) ->
|
||||
Msg = io_lib:format("Conflicting updates for field '~s'\n", [Key]),
|
||||
mk_t_err(pos(Upd), Msg);
|
||||
mk_error(Err) ->
|
||||
Msg = io_lib:format("Unknown error: ~p\n", [Err]),
|
||||
mk_t_err(pos(0, 0), Msg).
|
||||
@@ -2785,7 +2813,7 @@ desugar_updates([Upd | Updates]) ->
|
||||
{More, Updates1} = updates_key(Key, Updates),
|
||||
%% Check conflicts
|
||||
case length([ [] || [] <- [Rest | More] ]) of
|
||||
N when N > 1 -> error({conflicting_updates_for_field, Upd, Key});
|
||||
N when N > 1 -> type_error({conflicting_updates_for_field, Upd, Key});
|
||||
_ -> ok
|
||||
end,
|
||||
[MakeField(lists:append([Rest | More])) | desugar_updates(Updates1)].
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
| pp_assembler
|
||||
| pp_bytecode
|
||||
| no_code
|
||||
| keep_included
|
||||
| keep_included
|
||||
| debug_mode
|
||||
| {backend, aevm | fate}
|
||||
| {include, {file_system, [string()]} |
|
||||
{explicit_files, #{string() => binary()}}}
|
||||
|
||||
@@ -83,8 +83,8 @@ test_cases(3) ->
|
||||
DecACI = <<"contract C =\n"
|
||||
" type state = unit\n"
|
||||
" datatype event = SingleEventDefined\n"
|
||||
" datatype bert('a) = Bin('a)\n"
|
||||
" entrypoint a : (C.bert(string)) => int\n">>,
|
||||
" datatype bert('a) = Bin('a)\n"
|
||||
" entrypoint a : (C.bert(string)) => int\n">>,
|
||||
{Contract,MapACI,DecACI}.
|
||||
|
||||
%% Roundtrip
|
||||
@@ -97,7 +97,10 @@ all_contracts() -> aeso_compiler_tests:compilable_contracts().
|
||||
|
||||
aci_test_contract(Name) ->
|
||||
String = aeso_test_utils:read_contract(Name),
|
||||
Opts = [{include, {file_system, [aeso_test_utils:contract_path()]}}],
|
||||
Opts = case lists:member(Name, aeso_compiler_tests:debug_mode_contracts()) of
|
||||
true -> [debug_mode];
|
||||
false -> []
|
||||
end ++ [{include, {file_system, [aeso_test_utils:contract_path()]}}],
|
||||
{ok, JSON} = aeso_aci:contract_interface(json, String, Opts),
|
||||
{ok, #{aci := JSON1}} = aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]),
|
||||
?assertEqual(JSON, JSON1),
|
||||
|
||||
@@ -110,7 +110,15 @@ compile(Backend, Name) ->
|
||||
|
||||
compile(Backend, Name, Options) ->
|
||||
String = aeso_test_utils:read_contract(Name),
|
||||
case aeso_compiler:from_string(String, [{src_file, Name ++ ".aes"}, {backend, Backend} | Options]) of
|
||||
Options1 =
|
||||
case lists:member(Name, debug_mode_contracts()) of
|
||||
true -> [debug_mode];
|
||||
false -> []
|
||||
end ++
|
||||
[ {src_file, Name ++ ".aes"}, {backend, Backend}
|
||||
, {include, {file_system, [aeso_test_utils:contract_path()]}}
|
||||
] ++ Options,
|
||||
case aeso_compiler:from_string(String, Options1) of
|
||||
{ok, Map} -> Map;
|
||||
{error, ErrorString} when is_binary(ErrorString) -> ErrorString;
|
||||
{error, Errors} -> Errors
|
||||
@@ -165,15 +173,20 @@ compilable_contracts() ->
|
||||
"underscore_number_literals",
|
||||
"qualified_constructor",
|
||||
"let_patterns",
|
||||
"lhs_matching"
|
||||
"lhs_matching",
|
||||
"hermetization_turnoff"
|
||||
].
|
||||
|
||||
not_compilable_on(fate) -> [];
|
||||
not_compilable_on(aevm) ->
|
||||
["stdlib_include",
|
||||
"manual_stdlib_include"
|
||||
"manual_stdlib_include",
|
||||
"hermetization_turnoff"
|
||||
].
|
||||
|
||||
debug_mode_contracts() ->
|
||||
["hermetization_turnoff"].
|
||||
|
||||
%% Contracts that should produce type errors
|
||||
|
||||
-define(Pos(Kind, File, Line, Col), (list_to_binary(Kind))/binary, " error in '",
|
||||
@@ -700,6 +713,9 @@ failing_contracts() ->
|
||||
" g : (int, string) => 'c\nto arguments\n"
|
||||
" \"Litwo, ojczyzno moja\" : string">>
|
||||
])
|
||||
, ?TYPE_ERROR(bad_state,
|
||||
[<<?Pos(4, 16)
|
||||
"Conflicting updates for field 'foo'">>])
|
||||
].
|
||||
|
||||
-define(Path(File), "code_errors/" ??File).
|
||||
@@ -853,6 +869,11 @@ validate(Contract1, Contract2) ->
|
||||
ByteCode = #{ fate_code := FCode } = compile(fate, Contract1),
|
||||
FCode1 = aeb_fate_code:serialize(aeb_fate_code:strip_init_function(FCode)),
|
||||
Source = aeso_test_utils:read_contract(Contract2),
|
||||
aeso_compiler:validate_byte_code(ByteCode#{ byte_code := FCode1 }, Source,
|
||||
[{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]).
|
||||
aeso_compiler:validate_byte_code(
|
||||
ByteCode#{ byte_code := FCode1 }, Source,
|
||||
case lists:member(Contract2, debug_mode_contracts()) of
|
||||
true -> [debug_mode];
|
||||
false -> []
|
||||
end ++
|
||||
[{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]).
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
contract C =
|
||||
record state = { foo : int }
|
||||
entrypoint init(i : int) =
|
||||
state{ foo = i,
|
||||
foo = 42 }
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace M =
|
||||
function mf() = mg()
|
||||
function mg() = mf()
|
||||
|
||||
namespace N =
|
||||
function nf() = ng() + M.mf() + M.mg()
|
||||
private function ng() = nf() + M.mf() + M.mg()
|
||||
|
||||
contract C =
|
||||
entrypoint f() = N.ng() + N.nf() + g()
|
||||
function g() = N.ng() + N.nf() + f()
|
||||
Reference in New Issue
Block a user