diff --git a/src/aeso_aci.erl b/src/aeso_aci.erl index 29f9ea6..5f9fd25 100644 --- a/src/aeso_aci.erl +++ b/src/aeso_aci.erl @@ -264,7 +264,10 @@ decode_types(Ets) -> decode_type(#{tuple := Ets}) -> Ts = decode_types(Ets), - [$(,lists:join(",", Ts),$)]; + case Ts of + [] -> ["unit"]; + _ -> ["(", $(,lists:join(" * ", Ts),$), ")"] + end; decode_type(#{record := Efs}) -> Fs = decode_fields(Efs), [${,lists:join(",", Fs),$}]; diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index b239458..697845b 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -388,6 +388,7 @@ global_env() -> {"require", Fun([Bool, String], Unit)}]) , types = MkDefs( [{"int", 0}, {"bool", 0}, {"char", 0}, {"string", 0}, {"address", 0}, + {"unit", {[], {alias_t, Unit}}}, {"hash", {[], {alias_t, Bytes(32)}}}, {"signature", {[], {alias_t, Bytes(64)}}}, {"bits", 0}, @@ -2320,8 +2321,10 @@ pp({uvar, _, Ref}) -> ["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ]; pp({tvar, _, Name}) -> Name; +pp({tuple_t, _, []}) -> + "unit"; pp({tuple_t, _, Cpts}) -> - ["(", pp(Cpts), ")"]; + ["(", string:join(lists:map(fun pp/1, Cpts), " * "), ")"]; pp({bytes_t, _, any}) -> "bytes(_)"; pp({bytes_t, _, Len}) -> ["bytes(", integer_to_list(Len), ")"]; diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 4e127b7..cdcb2c8 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -141,7 +141,7 @@ type200() -> ?RULE(many({fun_domain(), keyword('=>')}), type300(), fun_t(_1, _2)). type300() -> - ?RULE(sep1(type400(), tok('*')), tuple_t(get_ann(_1), _1)). + ?RULE(sep1(type400(), tok('*')), tuple_t(get_ann(lists:nth(1, _1)), _1)). type400() -> choice( @@ -156,12 +156,17 @@ type400() -> typeAtom() -> ?LAZY_P(choice( - [ id(), token(con), token(qcon), token(qid), tvar() + [ parens(type()) + , id(), token(con), token(qcon), token(qid), tvar() ])). fun_domain() -> ?LAZY_P(choice( - [ paren_list(type()) - , type300() + [ ?RULE(tok('('), tok(')'), []) + %% Note avoidance of ambiguity: `(int)` can be treated as: + %% - literally `int` + %% - list of arguments with just one element – int. This approach is dropped. + , ?RULE(tok('('), type(), tok(','), sep1(type(), tok(',')), tok(')'), [_2|_4]) + , ?RULE(type300(), [_1]) ])). %% -- Statements ------------------------------------------------------------- diff --git a/src/aeso_pretty.erl b/src/aeso_pretty.erl index 9db524d..ea76b42 100644 --- a/src/aeso_pretty.erl +++ b/src/aeso_pretty.erl @@ -222,7 +222,7 @@ typedef({variant_t, Constructors}) -> -spec constructor_t(aeso_syntax:constructor_t()) -> doc(). constructor_t({constr_t, _, C, []}) -> name(C); -constructor_t({constr_t, _, C, Args}) -> beside(name(C), tuple_type(Args)). +constructor_t({constr_t, _, C, Args}) -> beside(name(C), args_type(Args)). -spec field_t(aeso_syntax:field_t()) -> doc(). field_t({field_t, _, Name, Type}) -> @@ -234,11 +234,11 @@ type(Type, Options) -> -spec type(aeso_syntax:type()) -> doc(). type({fun_t, _, Named, Args, Ret}) -> - follow(hsep(tuple_type(Named ++ Args), text("=>")), type(Ret)); + follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret)); type({app_t, _, Type, []}) -> type(Type); type({app_t, _, Type, Args}) -> - beside(type(Type), tuple_type(Args)); + beside(type(Type), args_type(Args)); type({tuple_t, _, Args}) -> tuple_type(Args); type({bytes_t, _, any}) -> text("bytes(_)"); @@ -256,10 +256,20 @@ type(T = {con, _, _}) -> name(T); type(T = {qcon, _, _}) -> name(T); type(T = {tvar, _, _}) -> name(T). --spec tuple_type([aeso_syntax:type()]) -> doc(). -tuple_type(Args) -> +-spec args_type([aeso_syntax:type()]) -> doc(). +args_type(Args) -> tuple(lists:map(fun type/1, Args)). +-spec tuple_type([aeso_syntax:type()]) -> doc(). +tuple_type([]) -> + text("unit"); +tuple_type(Factors) -> + beside( + [ text("(") + , par(punctuate(text(" *"), lists:map(fun type/1, Factors)), 0) + , text(")") + ]). + -spec arg_expr(aeso_syntax:arg_expr()) -> doc(). arg_expr({named_arg, _, Name, E}) -> follow(hsep(expr(Name), text("=")), expr(E)); diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index f2e58ab..c7e0c57 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -66,7 +66,7 @@ encode_decode_sophia_test() -> ok = Check("bool", "true"), ok = Check("bool", "false"), ok = Check("string", "\"Hello\""), - ok = Check("(string, list(int), option(bool))", + ok = Check("string * list(int) * option(bool)", "(\"Hello\", [1, 2, 3], Some(true))"), ok = Check("variant", "Blue({[\"x\"] = 1})"), ok = Check("r", "{x = (\"foo\", 0), y = Red}"), @@ -76,7 +76,7 @@ encode_decode_sophia_string(SophiaType, String) -> io:format("String ~p~n", [String]), Code = [ "contract MakeCall =\n" , " type arg_type = ", SophiaType, "\n" - , " type an_alias('a) = (string, 'a)\n" + , " type an_alias('a) = string * 'a\n" , " record r = {x : an_alias(int), y : variant}\n" , " datatype variant = Red | Blue(map(string, int))\n" , " entrypoint foo : arg_type => arg_type\n" ], @@ -137,10 +137,10 @@ parameterized_contract(FunName, Types) -> parameterized_contract(ExtraCode, FunName, Types) -> lists:flatten( ["contract Remote =\n" - " entrypoint bla : () => ()\n\n" + " entrypoint bla : () => unit\n\n" "contract Dummy =\n", ExtraCode, "\n", - " type an_alias('a) = (string, 'a)\n" + " type an_alias('a) = string * 'a\n" " record r = {x : an_alias(int), y : variant}\n" " datatype variant = Red | Blue(map(string, int))\n" " entrypoint ", FunName, " : (", string:join(Types, ", "), ") => int\n" ]). diff --git a/test/aeso_aci_tests.erl b/test/aeso_aci_tests.erl index 329e705..ee719be 100644 --- a/test/aeso_aci_tests.erl +++ b/test/aeso_aci_tests.erl @@ -53,7 +53,7 @@ test_cases(2) -> {Contract,MapACI,DecACI}; test_cases(3) -> Contract = <<"contract C =\n" - " type state = ()\n" + " type state = unit\n" " datatype event = SingleEventDefined\n" " datatype bert('a) = Bin('a)\n" " entrypoint a(i : bert(string)) = 1\n">>, @@ -67,7 +67,7 @@ test_cases(3) -> stateful => false}], name => <<"C">>, event => #{variant => [#{<<"SingleEventDefined">> => []}]}, - state => #{tuple => []}, + state => <<"unit">>, type_defs => [#{name => <<"bert">>, typedef => @@ -75,7 +75,7 @@ test_cases(3) -> [#{<<"Bin">> => [<<"'a">>]}]}, vars => [#{name => <<"'a">>}]}]}}, DecACI = <<"contract C =\n" - " type state = ()\n" + " type state = unit\n" " datatype event = SingleEventDefined\n" " datatype bert('a) = Bin('a)\n" " entrypoint a : (C.bert(string)) => int\n">>, diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 390b37a..6851c3d 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -220,7 +220,7 @@ failing_contracts() -> "when checking that 'init' returns a value of type 'state' at line 7, column 3">>]} , {"missing_state_type", [<<"Cannot unify string\n" - " and ()\n" + " and unit\n" "when checking that 'init' returns a value of type 'state' at line 5, column 3">>]} , {"missing_fields_in_record_expression", [<<"The field x is missing when constructing an element of type r('a) (at line 7, column 42)">>, @@ -325,7 +325,7 @@ failing_contracts() -> <<"Cannot reference stateful function local_spend (at line 14, column 35)\nin the definition of non-stateful function fail2.">>, <<"Cannot reference stateful function Chain.spend (at line 16, column 15)\nin the definition of non-stateful function fail3.">>, <<"Cannot reference stateful function Chain.spend (at line 20, column 31)\nin the definition of non-stateful function fail4.">>, - <<"Cannot reference stateful function Chain.spend (at line 35, column 45)\nin the definition of non-stateful function fail5.">>, + <<"Cannot reference stateful function Chain.spend (at line 35, column 47)\nin the definition of non-stateful function fail5.">>, <<"Cannot pass non-zero value argument 1000 (at line 48, column 57)\nin the definition of non-stateful function fail6.">>, <<"Cannot pass non-zero value argument 1000 (at line 49, column 56)\nin the definition of non-stateful function fail7.">>, <<"Cannot pass non-zero value argument 1000 (at line 52, column 17)\nin the definition of non-stateful function fail8.">>]} @@ -350,7 +350,7 @@ failing_contracts() -> <<"Namespaces cannot contain entrypoints (at line 3, column 3). Use 'function' instead.">>, <<"The contract Remote (at line 5, column 10) has no entrypoints. Since Sophia version 3.2, public\ncontract functions must be declared with the 'entrypoint' keyword instead of\n'function'.">>, <<"The entrypoint wha (at line 12, column 3) cannot be private. Use 'function' instead.">>, - <<"Use 'entrypoint' for declaration of foo (at line 6, column 3):\n entrypoint foo : () => ()">>, + <<"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 : () => ()">>]} + <<"Use 'entrypoint' instead of 'function' for public function foo (at line 6, column 3):\n entrypoint foo : () => unit">>]} ]. diff --git a/test/contracts/abort_test.aes b/test/contracts/abort_test.aes index 5360930..3da5324 100644 --- a/test/contracts/abort_test.aes +++ b/test/contracts/abort_test.aes @@ -8,7 +8,7 @@ contract AbortTest = { value = v } // Aborting - public function do_abort(v : int, s : string) : () = + public function do_abort(v : int, s : string) : unit = put_value(v) revert_abort(s) diff --git a/test/contracts/abort_test_int.aes b/test/contracts/abort_test_int.aes index 970fa5f..ab1e9fe 100644 --- a/test/contracts/abort_test_int.aes +++ b/test/contracts/abort_test_int.aes @@ -1,9 +1,9 @@ contract Interface = - function do_abort : (int, string) => () + function do_abort : (int, string) => unit function get_value : () => int - function put_value : (int) => () + function put_value : (int) => unit function get_values : () => list(int) - function put_values : (int) => () + function put_values : (int) => unit contract AbortTestInt = diff --git a/test/contracts/address_chain.aes b/test/contracts/address_chain.aes index fbf6b7b..e8edccf 100644 --- a/test/contracts/address_chain.aes +++ b/test/contracts/address_chain.aes @@ -1,5 +1,5 @@ contract Remote = - entrypoint main : (int) => () + entrypoint main : (int) => unit contract AddrChain = type o_type = oracle(string, map(string, int)) diff --git a/test/contracts/address_literals.aes b/test/contracts/address_literals.aes index c2ae92a..fd69227 100644 --- a/test/contracts/address_literals.aes +++ b/test/contracts/address_literals.aes @@ -1,6 +1,6 @@ contract Remote = - entrypoint foo : () => () + entrypoint foo : () => unit contract AddressLiterals = entrypoint addr() : address = diff --git a/test/contracts/aens.aes b/test/contracts/aens.aes index 5c3bae4..9d5e063 100644 --- a/test/contracts/aens.aes +++ b/test/contracts/aens.aes @@ -12,44 +12,44 @@ contract AENSTest = // Transactions stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address) - chash : hash) : () = // Commitment hash + chash : hash) : unit = // Commitment hash AENS.preclaim(addr, chash) stateful entrypoint signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address) chash : hash, // Commitment hash - sign : signature) : () = // Signed by addr (if not Contract.address) + sign : signature) : unit = // Signed by addr (if not Contract.address) AENS.preclaim(addr, chash, signature = sign) stateful entrypoint claim(addr : address, name : string, - salt : int) : () = + salt : int) : unit = AENS.claim(addr, name, salt) stateful entrypoint signedClaim(addr : address, name : string, salt : int, - sign : signature) : () = + sign : signature) : unit = AENS.claim(addr, name, salt, signature = sign) // TODO: update() -- how to handle pointers? stateful entrypoint transfer(owner : address, new_owner : address, - name : string) : () = + name : string) : unit = AENS.transfer(owner, new_owner, name) stateful entrypoint signedTransfer(owner : address, new_owner : address, name : string, - sign : signature) : () = + sign : signature) : unit = AENS.transfer(owner, new_owner, name, signature = sign) stateful entrypoint revoke(owner : address, - name : string) : () = + name : string) : unit = AENS.revoke(owner, name) stateful entrypoint signedRevoke(owner : address, name : string, - sign : signature) : () = + sign : signature) : unit = AENS.revoke(owner, name, signature = sign) diff --git a/test/contracts/all_syntax.aes b/test/contracts/all_syntax.aes index 1b3b7c1..a88bae8 100644 --- a/test/contracts/all_syntax.aes +++ b/test/contracts/all_syntax.aes @@ -24,7 +24,7 @@ contract AllSyntax = if(valWithType(Map.empty) == None) print(42 mod 10 * 5 / 3) - function funWithType(x : int, y) : (int, list(int)) = (x, 0 :: [y] ++ []) + function funWithType(x : int, y) : int * list(int) = (x, 0 :: [y] ++ []) function funNoType() = let foo = (x, y : bool) => if (! (y && x =< 0x0b || true)) [x] diff --git a/test/contracts/bad_address_literals.aes b/test/contracts/bad_address_literals.aes index 4283f3d..216ab57 100644 --- a/test/contracts/bad_address_literals.aes +++ b/test/contracts/bad_address_literals.aes @@ -1,6 +1,6 @@ contract Remote = - entrypoint foo : () => () + entrypoint foo : () => unit contract AddressLiterals = entrypoint addr1() : bytes(32) = diff --git a/test/contracts/complex_types.aes b/test/contracts/complex_types.aes index 3fa1a72..613371f 100644 --- a/test/contracts/complex_types.aes +++ b/test/contracts/complex_types.aes @@ -3,8 +3,8 @@ contract Remote = entrypoint up_to : (int) => list(int) entrypoint sum : (list(int)) => int entrypoint some_string : () => string - entrypoint pair : (int, string) => (int, string) - entrypoint squares : (int) => list((int, int)) + entrypoint pair : (int, string) => int * string + entrypoint squares : (int) => list(int * int) entrypoint filter_some : (list(option(int))) => list(int) entrypoint all_some : (list(option(int))) => option(list(int)) @@ -47,7 +47,7 @@ contract ComplexTypes = entrypoint pair(x : int, y : string) = (x, y) - entrypoint remote_pair(n : int, s : string) : (int, string) = + entrypoint remote_pair(n : int, s : string) : int * string = state.worker.pair(gas = 10000, n, s) entrypoint map(f, xs) = @@ -58,7 +58,7 @@ contract ComplexTypes = entrypoint squares(n) = map((i) => (i, i * i), up_to(n)) - entrypoint remote_squares(n) : list((int, int)) = + entrypoint remote_squares(n) : list(int * int) = state.worker.squares(n) // option types diff --git a/test/contracts/events.aes b/test/contracts/events.aes index 2eec912..74a3393 100644 --- a/test/contracts/events.aes +++ b/test/contracts/events.aes @@ -1,5 +1,5 @@ contract Remote = - entrypoint dummy : () => () + entrypoint dummy : () => unit contract Events = diff --git a/test/contracts/funargs.aes b/test/contracts/funargs.aes index 1d66ce7..b63edff 100644 --- a/test/contracts/funargs.aes +++ b/test/contracts/funargs.aes @@ -40,7 +40,7 @@ contract FunctionArguments = entrypoint traffic_light(c : colour) = Red - entrypoint tuples(t : ()) = + entrypoint tuples(t : unit) = t entrypoint due(t : Chain.ttl) = diff --git a/test/contracts/maps.aes b/test/contracts/maps.aes index f5f07b3..e707a84 100644 --- a/test/contracts/maps.aes +++ b/test/contracts/maps.aes @@ -93,8 +93,8 @@ contract Maps = entrypoint tolist_state_s() = tolist_s(state.map_s) // Map.from_list - entrypoint fromlist_i(xs : list((int, pt))) = Map.from_list(xs) - entrypoint fromlist_s(xs : list((string, pt))) = Map.from_list(xs) + entrypoint fromlist_i(xs : list(int * pt)) = Map.from_list(xs) + entrypoint fromlist_s(xs : list(string * pt)) = Map.from_list(xs) stateful entrypoint fromlist_state_i(xs) = put(state{ map_i = fromlist_i(xs) }) stateful entrypoint fromlist_state_s(xs) = put(state{ map_s = fromlist_s(xs) }) diff --git a/test/contracts/modifier_checks.aes b/test/contracts/modifier_checks.aes index 80fb765..68ad337 100644 --- a/test/contracts/modifier_checks.aes +++ b/test/contracts/modifier_checks.aes @@ -3,7 +3,7 @@ namespace Lib = entrypoint foo() = () contract Remote = - public function foo : () => () + public function foo : () => unit function bla() = () contract Contract = diff --git a/test/contracts/oracles.aes b/test/contracts/oracles.aes index 0fe33d0..20afcbc 100644 --- a/test/contracts/oracles.aes +++ b/test/contracts/oracles.aes @@ -60,23 +60,23 @@ contract Oracles = res stateful entrypoint extendOracle(o : oracle_id, - ttl : ttl) : () = + ttl : ttl) : unit = Oracle.extend(o, ttl) stateful entrypoint signedExtendOracle(o : oracle_id, sign : signature, // Signed oracle address - ttl : ttl) : () = + ttl : ttl) : unit = Oracle.extend(o, signature = sign, ttl) stateful entrypoint respond(o : oracle_id, q : query_id, - r : answer_t) : () = + r : answer_t) : unit = Oracle.respond(o, q, r) stateful entrypoint signedRespond(o : oracle_id, q : query_id, sign : signature, - r : answer_t) : () = + r : answer_t) : unit = Oracle.respond(o, q, signature = sign, r) entrypoint getQuestion(o : oracle_id, diff --git a/test/contracts/oracles_no_vm.aes b/test/contracts/oracles_no_vm.aes index b0ba9ac..28d3c3f 100644 --- a/test/contracts/oracles_no_vm.aes +++ b/test/contracts/oracles_no_vm.aes @@ -21,7 +21,7 @@ contract Oracles = function respond(o : oracle_id, q : query_id, sign : signature, - r : answer_t) : () = + r : answer_t) : unit = Oracle.respond(o, q, signature = sign, r) diff --git a/test/contracts/remote_call.aes b/test/contracts/remote_call.aes index 340498a..e644a0f 100644 --- a/test/contracts/remote_call.aes +++ b/test/contracts/remote_call.aes @@ -7,7 +7,7 @@ contract Remote2 = contract Remote3 = entrypoint get : () => int - entrypoint tick : () => () + entrypoint tick : () => unit contract RemoteCall = diff --git a/test/contracts/state_handling.aes b/test/contracts/state_handling.aes index a1ee0c5..e4311d6 100644 --- a/test/contracts/state_handling.aes +++ b/test/contracts/state_handling.aes @@ -1,7 +1,7 @@ contract Remote = record rstate = { i : int, s : string, m : map(int, int) } - entrypoint look_at : (rstate) => () + entrypoint look_at : (rstate) => unit entrypoint return_s : (bool) => string entrypoint return_m : (bool) => map(int, int) entrypoint get : (rstate) => rstate diff --git a/test/contracts/stateful.aes b/test/contracts/stateful.aes index 782a6c0..e6ea8be 100644 --- a/test/contracts/stateful.aes +++ b/test/contracts/stateful.aes @@ -1,6 +1,6 @@ contract Remote = - stateful entrypoint remote_spend : (address, int) => () + stateful entrypoint remote_spend : (address, int) => unit entrypoint remote_pure : int => int contract Stateful = @@ -32,7 +32,7 @@ contract Stateful = entrypoint ok4(a : address) = fail4(a) // Lamdbas are checked at the construction site - function fail5() : address => () = (a) => Chain.spend(a, 1000) + function fail5() : address => unit = (a) => Chain.spend(a, 1000) // .. so you can pass a stateful lambda to a non-stateful higher-order // function: diff --git a/test/contracts/voting.aes b/test/contracts/voting.aes index 8560488..0154e28 100644 --- a/test/contracts/voting.aes +++ b/test/contracts/voting.aes @@ -8,7 +8,7 @@ contract VotingType = function delegate : address => unit function vote : int => unit function winnerName : unit => string - function currentTally : unit => list((string, int)) + function currentTally : unit => list(string * int) /* Contract implementation */ contract Voting =