diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 7f6bc2b..f4eaf63 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -798,7 +798,7 @@ check_sccs(Env = #env{}, Funs, [{acyclic, X} | SCCs], Acc) -> end; check_sccs(Env = #env{}, Funs, [{cyclic, Xs} | SCCs], Acc) -> Defs = [ maps:get(X, Funs) || X <- Xs ], - {TypeSigs, {letrec, _, Defs1}} = infer_letrec(Env, {letrec, [], Defs}), + {TypeSigs, Defs1} = infer_letrec(Env, Defs), Env1 = bind_funs(TypeSigs, Env), check_sccs(Env1, Funs, SCCs, Defs1 ++ Acc). @@ -840,7 +840,7 @@ check_special_funs(_, _) -> ok. typesig_to_fun_t({type_sig, Ann, Named, Args, Res}) -> {fun_t, Ann, Named, Args, Res}. -infer_letrec(Env, {letrec, Attrs, Defs}) -> +infer_letrec(Env, Defs) -> create_constraints(), Funs = [{Name, fresh_uvar(A)} || {letfun, _, {id, A, Name}, _, _, _} <- Defs], @@ -860,7 +860,7 @@ infer_letrec(Env, {letrec, Attrs, Defs}) -> TypeSigs = instantiate([Sig || {Sig, _} <- Inferred]), NewDefs = instantiate([D || {_, D} <- Inferred]), [print_typesig(S) || S <- TypeSigs], - {TypeSigs, {letrec, Attrs, NewDefs}}. + {TypeSigs, NewDefs}. infer_letfun(Env, {letfun, Attrib, {id, NameAttrib, Name}, Args, What, Body}) -> ArgTypes = [{ArgName, check_type(Env, arg_type(T))} || {arg, _, ArgName, T} <- Args], @@ -1133,12 +1133,11 @@ infer_case(Env, Attrs, Pattern, ExprType, Branch, SwitchType) -> %% NewStmts = infer_block(Env, Attrs, Stmts, BlockType) infer_block(_Env, Attrs, [], BlockType) -> error({impossible, empty_block, Attrs, BlockType}); -infer_block(Env, Attrs, [Def={letfun, _, _, _, _, _}|Rest], BlockType) -> - NewDef = infer_letfun(Env, Def), - [NewDef|infer_block(Env, Attrs, Rest, BlockType)]; -infer_block(Env, Attrs, [Def={letrec, _, _}|Rest], BlockType) -> - NewDef = infer_letrec(Env, Def), - [NewDef|infer_block(Env, Attrs, Rest, BlockType)]; +infer_block(Env, Attrs, [Def={letfun, Ann, _, _, _, _}|Rest], BlockType) -> + {{Name, TypeSig}, LetFun} = infer_letfun(Env, Def), + FunT = freshen_type(typesig_to_fun_t(TypeSig)), + NewE = bind_var({id, Ann, Name}, FunT, Env), + [LetFun|infer_block(NewE, Attrs, Rest, BlockType)]; infer_block(Env, _, [{letval, Attrs, Pattern, Type, E}|Rest], BlockType) -> NewE = {typed, _, _, PatType} = infer_expr(Env, {typed, Attrs, E, arg_type(Type)}), {'case', _, NewPattern, {typed, _, {block, _, NewRest}, _}} = diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 0e06cac..76f64ad 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -722,6 +722,9 @@ decision_tree_to_fcode({'if', A, Then, Else}) -> -spec stmts_to_fcode(env(), [aeso_syntax:stmt()]) -> fexpr(). stmts_to_fcode(Env, [{letval, _, {typed, _, {id, _, X}, _}, _, Expr} | Stmts]) -> {'let', X, expr_to_fcode(Env, Expr), stmts_to_fcode(bind_var(Env, X), Stmts)}; +stmts_to_fcode(Env, [{letfun, Ann, {id, _, X}, Args, _Type, Expr} | Stmts]) -> + {'let', X, expr_to_fcode(Env, {lam, Ann, Args, Expr}), + stmts_to_fcode(bind_var(Env, X), Stmts)}; stmts_to_fcode(Env, [Expr]) -> expr_to_fcode(Env, Expr); stmts_to_fcode(Env, [Expr | Stmts]) -> diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index 7ff8d6a..b1bddc2 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -102,13 +102,6 @@ contract_to_icode([{letfun, Attrib, Name, Args, _What, Body={typed,_,_,T}}|Rest] QName = aeso_icode:qualify(Name, Icode), NewIcode = ast_fun_to_icode(ast_id(QName), FunAttrs, FunArgs, FunBody, TypeRep, Icode), contract_to_icode(Rest, NewIcode); -contract_to_icode([{letrec,_,Defs}|Rest], Icode) -> - %% OBS! This code ignores the letrec structure of the source, - %% because the back end treats ALL declarations as recursive! We - %% need to decide whether to (a) modify the back end to respect - %% the letrec structure, or (b) (preferably) modify the front end - %% just to parse a list of (mutually recursive) definitions. - contract_to_icode(Defs++Rest, Icode); contract_to_icode([], Icode) -> Icode; contract_to_icode([{fun_decl, _, _, _} | Code], Icode) -> contract_to_icode(Code, Icode); @@ -530,6 +523,8 @@ ast_body({switch,_,A,Cases}, Icode) -> ast_body({block,As,[{letval,_,Pat,_,E}|Rest]}, Icode) -> #switch{expr=ast_body(E, Icode), cases=[{ast_body(Pat, Icode),ast_body({block,As,Rest}, Icode)}]}; +ast_body({block, As, [{letfun, Ann, F, Args, _Type, Expr} | Rest]}, Icode) -> + ast_body({block, As, [{letval, Ann, F, unused, {lam, Ann, Args, Expr}} | Rest]}, Icode); ast_body({block,_,[]}, _Icode) -> #tuple{cpts=[]}; ast_body({block,_,[E]}, Icode) -> diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 48f25a8..40e3edf 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -241,7 +241,7 @@ to_scode(Env, {remote, Ct, Fun, [{builtin, call_gas_left, _}, Value | Args]}) -> Call = if Env#env.tailpos -> aeb_fate_code:call_tr(?a, Lbl, ?a); true -> aeb_fate_code:call_r(?a, Lbl, ?a) end, - call_to_scode(Env, [to_scode(Env, Value), to_scode(Env, Ct), Call], Args); + call_to_scode(Env, Call, [Ct, Value | Args]); to_scode(Env, {remote, Ct, Fun, [Gas, Value | Args]}) -> %% Gas is limited. @@ -249,10 +249,7 @@ to_scode(Env, {remote, Ct, Fun, [Gas, Value | Args]}) -> Call = if Env#env.tailpos -> aeb_fate_code:call_gtr(?a, Lbl, ?a, ?a); true -> aeb_fate_code:call_gr(?a, Lbl, ?a, ?a) end, - call_to_scode(Env, [to_scode(Env, Gas), - to_scode(Env, Value), - to_scode(Env, Ct), Call], - Args); + call_to_scode(Env, Call, [Ct, Value, Gas | Args]); to_scode(Env, {closure, Fun, FVs}) -> to_scode(Env, {tuple, [{lit, {string, make_function_name(Fun)}}, FVs]}); diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index ba9e8b9..f96505d 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -87,7 +87,7 @@ constructors() -> sep1(constructor(), tok('|')). constructor() -> %% TODO: format for Con() vs Con - choice(?RULE(con(), {constr_t, get_ann(_1), _1, []}), + choice(?RULE(con(), {constr_t, get_ann(_1), _1, []}), ?RULE(con(), con_args(), {constr_t, get_ann(_1), _1, _2})). con_args() -> paren_list(con_arg()). @@ -99,9 +99,7 @@ con_arg() -> choice(type(), ?RULE(keyword(indexed), type(), set_ann(indexed, %% -- Let declarations ------------------------------------------------------- letdecl() -> - choice( - ?RULE(keyword('let'), letdef(), set_pos(get_pos(_1), _2)), - ?RULE(keyword('let'), tok(rec), sep1(letdef(), tok('and')), {letrec, _1, _3})). + ?RULE(keyword('let'), letdef(), set_pos(get_pos(_1), _2)). letdef() -> choice(valdef(), fundef()). diff --git a/src/aeso_pretty.erl b/src/aeso_pretty.erl index 3177e60..094138d 100644 --- a/src/aeso_pretty.erl +++ b/src/aeso_pretty.erl @@ -160,8 +160,7 @@ decl(D = {letfun, Attrs, _, _, _, _}) -> text(atom_to_list(Mod)); (_) -> empty() end, hsep(lists:map(Mod, Attrs) ++ [letdecl("function", D)]); -decl(D = {letval, _, _, _, _}) -> letdecl("let", D); -decl(D = {letrec, _, _}) -> letdecl("let", D). +decl(D = {letval, _, _, _, _}) -> letdecl("let", D). -spec expr(aeso_syntax:expr(), options()) -> doc(). expr(E, Options) -> @@ -184,9 +183,7 @@ name({typed, _, Name, _}) -> name(Name). letdecl(Let, {letval, _, F, T, E}) -> block_expr(0, hsep([text(Let), typed(name(F), T), text("=")]), E); letdecl(Let, {letfun, _, F, Args, T, E}) -> - block_expr(0, hsep([text(Let), typed(beside(name(F), args(Args)), T), text("=")]), E); -letdecl(Let, {letrec, _, [D | Ds]}) -> - hsep(text(Let), above([ letdecl("rec", D) | [ letdecl("and", D1) || D1 <- Ds ] ])). + block_expr(0, hsep([text(Let), typed(beside(name(F), args(Args)), T), text("=")]), E). -spec args([aeso_syntax:arg()]) -> doc(). args(Args) -> @@ -435,7 +432,6 @@ statements(Stmts) -> statement(S = {letval, _, _, _, _}) -> letdecl("let", S); statement(S = {letfun, _, _, _, _, _}) -> letdecl("let", S); -statement(S = {letrec, _, _}) -> letdecl("let", S); statement(E) -> expr(E). get_elifs(Expr) -> get_elifs(Expr, []). diff --git a/src/aeso_scan.erl b/src/aeso_scan.erl index e377438..e8dc7e1 100644 --- a/src/aeso_scan.erl +++ b/src/aeso_scan.erl @@ -36,8 +36,8 @@ lexer() -> , {"\\*/", pop(skip())} , {"[^/*]+|[/*]", skip()} ], - Keywords = ["contract", "include", "let", "rec", "switch", "type", "record", "datatype", "if", "elif", "else", "function", - "stateful", "true", "false", "and", "mod", "public", "private", "indexed", "internal", "namespace"], + Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function", + "stateful", "true", "false", "mod", "public", "private", "indexed", "internal", "namespace"], KW = string:join(Keywords, "|"), Rules = diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index 63f192d..b3cc3ce 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -43,8 +43,7 @@ -type letbind() :: {letval, ann(), id(), type(), expr()} - | {letfun, ann(), id(), [arg()], type(), expr()} - | {letrec, ann(), [letbind()]}. + | {letfun, ann(), id(), [arg()], type(), expr()}. -type arg() :: {arg, ann(), id(), type()}. diff --git a/src/aeso_syntax_utils.erl b/src/aeso_syntax_utils.erl index a8aceea..8a7f374 100644 --- a/src/aeso_syntax_utils.erl +++ b/src/aeso_syntax_utils.erl @@ -39,11 +39,6 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) -> BindExpr = fun(P) -> fold(Alg, Fun, bind_expr, P) end, BindType = fun(T) -> fold(Alg, Fun, bind_type, T) end, Top = Fun(K, X), - Bound = fun LB ({letval, _, Y, _, _}) -> BindExpr(Y); - LB ({letfun, _, F, _, _, _}) -> BindExpr(F); - LB ({letrec, _, Ds}) -> Sum(lists:map(LB, Ds)); - LB (_) -> Zero - end, Rec = case X of %% lists (bound things in head scope over tail) [A | As] -> Scoped(Same(A), Same(As)); @@ -55,7 +50,6 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) -> {fun_decl, _, _, T} -> Type(T); {letval, _, F, T, E} -> Sum([BindExpr(F), Type(T), Expr(E)]); {letfun, _, F, Xs, T, E} -> Sum([BindExpr(F), Type(T), Scoped(BindExpr(Xs), Expr(E))]); - {letrec, _, Ds} -> Plus(Bound(Ds), Decl(Ds)); %% typedef() {alias_t, T} -> Type(T); {record_t, Fs} -> Type(Fs); diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index fb630b7..dffb324 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -86,6 +86,7 @@ compilable_contracts() -> "dutch_auction", "environment", "factorial", + "functions", "fundme", "identity", "maps", diff --git a/test/aeso_scan_tests.erl b/test/aeso_scan_tests.erl index 32b8456..685d3ac 100644 --- a/test/aeso_scan_tests.erl +++ b/test/aeso_scan_tests.erl @@ -41,7 +41,7 @@ all_tokens() -> %% Operators lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod, ':', '::', '->', '=>', '||', '&&', '!']) ++ %% Keywords - lists:map(Lit, [contract, type, 'let', switch, rec, 'and']) ++ + lists:map(Lit, [contract, type, 'let', switch]) ++ %% Comment token (not an actual token), just for tests [{comment, 0, "// *Comment!\"\n"}, {comment, 0, "/* bla /* bla bla */*/"}] ++ diff --git a/test/contracts/all_syntax.aes b/test/contracts/all_syntax.aes index 4b80311..1b3b7c1 100644 --- a/test/contracts/all_syntax.aes +++ b/test/contracts/all_syntax.aes @@ -36,11 +36,6 @@ contract AllSyntax = (x, [y, z]) => bar({x = z, y = -y + - -z * (-1)}) (x, y :: _) => () - function mutual() = - let rec recFun(x : int) = mutFun(x) - and mutFun(x) = if(x =< 0) 1 else x * recFun(x - 1) - recFun(0) - let hash : address = #01ab0fff11 let b = false let qcon = Mod.Con diff --git a/test/contracts/functions.aes b/test/contracts/functions.aes new file mode 100644 index 0000000..058950c --- /dev/null +++ b/test/contracts/functions.aes @@ -0,0 +1,15 @@ +contract Functions = + private function curry(f : ('a, 'b) => 'c) = + (x) => (y) => f(x, y) + private function map(f : 'a => 'b, xs : list('a)) = + switch(xs) + [] => [] + x :: xs => f(x) :: map(f, xs) + private function map'() = map + private function plus(x, y) = x + y + function test1(xs : list(int)) = map(curry(plus)(5), xs) + function test2(xs : list(int)) = map'()(((x) => (y) => ((x, y) => x + y)(x, y))(100), xs) + function test3(xs : list(int)) = + let m(f, xs) = map(f, xs) + m((x) => x + 1, xs) +