diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index cfeb08e..76e8d62 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -79,6 +79,7 @@ global_env() -> Event = {id, Ann, "event"}, State = {id, Ann, "state"}, Hash = {id, Ann, "hash"}, + Bits = {id, Ann, "bits"}, Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end, Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end, Unit = {tuple_t, Ann, []}, @@ -159,6 +160,16 @@ global_env() -> {["String", "sha3"], Fun1(String, Hash)}, {["String", "sha256"], Fun1(String, Hash)}, {["String", "blake2b"], Fun1(String, Hash)}, + %% Bits + {["Bits", "set"], Fun([Bits, Int], Bits)}, + {["Bits", "clear"], Fun([Bits, Int], Bits)}, + {["Bits", "test"], Fun([Bits, Int], Bool)}, + {["Bits", "sum"], Fun1(Bits, Int)}, + {["Bits", "intersection"], Fun([Bits, Bits], Bits)}, + {["Bits", "union"], Fun([Bits, Bits], Bits)}, + {["Bits", "difference"], Fun([Bits, Bits], Bits)}, + {["Bits", "none"], Bits}, + {["Bits", "all"], Bits}, %% Conversion {["Int", "to_str"], Fun1(Int, String)}, {["Address", "to_str"], Fun1(Address, String)} @@ -691,8 +702,7 @@ infer_infix({BoolOp, As}) {fun_t, As, [], [Bool,Bool], Bool}; infer_infix({IntOp, As}) when IntOp == '+'; IntOp == '-'; IntOp == '*'; IntOp == '/'; - IntOp == '^'; IntOp == 'mod'; IntOp == 'bsl'; IntOp == 'bsr'; - IntOp == 'band'; IntOp == 'bor'; IntOp == 'bxor' -> + IntOp == '^'; IntOp == 'mod' -> Int = {id, As, "int"}, {fun_t, As, [], [Int, Int], Int}; infer_infix({RelOp, As}) @@ -714,8 +724,7 @@ infer_infix({'++', As}) -> infer_prefix({'!',As}) -> Bool = {id, As, "bool"}, {fun_t, As, [], [Bool], Bool}; -infer_prefix({IntOp,As}) - when IntOp =:= '-'; IntOp =:= 'bnot' -> +infer_prefix({IntOp,As}) when IntOp =:= '-' -> Int = {id, As, "int"}, {fun_t, As, [], [Int], Int}. diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index 0509847..2e00104 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -348,6 +348,33 @@ ast_body(?qid_app(["String", "concat"], [String1, String2], _, _), Icode) -> ast_body(?qid_app(["String", "sha3"], [String], _, _), Icode) -> #unop{ op = 'sha3', rand = ast_body(String, Icode) }; +%% -- Bits +ast_body(?qid_app(["Bits", Fun], Args, _, _), Icode) + when Fun == "test"; Fun == "set"; Fun == "clear"; + Fun == "union"; Fun == "intersection"; Fun == "difference" -> + C = fun(N) when is_integer(N) -> #integer{ value = N }; + (X) -> X end, + Bin = fun(O) -> fun(A, B) -> #binop{ op = O, left = C(A), right = C(B) } end end, + And = Bin('band'), + Or = Bin('bor'), + Bsl = fun(A, B) -> (Bin('bsl'))(B, A) end, %% flipped arguments + Bsr = fun(A, B) -> (Bin('bsr'))(B, A) end, + Neg = fun(A) -> #unop{ op = 'bnot', rand = C(A) } end, + case [Fun | [ ast_body(Arg, Icode) || Arg <- Args ]] of + ["test", Bits, Ix] -> And(Bsr(Bits, Ix), 1); + ["set", Bits, Ix] -> Or(Bits, Bsl(1, Ix)); + ["clear", Bits, Ix] -> And(Bits, Neg(Bsl(1, Ix))); + ["union", A, B] -> Or(A, B); + ["intersection", A, B] -> And(A, B); + ["difference", A, B] -> And(A, Neg(And(A, B))) + end; +ast_body({qid, _, ["Bits", "none"]}, _Icode) -> + #integer{ value = 0 }; +ast_body({qid, _, ["Bits", "all"]}, _Icode) -> + #integer{ value = 1 bsl 256 - 1 }; +ast_body(?qid_app(["Bits", "sum"], [Bits], _, _), Icode) -> + builtin_call(popcount, [ast_body(Bits, Icode), #integer{ value = 0 }]); + %% -- Conversion ast_body(?qid_app(["Int", "to_str"], [Int], _, _), Icode) -> builtin_call(int_to_str, [ast_body(Int, Icode)]); @@ -526,9 +553,6 @@ ast_binop(Op, Ann, {typed, _, A, Type}, B, Icode) ast_binop('++', _, A, B, Icode) -> #funcall{ function = #var_ref{ name = {builtin, list_concat} }, args = [ast_body(A, Icode), ast_body(B, Icode)] }; -%% Bit shift operations takes their arguments backwards!? -ast_binop(Op, _, A, B, Icode) when Op =:= 'bsl'; Op =:= 'bsr' -> - #binop{op = Op, right = ast_body(A, Icode), left = ast_body(B, Icode)}; ast_binop(Op, _, A, B, Icode) -> #binop{op = Op, left = ast_body(A, Icode), right = ast_body(B, Icode)}. diff --git a/src/aeso_builtins.erl b/src/aeso_builtins.erl index 0b44692..383fcf4 100644 --- a/src/aeso_builtins.erl +++ b/src/aeso_builtins.erl @@ -148,6 +148,7 @@ builtin_function(BF) -> string_shift_copy -> bfun(BF, builtin_string_shift_copy()); str_equal_p -> bfun(BF, builtin_str_equal_p()); str_equal -> bfun(BF, builtin_str_equal()); + popcount -> bfun(BF, builtin_popcount()); int_to_str -> bfun(BF, builtin_int_to_str()); addr_to_str -> bfun(BF, builtin_addr_to_str()); {baseX_int, X} -> bfun(BF, builtin_baseX_int(X)); @@ -398,6 +399,17 @@ builtin_str_equal() -> )), word}. +%% Count the number of 1s in a bit field. +builtin_popcount() -> + %% function popcount(bits, acc) = + %% if (bits == 0) acc + %% else popcount(bits bsr 1, acc + bits band 1) + {[{"bits", word}, {"acc", word}], + {ifte, ?EQ(bits, 0), + ?V(acc), + ?call(popcount, [op('bsr', 1, bits), ?ADD(acc, op('band', bits, 1))]) + }, word}. + builtin_int_to_str() -> {[{"i", word}], ?call({baseX_int, 10}, [?V(i)]), word}. diff --git a/src/aeso_icode.erl b/src/aeso_icode.erl index 33fcc56..8ee3839 100644 --- a/src/aeso_icode.erl +++ b/src/aeso_icode.erl @@ -59,6 +59,7 @@ builtin_types() -> Word = fun([]) -> word end, #{ "bool" => Word , "int" => Word + , "bits" => Word , "string" => fun([]) -> string end , "address" => Word , "hash" => Word diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index f84295e..57c61cf 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -176,11 +176,11 @@ expr200() -> infixr(expr300(), binop('||')). expr300() -> infixr(expr400(), binop('&&')). expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])). expr500() -> infixr(expr600(), binop(['::', '++'])). -expr600() -> infixl(expr650(), binop(['+', '-', 'bor', 'bxor', 'bsr', 'bsl'])). +expr600() -> infixl(expr650(), binop(['+', '-'])). expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)). -expr700() -> infixl(expr750(), binop(['*', '/', mod, 'band'])). +expr700() -> infixl(expr750(), binop(['*', '/', mod])). expr750() -> infixl(expr800(), binop(['^'])). -expr800() -> ?RULE(many(choice(token('!'), token('bnot'))), expr900(), prefixes(_1, _2)). +expr800() -> ?RULE(many(token('!')), expr900(), prefixes(_1, _2)). expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)). exprAtom() -> diff --git a/src/aeso_pretty.erl b/src/aeso_pretty.erl index 3462a2e..f9209dd 100644 --- a/src/aeso_pretty.erl +++ b/src/aeso_pretty.erl @@ -359,20 +359,14 @@ bin_prec('++') -> {500, 600, 500}; bin_prec('::') -> {500, 600, 500}; bin_prec('+') -> {600, 600, 650}; bin_prec('-') -> {600, 600, 650}; -bin_prec('bor') -> {600, 600, 650}; -bin_prec('bxor') -> {600, 600, 650}; -bin_prec('bsl') -> {600, 600, 650}; -bin_prec('bsr') -> {600, 600, 650}; bin_prec('*') -> {700, 700, 750}; bin_prec('/') -> {700, 700, 750}; bin_prec(mod) -> {700, 700, 750}; -bin_prec('band') -> {700, 700, 750}; bin_prec('^') -> {750, 750, 800}. -spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}. un_prec('-') -> {650, 650}; -un_prec('!') -> {800, 800}; -un_prec('bnot') -> {800, 800}. +un_prec('!') -> {800, 800}. equals(Ann, A, B) -> {app, [{format, infix} | Ann], {'=', Ann}, [A, B]}. diff --git a/src/aeso_scan.erl b/src/aeso_scan.erl index 5cee063..17f4153 100644 --- a/src/aeso_scan.erl +++ b/src/aeso_scan.erl @@ -37,8 +37,7 @@ lexer() -> , {"[^/*]+|[/*]", skip()} ], Keywords = ["contract", "import", "let", "rec", "switch", "type", "record", "datatype", "if", "elif", "else", "function", - "stateful", "true", "false", "and", "mod", "public", "private", "indexed", "internal", - "band", "bor", "bxor", "bsl", "bsr", "bnot"], + "stateful", "true", "false", "and", "mod", "public", "private", "indexed", "internal"], KW = string:join(Keywords, "|"), Rules = diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index 5767e82..f8dc364 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -75,10 +75,10 @@ -type op() :: bin_op() | un_op(). --type bin_op() :: '+' | '-' | '*' | '/' | mod | '^' | 'band' | 'bor' | 'bsl' | 'bsr' | 'bxor' +-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^' | '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!=' | '||' | '&&' | '..'. --type un_op() :: '-' | '!' | 'bnot'. +-type un_op() :: '-' | '!'. -type expr() :: {lam, ann(), [arg()], expr()} diff --git a/test/contracts/all_syntax.aes b/test/contracts/all_syntax.aes index b3ac623..4b80311 100644 --- a/test/contracts/all_syntax.aes +++ b/test/contracts/all_syntax.aes @@ -36,8 +36,6 @@ contract AllSyntax = (x, [y, z]) => bar({x = z, y = -y + - -z * (-1)}) (x, y :: _) => () - function bitOperations(x, y) = bnot (0xff00 band x bsl 4 bxor 0xa5a5a5 bsr 4 bor y) - function mutual() = let rec recFun(x : int) = mutFun(x) and mutFun(x) = if(x =< 0) 1 else x * recFun(x - 1) diff --git a/test/contracts/multi_sig.aes b/test/contracts/multi_sig.aes index 6c8c9ff..028b621 100644 --- a/test/contracts/multi_sig.aes +++ b/test/contracts/multi_sig.aes @@ -5,7 +5,7 @@ contract MultiSig = - record pending_state = { yetNeeded : uint, ownersDone : uint, index : uint } + record pending_state = { yetNeeded : int, ownersDone : bits, index : int } datatype event = Confirmation (address, hash) // of { .owner : Address, .operation : Hash } @@ -13,18 +13,18 @@ contract MultiSig = | OwnerChanged (address, address) // of { .oldOwner : Address, .newOwner : Address } | OwnerAdded (address) // of { .newOwner : Address } | OwnerRemoved (address) // of { .removedOwner : Address } - | ReqChanged (uint) // of { .newReq : uint } + | ReqChanged (int) // of { .newReq : int } - let maxOwners : uint = 250 + let maxOwners : int = 250 - record state = { nRequired : uint - , nOwners : uint - , owners : map(uint, address) - , ownerIndex : map(address, uint) + record state = { nRequired : int + , nOwners : int + , owners : map(int, address) + , ownerIndex : map(address, int) , pending : map(hash, pending_state) , pendingIndex : list(address) } - function init (owners : list(address), nRequired : uint) : state = + function init (owners : list(address), nRequired : int) : state = let n = length(owners) + 1 { nRequired = nRequired, nOwners = n, @@ -39,10 +39,9 @@ contract MultiSig = function revoke(operation : hash) = let ownerIx = lookup(state.ownerIndex, caller()) let pending = lookup(state.pendingIndex, operation) - let ownerIxBit = 1 bsl (ownerIx - 1) - let _ = require(pending.ownersDone band ownerIxBit > 0) + let _ = require(Bits.test(pending.ownersDone, ownerIx)) let pending' = pending { yetNeeded = pending.yetNeeded + 1 - , ownersDone = pending.ownersDone - ownerIxBit } + , ownersDone = Bits.clear(pending.ownersDone, ownerIx - 1) } put(state{ pendingIndex.operator = pending' }) event(Revoke(caller, operation)) @@ -91,7 +90,7 @@ contract MultiSig = , pendingIx = [] }, event = [OwnerRemoved(oldOwner)] } - function changeRequirement(newReq : uint) = + function changeRequirement(newReq : int) = let _ = require(newReq =< state.nOwners) switch(check_pending(callhash())) CheckFail(state') => { state = state' } @@ -102,7 +101,7 @@ contract MultiSig = event = [ReqChanged(newReq)] } - function getOwner(ownerIx0 : uint) = + function getOwner(ownerIx0 : int) = lookup(state.owners, ownerIx0 + 1) function isOwner(owner : address) = @@ -116,8 +115,7 @@ contract MultiSig = Some(pending) => let _ = require(isOwner(owner)) let ownerIx = lookup(state.ownerIndex, owner) - let ownerIxBit = 1 bsl (ownerIx - 1) - (pending.ownersDone band ownerIxBit) != 0 + Bits.test(pending.ownersDone, ownerIx - 1) /* Leave the rest for now... */ diff --git a/test/contracts/operators.aes b/test/contracts/operators.aes index 9ac89fb..1363a6c 100644 --- a/test/contracts/operators.aes +++ b/test/contracts/operators.aes @@ -1,5 +1,4 @@ // - + * / mod arithmetic operators -// bnot band bor bxor bsl bsr bitwise operators // ! && || logical operators // == != < > =< >= comparison operators // :: ++ list operators @@ -13,12 +12,6 @@ contract Operators = "/" => a / b "mod" => a mod b "^" => a ^ b - "bnot" => bnot a - "band" => a band b - "bor" => a bor b - "bxor" => a bxor b - "bsl" => a bsl b - "bsr" => a bsr b function bool_op(a : bool, b : bool, op : string) = switch(op)