From 18235e5b56ba2fef131076211b40c67d12fea78d Mon Sep 17 00:00:00 2001 From: radrow Date: Fri, 24 May 2019 13:36:00 +0200 Subject: [PATCH 01/19] Added standard List library and list comprehensions Added List library. Flatmaps WIP Fixed dependency in flat_map fcode generation Updated tests to use custom list lib Added comprehension test Added stdlib sanity Test --- src/aeso_ast_infer_types.erl | 26 +++++++++++++++ src/aeso_ast_to_fcode.erl | 9 +++++- src/aeso_ast_to_icode.erl | 12 +++++++ src/aeso_parser.erl | 19 +++++++++-- src/aeso_pretty.erl | 2 ++ src/aeso_stdlib.erl | 48 ++++++++++++++++++++++++++++ src/aeso_syntax.erl | 3 ++ test/aeso_compiler_tests.erl | 3 +- test/contracts/deadcode.aes | 6 ++-- test/contracts/list_comp.aes | 16 ++++++++++ test/contracts/multi_sig.aes | 4 +-- test/contracts/nodeadcode.aes | 6 ++-- test/contracts/reason/voting.re | 6 ++-- test/contracts/reason/voting_test.re | 2 +- test/contracts/voting.aes | 6 ++-- 15 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 src/aeso_stdlib.erl create mode 100644 test/contracts/list_comp.aes diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 725323d..57ad1e7 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -363,6 +363,8 @@ global_env() -> Pair = fun(A, B) -> {tuple_t, Ann, [A, B]} end, Fun = fun(Ts, T) -> {type_sig, Ann, [], Ts, T} end, Fun1 = fun(S, T) -> Fun([S], T) end, + %% Lambda = fun(Ts, T) -> {fun_t, Ann, [], Ts, T} end, + %% Lambda1 = fun(S, T) -> ArgFun([S], T) end, StateFun = fun(Ts, T) -> {type_sig, [stateful|Ann], [], Ts, T} end, TVar = fun(X) -> {tvar, Ann, "'" ++ X} end, SignId = {id, Ann, "signature"}, @@ -1074,6 +1076,23 @@ infer_expr(Env, {list, As, Elems}) -> ElemType = fresh_uvar(As), NewElems = [check_expr(Env, X, ElemType) || X <- Elems], {typed, As, {list, As, NewElems}, {app_t, As, {id, As, "list"}, [ElemType]}}; +infer_expr(Env, {list_comp, As, Yield, []}) -> + {typed, _, TypedYield, Type} = infer_expr(Env, Yield), + {typed, As, {list_comp, As, TypedYield, []}, {app_t, As, {id, As, "list"}, [Type]}}; +infer_expr(Env, {list_comp, As, Yield, [{comprehension_bind, Arg, BExpr}|Rest]}) -> + BindVarType = fresh_uvar(As), + TypedBind = {typed, As2, _, TypeBExpr} = infer_expr(Env, BExpr), + unify( Env + , TypeBExpr + , {app_t, As, {id, As, "list"}, [BindVarType]} + , {list_comp, TypedBind, TypeBExpr, {app_t, As2, {id, As, "list"}, [BindVarType]}}), + NewE = bind_var(Arg, BindVarType, Env), + {typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} = + infer_expr(NewE, {list_comp, As, Yield, Rest}), + { typed + , As + , {list_comp, As, TypedYield, [{comprehension_bind, {typed, Arg, BindVarType}, TypedBind}|TypedRest]} + , ResType}; infer_expr(Env, {typed, As, Body, Type}) -> Type1 = check_type(Env, Type), {typed, _, NewBody, NewType} = check_expr(Env, Body, Type1), @@ -2253,6 +2272,13 @@ pp_when({check_expr, Expr, Inferred0, Expected0}) -> pp_when({checking_init_type, Ann}) -> io_lib:format("when checking that 'init' returns a value of type 'state' at ~s\n", [pp_loc(Ann)]); +pp_when({list_comp, BindExpr, Inferred0, Expected0}) -> + {Inferred, Expected} = instantiate({Inferred0, Expected0}), + io_lib:format("when checking rvalue of list comprehension binding at ~s\n~s\n" + "against type \n~s\n", + [pp_loc(BindExpr), pp_typed(" ", BindExpr, Inferred), pp_type(" ", Expected)] + ); + pp_when(unknown) -> "". -spec pp_why_record(why_record()) -> iolist(). diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 7ce42ab..b3363fd 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -360,7 +360,6 @@ make_let(Expr, Body) -> {'let', X, Expr, Body({var, X})} end. --spec expr_to_fcode(env(), aeso_syntax:expr()) -> fexpr(). expr_to_fcode(Env, {typed, _, Expr, Type}) -> expr_to_fcode(Env, Type, Expr); expr_to_fcode(Env, Expr) -> @@ -444,6 +443,14 @@ expr_to_fcode(Env, _Type, {list, _, Es}) -> lists:foldr(fun(E, L) -> {op, '::', [expr_to_fcode(Env, E), L]} end, nil, Es); +expr_to_fcode(Env, _Type, {list_comp, _, Yield, []}) -> + {op, '::', [expr_to_fcode(Env, Yield), nil]}; +expr_to_fcode(Env, _Type, {list_comp, As, Yield, [{comprehension_bind, {typed, {id, _, Arg}, _}, BindExpr}|Rest]}) -> + Env1 = bind_var(Env, Arg), + Bind = {lam, [Arg], expr_to_fcode(Env1, {list_comp, As, Yield, Rest})}, + {funcall, resolve_fun(Env, ["List", "flat_map"]), [expr_to_fcode(Env, BindExpr), Bind]}; + + %% Conditionals expr_to_fcode(Env, _Type, {'if', _, Cond, Then, Else}) -> make_if(expr_to_fcode(Env, Cond), diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index e611f8f..f06b02d 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -519,6 +519,18 @@ ast_body({app,As,Fun,Args}, Icode) -> #funcall{function=ast_body(Fun, Icode), args=[ast_body(A, Icode) || A <- Args]} end; +ast_body({list_comp, _, Yield, []}, Icode) -> + #list{elems = [ast_body(Yield, Icode)]}; +ast_body({list_comp, As, Yield, [{comprehension_bind, {typed, Arg, ArgType}, BindExpr}|Rest]}, Icode) -> + #funcall + { function = #var_ref{ name = ["List", "flat_map"] } + , args = + [ #lambda{ args=[#arg{name = ast_id(Arg), type = ast_type(ArgType, Icode)}] + , body = ast_body({list_comp, As, Yield, Rest}, Icode) + } + , ast_body(BindExpr, Icode) + ] + }; ast_body({'if',_,Dec,Then,Else}, Icode) -> #ifte{decision = ast_body(Dec, Icode) ,then = ast_body(Then, Icode) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index cdcb2c8..25fc23a 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -230,11 +230,15 @@ exprAtom() -> , {bool, keyword(false), false} , ?LET_P(Fs, brace_list(?LAZY_P(field_assignment())), record(Fs)) , {list, [], bracket_list(Expr)} + , ?RULE(keyword('['), Expr, tok('|'), comma_sep(?LAZY_P(comprehension_bind())), tok(']'), list_comp_e(_1, _2, _4)) , ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4)) , ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2)) ]) end). +comprehension_bind() -> + ?RULE(id(), tok('<-'), expr(), {comprehension_bind, _1, _3}). + arg_expr() -> ?LAZY_P( choice([ ?RULE(id(), tok('='), expr(), {named_arg, [], _1, _3}) @@ -481,6 +485,8 @@ fun_t(Domains, Type) -> tuple_e(_Ann, [Expr]) -> Expr; %% Not a tuple tuple_e(Ann, Exprs) -> {tuple, Ann, Exprs}. +list_comp_e(Ann, Expr, Binds) -> {list_comp, Ann, Expr, Binds}. + -spec parse_pattern(aeso_syntax:expr()) -> aeso_parse_lib:parser(aeso_syntax:pat()). parse_pattern({app, Ann, Con = {'::', _}, Es}) -> {app, Ann, Con, lists:map(fun parse_pattern/1, Es)}; @@ -527,8 +533,15 @@ expand_includes(AST, Opts) -> expand_includes([], Acc, _Opts) -> {ok, lists:reverse(Acc)}; expand_includes([{include, _, S = {string, _, File}} | AST], Acc, Opts) -> - case read_file(File, Opts) of - {ok, Bin} -> + case {read_file(File, Opts), maps:find(File, aeso_stdlib:stdlib())} of + {_, {ok, Lib}} -> + case string(Lib) of + {ok, AST1} -> + expand_includes(AST1 ++ AST, Acc, Opts); + Err = {error, _} -> + Err + end; + {{ok, Bin}, _} -> Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}), case string(binary_to_list(Bin), Opts1) of {ok, AST1} -> @@ -536,7 +549,7 @@ expand_includes([{include, _, S = {string, _, File}} | AST], Acc, Opts) -> Err = {error, _} -> Err end; - {error, _} -> + {{error, _}, _} -> {error, {get_pos(S), include_error, File}} end; expand_includes([E | AST], Acc, Opts) -> diff --git a/src/aeso_pretty.erl b/src/aeso_pretty.erl index ea76b42..e368354 100644 --- a/src/aeso_pretty.erl +++ b/src/aeso_pretty.erl @@ -235,6 +235,8 @@ type(Type, Options) -> -spec type(aeso_syntax:type()) -> doc(). type({fun_t, _, Named, Args, Ret}) -> follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret)); +type({type_sig, _, Named, Args, Ret}) -> + follow(hsep(tuple_type(Named ++ Args), text("=>")), type(Ret)); type({app_t, _, Type, []}) -> type(Type); type({app_t, _, Type, Args}) -> diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl new file mode 100644 index 0000000..4cbc768 --- /dev/null +++ b/src/aeso_stdlib.erl @@ -0,0 +1,48 @@ +%%%------------------------------------------------------------------- +%%% @author Radosław Rowicki +%%% @copyright (C) 2019, Aeternity Anstalt +%%% @doc +%%% Standard library for Sophia +%%% @end +%%% Created : 6 July 2019 +%%% +%%%------------------------------------------------------------------- + +-module(aeso_stdlib). + +-export([stdlib/0]). + +stdlib() -> + maps:from_list( + [ {<<"List.aes">>, std_list()} + ] + ). + +std_list() -> +" +namespace List = + function length(l) = length_(l, 0) + private function length_(l, n) = switch(l) + [] => n + _::t => length_(t, n + 1) + + function foldr(cons, nil, l) = switch(l) + [] => nil + h::t => cons(h, foldr(cons, nil, t)) + + function foldl(rcons, acc, l) = switch(l) + [] => acc + h::t => foldl(rcons, rcons(acc, h), t) + + function reverse(l) = + foldr((el, cont) => (lst) => cont(el::lst), (x) => x, l)([]) + + function map(f, l) = map_(f, l, []) + private function map_(f, l, acc) = switch(l) + [] => reverse(acc) + h::t => map_(f, t, f(h)::acc) + + function flat_map(f, l) = switch(l) + [] => [] + h::t => f(h) ++ flat_map(f, t) +". diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index 5c9e81c..e40ae44 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -92,6 +92,7 @@ | {proj, ann(), expr(), id()} | {tuple, ann(), [expr()]} | {list, ann(), [expr()]} + | {list_comp, ann(), expr(), [comprehension_bind()]} | {typed, ann(), expr(), type()} | {record, ann(), [field(expr())]} | {record, ann(), expr(), [field(expr())]} %% record update @@ -104,6 +105,8 @@ | id() | qid() | con() | qcon() | constant(). +-type comprehension_bind() :: [{comprehension_bind, ann(), id(), type()}]. + -type arg_expr() :: expr() | {named_arg, ann(), id(), expr()}. %% When lvalue is a projection this is sugar for accessing fields in nested diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 911e635..36af710 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -118,7 +118,8 @@ compilable_contracts() -> "namespace_bug", "bytes_to_x", "aens", - "tuple_match" + "tuple_match", + "list_comp" ]. not_yet_compilable(fate) -> []; diff --git a/test/contracts/deadcode.aes b/test/contracts/deadcode.aes index 450e52a..c94ef6d 100644 --- a/test/contracts/deadcode.aes +++ b/test/contracts/deadcode.aes @@ -1,5 +1,5 @@ -namespace List = +namespace MyList = function map1(f : 'a => 'b, xs : list('a)) = switch(xs) @@ -14,8 +14,8 @@ namespace List = contract Deadcode = entrypoint inc1(xs : list(int)) : list(int) = - List.map1((x) => x + 1, xs) + MyList.map1((x) => x + 1, xs) entrypoint inc2(xs : list(int)) : list(int) = - List.map1((x) => x + 1, xs) + MyList.map1((x) => x + 1, xs) diff --git a/test/contracts/list_comp.aes b/test/contracts/list_comp.aes new file mode 100644 index 0000000..de3359e --- /dev/null +++ b/test/contracts/list_comp.aes @@ -0,0 +1,16 @@ +include "List.aes" + +contract ListComp = + + entrypoint sample1() = [1,2,3] + entrypoint sample2() = [4,5] + + entrypoint l1() = [x | x <- sample1()] + entrypoint l1_true() = [1,2,3] + + entrypoint l2() = [x + y | x <- sample1(), y <- sample2()] + entrypoint l2_true() = [5,6,6,7,7,8] + + entrypoint l3() = [x ++ y | x <- [[":)"] | x <- [1,2]] + , y <- [[":("]]] + entrypoint l3_true() = [[":)", ":("], [":)", ":("]] diff --git a/test/contracts/multi_sig.aes b/test/contracts/multi_sig.aes index 028b621..cb1363f 100644 --- a/test/contracts/multi_sig.aes +++ b/test/contracts/multi_sig.aes @@ -28,8 +28,8 @@ contract MultiSig = let n = length(owners) + 1 { nRequired = nRequired, nOwners = n, - owners = Map.from_list(List.zip([1..n], caller() :: owners)), - ownerIndex = Map.from_list(List.zip(caller() :: owners, [1..n])) } + owners = Map.from_list(MyList.zip([1..n], caller() :: owners)), + ownerIndex = Map.from_list(MyList.zip(caller() :: owners, [1..n])) } function lookup(map, key) = switch(Map.get(key, map)) diff --git a/test/contracts/nodeadcode.aes b/test/contracts/nodeadcode.aes index 76f6f3b..b7df43f 100644 --- a/test/contracts/nodeadcode.aes +++ b/test/contracts/nodeadcode.aes @@ -1,5 +1,5 @@ -namespace List = +namespace MyList = function map1(f : 'a => 'b, xs : list('a)) = switch(xs) @@ -14,8 +14,8 @@ namespace List = contract Deadcode = entrypoint inc1(xs : list(int)) : list(int) = - List.map1((x) => x + 1, xs) + MyList.map1((x) => x + 1, xs) entrypoint inc2(xs : list(int)) : list(int) = - List.map2((x) => x + 1, xs) + MyList.map2((x) => x + 1, xs) diff --git a/test/contracts/reason/voting.re b/test/contracts/reason/voting.re index 5736350..6739af7 100644 --- a/test/contracts/reason/voting.re +++ b/test/contracts/reason/voting.re @@ -47,7 +47,7 @@ module Voting : Voting = { let init(proposalNames: args): state = { chairPerson: caller(), voters: AddrMap.empty, - proposals: List.map((name) => {name: name, voteCount: 0}, proposalNames) + proposals: MyList.map((name) => {name: name, voteCount: 0}, proposalNames) }; /* Boilerplate */ @@ -73,7 +73,7 @@ module Voting : Voting = { }; let addVote(candidate, weight) = { - let proposal = List.nth(state().proposals, candidate); + let proposal = MyList.nth(state().proposals, candidate); proposal.voteCount = proposal.voteCount + weight; }; @@ -121,6 +121,6 @@ module Voting : Voting = { /* const */ let currentTally() = - List.map((p) => (p.name, p.voteCount), state().proposals); + MyList.map((p) => (p.name, p.voteCount), state().proposals); } diff --git a/test/contracts/reason/voting_test.re b/test/contracts/reason/voting_test.re index 85cd587..c0b7420 100644 --- a/test/contracts/reason/voting_test.re +++ b/test/contracts/reason/voting_test.re @@ -13,7 +13,7 @@ open Voting; let print_tally() = { let tally = call(other, () => currentTally()); - List.map(((name, count)) => Printf.printf("%s: %d\n", name, count), tally); + MyList.map(((name, count)) => Printf.printf("%s: %d\n", name, count), tally); let winner = call(other, () => winnerName()); Printf.printf("Winner: %s\n", winner); }; diff --git a/test/contracts/voting.aes b/test/contracts/voting.aes index 0154e28..d786f6d 100644 --- a/test/contracts/voting.aes +++ b/test/contracts/voting.aes @@ -36,7 +36,7 @@ contract Voting = function init(proposalNames: list(string)): state = { chairPerson = caller(), voters = Map.empty, - proposals = List.map((name) => {name = name, voteCount = 0}, proposalNames) } + proposals = MyList.map((name) => {name = name, voteCount = 0}, proposalNames) } function initVoter() = { weight = 1, vote = NotVoted} @@ -53,7 +53,7 @@ contract Voting = _ => delegate function addVote(candidate, weight) = - let proposal = List.nth(state.proposals, candidate) + let proposal = MyList.nth(state.proposals, candidate) proposal{ voteCount = proposal.voteCount + weight } function delegateVote(delegateTo: address, weight: uint) = @@ -93,5 +93,5 @@ contract Voting = // const function currentTally() = - List.map((p) => (p.name, p.voteCount), state.proposals) + MyList.map((p) => (p.name, p.voteCount), state.proposals) -- 2.30.2 From 7a762be17ea58f7e22be0bb6660b0c97860ef69c Mon Sep 17 00:00:00 2001 From: radrow Date: Tue, 9 Jul 2019 17:39:58 +0200 Subject: [PATCH 02/19] Extended stdlib for lists. Added error message for redefinition of stdlibx --- src/aeso_ast_to_fcode.erl | 4 +-- src/aeso_parser.erl | 6 ++-- src/aeso_stdlib.erl | 72 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index b3363fd..5a46718 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -448,8 +448,8 @@ expr_to_fcode(Env, _Type, {list_comp, _, Yield, []}) -> expr_to_fcode(Env, _Type, {list_comp, As, Yield, [{comprehension_bind, {typed, {id, _, Arg}, _}, BindExpr}|Rest]}) -> Env1 = bind_var(Env, Arg), Bind = {lam, [Arg], expr_to_fcode(Env1, {list_comp, As, Yield, Rest})}, - {funcall, resolve_fun(Env, ["List", "flat_map"]), [expr_to_fcode(Env, BindExpr), Bind]}; - + {def_u, FlatMap, _} = resolve_fun(Env, ["List", "flat_map"]), + {def, FlatMap, [Bind, expr_to_fcode(Env, BindExpr)]}; %% Conditionals expr_to_fcode(Env, _Type, {'if', _, Cond, Then, Else}) -> diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 25fc23a..de71794 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -505,7 +505,7 @@ parse_pattern(E = {bool, _, _}) -> E; parse_pattern(E = {bytes, _, _}) -> E; parse_pattern(E = {string, _, _}) -> E; parse_pattern(E = {char, _, _}) -> E; -parse_pattern(E) -> bad_expr_err("Not a valid pattern", E). +parse_pattern(E) -> bad_expr_err("Not a valid patxstern", E). -spec parse_field_pattern(aeso_syntax:field(aeso_syntax:expr())) -> aeso_parse_lib:parser(aeso_syntax:field(aeso_syntax:pat())). parse_field_pattern({field, Ann, F, E}) -> @@ -532,8 +532,10 @@ expand_includes(AST, Opts) -> expand_includes([], Acc, _Opts) -> {ok, lists:reverse(Acc)}; -expand_includes([{include, _, S = {string, _, File}} | AST], Acc, Opts) -> +expand_includes([{include, Ann, S = {string, _, File}} | AST], Acc, Opts) -> case {read_file(File, Opts), maps:find(File, aeso_stdlib:stdlib())} of + {{ok, _}, {ok,_ }} -> + return_error(ann_pos(Ann), "Illegal redefinition of standard library " ++ File); {_, {ok, Lib}} -> case string(Lib) of {ok, AST1} -> diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index 4cbc768..8ac8351 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -21,10 +21,15 @@ stdlib() -> std_list() -> " namespace List = + + function empty(l) = switch(l) + [] => true + _ => false + function length(l) = length_(l, 0) - private function length_(l, n) = switch(l) - [] => n - _::t => length_(t, n + 1) + private function length_(l, acc) = switch(l) + [] => acc + _::t => length_(t, acc + 1) function foldr(cons, nil, l) = switch(l) [] => nil @@ -34,8 +39,7 @@ namespace List = [] => acc h::t => foldl(rcons, rcons(acc, h), t) - function reverse(l) = - foldr((el, cont) => (lst) => cont(el::lst), (x) => x, l)([]) + function reverse(l) = foldl((lst, el) => el :: lst, [], l) function map(f, l) = map_(f, l, []) private function map_(f, l, acc) = switch(l) @@ -45,4 +49,62 @@ namespace List = function flat_map(f, l) = switch(l) [] => [] h::t => f(h) ++ flat_map(f, t) + + function concats(ll) = foldr((l1, l2) => l1 ++ l2, [], ll) + + function filter(p, l) = filter_(p, l, []) + private function filter_(p, l, acc) = switch(l) + [] => reverse(acc) + h::t => filter_(p, t, if(p(h)) h::acc else acc) + + function all(p, l) = foldl((prev, next) => prev && p(next), true, l) + function any(p, l) = foldl((prev, next) => prev || p(next), false, l) + + /* Take `n` first elements */ + function take(n, l) = if(n < 0) abort(\"Take negative number of elements\") else take_(n, l, []) + private function take_(n, l, acc) = + if(n == 0) reverse(acc) + else switch(l) + [] => reverse(acc) + h::t => take_(n-1, t, h::acc) + + /* Drop `n` first elements */ + function drop(n, l) = + if(n < 0) abort(\"Drop negative number of elements\") + elif (n == 0) l + else switch(l) + [] => [] + h::t => drop(n-1, t) + + /* Get the longest prefix of a list in which every element matches predicate `p` */ + function take_while(p, l) = take_while_(p, l, []) + private function take_while_(p, l, acc) = switch(l) + [] => reverse(acc) + h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc) + + /* Drop elements from `l` until `p` holds */ + function drop_while(p, l) = switch(l) + [] => [] + h::t => if(p(h)) drop_while(p, t) else l + + /* Zips two list by applying bimapping function on respective elements. Drops longer tail. */ + function zip_with(f, l1, l2) = zip_with_(f, l1, l2, []) + private function zip_with_(f, l1, l2, acc) = switch ((l1, l2)) + (h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc) + _ => reverse(acc) + + /* Zips two lists into list of pairs. Drops longer tail. */ + function zip(l1, l2) = zip_with((a, b) => (a, b), l1, l2) + + /* Splits list into two lists of elements that respectively match and don't match predicate `p` */ + function partition(p, l) = partition_(p, l, [], []) + private function partition_(p, l, acc_t, acc_f) = switch(l) + [] => (reverse(acc_t), reverse(acc_f)) + h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) + + // TODO: Improve? + function sort(lesser_cmp, l) = switch(l) + [] => [] + h::t => switch (partition((x) => lesser_cmp(x, h), t)) + (lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger) ". -- 2.30.2 From 8b06775331fbaa868d2a5a62eec0ad05742b5f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Rowicki?= <35342116+radrow@users.noreply.github.com> Date: Tue, 9 Jul 2019 23:25:20 +0200 Subject: [PATCH 03/19] Fixed type template --- src/aeso_ast_infer_types.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 57ad1e7..b28bca8 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -364,7 +364,7 @@ global_env() -> Fun = fun(Ts, T) -> {type_sig, Ann, [], Ts, T} end, Fun1 = fun(S, T) -> Fun([S], T) end, %% Lambda = fun(Ts, T) -> {fun_t, Ann, [], Ts, T} end, - %% Lambda1 = fun(S, T) -> ArgFun([S], T) end, + %% Lambda1 = fun(S, T) -> Lambda([S], T) end, StateFun = fun(Ts, T) -> {type_sig, [stateful|Ann], [], Ts, T} end, TVar = fun(X) -> {tvar, Ann, "'" ++ X} end, SignId = {id, Ann, "signature"}, -- 2.30.2 From 45c002010194a4fb1015510ed831a27455cbd7f6 Mon Sep 17 00:00:00 2001 From: radrow Date: Wed, 10 Jul 2019 01:32:10 +0200 Subject: [PATCH 04/19] Improved stdlib --- src/aeso_stdlib.erl | 107 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 9 deletions(-) diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index 8ac8351..99995ec 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -15,9 +15,41 @@ stdlib() -> maps:from_list( [ {<<"List.aes">>, std_list()} + %% , {<<"Func.aes">>, std_function()} ] ). +std_function() -> +" +namespace Func = + + function id(x) = x + + function const(x) = (y) => x + + function comp(f, g) = (x) => f(g(x)) + + function iter(n, f) = iter_(n, f, (x) => x) + private function iter_(n, f, acc) = + if(n == 0) acc + elif(n == 1) comp(f, acc) + else iter_(n / 2, f, if(n mod 2 == 0) comp(acc, acc) else comp(f, comp(acc, acc))) + + function curry2(f) = (x) => (y) => f(x, y) + function curry3(f) = (x) => (y) => (z) => f(x, y, z) + + function uncurry2(f) = (x, y) => f(x)(y) + function uncurry3(f) = (x, y, z) => f(x)(y)(z) + + function tuplify2(f) = (t) => switch(t) + (x, y) => f(x, y) +// function tuplify3(f) = (t) => switch(t) +// (x, y, z) => f(x, y, z) + + function untuplify2(f) = (x, y) => f((x, y)) + function untuplify3(f) = (x, y, z) => f((x, y, z)) +". + std_list() -> " namespace List = @@ -26,6 +58,33 @@ namespace List = [] => true _ => false + + function first(l) = switch(l) + [] => None + h::_ => Some(h) + + function tail(l) = switch(l) + [] => None + _::t => Some(t) + + function last(l) = switch(l) + [] => None + [x] => Some(x) + _::t => last(t) + + function find(p, l) = switch(l) + [] => None + h::t => if(p(h)) Some(h) else find(p, t) + + function nth(n, l) = switch(l) + h::t => if(n == 0) Some(h) else nth(n-1, t) + [] => None + + function get(n, l) = switch(l) + [] => abort(\"Out of index get\") + h::t => if(n == 0) h else get(n-1, t) + + function length(l) = length_(l, 0) private function length_(l, acc) = switch(l) [] => acc @@ -39,8 +98,10 @@ namespace List = [] => acc h::t => foldl(rcons, rcons(acc, h), t) + function reverse(l) = foldl((lst, el) => el :: lst, [], l) + function map(f, l) = map_(f, l, []) private function map_(f, l, acc) = switch(l) [] => reverse(acc) @@ -50,16 +111,12 @@ namespace List = [] => [] h::t => f(h) ++ flat_map(f, t) - function concats(ll) = foldr((l1, l2) => l1 ++ l2, [], ll) function filter(p, l) = filter_(p, l, []) private function filter_(p, l, acc) = switch(l) [] => reverse(acc) h::t => filter_(p, t, if(p(h)) h::acc else acc) - function all(p, l) = foldl((prev, next) => prev && p(next), true, l) - function any(p, l) = foldl((prev, next) => prev || p(next), false, l) - /* Take `n` first elements */ function take(n, l) = if(n < 0) abort(\"Take negative number of elements\") else take_(n, l, []) private function take_(n, l, acc) = @@ -87,6 +144,24 @@ namespace List = [] => [] h::t => if(p(h)) drop_while(p, t) else l + /* Splits list into two lists of elements that respectively match and don't match predicate `p` */ + function partition(p, l) = partition_(p, l, [], []) + private function partition_(p, l, acc_t, acc_f) = switch(l) + [] => (reverse(acc_t), reverse(acc_f)) + h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) + + + function concats(ll) = foldr((l1, l2) => l1 ++ l2, [], ll) + + function all(p, l) = foldl((prev, next) => prev && p(next), true, l) + + function any(p, l) = foldl((prev, next) => prev || p(next), false, l) + + function sum(l) = foldl ((a, b) => a + b, 0, l) + + function product(l) = foldl((a, b) => a * b, 1, l) + + /* Zips two list by applying bimapping function on respective elements. Drops longer tail. */ function zip_with(f, l1, l2) = zip_with_(f, l1, l2, []) private function zip_with_(f, l1, l2, acc) = switch ((l1, l2)) @@ -96,15 +171,29 @@ namespace List = /* Zips two lists into list of pairs. Drops longer tail. */ function zip(l1, l2) = zip_with((a, b) => (a, b), l1, l2) - /* Splits list into two lists of elements that respectively match and don't match predicate `p` */ - function partition(p, l) = partition_(p, l, [], []) - private function partition_(p, l, acc_t, acc_f) = switch(l) - [] => (reverse(acc_t), reverse(acc_f)) - h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) + function unzip(l) = unzip_(l, [], []) + private function unzip_(l, acc_l, acc_r) = switch(l) + [] => (reverse(acc_l), reverse(acc_r)) + (left, right)::t => unzip_(t, left::acc_l, right::acc_r) + // TODO: Improve? function sort(lesser_cmp, l) = switch(l) [] => [] h::t => switch (partition((x) => lesser_cmp(x, h), t)) (lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger) + + + function intersperse(delim, l) = intersperse_(delim, l, []) + private function intersperse_(delim, l, acc) = switch(l) + [] => reverse(acc) + [e] => reverse(e::acc) + h::t => intersperse_(delim, t, h::delim::acc) + + + function index(l) = index_(l, 0, []) + private function index_(l, n, acc) = switch(l) + [] => reverse(acc) + h::t => index_(t, n + 1, (n, h)::acc) + ". -- 2.30.2 From d64f41dc138fddab42d1fd1002698d2c2cf4ea59 Mon Sep 17 00:00:00 2001 From: radrow Date: Wed, 10 Jul 2019 11:31:06 +0200 Subject: [PATCH 05/19] More functions --- src/aeso_stdlib.erl | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index 99995ec..e726840 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -43,8 +43,8 @@ namespace Func = function tuplify2(f) = (t) => switch(t) (x, y) => f(x, y) -// function tuplify3(f) = (t) => switch(t) -// (x, y, z) => f(x, y, z) + function tuplify3(f) = (t) => switch(t) + (x, y, z) => f(x, y, z) function untuplify2(f) = (x, y) => f((x, y)) function untuplify3(f) = (x, y, z) => f((x, y, z)) @@ -76,6 +76,16 @@ namespace List = [] => None h::t => if(p(h)) Some(h) else find(p, t) + function find_all(p, l) = find_all_(p, l, []) + private function find_all_(p, l, acc) = switch(l) + [] => reverse(acc) + h::t => find_all_(p, t, if(p(h)) h::acc else acc) + + function find_indices(p, l) = find_indices_(p, l, 0, []) + private function find_indices_(p, l, n, acc) = switch(l) + [] => reverse(acc) + h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc) + function nth(n, l) = switch(l) h::t => if(n == 0) Some(h) else nth(n-1, t) [] => None @@ -90,6 +100,25 @@ namespace List = [] => acc _::t => length_(t, acc + 1) + + /* Unsafe. Makes `e` be `n`th element of `l`. Crashes on over/underflow */ + function insert_at(n, e, l) = if(n<0) abort(\"insert_at underflow\") else insert_at_(n, e, l, []) + private function insert_at_(n, e, l, acc) = + if (n == 0) reverse(e::acc) ++ l + else switch(l) + [] => abort(\"insert_at overflow\") + h::t => insert_at_(n-1, e, t, h::acc) + + function insert_by(f: (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = + switch(l) + [] => [x] + (e :: l') => + if(f(x, e)) + e :: insert_by(f, x, l') + else + x :: l + + function foldr(cons, nil, l) = switch(l) [] => nil h::t => cons(h, foldr(cons, nil, t)) @@ -98,6 +127,13 @@ namespace List = [] => acc h::t => foldl(rcons, rcons(acc, h), t) + function foreach(f : 'a => 'b, l : list('a)) = + switch(l) + [] => [] + e :: l' => + f(e) + foreach(f, l') + function reverse(l) = foldl((lst, el) => el :: lst, [], l) -- 2.30.2 From d170dd00eb68051c5c1abcc9a677ad9cd329b85b Mon Sep 17 00:00:00 2001 From: radrow Date: Tue, 16 Jul 2019 12:03:07 +0200 Subject: [PATCH 06/19] Fixed cyclic includes --- src/aeso_compiler.erl | 1 + src/aeso_parser.erl | 74 +++++++++++++++++++++++--------------- src/aeso_stdlib.erl | 16 +++++---- test/aeso_aci_tests.erl | 4 +-- test/aeso_parser_tests.erl | 14 ++++---- 5 files changed, 66 insertions(+), 43 deletions(-) diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 0280edd..1703d40 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -36,6 +36,7 @@ | pp_assembler | pp_bytecode | no_code + | no_implicit_stdlib | {backend, aevm | fate} | {include, {file_system, [string()]} | {explicit_files, #{string() => binary()}}} diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index de71794..4cce718 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -6,6 +6,7 @@ -export([string/1, string/2, + string/3, type/1]). -include("aeso_parse_lib.hrl"). @@ -16,13 +17,25 @@ -spec string(string()) -> parse_result(). string(String) -> - string(String, []). + string(String, sets:new(), []). --spec string(string(), aeso_compiler:options()) -> parse_result(). + +-spec string(string(), compiler:options()) -> parse_result(). string(String, Opts) -> + string(String, sets:new(), Opts). + +-spec string(string(), sets:set(string()), aeso_compiler:options()) -> parse_result(). +string(String, Included, Opts) -> case parse_and_scan(file(), String, Opts) of {ok, AST} -> - expand_includes(AST, Opts); + STD = case lists:member(no_implicit_stdlib, Opts) of + false -> [{ include, [{src_file, File}, {origin, system}] + , {string, [{src_file, File}, {origin, system}], File}} + || {File, _} <- aeso_stdlib:stdlib_list() + ]; + true -> [] + end, + expand_includes(STD ++ AST, Included, Opts); Err = {error, _} -> Err end. @@ -230,7 +243,7 @@ exprAtom() -> , {bool, keyword(false), false} , ?LET_P(Fs, brace_list(?LAZY_P(field_assignment())), record(Fs)) , {list, [], bracket_list(Expr)} - , ?RULE(keyword('['), Expr, tok('|'), comma_sep(?LAZY_P(comprehension_bind())), tok(']'), list_comp_e(_1, _2, _4)) + , ?RULE(keyword('['), Expr, token('|'), comma_sep(?LAZY_P(comprehension_bind())), tok(']'), list_comp_e(_1, _2, _4)) , ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4)) , ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2)) ]) @@ -527,35 +540,40 @@ bad_expr_err(Reason, E) -> prettypr:nest(2, aeso_pretty:expr(E))])). %% -- Helper functions ------------------------------------------------------- -expand_includes(AST, Opts) -> - expand_includes(AST, [], Opts). +expand_includes(AST, Included, Opts) -> + expand_includes(AST, Included, [], Opts). -expand_includes([], Acc, _Opts) -> +expand_includes([], _Included, Acc, _Opts) -> {ok, lists:reverse(Acc)}; -expand_includes([{include, Ann, S = {string, _, File}} | AST], Acc, Opts) -> - case {read_file(File, Opts), maps:find(File, aeso_stdlib:stdlib())} of - {{ok, _}, {ok,_ }} -> - return_error(ann_pos(Ann), "Illegal redefinition of standard library " ++ File); - {_, {ok, Lib}} -> - case string(Lib) of - {ok, AST1} -> - expand_includes(AST1 ++ AST, Acc, Opts); - Err = {error, _} -> - Err - end; - {{ok, Bin}, _} -> +expand_includes([{include, Ann, S = {string, _, File}} | AST], Included, Acc, Opts) -> + case sets:is_element(File, Included) of + false -> Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}), - case string(binary_to_list(Bin), Opts1) of - {ok, AST1} -> - expand_includes(AST1 ++ AST, Acc, Opts); - Err = {error, _} -> - Err + Included1 = sets:add_element(File, Included), + case {read_file(File, Opts), maps:find(File, aeso_stdlib:stdlib())} of + {{ok, _}, {ok,_ }} -> + return_error(ann_pos(Ann), "Illegal redefinition of standard library " ++ File); + {_, {ok, Lib}} -> + case string(Lib, Included1, Opts1) of + {ok, AST1} -> + expand_includes(AST1 ++ AST, Included1, Acc, Opts); + Err = {error, _} -> + Err + end; + {{ok, Bin}, _} -> + case string(binary_to_list(Bin), Included1, Opts1) of + {ok, AST1} -> + expand_includes(AST1 ++ AST, Included1, Acc, Opts); + Err = {error, _} -> + Err + end; + {_, _} -> + {error, {get_pos(S), include_error, File}} end; - {{error, _}, _} -> - {error, {get_pos(S), include_error, File}} + true -> expand_includes(AST, Included, Acc, Opts) end; -expand_includes([E | AST], Acc, Opts) -> - expand_includes(AST, [E | Acc], Opts). +expand_includes([E | AST], Included, Acc, Opts) -> + expand_includes(AST, Included, [E | Acc], Opts). read_file(File, Opts) -> case proplists:get_value(include, Opts, {explicit_files, #{}}) of diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index e726840..4988b54 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -10,14 +10,15 @@ -module(aeso_stdlib). --export([stdlib/0]). +-export([stdlib/0, stdlib_list/0]). stdlib() -> - maps:from_list( - [ {<<"List.aes">>, std_list()} - %% , {<<"Func.aes">>, std_function()} - ] - ). + maps:from_list(stdlib_list()). + +stdlib_list() -> + [ {<<"List.aes">>, std_list()} + %% , {<<"Func.aes">>, std_function()} + ]. std_function() -> " @@ -51,7 +52,8 @@ namespace Func = ". std_list() -> -" +"namespace List =\n function flat_map() = 3\n". +x() ->" namespace List = function empty(l) = switch(l) diff --git a/test/aeso_aci_tests.erl b/test/aeso_aci_tests.erl index ee719be..0faef0e 100644 --- a/test/aeso_aci_tests.erl +++ b/test/aeso_aci_tests.erl @@ -9,7 +9,7 @@ simple_aci_test_() -> test_contract(N) -> {Contract,MapACI,DecACI} = test_cases(N), - {ok,JSON} = aeso_aci:contract_interface(json, Contract), + {ok,JSON} = aeso_aci:contract_interface(json, Contract, [no_implicit_stdlib]), ?assertEqual([MapACI], JSON), ?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)). @@ -98,7 +98,7 @@ aci_test_contract(Name) -> {ok, ContractStub} = aeso_aci:render_aci_json(JSON), io:format("STUB:\n~s\n", [ContractStub]), - check_stub(ContractStub, [{src_file, Name}]), + check_stub(ContractStub, [{src_file, Name}, no_implicit_stdlib]), ok. diff --git a/test/aeso_parser_tests.erl b/test/aeso_parser_tests.erl index dcbbcd0..9b54d94 100644 --- a/test/aeso_parser_tests.erl +++ b/test/aeso_parser_tests.erl @@ -15,7 +15,7 @@ simple_contracts_test_() -> ?assertMatch( [{contract, _, {con, _, "Identity"}, [{letfun, _, {id, _, "id"}, [{arg, _, {id, _, "x"}, {id, _, "_"}}], {id, _, "_"}, - {id, _, "x"}}]}], parse_string(Text)), + {id, _, "x"}}]}], parse_string(Text, [no_implicit_stdlib])), ok end}, {"Operator precedence test.", @@ -71,21 +71,23 @@ parse_contract(Name) -> roundtrip_contract(Name) -> round_trip(aeso_test_utils:read_contract(Name)). -parse_string(Text) -> - case aeso_parser:string(Text) of +parse_string(Text) -> parse_string(Text, []). + +parse_string(Text, Opts) -> + case aeso_parser:string(Text, Opts) of {ok, Contract} -> Contract; Err -> error(Err) end. parse_expr(Text) -> [{letval, _, _, _, Expr}] = - parse_string("let _ = " ++ Text), + parse_string("let _ = " ++ Text, [no_implicit_stdlib]), Expr. round_trip(Text) -> - Contract = parse_string(Text), + Contract = parse_string(Text, [no_implicit_stdlib]), Text1 = prettypr:format(aeso_pretty:decls(Contract)), - Contract1 = parse_string(Text1), + Contract1 = parse_string(Text1, [no_implicit_stdlib]), NoSrcLoc = remove_line_numbers(Contract), NoSrcLoc1 = remove_line_numbers(Contract1), ?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)). -- 2.30.2 From bff1acae698d2fba1ef2754b17bb101891f0958e Mon Sep 17 00:00:00 2001 From: radrow Date: Tue, 16 Jul 2019 16:13:30 +0200 Subject: [PATCH 07/19] Refixed imports and added few tests --- src/aeso_parser.erl | 11 +- src/aeso_stdlib.erl | 191 ++++++++++++++---------- test/aeso_compiler_tests.erl | 3 +- test/contracts/cyclic_include.aes | 4 + test/contracts/cyclic_include_back.aes | 4 + test/contracts/cyclic_include_forth.aes | 4 + test/contracts/list_comp.aes | 2 - test/contracts/stdlib_include.aes | 3 + 8 files changed, 139 insertions(+), 83 deletions(-) create mode 100644 test/contracts/cyclic_include.aes create mode 100644 test/contracts/cyclic_include_back.aes create mode 100644 test/contracts/cyclic_include_forth.aes create mode 100644 test/contracts/stdlib_include.aes diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 4cce718..6faf193 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -21,8 +21,11 @@ string(String) -> -spec string(string(), compiler:options()) -> parse_result(). -string(String, Opts) -> - string(String, sets:new(), Opts). +string(String, Opts) -> io:format("STRING WITH OPTS: ~p\n", [Opts]), + case lists:keyfind(src_file, 1, Opts) of + {src_file, File} -> string(String, sets:add_element(File, sets:new()), Opts); + false -> string(String, sets:new(), Opts) + end. -spec string(string(), sets:set(string()), aeso_compiler:options()) -> parse_result(). string(String, Included, Opts) -> @@ -553,8 +556,8 @@ expand_includes([{include, Ann, S = {string, _, File}} | AST], Included, Acc, Op case {read_file(File, Opts), maps:find(File, aeso_stdlib:stdlib())} of {{ok, _}, {ok,_ }} -> return_error(ann_pos(Ann), "Illegal redefinition of standard library " ++ File); - {_, {ok, Lib}} -> - case string(Lib, Included1, Opts1) of + {_, {ok, Lib}} -> + case string(Lib, Included1, [no_implicit_stdlib, Opts1]) of {ok, AST1} -> expand_includes(AST1 ++ AST, Included1, Acc, Opts); Err = {error, _} -> diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index 4988b54..8cde77e 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -17,101 +17,129 @@ stdlib() -> stdlib_list() -> [ {<<"List.aes">>, std_list()} - %% , {<<"Func.aes">>, std_function()} + , {<<"Func.aes">>, std_func()} ]. -std_function() -> +std_func() -> " namespace Func = - function id(x) = x + function id(x : 'a) : 'a = x - function const(x) = (y) => x + function const(x : 'a) : 'b => 'a = (y) => x - function comp(f, g) = (x) => f(g(x)) + function flip(f : ('a, 'b) => 'c) : ('b, 'a) => 'c = (b, a) => f(a, b) - function iter(n, f) = iter_(n, f, (x) => x) - private function iter_(n, f, acc) = + function comp(f : 'b => 'c, g : 'a => 'b) : 'a => 'c = (x) => f(g(x)) + + function pipe(f : 'a => 'b, g : 'b => 'c) : 'a => 'c = (x) => g(f(x)) + + function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => x) + private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a = if(n == 0) acc elif(n == 1) comp(f, acc) else iter_(n / 2, f, if(n mod 2 == 0) comp(acc, acc) else comp(f, comp(acc, acc))) - function curry2(f) = (x) => (y) => f(x, y) - function curry3(f) = (x) => (y) => (z) => f(x, y, z) + function z(f : ('a => 'a, 'a) => 'a) : 'a => 'a = (x) => f(z(f), x) - function uncurry2(f) = (x, y) => f(x)(y) - function uncurry3(f) = (x, y, z) => f(x)(y)(z) + function curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) = + (x) => (y) => f(x, y) + function curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd)) = + (x) => (y) => (z) => f(x, y, z) - function tuplify2(f) = (t) => switch(t) - (x, y) => f(x, y) - function tuplify3(f) = (t) => switch(t) - (x, y, z) => f(x, y, z) + function uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c = + (x, y) => f(x)(y) + function uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd = + (x, y, z) => f(x)(y)(z) - function untuplify2(f) = (x, y) => f((x, y)) - function untuplify3(f) = (x, y, z) => f((x, y, z)) +/* TODO : parser fails here, probably a bug + function tuplify2(f : ('a, 'b) => 'c) : (('a, 'b)) => 'c = + (t) => switch(t) + (x, y) => f(x, y) + function tuplify3(f : ('a, 'b, 'c) => 'd) : (('a, 'b, 'c)) => 'd = + (t) => switch(t) + (x, y, z) => f(x, y, z) + + function untuplify2(f : (('a, 'b)) => 'c) : ('a, 'b) => 'c = + (x, y) => f((x, y)) + function untuplify3(f : (('a, 'b, 'c)) => 'd) : ('a, 'b, 'c) => 'd = + (x, y, z) => f((x, y, z)) + */ ". -std_list() -> -"namespace List =\n function flat_map() = 3\n". -x() ->" +std_list() ->" namespace List = - function empty(l) = switch(l) + function empty(l : list('a)) : bool = switch(l) [] => true _ => false - - function first(l) = switch(l) + function first(l : list('a)) : option('a) = switch(l) [] => None h::_ => Some(h) - function tail(l) = switch(l) + function tail(l : list('a)) : option(list('a)) = switch(l) [] => None _::t => Some(t) - function last(l) = switch(l) + function last(l : list('a)) : option('a) = switch(l) [] => None [x] => Some(x) _::t => last(t) - function find(p, l) = switch(l) + function find(p : 'a => bool, l : list('a)) : option('a) = switch(l) [] => None h::t => if(p(h)) Some(h) else find(p, t) - function find_all(p, l) = find_all_(p, l, []) - private function find_all_(p, l, acc) = switch(l) + function find_all(p : 'a => bool, l : list('a)) : list('a) = find_all_(p, l, []) + private function find_all_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) [] => reverse(acc) h::t => find_all_(p, t, if(p(h)) h::acc else acc) - function find_indices(p, l) = find_indices_(p, l, 0, []) - private function find_indices_(p, l, n, acc) = switch(l) + function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, []) + private function find_indices_( p : 'a => bool + , l : list('a) + , n : int + , acc : list(int) + ) : list(int) = switch(l) [] => reverse(acc) h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc) - function nth(n, l) = switch(l) + function nth(n : int, l : list('a)) : option('a) = switch(l) h::t => if(n == 0) Some(h) else nth(n-1, t) [] => None - function get(n, l) = switch(l) + /* Unsafe version of `nth` */ + function get(n : int, l : list('a)) : 'a = switch(l) [] => abort(\"Out of index get\") h::t => if(n == 0) h else get(n-1, t) - function length(l) = length_(l, 0) - private function length_(l, acc) = switch(l) + function length(l : list('a)) : int = length_(l, 0) + private function length_(l : list('a), acc : int) : int = switch(l) [] => acc _::t => length_(t, acc + 1) - /* Unsafe. Makes `e` be `n`th element of `l`. Crashes on over/underflow */ - function insert_at(n, e, l) = if(n<0) abort(\"insert_at underflow\") else insert_at_(n, e, l, []) - private function insert_at_(n, e, l, acc) = + /* Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow */ + function replace_at(n : int, e : 'a, l : list('a)) : list('a) = + if(n<0) abort(\"insert_at underflow\") else replace_at_(n, e, l, []) + private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = + switch(l) + [] => abort(\"replace_at overflow\") + h::t => if (n == 0) reverse(e::acc) ++ t + else replace_at_(n-1, e, t, h::acc) + + /* Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow */ + function insert_at(n : int, e : 'a, l : list('a)) : list('a) = + if(n<0) abort(\"insert_at underflow\") else insert_at_(n, e, l, []) + private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = if (n == 0) reverse(e::acc) ++ l else switch(l) [] => abort(\"insert_at overflow\") h::t => insert_at_(n-1, e, t, h::acc) - function insert_by(f: (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = + function insert_by(f : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = switch(l) [] => [x] (e :: l') => @@ -120,51 +148,51 @@ namespace List = else x :: l - - function foldr(cons, nil, l) = switch(l) + function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l) [] => nil h::t => cons(h, foldr(cons, nil, t)) - function foldl(rcons, acc, l) = switch(l) + function foldl(rcons : ('b, 'a) => 'b, acc : 'b, l : list('a)) : 'b = switch(l) [] => acc h::t => foldl(rcons, rcons(acc, h), t) - function foreach(f : 'a => 'b, l : list('a)) = + function foreach(f : 'a => 'b, l : list('a)) : () = switch(l) - [] => [] + [] => () e :: l' => f(e) foreach(f, l') - function reverse(l) = foldl((lst, el) => el :: lst, [], l) + function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l) - function map(f, l) = map_(f, l, []) - private function map_(f, l, acc) = switch(l) + function map(f : 'a => 'b, l : list('a)) : list('b) = map_(f, l, []) + private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l) [] => reverse(acc) h::t => map_(f, t, f(h)::acc) - function flat_map(f, l) = switch(l) - [] => [] - h::t => f(h) ++ flat_map(f, t) + function flat_map(f : 'a => list('b), l : list('a)) : list('b) = flat_map_(f, l, []) + private function flat_map_(f : 'a => list('b), l : list('a), acc : list('b)) : list('b) = switch(l) + [] => reverse(acc) + h::t => flat_map_(f, t, reverse(f(h)) ++ acc) - - function filter(p, l) = filter_(p, l, []) - private function filter_(p, l, acc) = switch(l) + function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, []) + private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) [] => reverse(acc) h::t => filter_(p, t, if(p(h)) h::acc else acc) /* Take `n` first elements */ - function take(n, l) = if(n < 0) abort(\"Take negative number of elements\") else take_(n, l, []) - private function take_(n, l, acc) = + function take(n : int, l : list('a)) : list('a) = + if(n < 0) abort(\"Take negative number of elements\") else take_(n, l, []) + private function take_(n : int, l : list('a), acc : list('a)) : list('a) = if(n == 0) reverse(acc) else switch(l) [] => reverse(acc) h::t => take_(n-1, t, h::acc) /* Drop `n` first elements */ - function drop(n, l) = + function drop(n : int, l : list('a)) : list('a) = if(n < 0) abort(\"Drop negative number of elements\") elif (n == 0) l else switch(l) @@ -172,66 +200,77 @@ namespace List = h::t => drop(n-1, t) /* Get the longest prefix of a list in which every element matches predicate `p` */ - function take_while(p, l) = take_while_(p, l, []) - private function take_while_(p, l, acc) = switch(l) + function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, []) + private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) [] => reverse(acc) h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc) /* Drop elements from `l` until `p` holds */ - function drop_while(p, l) = switch(l) + function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l) [] => [] h::t => if(p(h)) drop_while(p, t) else l /* Splits list into two lists of elements that respectively match and don't match predicate `p` */ - function partition(p, l) = partition_(p, l, [], []) - private function partition_(p, l, acc_t, acc_f) = switch(l) + function partition(p : 'a => bool, l : list('a)) : (list('a), list('a)) = partition_(p, l, [], []) + private function partition_( p : 'a => bool + , l : list('a) + , acc_t : list('a) + , acc_f : list('a) + ) : (list('a), list('a)) = switch(l) [] => (reverse(acc_t), reverse(acc_f)) h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) - function concats(ll) = foldr((l1, l2) => l1 ++ l2, [], ll) + function concats(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll) - function all(p, l) = foldl((prev, next) => prev && p(next), true, l) + function all(p : 'a => bool, l : list('a)) : bool = foldl((prev, next) => prev && p(next), true, l) - function any(p, l) = foldl((prev, next) => prev || p(next), false, l) + function any(p : 'a => bool, l : list('a)) : bool = foldl((prev, next) => prev || p(next), false, l) - function sum(l) = foldl ((a, b) => a + b, 0, l) + function sum(l : list(int)) : int = foldl ((a, b) => a + b, 0, l) - function product(l) = foldl((a, b) => a * b, 1, l) + function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l) /* Zips two list by applying bimapping function on respective elements. Drops longer tail. */ - function zip_with(f, l1, l2) = zip_with_(f, l1, l2, []) - private function zip_with_(f, l1, l2, acc) = switch ((l1, l2)) + function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, []) + private function zip_with_( f : ('a, 'b) => 'c + , l1 : list('a) + , l2 : list('b) + , acc : list('c) + ) : list('c) = switch ((l1, l2)) (h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc) _ => reverse(acc) /* Zips two lists into list of pairs. Drops longer tail. */ - function zip(l1, l2) = zip_with((a, b) => (a, b), l1, l2) + function zip(l1 : list('a), l2 : list('b)) : list(('a, 'b)) = zip_with((a, b) => (a, b), l1, l2) - function unzip(l) = unzip_(l, [], []) - private function unzip_(l, acc_l, acc_r) = switch(l) + function unzip(l : list(('a, 'b))) : (list('a), list('b)) = unzip_(l, [], []) + private function unzip_( l : list(('a, 'b)) + , acc_l : list('a) + , acc_r : list('b) + ) : (list('a), list('b)) = switch(l) [] => (reverse(acc_l), reverse(acc_r)) (left, right)::t => unzip_(t, left::acc_l, right::acc_r) // TODO: Improve? - function sort(lesser_cmp, l) = switch(l) + 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) - function intersperse(delim, l) = intersperse_(delim, l, []) - private function intersperse_(delim, l, acc) = switch(l) + function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, []) + private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) [] => reverse(acc) [e] => reverse(e::acc) h::t => intersperse_(delim, t, h::delim::acc) - function index(l) = index_(l, 0, []) - private function index_(l, n, acc) = switch(l) + function enumerate(l : list('a)) : list((int, 'a)) = enumerate_(l, 0, []) + private function enumerate_(l : list('a), n : int, acc : list((int, 'a))) : list((int, 'a)) = switch(l) [] => reverse(acc) - h::t => index_(t, n + 1, (n, h)::acc) + h::t => enumerate_(t, n + 1, (n, h)::acc) ". diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 36af710..7a8d2e0 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -119,7 +119,8 @@ compilable_contracts() -> "bytes_to_x", "aens", "tuple_match", - "list_comp" + "cyclic_include", + "stdlib_include" ]. not_yet_compilable(fate) -> []; diff --git a/test/contracts/cyclic_include.aes b/test/contracts/cyclic_include.aes new file mode 100644 index 0000000..3cc2cfb --- /dev/null +++ b/test/contracts/cyclic_include.aes @@ -0,0 +1,4 @@ +include "cyclic_include_forth.aes" + +contract CI = + entrypoint ci() = Back.back() + Forth.forth() diff --git a/test/contracts/cyclic_include_back.aes b/test/contracts/cyclic_include_back.aes new file mode 100644 index 0000000..fd92a15 --- /dev/null +++ b/test/contracts/cyclic_include_back.aes @@ -0,0 +1,4 @@ +include "cyclic_include_forth.aes" + +namespace Back = + function back() = 2 diff --git a/test/contracts/cyclic_include_forth.aes b/test/contracts/cyclic_include_forth.aes new file mode 100644 index 0000000..765a87d --- /dev/null +++ b/test/contracts/cyclic_include_forth.aes @@ -0,0 +1,4 @@ +include "cyclic_include_back.aes" + +namespace Forth = + function forth() = 3 diff --git a/test/contracts/list_comp.aes b/test/contracts/list_comp.aes index de3359e..ac6fb9d 100644 --- a/test/contracts/list_comp.aes +++ b/test/contracts/list_comp.aes @@ -1,5 +1,3 @@ -include "List.aes" - contract ListComp = entrypoint sample1() = [1,2,3] diff --git a/test/contracts/stdlib_include.aes b/test/contracts/stdlib_include.aes new file mode 100644 index 0000000..6149db2 --- /dev/null +++ b/test/contracts/stdlib_include.aes @@ -0,0 +1,3 @@ +contract StdInc = + entrypoint test() = List.map((x) => Func.id(x), [1,2,3,4]) + -- 2.30.2 From 672ffd6ce1c7792cbd5efc4afca711f7619fbb01 Mon Sep 17 00:00:00 2001 From: radrow Date: Tue, 16 Jul 2019 16:25:19 +0200 Subject: [PATCH 08/19] Added fail test --- test/aeso_compiler_tests.erl | 3 +++ test/contracts/list_comp_fail.aes | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 test/contracts/list_comp_fail.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 7a8d2e0..4d2779a 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -356,4 +356,7 @@ failing_contracts() -> <<"Use 'entrypoint' for declaration of foo (at line 6, column 3):\n entrypoint foo : () => unit">>, <<"Use 'entrypoint' instead of 'function' for public function foo (at line 10, column 3):\n entrypoint foo() = ()">>, <<"Use 'entrypoint' instead of 'function' for public function foo (at line 6, column 3):\n entrypoint foo : () => unit">>]} + , {"list_comp_fail", + [<<"Cannot unify int\n and list('a)\nwhen checking rvalue of list comprehension binding at line 2, column 36\n 1 : int\nagainst type \n list('a)">> + ]} ]. diff --git a/test/contracts/list_comp_fail.aes b/test/contracts/list_comp_fail.aes new file mode 100644 index 0000000..41321cb --- /dev/null +++ b/test/contracts/list_comp_fail.aes @@ -0,0 +1,2 @@ +contract ListCompBad = + entrypoint failing() = [x | x <- 1] -- 2.30.2 From b0b560d335bd64feeaa4a2cb2a2c42a8d55583ae Mon Sep 17 00:00:00 2001 From: radrow Date: Tue, 16 Jul 2019 16:28:09 +0200 Subject: [PATCH 09/19] Undelete removed type spec --- src/aeso_ast_to_fcode.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 5a46718..3ca0f77 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -360,6 +360,7 @@ make_let(Expr, Body) -> {'let', X, Expr, Body({var, X})} end. +-spec expr_to_fcode(env(), aeso_syntax:expr()) -> fexpr(). expr_to_fcode(Env, {typed, _, Expr, Type}) -> expr_to_fcode(Env, Type, Expr); expr_to_fcode(Env, Expr) -> -- 2.30.2 From b41773ebae2507f6f0586ffd8e3ae7dc207ffc3b Mon Sep 17 00:00:00 2001 From: radrow Date: Tue, 16 Jul 2019 16:33:15 +0200 Subject: [PATCH 10/19] Remove typo --- src/aeso_parser.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 6faf193..a297bcc 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -521,7 +521,7 @@ parse_pattern(E = {bool, _, _}) -> E; parse_pattern(E = {bytes, _, _}) -> E; parse_pattern(E = {string, _, _}) -> E; parse_pattern(E = {char, _, _}) -> E; -parse_pattern(E) -> bad_expr_err("Not a valid patxstern", E). +parse_pattern(E) -> bad_expr_err("Not a valid pattern", E). -spec parse_field_pattern(aeso_syntax:field(aeso_syntax:expr())) -> aeso_parse_lib:parser(aeso_syntax:field(aeso_syntax:pat())). parse_field_pattern({field, Ann, F, E}) -> -- 2.30.2 From 8d4e246686c8d69ffacea4354ad3758425979bf7 Mon Sep 17 00:00:00 2001 From: radrow Date: Tue, 16 Jul 2019 16:36:59 +0200 Subject: [PATCH 11/19] Fix iter function --- src/aeso_stdlib.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index 8cde77e..b5cf34c 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -38,9 +38,7 @@ namespace Func = private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a = if(n == 0) acc elif(n == 1) comp(f, acc) - else iter_(n / 2, f, if(n mod 2 == 0) comp(acc, acc) else comp(f, comp(acc, acc))) - - function z(f : ('a => 'a, 'a) => 'a) : 'a => 'a = (x) => f(z(f), x) + else else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc)) function curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) = (x) => (y) => f(x, y) -- 2.30.2 From a05e6ba8001eb1101435ee808642425375d80825 Mon Sep 17 00:00:00 2001 From: radrow Date: Tue, 16 Jul 2019 17:10:50 +0200 Subject: [PATCH 12/19] Fixed typo --- src/aeso_parser.erl | 2 +- src/aeso_stdlib.erl | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index a297bcc..34ff8d5 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -21,7 +21,7 @@ string(String) -> -spec string(string(), compiler:options()) -> parse_result(). -string(String, Opts) -> io:format("STRING WITH OPTS: ~p\n", [Opts]), +string(String, Opts) -> case lists:keyfind(src_file, 1, Opts) of {src_file, File} -> string(String, sets:add_element(File, sets:new()), Opts); false -> string(String, sets:new(), Opts) diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index b5cf34c..4458859 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -38,7 +38,7 @@ namespace Func = private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a = if(n == 0) acc elif(n == 1) comp(f, acc) - else else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc)) + else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc)) function curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) = (x) => (y) => f(x, y) @@ -50,19 +50,18 @@ namespace Func = function uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd = (x, y, z) => f(x)(y)(z) -/* TODO : parser fails here, probably a bug - function tuplify2(f : ('a, 'b) => 'c) : (('a, 'b)) => 'c = - (t) => switch(t) - (x, y) => f(x, y) - function tuplify3(f : ('a, 'b, 'c) => 'd) : (('a, 'b, 'c)) => 'd = - (t) => switch(t) - (x, y, z) => f(x, y, z) +// TODO : parser fails here, probably a bug. Uncomment when resolved. +// function tuplify2(f : ('a, 'b) => 'c) : (('a, 'b)) => 'c = +// (t) => switch(t) +// (x, y) => f(x, y) +// function tuplify3(f : ('a, 'b, 'c) => 'd) : (('a, 'b, 'c)) => 'd = +// (t) => switch(t) +// (x, y, z) => f(x, y, z) - function untuplify2(f : (('a, 'b)) => 'c) : ('a, 'b) => 'c = - (x, y) => f((x, y)) - function untuplify3(f : (('a, 'b, 'c)) => 'd) : ('a, 'b, 'c) => 'd = - (x, y, z) => f((x, y, z)) - */ +// function untuplify2(f : (('a, 'b)) => 'c) : ('a, 'b) => 'c = +// (x, y) => f((x, y)) +// function untuplify3(f : (('a, 'b, 'c)) => 'd) : ('a, 'b, 'c) => 'd = +// (x, y, z) => f((x, y, z)) ". std_list() ->" -- 2.30.2 From c68b52d78d471ad70a5426b62c800f9d06337536 Mon Sep 17 00:00:00 2001 From: radrow Date: Wed, 17 Jul 2019 15:45:21 +0200 Subject: [PATCH 13/19] Added if guards and let statements in list comp --- src/aeso_ast_infer_types.erl | 35 +++++++++++++++++++++++++++++++++++ src/aeso_ast_to_fcode.erl | 9 +++++++++ src/aeso_ast_to_icode.erl | 6 ++++++ src/aeso_parser.erl | 12 +++++++++++- src/aeso_stdlib.erl | 2 +- src/aeso_syntax.erl | 6 ++++-- test/contracts/list_comp.aes | 9 +++++++++ 7 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index b28bca8..0f506d8 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1093,6 +1093,41 @@ infer_expr(Env, {list_comp, As, Yield, [{comprehension_bind, Arg, BExpr}|Rest]}) , As , {list_comp, As, TypedYield, [{comprehension_bind, {typed, Arg, BindVarType}, TypedBind}|TypedRest]} , ResType}; +infer_expr(Env, {list_comp, AttrsL, Yield, [{comprehension_if, AttrsIF, Cond}|Rest]}) -> + NewCond = check_expr(Env, Cond, {id, AttrsIF, "bool"}), + {typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} = + infer_expr(Env, {list_comp, AttrsL, Yield, Rest}), + { typed + , AttrsL + , {list_comp, AttrsL, TypedYield, [{comprehension_if, AttrsIF, NewCond}|TypedRest]} + , ResType}; +infer_expr(Env, {list_comp, AsLC, Yield, [{letval, AsLV, Pattern, Type, E}|Rest]}) -> + NewE = {typed, _, _, PatType} = infer_expr(Env, {typed, AsLV, E, arg_type(Type)}), + BlockType = fresh_uvar(AsLV), + {'case', _, NewPattern, NewRest} = + infer_case( Env + , AsLC + , Pattern + , PatType + , {list_comp, AsLC, Yield, Rest} + , BlockType), + {typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} = NewRest, + { typed + , AsLC + , {list_comp, AsLC, TypedYield, [{letval, AsLV, NewPattern, Type, NewE}|TypedRest]} + , ResType + }; +infer_expr(Env, {list_comp, AsLC, Yield, [Def={letfun, AsLF, _, _, _, _}|Rest]}) -> + {{Name, TypeSig}, LetFun} = infer_letfun(Env, Def), + FunT = freshen_type(AsLF, typesig_to_fun_t(TypeSig)), + NewE = bind_var({id, AsLF, Name}, FunT, Env), + {typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} = + infer_expr(NewE, {list_comp, AsLC, Yield, Rest}), + { typed + , AsLC + , {list_comp, AsLC, TypedYield, [LetFun|TypedRest]} + , ResType + }; infer_expr(Env, {typed, As, Body, Type}) -> Type1 = check_type(Env, Type), {typed, _, NewBody, NewType} = check_expr(Env, Body, Type1), diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 3ca0f77..cc51155 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -451,6 +451,15 @@ expr_to_fcode(Env, _Type, {list_comp, As, Yield, [{comprehension_bind, {typed, { Bind = {lam, [Arg], expr_to_fcode(Env1, {list_comp, As, Yield, Rest})}, {def_u, FlatMap, _} = resolve_fun(Env, ["List", "flat_map"]), {def, FlatMap, [Bind, expr_to_fcode(Env, BindExpr)]}; +expr_to_fcode(Env, Type, {list_comp, As, Yield, [{comprehension_if, _, Cond}|Rest]}) -> + make_if(expr_to_fcode(Env, Cond), + expr_to_fcode(Env, Type, {list_comp, As, Yield, Rest}), + nil + ); +expr_to_fcode(Env, Type, {list_comp, As, Yield, [LV = {letval, _, _, _, _}|Rest]}) -> + expr_to_fcode(Env, Type, {block, As, [LV, {list_comp, As, Yield, Rest}]}); +expr_to_fcode(Env, Type, {list_comp, As, Yield, [LF = {letfun, _, _, _, _, _}|Rest]}) -> + expr_to_fcode(Env, Type, {block, As, [LF, {list_comp, As, Yield, Rest}]}); %% Conditionals expr_to_fcode(Env, _Type, {'if', _, Cond, Then, Else}) -> diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index f06b02d..6aac6c1 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -531,6 +531,12 @@ ast_body({list_comp, As, Yield, [{comprehension_bind, {typed, Arg, ArgType}, Bin , ast_body(BindExpr, Icode) ] }; +ast_body({list_comp, As, Yield, [{comprehension_if, AsIF, Cond}|Rest]}, Icode) -> + ast_body({'if', AsIF, Cond, {list_comp, As, Yield, Rest}, {list, As, []}}, Icode); +ast_body({list_comp, As, Yield, [LV = {letval, _, _, _, _}|Rest]}, Icode) -> + ast_body({block, As, [LV, {list_comp, As, Yield, Rest}]}, Icode); +ast_body({list_comp, As, Yield, [LF = {letfun, _, _, _, _, _}|Rest]}, Icode) -> + ast_body({block, As, [LF, {list_comp, As, Yield, Rest}]}, Icode); ast_body({'if',_,Dec,Then,Else}, Icode) -> #ifte{decision = ast_body(Dec, Icode) ,then = ast_body(Then, Icode) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 34ff8d5..f39e68e 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -246,12 +246,22 @@ exprAtom() -> , {bool, keyword(false), false} , ?LET_P(Fs, brace_list(?LAZY_P(field_assignment())), record(Fs)) , {list, [], bracket_list(Expr)} - , ?RULE(keyword('['), Expr, token('|'), comma_sep(?LAZY_P(comprehension_bind())), tok(']'), list_comp_e(_1, _2, _4)) + , ?RULE(keyword('['), Expr, token('|'), comma_sep(comprehension_exp()), tok(']'), list_comp_e(_1, _2, _4)) , ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4)) , ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2)) ]) end). +comprehension_exp() -> + ?LAZY_P(choice( + [ comprehension_bind() + , letdecl() + , comprehension_if() + ])). + +comprehension_if() -> + ?RULE(keyword('if'), parens(expr()), {comprehension_if, _1, _2}). + comprehension_bind() -> ?RULE(id(), tok('<-'), expr(), {comprehension_bind, _1, _3}). diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index 4458859..560de69 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -67,7 +67,7 @@ namespace Func = std_list() ->" namespace List = - function empty(l : list('a)) : bool = switch(l) + function is_empty(l : list('a)) : bool = switch(l) [] => true _ => false diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index e40ae44..403dfaa 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -92,7 +92,7 @@ | {proj, ann(), expr(), id()} | {tuple, ann(), [expr()]} | {list, ann(), [expr()]} - | {list_comp, ann(), expr(), [comprehension_bind()]} + | {list_comp, ann(), expr(), [comprehension_exp()]} | {typed, ann(), expr(), type()} | {record, ann(), [field(expr())]} | {record, ann(), expr(), [field(expr())]} %% record update @@ -105,7 +105,9 @@ | id() | qid() | con() | qcon() | constant(). --type comprehension_bind() :: [{comprehension_bind, ann(), id(), type()}]. +-type comprehension_exp() :: [{ comprehension_bind, ann(), id(), expr()} + | {comprehension_if, expr()} + | letbind()]. -type arg_expr() :: expr() | {named_arg, ann(), id(), expr()}. diff --git a/test/contracts/list_comp.aes b/test/contracts/list_comp.aes index ac6fb9d..266f2e8 100644 --- a/test/contracts/list_comp.aes +++ b/test/contracts/list_comp.aes @@ -12,3 +12,12 @@ contract ListComp = entrypoint l3() = [x ++ y | x <- [[":)"] | x <- [1,2]] , y <- [[":("]]] entrypoint l3_true() = [[":)", ":("], [":)", ":("]] + + entrypoint l4() = [(a, b, c) | let is_pit(a, b, c) = a*a + b*b == c*c + , let base = [1,2,3,4,5,6,7,8,9,10] + , a <- base + , b <- base, if (b >= a) + , c <- base, if (c >= b) + , if (is_pit(a, b, c)) + ] + entrypoint l4_true() = [(3, 4, 5), (6, 8, 10)] -- 2.30.2 From 70f963f4b9049cd5e7ee5491f7eae90f1a782ee1 Mon Sep 17 00:00:00 2001 From: radrow Date: Thu, 18 Jul 2019 12:59:08 +0200 Subject: [PATCH 14/19] Added more fail tests --- test/aeso_compiler_tests.erl | 8 +++++++- test/contracts/list_comp_bad_shadow.aes | 2 ++ test/contracts/list_comp_if_not_bool.aes | 2 ++ .../{list_comp_fail.aes => list_comp_not_a_list.aes} | 0 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 test/contracts/list_comp_bad_shadow.aes create mode 100644 test/contracts/list_comp_if_not_bool.aes rename test/contracts/{list_comp_fail.aes => list_comp_not_a_list.aes} (100%) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 4d2779a..f0b73bd 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -356,7 +356,13 @@ failing_contracts() -> <<"Use 'entrypoint' for declaration of foo (at line 6, column 3):\n entrypoint foo : () => unit">>, <<"Use 'entrypoint' instead of 'function' for public function foo (at line 10, column 3):\n entrypoint foo() = ()">>, <<"Use 'entrypoint' instead of 'function' for public function foo (at line 6, column 3):\n entrypoint foo : () => unit">>]} - , {"list_comp_fail", + , {"list_comp_not_a_list", [<<"Cannot unify int\n and list('a)\nwhen checking rvalue of list comprehension binding at line 2, column 36\n 1 : int\nagainst type \n list('a)">> ]} + , {"list_comp_if_not_bool", + [<<"Cannot unify int\n and bool\nwhen checking the type of the expression at line 2, column 44\n 3 : int\nagainst the expected type\n bool">> + ]} + , {"list_comp_bad_shadow", + [<<"Cannot unify int\n and string\nwhen checking the type of the pattern at line 2, column 53\n x : int\nagainst the expected type\n string">> + ]} ]. diff --git a/test/contracts/list_comp_bad_shadow.aes b/test/contracts/list_comp_bad_shadow.aes new file mode 100644 index 0000000..b54735c --- /dev/null +++ b/test/contracts/list_comp_bad_shadow.aes @@ -0,0 +1,2 @@ +contract BadComp = + entrypoint failing() = [x + 1 | x <- [1,2,3], let x = "XD"] diff --git a/test/contracts/list_comp_if_not_bool.aes b/test/contracts/list_comp_if_not_bool.aes new file mode 100644 index 0000000..cd3c458 --- /dev/null +++ b/test/contracts/list_comp_if_not_bool.aes @@ -0,0 +1,2 @@ +contract BadComp = + entrypoint failing() = [x | x <- [], if (3)] diff --git a/test/contracts/list_comp_fail.aes b/test/contracts/list_comp_not_a_list.aes similarity index 100% rename from test/contracts/list_comp_fail.aes rename to test/contracts/list_comp_not_a_list.aes -- 2.30.2 From aa5b686ea86ad795b1c25ba47a3bc4286c93090e Mon Sep 17 00:00:00 2001 From: radrow Date: Thu, 18 Jul 2019 14:13:28 +0200 Subject: [PATCH 15/19] Option stliv --- src/.#aeso_stdlib.erl | 1 + src/aeso_stdlib.erl | 82 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) create mode 120000 src/.#aeso_stdlib.erl diff --git a/src/.#aeso_stdlib.erl b/src/.#aeso_stdlib.erl new file mode 120000 index 0000000..4d1b76a --- /dev/null +++ b/src/.#aeso_stdlib.erl @@ -0,0 +1 @@ +radek@radek-VirtualBox.2106:1563435182 \ No newline at end of file diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index 560de69..8392d6d 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -18,6 +18,7 @@ stdlib() -> stdlib_list() -> [ {<<"List.aes">>, std_list()} , {<<"Func.aes">>, std_func()} + , {<<"Option.aes">>, std_option()} ]. std_func() -> @@ -153,7 +154,7 @@ namespace List = [] => acc h::t => foldl(rcons, rcons(acc, h), t) - function foreach(f : 'a => 'b, l : list('a)) : () = + function foreach(f : 'a => (), l : list('a)) : () = switch(l) [] => () e :: l' => @@ -271,3 +272,82 @@ namespace List = h::t => enumerate_(t, n + 1, (n, h)::acc) ". + +std_option() -> " +include \"List.aes\" + +namespace Option = + + function is_none(o : option('a)) : bool = switch(o) + None => true + Some(_) => false + + function is_some(o : option('a)) : bool = switch(o) + None => false + Some(_) => true + + + function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o) + None => n + Some(x) => s(x) + + function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o) + + function force(o : option('a)) : 'a = default(abort(\"Forced None value\"), o) + + function on_elem(f : 'a => (), o : option('a)) : () = match((), f, o) + + + function map(f : 'a => 'b, o : option('a)) : option('b) = switch(o) + None => None + Some(x) => Some(f(x)) + + function map2(f : ('a, 'b) => 'c + , o1 : option('a) + , o2 : option('b) + ) : option('c) = switch((o1, o2)) + (Some(x1), Some(x2)) => Some(f(x1, x2)) + _ => None + + function map3( f : ('a, 'b, 'c) => 'd + , o1 : option('a) + , o2 : option('b) + , o3 : option('c) + ) : option('d) = switch((o1, o2, o3)) + (Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3)) + _ => None + + function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o)) + (Some(ff), Some(xx)) => Some(ff(xx)) + _ => None + + function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o) + None => None + Some(x) => f(x) + + + function to_list(o : option('a)) : list('a) = switch(o) + None => [] + Some(x) => [x] + + function filter_options(l : list(option('a))) : list('a) = filter_options_(l, []) + private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l) + [] => List.reverse(acc) + None::t => filter_options_(t, acc) + Some(x)::t => filter_options_(t, x::acc) + + function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, []) + private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l) + [] => Some(List.reverse(acc)) + None::t => None + Some(x)::t => seq_options_(t, x::acc) + + + function choose(o1 : option('a), o2 : option('a)) : option('a) = + if(is_some(o1)) o1 else o2 + + function choose_first(l : list(option('a))) : option('a) = switch(l) + [] => None + None::t => choose_first(t) + Some(x)::_ => Some(x) +". -- 2.30.2 From 9101d2c7fee1041d5b76c40ac8b98824ac9d7eeb Mon Sep 17 00:00:00 2001 From: radrow Date: Thu, 18 Jul 2019 14:39:31 +0200 Subject: [PATCH 16/19] 2 and 3 tuple stdlib --- src/.#aeso_stdlib.erl | 1 - src/aeso_stdlib.erl | 66 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) delete mode 120000 src/.#aeso_stdlib.erl diff --git a/src/.#aeso_stdlib.erl b/src/.#aeso_stdlib.erl deleted file mode 120000 index 4d1b76a..0000000 --- a/src/.#aeso_stdlib.erl +++ /dev/null @@ -1 +0,0 @@ -radek@radek-VirtualBox.2106:1563435182 \ No newline at end of file diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index 8392d6d..75c8981 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -19,6 +19,8 @@ stdlib_list() -> [ {<<"List.aes">>, std_list()} , {<<"Func.aes">>, std_func()} , {<<"Option.aes">>, std_option()} + , {<<"Pair.aes">>, std_pair()} + , {<<"Triple.aes">>, std_triple()} ]. std_func() -> @@ -351,3 +353,67 @@ namespace Option = None::t => choose_first(t) Some(x)::_ => Some(x) ". + +std_pair() -> " +namespace Pair = + + function fst(t : ('a, 'b)) : 'a = switch(t) + (x, _) => x + + function snd(t : ('a, 'b)) : 'b = switch(t) + (_, y) => y + + + function map1(f : 'a => 'c, t : ('a, 'b)) : ('c, 'b) = switch(t) + (x, y) => (f(x), y) + + function map2(f : 'b => 'c, t : ('a, 'b)) : ('a, 'c) = switch(t) + (x, y) => (x, f(y)) + + function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a, 'b)) : ('c, 'd) = switch(t) + (x, y) => (f(x), g(y)) + + + function swap(t : ('a, 'b)) : ('b, 'a) = switch(t) + (x, y) => (y, x) +". + +std_triple() -> " +namespace Triple = + + function fst(t : ('a, 'b, 'c)) : 'a = switch(t) + (x, _, _) => x + + function snd(t : ('a, 'b, 'c)) : 'b = switch(t) + (_, y, _) => y + + function thd(t : ('a, 'b, 'c)) : 'c = switch(t) + (_, _, z) => z + + + function map1(f : 'a => 'm, t : ('a, 'b, 'c)) : ('m, 'b, 'c) = switch(t) + (x, y, z) => (f(x), y, z) + + function map2(f : 'b => 'm, t : ('a, 'b, 'c)) : ('a, 'm, 'c) = switch(t) + (x, y, z) => (x, f(y), z) + + function map3(f : 'c => 'm, t : ('a, 'b, 'c)) : ('a, 'b, 'm) = switch(t) + (x, y, z) => (x, y, f(z)) + + function trimap( f : 'a => 'x + , g : 'b => 'y + , h : 'c => 'z + , t : ('a, 'b, 'c) + ) : ('x, 'y, 'z) = switch(t) + (x, y, z) => (f(x), g(y), h(z)) + + + function swap(t : ('a, 'b, 'c)) : ('c, 'b, 'a) = switch(t) + (x, y, z) => (z, y, x) + + function rotr(t : ('a, 'b, 'c)) : ('c, 'a, 'b) = switch(t) + (x, y, z) => (z, x, y) + + function rotl(t : ('a, 'b, 'c)) : ('b, 'c, 'a) = switch(t) + (x, y, z) => (y, z, x) +". -- 2.30.2 From ba8138d89296e4e1df601cd45512fbd6c1a1d7f6 Mon Sep 17 00:00:00 2001 From: radrow Date: Mon, 5 Aug 2019 16:15:42 +0200 Subject: [PATCH 17/19] Updated stdlib to new syntax. Added recursor and changed all/any functions --- src/aeso_stdlib.erl | 154 +++++++++++++++++++---------------- test/aeso_compiler_tests.erl | 3 +- 2 files changed, 84 insertions(+), 73 deletions(-) diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index 75c8981..efc192b 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -37,6 +37,13 @@ namespace Func = function pipe(f : 'a => 'b, g : 'b => 'c) : 'a => 'c = (x) => g(f(x)) + function rapply(x : 'a, f : 'a => 'b) : 'b = f(x) + + /* The Z combinator - replacement for local and anonymous recursion. + */ + function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res = + (x) => f(recur(f), x) + function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => x) private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a = if(n == 0) acc @@ -53,18 +60,17 @@ namespace Func = function uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd = (x, y, z) => f(x)(y)(z) -// TODO : parser fails here, probably a bug. Uncomment when resolved. -// function tuplify2(f : ('a, 'b) => 'c) : (('a, 'b)) => 'c = -// (t) => switch(t) -// (x, y) => f(x, y) -// function tuplify3(f : ('a, 'b, 'c) => 'd) : (('a, 'b, 'c)) => 'd = -// (t) => switch(t) -// (x, y, z) => f(x, y, z) + function tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c = + (t) => switch(t) + (x, y) => f(x, y) + function tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd = + (t) => switch(t) + (x, y, z) => f(x, y, z) -// function untuplify2(f : (('a, 'b)) => 'c) : ('a, 'b) => 'c = -// (x, y) => f((x, y)) -// function untuplify3(f : (('a, 'b, 'c)) => 'd) : ('a, 'b, 'c) => 'd = -// (x, y, z) => f((x, y, z)) + function untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c = + (x, y) => f((x, y)) + function untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd = + (x, y, z) => f((x, y, z)) ". std_list() ->" @@ -75,25 +81,25 @@ namespace List = _ => false function first(l : list('a)) : option('a) = switch(l) - [] => None + [] => None h::_ => Some(h) function tail(l : list('a)) : option(list('a)) = switch(l) - [] => None + [] => None _::t => Some(t) function last(l : list('a)) : option('a) = switch(l) - [] => None + [] => None [x] => Some(x) _::t => last(t) function find(p : 'a => bool, l : list('a)) : option('a) = switch(l) - [] => None + [] => None h::t => if(p(h)) Some(h) else find(p, t) function find_all(p : 'a => bool, l : list('a)) : list('a) = find_all_(p, l, []) private function find_all_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) + [] => reverse(acc) h::t => find_all_(p, t, if(p(h)) h::acc else acc) function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, []) @@ -102,12 +108,12 @@ namespace List = , n : int , acc : list(int) ) : list(int) = switch(l) - [] => reverse(acc) + [] => reverse(acc) h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc) function nth(n : int, l : list('a)) : option('a) = switch(l) + [] => None h::t => if(n == 0) Some(h) else nth(n-1, t) - [] => None /* Unsafe version of `nth` */ function get(n : int, l : list('a)) : 'a = switch(l) @@ -117,7 +123,7 @@ namespace List = function length(l : list('a)) : int = length_(l, 0) private function length_(l : list('a), acc : int) : int = switch(l) - [] => acc + [] => acc _::t => length_(t, acc + 1) @@ -126,7 +132,7 @@ namespace List = if(n<0) abort(\"insert_at underflow\") else replace_at_(n, e, l, []) private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => abort(\"replace_at overflow\") + [] => abort(\"replace_at overflow\") h::t => if (n == 0) reverse(e::acc) ++ t else replace_at_(n-1, e, t, h::acc) @@ -136,12 +142,12 @@ namespace List = private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = if (n == 0) reverse(e::acc) ++ l else switch(l) - [] => abort(\"insert_at overflow\") + [] => abort(\"insert_at overflow\") h::t => insert_at_(n-1, e, t, h::acc) function insert_by(f : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = switch(l) - [] => [x] + [] => [x] (e :: l') => if(f(x, e)) e :: insert_by(f, x, l') @@ -149,16 +155,16 @@ namespace List = x :: l function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l) - [] => nil + [] => nil h::t => cons(h, foldr(cons, nil, t)) function foldl(rcons : ('b, 'a) => 'b, acc : 'b, l : list('a)) : 'b = switch(l) - [] => acc + [] => acc h::t => foldl(rcons, rcons(acc, h), t) - function foreach(f : 'a => (), l : list('a)) : () = + function foreach(f : 'a => unit, l : list('a)) : unit = switch(l) - [] => () + [] => () e :: l' => f(e) foreach(f, l') @@ -169,17 +175,17 @@ namespace List = function map(f : 'a => 'b, l : list('a)) : list('b) = map_(f, l, []) private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l) - [] => reverse(acc) + [] => reverse(acc) h::t => map_(f, t, f(h)::acc) function flat_map(f : 'a => list('b), l : list('a)) : list('b) = flat_map_(f, l, []) private function flat_map_(f : 'a => list('b), l : list('a), acc : list('b)) : list('b) = switch(l) - [] => reverse(acc) + [] => reverse(acc) h::t => flat_map_(f, t, reverse(f(h)) ++ acc) function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, []) private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) + [] => reverse(acc) h::t => filter_(p, t, if(p(h)) h::acc else acc) /* Take `n` first elements */ @@ -188,7 +194,7 @@ namespace List = private function take_(n : int, l : list('a), acc : list('a)) : list('a) = if(n == 0) reverse(acc) else switch(l) - [] => reverse(acc) + [] => reverse(acc) h::t => take_(n-1, t, h::acc) /* Drop `n` first elements */ @@ -196,36 +202,40 @@ namespace List = if(n < 0) abort(\"Drop negative number of elements\") elif (n == 0) l else switch(l) - [] => [] + [] => [] h::t => drop(n-1, t) /* Get the longest prefix of a list in which every element matches predicate `p` */ function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, []) private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) + [] => reverse(acc) h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc) /* Drop elements from `l` until `p` holds */ function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l) - [] => [] + [] => [] h::t => if(p(h)) drop_while(p, t) else l /* Splits list into two lists of elements that respectively match and don't match predicate `p` */ - function partition(p : 'a => bool, l : list('a)) : (list('a), list('a)) = partition_(p, l, [], []) + function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], []) private function partition_( p : 'a => bool , l : list('a) , acc_t : list('a) , acc_f : list('a) - ) : (list('a), list('a)) = switch(l) - [] => (reverse(acc_t), reverse(acc_f)) + ) : (list('a) * list('a)) = switch(l) + [] => (reverse(acc_t), reverse(acc_f)) h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) function concats(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll) - function all(p : 'a => bool, l : list('a)) : bool = foldl((prev, next) => prev && p(next), true, l) + function all(p : 'a => bool, l : list('a)) : bool = switch(l) + [] => true + h::t => if(p(h)) all(p, t) else false - function any(p : 'a => bool, l : list('a)) : bool = foldl((prev, next) => prev || p(next), false, l) + function any(p : 'a => bool, l : list('a)) : bool = switch(l) + [] => false + h::t => if(p(h)) true else any(p, t) function sum(l : list(int)) : int = foldl ((a, b) => a + b, 0, l) @@ -243,34 +253,34 @@ namespace List = _ => reverse(acc) /* Zips two lists into list of pairs. Drops longer tail. */ - function zip(l1 : list('a), l2 : list('b)) : list(('a, 'b)) = zip_with((a, b) => (a, b), l1, l2) + function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2) - function unzip(l : list(('a, 'b))) : (list('a), list('b)) = unzip_(l, [], []) - private function unzip_( l : list(('a, 'b)) + function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], []) + private function unzip_( l : list('a * 'b) , acc_l : list('a) , acc_r : list('b) - ) : (list('a), list('b)) = switch(l) - [] => (reverse(acc_l), reverse(acc_r)) + ) : (list('a) * list('b)) = switch(l) + [] => (reverse(acc_l), reverse(acc_r)) (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) function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, []) private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - [e] => reverse(e::acc) + [] => reverse(acc) + [e] => reverse(e::acc) h::t => intersperse_(delim, t, h::delim::acc) - function enumerate(l : list('a)) : list((int, 'a)) = enumerate_(l, 0, []) - private function enumerate_(l : list('a), n : int, acc : list((int, 'a))) : list((int, 'a)) = switch(l) - [] => reverse(acc) + function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, []) + private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l) + [] => reverse(acc) h::t => enumerate_(t, n + 1, (n, h)::acc) ". @@ -297,7 +307,7 @@ namespace Option = function force(o : option('a)) : 'a = default(abort(\"Forced None value\"), o) - function on_elem(f : 'a => (), o : option('a)) : () = match((), f, o) + function on_elem(f : 'a => unit, o : option('a)) : unit = match((), f, o) function map(f : 'a => 'b, o : option('a)) : option('b) = switch(o) @@ -334,14 +344,14 @@ namespace Option = function filter_options(l : list(option('a))) : list('a) = filter_options_(l, []) private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l) - [] => List.reverse(acc) - None::t => filter_options_(t, acc) + [] => List.reverse(acc) + None::t => filter_options_(t, acc) Some(x)::t => filter_options_(t, x::acc) function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, []) private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l) - [] => Some(List.reverse(acc)) - None::t => None + [] => Some(List.reverse(acc)) + None::t => None Some(x)::t => seq_options_(t, x::acc) @@ -350,70 +360,70 @@ namespace Option = function choose_first(l : list(option('a))) : option('a) = switch(l) [] => None - None::t => choose_first(t) + None::t => choose_first(t) Some(x)::_ => Some(x) ". std_pair() -> " namespace Pair = - function fst(t : ('a, 'b)) : 'a = switch(t) + function fst(t : ('a * 'b)) : 'a = switch(t) (x, _) => x - function snd(t : ('a, 'b)) : 'b = switch(t) + function snd(t : ('a * 'b)) : 'b = switch(t) (_, y) => y - function map1(f : 'a => 'c, t : ('a, 'b)) : ('c, 'b) = switch(t) + function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t) (x, y) => (f(x), y) - function map2(f : 'b => 'c, t : ('a, 'b)) : ('a, 'c) = switch(t) + function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t) (x, y) => (x, f(y)) - function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a, 'b)) : ('c, 'd) = switch(t) + function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t) (x, y) => (f(x), g(y)) - function swap(t : ('a, 'b)) : ('b, 'a) = switch(t) + function swap(t : ('a * 'b)) : ('b * 'a) = switch(t) (x, y) => (y, x) ". std_triple() -> " namespace Triple = - function fst(t : ('a, 'b, 'c)) : 'a = switch(t) + function fst(t : ('a * 'b * 'c)) : 'a = switch(t) (x, _, _) => x - function snd(t : ('a, 'b, 'c)) : 'b = switch(t) + function snd(t : ('a * 'b * 'c)) : 'b = switch(t) (_, y, _) => y - function thd(t : ('a, 'b, 'c)) : 'c = switch(t) + function thd(t : ('a * 'b * 'c)) : 'c = switch(t) (_, _, z) => z - function map1(f : 'a => 'm, t : ('a, 'b, 'c)) : ('m, 'b, 'c) = switch(t) + function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t) (x, y, z) => (f(x), y, z) - function map2(f : 'b => 'm, t : ('a, 'b, 'c)) : ('a, 'm, 'c) = switch(t) + function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t) (x, y, z) => (x, f(y), z) - function map3(f : 'c => 'm, t : ('a, 'b, 'c)) : ('a, 'b, 'm) = switch(t) + function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t) (x, y, z) => (x, y, f(z)) function trimap( f : 'a => 'x , g : 'b => 'y , h : 'c => 'z - , t : ('a, 'b, 'c) - ) : ('x, 'y, 'z) = switch(t) + , t : ('a * 'b * 'c) + ) : ('x * 'y * 'z) = switch(t) (x, y, z) => (f(x), g(y), h(z)) - function swap(t : ('a, 'b, 'c)) : ('c, 'b, 'a) = switch(t) + function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t) (x, y, z) => (z, y, x) - function rotr(t : ('a, 'b, 'c)) : ('c, 'a, 'b) = switch(t) + function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t) (x, y, z) => (z, x, y) - function rotl(t : ('a, 'b, 'c)) : ('b, 'c, 'a) = switch(t) + function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t) (x, y, z) => (y, z, x) ". diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index f0b73bd..2b51022 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -120,7 +120,8 @@ compilable_contracts() -> "aens", "tuple_match", "cyclic_include", - "stdlib_include" + "stdlib_include", + "list_comp" ]. not_yet_compilable(fate) -> []; -- 2.30.2 From 4d13e0117704a9ffce6cef945a2bb67d2b42fc9d Mon Sep 17 00:00:00 2001 From: radrow Date: Wed, 7 Aug 2019 12:56:16 +0200 Subject: [PATCH 18/19] Fixed performance issues. Changed include management --- src/aeso_compiler.erl | 23 +++++++- src/aeso_parser.erl | 67 +++++++++++++----------- src/aeso_stdlib.erl | 11 ++-- test/aeso_abi_tests.erl | 14 ++--- test/aeso_aci_tests.erl | 5 +- test/aeso_calldata_tests.erl | 12 +++-- test/aeso_compiler_tests.erl | 16 +++++- test/contracts/double_include.aes | 7 +++ test/contracts/manual_stdlib_include.aes | 7 +++ 9 files changed, 113 insertions(+), 49 deletions(-) create mode 100644 test/contracts/double_include.aes create mode 100644 test/contracts/manual_stdlib_include.aes diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 1703d40..fae063f 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -140,7 +140,16 @@ from_string1(fate, ContractString, Options) -> -spec string_to_code(string(), options()) -> map(). string_to_code(ContractString, Options) -> - Ast = parse(ContractString, Options), + Ast = case lists:member(no_implicit_stdlib, Options) of + true -> parse(ContractString, Options); + false -> + IncludedSTD = sets:from_list( + [aeso_parser:hash_include(F, C) + || {F, C} <- aeso_stdlib:stdlib_list()]), + InitAst = parse(ContractString, IncludedSTD, Options), + STD = parse_stdlib(), + STD ++ InitAst + end, pp_sophia_code(Ast, Options), pp_ast(Ast, Options), {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env]), @@ -569,6 +578,14 @@ pp(Code, Options, Option, PPFun) -> %% ------------------------------------------------------------------- %% TODO: Tempoary parser hook below... +parse_stdlib() -> + lists:foldr( + fun ({Lib, LibCode}, Acc) -> + parse(LibCode, [{src_file, Lib}]) ++ Acc + end, + [], + aeso_stdlib:stdlib_list()). + sophia_type_to_typerep(String) -> {ok, Ast} = aeso_parser:type(String), try aeso_ast_to_icode:ast_typerep(Ast) of @@ -577,8 +594,10 @@ sophia_type_to_typerep(String) -> end. parse(Text, Options) -> + parse(Text, sets:new(), Options). +parse(Text, Included, Options) -> %% Try and return something sensible here! - case aeso_parser:string(Text, Options) of + case aeso_parser:string(Text, Included, Options) of %% Yay, it worked! {ok, Contract} -> Contract; %% Scan errors. diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index f39e68e..194815b 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -7,6 +7,7 @@ -export([string/1, string/2, string/3, + hash_include/2, type/1]). -include("aeso_parse_lib.hrl"). @@ -27,18 +28,11 @@ string(String, Opts) -> false -> string(String, sets:new(), Opts) end. --spec string(string(), sets:set(string()), aeso_compiler:options()) -> parse_result(). +-spec string(string(), sets:set(binary()), aeso_compiler:options()) -> parse_result(). string(String, Included, Opts) -> case parse_and_scan(file(), String, Opts) of {ok, AST} -> - STD = case lists:member(no_implicit_stdlib, Opts) of - false -> [{ include, [{src_file, File}, {origin, system}] - , {string, [{src_file, File}, {origin, system}], File}} - || {File, _} <- aeso_stdlib:stdlib_list() - ]; - true -> [] - end, - expand_includes(STD ++ AST, Included, Opts); + expand_includes(AST, Included, Opts); Err = {error, _} -> Err end. @@ -558,32 +552,28 @@ expand_includes(AST, Included, Opts) -> expand_includes([], _Included, Acc, _Opts) -> {ok, lists:reverse(Acc)}; -expand_includes([{include, Ann, S = {string, _, File}} | AST], Included, Acc, Opts) -> - case sets:is_element(File, Included) of - false -> - Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}), - Included1 = sets:add_element(File, Included), - case {read_file(File, Opts), maps:find(File, aeso_stdlib:stdlib())} of - {{ok, _}, {ok,_ }} -> - return_error(ann_pos(Ann), "Illegal redefinition of standard library " ++ File); - {_, {ok, Lib}} -> - case string(Lib, Included1, [no_implicit_stdlib, Opts1]) of +expand_includes([{include, Ann, {string, SAnn, File}} | AST], Included, Acc, Opts) -> + case get_include_code(File, Ann, Opts) of + {ok, Code} -> + Hashed = hash_include(File, Code), + case sets:is_element(Hashed, Included) of + false -> + Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}), + Included1 = sets:add_element(Hashed, Included), + case string(Code, Included1, Opts1) of {ok, AST1} -> - expand_includes(AST1 ++ AST, Included1, Acc, Opts); + Dependencies = [ {include, Ann, {string, SAnn, Dep}} + || Dep <- aeso_stdlib:dependencies(File) + ], + expand_includes(Dependencies ++ AST1 ++ AST, Included1, Acc, Opts); Err = {error, _} -> Err end; - {{ok, Bin}, _} -> - case string(binary_to_list(Bin), Included1, Opts1) of - {ok, AST1} -> - expand_includes(AST1 ++ AST, Included1, Acc, Opts); - Err = {error, _} -> - Err - end; - {_, _} -> - {error, {get_pos(S), include_error, File}} + true -> + expand_includes(AST, Included, Acc, Opts) end; - true -> expand_includes(AST, Included, Acc, Opts) + Err = {error, _} -> + Err end; expand_includes([E | AST], Included, Acc, Opts) -> expand_includes(AST, Included, [E | Acc], Opts). @@ -601,3 +591,20 @@ read_file(File, Opts) -> end end. +get_include_code(File, Ann, Opts) -> + case {read_file(File, Opts), maps:find(File, aeso_stdlib:stdlib())} of + {{ok, _}, {ok,_ }} -> + return_error(ann_pos(Ann), "Illegal redefinition of standard library " ++ File); + {_, {ok, Lib}} -> + {ok, Lib}; + {{ok, Bin}, _} -> + {ok, binary_to_list(Bin)}; + {_, _} -> + {error, {ann_pos(Ann), include_error, File}} + end. + +-spec hash_include(string() | binary(), string()) -> binary(). +hash_include(File, Code) when is_binary(File) -> + hash_include(binary_to_list(File), Code); +hash_include(File, Code) when is_list(File) -> + {filename:basename(File), crypto:hash(sha256, Code)}. diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index efc192b..f154c7b 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -10,7 +10,7 @@ -module(aeso_stdlib). --export([stdlib/0, stdlib_list/0]). +-export([stdlib/0, stdlib_list/0, dependencies/1]). stdlib() -> maps:from_list(stdlib_list()). @@ -23,6 +23,13 @@ stdlib_list() -> , {<<"Triple.aes">>, std_triple()} ]. +dependencies(Q) -> + case Q of + <<"Option.aes">> -> + [<<"List.aes">>]; + _ -> [] + end. + std_func() -> " namespace Func = @@ -286,8 +293,6 @@ namespace List = ". std_option() -> " -include \"List.aes\" - namespace Option = function is_none(o : option('a)) : bool = switch(o) diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index c7e0c57..9d0a022 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -80,11 +80,11 @@ encode_decode_sophia_string(SophiaType, String) -> , " record r = {x : an_alias(int), y : variant}\n" , " datatype variant = Red | Blue(map(string, int))\n" , " entrypoint foo : arg_type => arg_type\n" ], - case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], []) of + case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], [no_implicit_stdlib]) of {ok, _, {[Type], _}, [Arg]} -> io:format("Type ~p~n", [Type]), Data = encode(Arg), - case aeso_compiler:to_sophia_value(Code, "foo", ok, Data) of + case aeso_compiler:to_sophia_value(Code, "foo", ok, Data, [no_implicit_stdlib]) of {ok, Sophia} -> lists:flatten(io_lib:format("~s", [prettypr:format(aeso_pretty:expr(Sophia))])); {error, Err} -> @@ -152,7 +152,7 @@ oracle_test() -> " Oracle.get_question(o, q)\n", {ok, _, {[word, word], {list, string}}, [16#123, 16#456]} = aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9", - "oq_1111111111111111111111111111113AFEFpt5"], []), + "oq_1111111111111111111111111111113AFEFpt5"], [no_implicit_stdlib]), ok. @@ -162,7 +162,7 @@ permissive_literals_fail_test() -> " stateful entrypoint haxx(o : oracle(list(string), option(int))) =\n" " Chain.spend(o, 1000000)\n", {error, <<"Type errors\nCannot unify", _/binary>>} = - aeso_compiler:check_call(Contract, "haxx", ["#123"], []), + aeso_compiler:check_call(Contract, "haxx", ["#123"], [no_implicit_stdlib]), ok. encode_decode_calldata(FunName, Types, Args) -> @@ -173,8 +173,8 @@ encode_decode_calldata(FunName, Types, Args, RetType) -> encode_decode_calldata_(Code, FunName, Args, RetType). encode_decode_calldata_(Code, FunName, Args, RetVMType) -> - {ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args), - {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}]), + {ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args, [no_implicit_stdlib]), + {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}, no_implicit_stdlib]), ?assertEqual(RetType, RetVMType), CalldataType = {tuple, [word, {tuple, ArgTypes}]}, {ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata), @@ -182,7 +182,7 @@ encode_decode_calldata_(Code, FunName, Args, RetVMType) -> "init" -> ok; _ -> - {ok, _ArgTypes, ValueASTs} = aeso_compiler:decode_calldata(Code, FunName, Calldata), + {ok, _ArgTypes, ValueASTs} = aeso_compiler:decode_calldata(Code, FunName, Calldata, [no_implicit_stdlib]), Values = [ prettypr:format(aeso_pretty:expr(V)) || V <- ValueASTs ], ?assertMatch({X, X}, {Args, Values}) end, diff --git a/test/aeso_aci_tests.erl b/test/aeso_aci_tests.erl index 0faef0e..6f348fd 100644 --- a/test/aeso_aci_tests.erl +++ b/test/aeso_aci_tests.erl @@ -87,7 +87,8 @@ aci_test_() -> fun() -> aci_test_contract(ContractName) end} || ContractName <- all_contracts()]. -all_contracts() -> aeso_compiler_tests:compilable_contracts(). +all_contracts() -> [C || C <- aeso_compiler_tests:compilable_contracts() + , not aeso_compiler_tests:wants_stdlib(C)]. aci_test_contract(Name) -> String = aeso_test_utils:read_contract(Name), @@ -98,7 +99,7 @@ aci_test_contract(Name) -> {ok, ContractStub} = aeso_aci:render_aci_json(JSON), io:format("STUB:\n~s\n", [ContractStub]), - check_stub(ContractStub, [{src_file, Name}, no_implicit_stdlib]), + check_stub(ContractStub, [{src_file, Name}]), ok. diff --git a/test/aeso_calldata_tests.erl b/test/aeso_calldata_tests.erl index a68f3b9..7fab206 100644 --- a/test/aeso_calldata_tests.erl +++ b/test/aeso_calldata_tests.erl @@ -21,12 +21,14 @@ calldata_test_() -> ContractString = aeso_test_utils:read_contract(ContractName), AevmExprs = case not lists:member(ContractName, not_yet_compilable(aevm)) of - true -> ast_exprs(ContractString, Fun, Args, [{backend, aevm}]); + true -> ast_exprs(ContractString, Fun, Args, [{backend, aevm}] + ++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]); false -> undefined end, FateExprs = case not lists:member(ContractName, not_yet_compilable(fate)) of - true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]); + true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}] + ++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]); false -> undefined end, case FateExprs == undefined orelse AevmExprs == undefined of @@ -45,12 +47,14 @@ calldata_aci_test_() -> io:format("ACI:\n~s\n", [ContractACIBin]), AevmExprs = case not lists:member(ContractName, not_yet_compilable(aevm)) of - true -> ast_exprs(ContractACI, Fun, Args, [{backend, aevm}]); + true -> ast_exprs(ContractACI, Fun, Args, [{backend, aevm}] + ++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]); false -> undefined end, FateExprs = case not lists:member(ContractName, not_yet_compilable(fate)) of - true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}]); + true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}] + ++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]); false -> undefined end, case FateExprs == undefined orelse AevmExprs == undefined of diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 2b51022..47f0efb 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -73,7 +73,9 @@ check_errors(Expect, ErrorString) -> end. compile(Backend, Name) -> - compile(Backend, Name, [{include, {file_system, [aeso_test_utils:contract_path()]}}]). + compile(Backend, Name, + [{include, {file_system, [aeso_test_utils:contract_path()]}}] + ++ [no_implicit_stdlib || not wants_stdlib(Name)]). compile(Backend, Name, Options) -> String = aeso_test_utils:read_contract(Name), @@ -121,6 +123,8 @@ compilable_contracts() -> "tuple_match", "cyclic_include", "stdlib_include", + "double_include", + "manual_stdlib_include", "list_comp" ]. @@ -367,3 +371,13 @@ failing_contracts() -> [<<"Cannot unify int\n and string\nwhen checking the type of the pattern at line 2, column 53\n x : int\nagainst the expected type\n string">> ]} ]. + +wants_stdlib(Name) -> + lists:member + (Name, + [ "stdlib_include", + "list_comp", + "list_comp_not_a_list", + "list_comp_if_not_bool", + "list_comp_bad_shadow" + ]). diff --git a/test/contracts/double_include.aes b/test/contracts/double_include.aes new file mode 100644 index 0000000..e9818b0 --- /dev/null +++ b/test/contracts/double_include.aes @@ -0,0 +1,7 @@ +include "included.aes" +include "../contracts/included.aes" + +contract Include = + entrypoint foo() = + Included.foo() + diff --git a/test/contracts/manual_stdlib_include.aes b/test/contracts/manual_stdlib_include.aes new file mode 100644 index 0000000..5937c37 --- /dev/null +++ b/test/contracts/manual_stdlib_include.aes @@ -0,0 +1,7 @@ +// This contract should be compiled with no_implicit_stdlib option. +// It should include Lists.aes implicitly however, because Option.aes depends on it. +include "Option.aes" + +contract Test = + entrypoint i_should_build() = + List.is_empty(Option.to_list(None)) -- 2.30.2 From 66d556907dd5fe09816f03f47e8d97fd2cac030f Mon Sep 17 00:00:00 2001 From: radrow Date: Wed, 7 Aug 2019 13:00:04 +0200 Subject: [PATCH 19/19] Fixed hash type --- src/aeso_parser.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 194815b..f0bd60f 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -16,6 +16,8 @@ | {error, {aeso_parse_lib:pos(), atom(), term()}} | {error, {aeso_parse_lib:pos(), atom()}}. +-type include_hash() :: {string(), binary()}. + -spec string(string()) -> parse_result(). string(String) -> string(String, sets:new(), []). @@ -28,7 +30,7 @@ string(String, Opts) -> false -> string(String, sets:new(), Opts) end. --spec string(string(), sets:set(binary()), aeso_compiler:options()) -> parse_result(). +-spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result(). string(String, Included, Opts) -> case parse_and_scan(file(), String, Opts) of {ok, AST} -> @@ -603,7 +605,7 @@ get_include_code(File, Ann, Opts) -> {error, {ann_pos(Ann), include_error, File}} end. --spec hash_include(string() | binary(), string()) -> binary(). +-spec hash_include(string() | binary(), string()) -> include_hash(). hash_include(File, Code) when is_binary(File) -> hash_include(binary_to_list(File), Code); hash_include(File, Code) when is_list(File) -> -- 2.30.2