Skip to content

Commit

Permalink
feat: bitwise NOT operator ~ (tact-lang#337)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gusarich authored Jun 9, 2024
1 parent c658c85 commit 0f65cfa
Show file tree
Hide file tree
Showing 17 changed files with 126 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- The bitwise NOT operation (`~`): PR [#337](https://github.com/tact-lang/tact/pull/337)
- Augmented assignment bitwise operators `|=`, `&=`, `^=`: PR [#350](https://github.com/tact-lang/tact/pull/350)
- Traversing maps from contract storage and structs is now allowed: PR [#389](https://github.com/tact-lang/tact/pull/389)

Expand Down
4 changes: 4 additions & 0 deletions src/generator/writers/writeExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,10 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string {
return "(~ " + writeExpression(f.right, ctx) + ")";
}

if (f.op === "~") {
return "(~ " + writeExpression(f.right, ctx) + ")";
}

if (f.op === "-") {
return "(- " + writeExpression(f.right, ctx) + ")";
}
Expand Down
2 changes: 1 addition & 1 deletion src/grammar/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export type ASTOpBinary = {
ref: ASTRef;
};

export type ASTUnaryOperation = "+" | "-" | "!" | "!!";
export type ASTUnaryOperation = "+" | "-" | "!" | "!!" | "~";

export type ASTOpUnary = {
kind: "op_unary";
Expand Down
1 change: 1 addition & 0 deletions src/grammar/grammar.ohm
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ Tact {
ExpressionUnary = "-" ExpressionUnary --minus
| "+" ExpressionUnary --plus
| "!" ExpressionUnary --not
| "~" ExpressionUnary --bitwiseNot
| ExpressionPrimary

// Order is important
Expand Down
8 changes: 8 additions & 0 deletions src/grammar/grammar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,14 @@ semantics.addOperation<ASTNode>("astOfExpression", {
ref: createRef(this),
});
},
ExpressionUnary_bitwiseNot(_tilde, operand) {
return createNode({
kind: "op_unary",
op: "~",
right: operand.astOfExpression(),
ref: createRef(this),
});
},
ExpressionParens(_lparen, expression, _rparen) {
return expression.astOfExpression();
},
Expand Down
42 changes: 31 additions & 11 deletions src/test/__snapshots__/feature-constants.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,16 @@ ConstantTester {
"type": "int",
},
},
{
"arguments": [],
"name": "something20",
"returnType": {
"format": 257,
"kind": "simple",
"optional": false,
"type": "int",
},
},
{
"arguments": [],
"name": "minInt1",
Expand Down Expand Up @@ -442,7 +452,7 @@ ConstantTester {
},
],
},
"address": kQBLPCFB6hZTJVoIVoSLp-zwDPnb5aQG3LnI_S3IHwDre99T,
"address": kQC0jy5E0g2P09qLQNz8948lt5CqpJwDVKDhilWC_jJpsHMo,
"init": {
"code": x{FF00F4A413F4BCF2C80B}
x{62_}
Expand Down Expand Up @@ -508,10 +518,15 @@ ConstantTester {
x{85FF}
x{2_}
x{5}
x{A975DB3CDB3C31}
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
x{7A}
x{2_}
x{A6EBB679B67863_}
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
x{7A}
x{A6A9B679B67863_}
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
x{80FA}
x{AAA2DB3CDB3C31}
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
Expand Down Expand Up @@ -546,7 +561,7 @@ ConstantTester {
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
x{800B}
x{AEE3435697066733A2F2F516D5952584D7874336E78397331376A3874474D64315751526D574D6A556764756E75365536685A4C764758627182_}
x{AEE3435697066733A2F2F516D665575695850684879515A4C34525831794B71716D7243473374424E7572564E736A39463957696B4D48655182_}
x{AA45DB3CDB3C31}
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
Expand Down Expand Up @@ -637,10 +652,15 @@ ConstantTester {
x{85FF}
x{2_}
x{5}
x{A975DB3CDB3C31}
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
x{7A}
x{2_}
x{A6EBB679B67863_}
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
x{7A}
x{A6A9B679B67863_}
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
x{80FA}
x{AAA2DB3CDB3C31}
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
Expand Down Expand Up @@ -675,7 +695,7 @@ ConstantTester {
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
x{800B}
x{AEE3435697066733A2F2F516D5952584D7874336E78397331376A3874474D64315751526D574D6A556764756E75365536685A4C764758627182_}
x{AEE3435697066733A2F2F516D665575695850684879515A4C34525831794B71716D7243473374424E7572564E736A39463957696B4D48655182_}
x{AA45DB3CDB3C31}
x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C}
x{6D}
Expand Down
1 change: 1 addition & 0 deletions src/test/feature-constants.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe("feature-constants", () => {
expect(await contract.getMinInt1()).toEqual(
-115792089237316195423570985008687907853269984665640564039457584007913129639936n,
);
expect(await contract.getSomething20()).toEqual(-6n);
expect(await contract.getGlobalConst()).toEqual(100n);
});
});
9 changes: 9 additions & 0 deletions src/test/feature-math.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ describe("feature-math", () => {
expect(await contract.getXor(2n, -3n)).toBe(-1n);
expect(await contract.getXor(-2n, 3n)).toBe(-3n);
expect(await contract.getXor(-2n, -3n)).toBe(3n);
expect(await contract.getBitwiseNot(2n)).toBe(-3n);
expect(await contract.getBitwiseNot(-2n)).toBe(1n);
expect(await contract.getBitwiseNot(6n)).toBe(-7n);

// Augmented Assign
expect(await contract.getAddAug(1n, 2n)).toBe(3n);
Expand Down Expand Up @@ -464,5 +467,11 @@ describe("feature-math", () => {
expect(await contract.getPrecedence10()).toBe(3n);
expect(await contract.getPrecedence11()).toBe(3n);
expect(await contract.getPrecedence12()).toBe(5n);

// Test multiple unary operations in a row
expect(await contract.getBitwiseNot1(5n)).toBe(5n);
expect(await contract.getBitwiseNot2(5n)).toBe(-6n);
expect(await contract.getBitwiseNot3(5n)).toBe(4n);
expect(await contract.getBitwiseNot4(5n)).toBe(6n);
});
});
5 changes: 5 additions & 0 deletions src/test/features/constants.tact
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ contract ConstantTester {
const something17: Int = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
const something18: Int = -(pow2(255) - 1 + pow2(255));
const something19: Int = -(pow2(255) - 1 + pow2(255)) - 1;
const something20: Int = ~5;

init() {

Expand Down Expand Up @@ -104,6 +105,10 @@ contract ConstantTester {
get fun something19(): Int {
return self.something19;
}

get fun something20(): Int {
return self.something20;
}

get fun minInt1(): Int {
return -115792089237316195423570985008687907853269984665640564039457584007913129639936;
Expand Down
20 changes: 20 additions & 0 deletions src/test/features/math.tact
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ contract MathTester with Deployable {
get fun xor(a: Int, b: Int): Int {
return a ^ b;
}

get fun bitwise_not(a: Int): Int {
return ~a;
}

//
// Augmented assignment
Expand Down Expand Up @@ -386,4 +390,20 @@ contract MathTester with Deployable {
get fun precedence12(): Int {
return 5 ^ (6 | 7) & 8;
}

get fun bitwiseNot1(x: Int): Int {
return ~~x;
}

get fun bitwiseNot2(x: Int): Int {
return ~~~x;
}

get fun bitwiseNot3(x: Int): Int {
return ~-x;
}

get fun bitwiseNot4(x: Int): Int {
return -~x;
}
}
18 changes: 18 additions & 0 deletions src/types/__snapshots__/resolveDescriptors.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,24 @@ Line 1, col 1:
"
`;
exports[`resolveDescriptors should fail descriptors for case-28 1`] = `
"<unknown>:4:18: Cannot reduce expression to a constant integer
Line 4, col 18:
3 |
> 4 | const a: Int = ~ true;
^~~~
"
`;
exports[`resolveDescriptors should fail descriptors for case-29 1`] = `
"<unknown>:4:17: Cannot reduce expression to a constant boolean
Line 4, col 17:
3 |
> 4 | const a: Bool = ~ true;
^~~~~~
"
`;
exports[`resolveDescriptors should resolve descriptors for case-0 1`] = `
{
"BaseTrait": {
Expand Down
10 changes: 10 additions & 0 deletions src/types/__snapshots__/resolveStatements.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,16 @@ Line 5, col 5:
"
`;
exports[`resolveStatements should fail statements for case-53 1`] = `
"<unknown>:5:12: Invalid type "Bool" for unary operator "~"
Line 5, col 12:
4 | fun test(a: Bool): Int {
> 5 | return ~a;
^~
6 | }
"
`;
exports[`resolveStatements should resolve statements for case-0 1`] = `
[
[
Expand Down
2 changes: 2 additions & 0 deletions src/types/resolveConstantValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ function reduceIntImpl(ast: ASTExpression): bigint {
return -reduceInt(ast.right);
} else if (ast.op === "+") {
return reduceInt(ast.right);
} else if (ast.op === "~") {
return ~reduceInt(ast.right);
}
} else if (ast.kind === "op_static_call") {
if (ast.name === "ton") {
Expand Down
2 changes: 1 addition & 1 deletion src/types/resolveExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ function resolveUnaryOp(

// Check right type dependent on operator
let resolvedType = getExpType(ctx, exp.right);
if (exp.op === "-" || exp.op === "+") {
if (exp.op === "-" || exp.op === "+" || exp.op === "~") {
if (
resolvedType.kind !== "ref" ||
resolvedType.optional ||
Expand Down
6 changes: 6 additions & 0 deletions src/types/stmts-failed/case-53.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
primitive Int;
primitive Bool;

fun test(a: Bool): Int {
return ~a;
}
4 changes: 4 additions & 0 deletions src/types/test-failed/case-28.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
primitive Int;
primitive Bool;

const a: Int = ~ true;
4 changes: 4 additions & 0 deletions src/types/test-failed/case-29.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
primitive Int;
primitive Bool;

const a: Bool = ~ true;

0 comments on commit 0f65cfa

Please sign in to comment.