diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index a2ec0bc..f6ecc13 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -507,6 +507,14 @@ icode_to_term(T = {map, KT, VT}, M) -> #{}; _ -> throw({todo, M}) end; +icode_to_term(word, {unop, 'bnot', A}) -> + bnot icode_to_term(word, A); +icode_to_term(word, {binop, 'bor', A, B}) -> + icode_to_term(word, A) bor icode_to_term(word, B); +icode_to_term(word, {binop, 'bsl', A, B}) -> + icode_to_term(word, B) bsl icode_to_term(word, A); +icode_to_term(word, {binop, 'band', A, B}) -> + icode_to_term(word, A) band icode_to_term(word, B); icode_to_term(typerep, _) -> throw({todo, typerep}); icode_to_term(T, V) -> diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 8c93490..00aa43d 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -261,6 +261,18 @@ term_to_fate({tuple, As}) -> term_to_fate({con, Ar, I, As}) -> FateAs = [ term_to_fate(A) || A <- As ], aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs)); +term_to_fate({builtin, bits_all, []}) -> + aeb_fate_data:make_bits(-1); +term_to_fate({builtin, bits_none, []}) -> + aeb_fate_data:make_bits(0); +term_to_fate({op, bits_set, [B, I]}) -> + {bits, N} = term_to_fate(B), + J = term_to_fate(I), + {bits, N bor (1 bsl J)}; +term_to_fate({op, bits_clear, [B, I]}) -> + {bits, N} = term_to_fate(B), + J = term_to_fate(I), + {bits, N band bnot (1 bsl J)}; term_to_fate({builtin, map_empty, []}) -> aeb_fate_data:make_map(#{}); term_to_fate({'let', _, {builtin, map_empty, []}, Set}) -> diff --git a/src/aeso_vm_decode.erl b/src/aeso_vm_decode.erl index bf61a48..d25512e 100644 --- a/src/aeso_vm_decode.erl +++ b/src/aeso_vm_decode.erl @@ -18,9 +18,14 @@ from_aevm(word, {id, _, "address"}, N) -> address_literal(ac from_aevm(word, {app_t, _, {id, _, "oracle"}, _}, N) -> address_literal(oracle_pubkey, N); from_aevm(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(oracle_query_id, N); from_aevm(word, {con, _, _Name}, N) -> address_literal(contract_pubkey, N); -from_aevm(word, {id, _, "int"}, N) -> <> = <>, {int, [], N1}; -from_aevm(word, {id, _, "bits"}, N) -> error({todo, bits, N}); -from_aevm(word, {id, _, "bool"}, N) -> {bool, [], N /= 0}; +from_aevm(word, {id, _, "int"}, N0) -> + <> = <>, + if N < 0 -> {app, [{format, prefix}], {'-', []}, [{int, [], -N}]}; + true -> {int, [], N} end; +from_aevm(word, {id, _, "bits"}, N0) -> + <> = <>, + make_bits(N); +from_aevm(word, {id, _, "bool"}, N) -> {bool, [], N /= 0}; from_aevm(word, {bytes_t, _, Len}, Val) when Len =< 32 -> <> = <>, {bytes, [], <>}; @@ -55,6 +60,7 @@ from_aevm({variant, VmCons}, {variant_t, Cons}, {variant, Tag, Args}) VmTypes = lists:nth(Tag + 1, VmCons), ConType = lists:nth(Tag + 1, Cons), from_aevm(VmTypes, ConType, Args); +from_aevm([], {constr_t, _, Con, []}, []) -> Con; from_aevm(VmTypes, {constr_t, _, Con, Types}, Args) when length(VmTypes) == length(Types), length(VmTypes) == length(Args) -> @@ -70,8 +76,10 @@ from_fate({app_t, _, {id, _, "oracle"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin}; from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin}; from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin}; -from_fate({id, _, "bits"}, ?FATE_BITS(Bin)) -> error({todo, bits, Bin}); -from_fate({id, _, "int"}, N) when is_integer(N) -> {int, [], N}; +from_fate({id, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N); +from_fate({id, _, "int"}, N) when is_integer(N) -> + if N < 0 -> {app, [{format, prefix}], {'-', []}, [{int, [], -N}]}; + true -> {int, [], N} end; from_fate({id, _, "bool"}, B) when is_boolean(B) -> {bool, [], B}; from_fate({id, _, "string"}, S) when is_binary(S) -> {string, [], S}; from_fate({app_t, _, {id, _, "list"}, [Type]}, List) when is_list(List) -> @@ -105,9 +113,23 @@ from_fate({variant_t, Cons}, {variant, Ar, Tag, Args}) from_fate(ConType, ArgList); _ -> throw(cannot_translate_to_sophia) end; +from_fate({constr_t, _, Con, []}, []) -> Con; from_fate({constr_t, _, Con, Types}, Args) when length(Types) == length(Args) -> {app, [], Con, [ from_fate(Type, Arg) || {Type, Arg} <- lists:zip(Types, Args) ]}; from_fate(_Type, _Data) -> throw(cannot_translate_to_sophia). + + +make_bits(N) -> + Id = fun(F) -> {qid, [], ["Bits", F]} end, + if N < 0 -> make_bits(Id("clear"), Id("all"), 0, bnot N); + true -> make_bits(Id("set"), Id("none"), 0, N) end. + +make_bits(_Set, Zero, _I, 0) -> Zero; +make_bits(Set, Zero, I, N) when 0 == N rem 2 -> + make_bits(Set, Zero, I + 1, N div 2); +make_bits(Set, Zero, I, N) -> + {app, [], Set, [make_bits(Set, Zero, I + 1, N div 2), {int, [], I}]}. + diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index ce04b33..6f1fb02 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -62,7 +62,7 @@ encode_decode_sophia_test() -> Other -> Other end end, ok = Check("int", "42"), - ok = Check("int", "-42"), + ok = Check("int", "- 42"), ok = Check("bool", "true"), ok = Check("bool", "false"), ok = Check("string", "\"Hello\""), diff --git a/test/aeso_calldata_tests.erl b/test/aeso_calldata_tests.erl index 2f419cc..46e0be9 100644 --- a/test/aeso_calldata_tests.erl +++ b/test/aeso_calldata_tests.erl @@ -29,11 +29,10 @@ calldata_test_() -> true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]); false -> undefined end, - case FateExprs == undefined orelse AevmExprs == undefined of - true -> ok; - false -> - ?assertEqual(FateExprs, AevmExprs) - end + ParsedExprs = parse_args(Fun, Args), + [ ?assertEqual(ParsedExprs, AevmExprs) || AevmExprs /= undefined ], + [ ?assertEqual(ParsedExprs, FateExprs) || FateExprs /= undefined ], + ok end} || {ContractName, Fun, Args} <- compilable_contracts()]. calldata_aci_test_() -> @@ -53,19 +52,34 @@ calldata_aci_test_() -> true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}]); false -> undefined end, - case FateExprs == undefined orelse AevmExprs == undefined of - true -> ok; - false -> - ?assertEqual(FateExprs, AevmExprs) - end + ParsedExprs = parse_args(Fun, Args), + [ ?assertEqual(ParsedExprs, AevmExprs) || AevmExprs /= undefined ], + [ ?assertEqual(ParsedExprs, FateExprs) || FateExprs /= undefined ], + ok end} || {ContractName, Fun, Args} <- compilable_contracts()]. +parse_args(Fun, Args) -> + [{contract, _, _, [{letfun, _, _, _, _, {app, _, _, AST}}]}] = + aeso_parser:string("contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"), + strip_ann(AST). + +strip_ann(T) when is_tuple(T) -> + strip_ann1(setelement(2, T, [])); +strip_ann(X) -> strip_ann1(X). + +strip_ann1({map, [], KVs}) -> + {map, [], [{strip_ann(K), strip_ann(V)} || {K, V} <- KVs]}; +strip_ann1(T) when is_tuple(T) -> + list_to_tuple(strip_ann1(tuple_to_list(T))); +strip_ann1(L) when is_list(L) -> + lists:map(fun strip_ann/1, L); +strip_ann1(X) -> X. ast_exprs(ContractString, Fun, Args, Opts) -> {ok, Data} = (catch aeso_compiler:create_calldata(ContractString, Fun, Args, Opts)), {ok, _Types, Exprs} = (catch aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts)), ?assert(is_list(Exprs)), - Exprs. + strip_ann(Exprs). check_errors(Expect, ErrorString) -> %% This removes the final single \n as well. @@ -85,7 +99,9 @@ compilable_contracts() -> {"maps", "init", []}, {"funargs", "menot", ["false"]}, {"funargs", "append", ["[\"false\", \" is\", \" not\", \" true\"]"]}, - %% TODO {"funargs", "bitsum", ["Bits.all"]}, + {"funargs", "bitsum", ["Bits.all"]}, + {"funargs", "bitsum", ["Bits.clear(Bits.clear(Bits.all, 4), 2)"]}, %% Order matters for test + {"funargs", "bitsum", ["Bits.set(Bits.set(Bits.none, 4), 2)"]}, {"funargs", "read", ["{label = \"question 1\", result = 4}"]}, {"funargs", "sjutton", ["#0011012003100011012003100011012003"]}, {"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940"