diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e135b959..4eb2a4b71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Augmented assignment bitwise operators `|=`, `&=`, `^=`: PR [#350](https://github.com/tact-lang/tact/pull/350) + ### Changed ### Fixed diff --git a/src/grammar/__snapshots__/grammar.spec.ts.snap b/src/grammar/__snapshots__/grammar.spec.ts.snap index 53cb52367..7574e43bd 100644 --- a/src/grammar/__snapshots__/grammar.spec.ts.snap +++ b/src/grammar/__snapshots__/grammar.spec.ts.snap @@ -5485,3 +5485,371 @@ exports[`grammar should parse case-34 1`] = ` "kind": "program", } `; + +exports[`grammar should parse case-35 1`] = ` +{ + "entries": [ + { + "args": [], + "attributes": [], + "id": 52, + "kind": "def_function", + "name": "testFunc", + "origin": "user", + "ref": fun testFunc(): Int { + let a: Int = 1; + let b: Int = 2; + a |= b; + b |= a; + a |= 3; + a |= b | 4; + b &= 1; + a &= b; + b &= a; + a &= b & 1; + b ^= 2; + a ^= b; + b ^= a; + a ^= b ^ 2; + return a; +}, + "return": { + "id": 1, + "kind": "type_ref_simple", + "name": "Int", + "optional": false, + "ref": Int, + }, + "statements": [ + { + "expression": { + "id": 3, + "kind": "number", + "ref": 1, + "value": 1n, + }, + "id": 4, + "kind": "statement_let", + "name": "a", + "ref": let a: Int = 1;, + "type": { + "id": 2, + "kind": "type_ref_simple", + "name": "Int", + "optional": false, + "ref": Int, + }, + }, + { + "expression": { + "id": 6, + "kind": "number", + "ref": 2, + "value": 2n, + }, + "id": 7, + "kind": "statement_let", + "name": "b", + "ref": let b: Int = 2;, + "type": { + "id": 5, + "kind": "type_ref_simple", + "name": "Int", + "optional": false, + "ref": Int, + }, + }, + { + "expression": { + "id": 9, + "kind": "id", + "ref": b, + "value": "b", + }, + "id": 10, + "kind": "statement_augmentedassign", + "op": "|", + "path": [ + { + "id": 8, + "kind": "lvalue_ref", + "name": "a", + "ref": a, + }, + ], + "ref": a |= b;, + }, + { + "expression": { + "id": 12, + "kind": "id", + "ref": a, + "value": "a", + }, + "id": 13, + "kind": "statement_augmentedassign", + "op": "|", + "path": [ + { + "id": 11, + "kind": "lvalue_ref", + "name": "b", + "ref": b, + }, + ], + "ref": b |= a;, + }, + { + "expression": { + "id": 15, + "kind": "number", + "ref": 3, + "value": 3n, + }, + "id": 16, + "kind": "statement_augmentedassign", + "op": "|", + "path": [ + { + "id": 14, + "kind": "lvalue_ref", + "name": "a", + "ref": a, + }, + ], + "ref": a |= 3;, + }, + { + "expression": { + "id": 20, + "kind": "op_binary", + "left": { + "id": 18, + "kind": "id", + "ref": b, + "value": "b", + }, + "op": "|", + "ref": b | 4, + "right": { + "id": 19, + "kind": "number", + "ref": 4, + "value": 4n, + }, + }, + "id": 21, + "kind": "statement_augmentedassign", + "op": "|", + "path": [ + { + "id": 17, + "kind": "lvalue_ref", + "name": "a", + "ref": a, + }, + ], + "ref": a |= b | 4;, + }, + { + "expression": { + "id": 23, + "kind": "number", + "ref": 1, + "value": 1n, + }, + "id": 24, + "kind": "statement_augmentedassign", + "op": "&", + "path": [ + { + "id": 22, + "kind": "lvalue_ref", + "name": "b", + "ref": b, + }, + ], + "ref": b &= 1;, + }, + { + "expression": { + "id": 26, + "kind": "id", + "ref": b, + "value": "b", + }, + "id": 27, + "kind": "statement_augmentedassign", + "op": "&", + "path": [ + { + "id": 25, + "kind": "lvalue_ref", + "name": "a", + "ref": a, + }, + ], + "ref": a &= b;, + }, + { + "expression": { + "id": 29, + "kind": "id", + "ref": a, + "value": "a", + }, + "id": 30, + "kind": "statement_augmentedassign", + "op": "&", + "path": [ + { + "id": 28, + "kind": "lvalue_ref", + "name": "b", + "ref": b, + }, + ], + "ref": b &= a;, + }, + { + "expression": { + "id": 34, + "kind": "op_binary", + "left": { + "id": 32, + "kind": "id", + "ref": b, + "value": "b", + }, + "op": "&", + "ref": b & 1, + "right": { + "id": 33, + "kind": "number", + "ref": 1, + "value": 1n, + }, + }, + "id": 35, + "kind": "statement_augmentedassign", + "op": "&", + "path": [ + { + "id": 31, + "kind": "lvalue_ref", + "name": "a", + "ref": a, + }, + ], + "ref": a &= b & 1;, + }, + { + "expression": { + "id": 37, + "kind": "number", + "ref": 2, + "value": 2n, + }, + "id": 38, + "kind": "statement_augmentedassign", + "op": "^", + "path": [ + { + "id": 36, + "kind": "lvalue_ref", + "name": "b", + "ref": b, + }, + ], + "ref": b ^= 2;, + }, + { + "expression": { + "id": 40, + "kind": "id", + "ref": b, + "value": "b", + }, + "id": 41, + "kind": "statement_augmentedassign", + "op": "^", + "path": [ + { + "id": 39, + "kind": "lvalue_ref", + "name": "a", + "ref": a, + }, + ], + "ref": a ^= b;, + }, + { + "expression": { + "id": 43, + "kind": "id", + "ref": a, + "value": "a", + }, + "id": 44, + "kind": "statement_augmentedassign", + "op": "^", + "path": [ + { + "id": 42, + "kind": "lvalue_ref", + "name": "b", + "ref": b, + }, + ], + "ref": b ^= a;, + }, + { + "expression": { + "id": 48, + "kind": "op_binary", + "left": { + "id": 46, + "kind": "id", + "ref": b, + "value": "b", + }, + "op": "^", + "ref": b ^ 2, + "right": { + "id": 47, + "kind": "number", + "ref": 2, + "value": 2n, + }, + }, + "id": 49, + "kind": "statement_augmentedassign", + "op": "^", + "path": [ + { + "id": 45, + "kind": "lvalue_ref", + "name": "a", + "ref": a, + }, + ], + "ref": a ^= b ^ 2;, + }, + { + "expression": { + "id": 50, + "kind": "id", + "ref": a, + "value": "a", + }, + "id": 51, + "kind": "statement_return", + "ref": return a;, + }, + ], + }, + ], + "id": 53, + "kind": "program", +} +`; diff --git a/src/grammar/ast.ts b/src/grammar/ast.ts index bb88f8810..d765f99d1 100644 --- a/src/grammar/ast.ts +++ b/src/grammar/ast.ts @@ -445,7 +445,15 @@ export type ASTStatementAssign = { ref: ASTRef; }; -export type ASTAugmentedAssignOperation = "+" | "-" | "*" | "/" | "%"; +export type ASTAugmentedAssignOperation = + | "+" + | "-" + | "*" + | "/" + | "%" + | "|" + | "&" + | "^"; export type ASTStatementAugmentedAssign = { kind: "statement_augmentedassign"; diff --git a/src/grammar/grammar.ohm b/src/grammar/grammar.ohm index aac01cad9..405b20ace 100644 --- a/src/grammar/grammar.ohm +++ b/src/grammar/grammar.ohm @@ -105,7 +105,7 @@ Tact { StatementExpression = Expression ";" - StatementAssign = LValue ("=" | "+=" | "-=" | "*=" | "/=" | "%=") Expression ";" + StatementAssign = LValue ("=" | "+=" | "-=" | "*=" | "/=" | "%=" | "|=" | "&=" | "^=") Expression ";" StatementCondition = if Expression "{" Statement* "}" ~else --noElse | if Expression "{" Statement* "}" else "{" Statement* "}" --withElse diff --git a/src/grammar/grammar.ts b/src/grammar/grammar.ts index cc7fff473..830ef260c 100644 --- a/src/grammar/grammar.ts +++ b/src/grammar/grammar.ts @@ -620,6 +620,15 @@ semantics.addOperation("astOfStatement", { case "%=": op = "%"; break; + case "|=": + op = "|"; + break; + case "&=": + op = "&"; + break; + case "^=": + op = "^"; + break; default: throw "Internal compiler error: unreachable augmented assignment operator. Please report at https://github.com/tact-lang/tact/issues"; } diff --git a/src/grammar/test/case-35.tact b/src/grammar/test/case-35.tact new file mode 100644 index 000000000..4ae436d51 --- /dev/null +++ b/src/grammar/test/case-35.tact @@ -0,0 +1,17 @@ +fun testFunc(): Int { + let a: Int = 1; + let b: Int = 2; + a |= b; + b |= a; + a |= 3; + a |= b | 4; + b &= 1; + a &= b; + b &= a; + a &= b & 1; + b ^= 2; + a ^= b; + b ^= a; + a ^= b ^ 2; + return a; +} diff --git a/src/test/feature-math.spec.ts b/src/test/feature-math.spec.ts index be530f93a..f1b916226 100644 --- a/src/test/feature-math.spec.ts +++ b/src/test/feature-math.spec.ts @@ -61,6 +61,15 @@ describe("feature-math", () => { expect(await contract.getDivAug(2n, 2n)).toBe(1n); expect(await contract.getModAug(2n, 2n)).toBe(0n); expect(await contract.getModAug(3n, 2n)).toBe(1n); + expect(await contract.getBitwiseOrAug(2n, 1n)).toBe(3n); + expect(await contract.getBitwiseOrAug(2n, 2n)).toBe(2n); + expect(await contract.getBitwiseOrAug(5n, 3n)).toBe(7n); + expect(await contract.getBitwiseAndAug(3n, 2n)).toBe(2n); + expect(await contract.getBitwiseAndAug(5n, 6n)).toBe(4n); + expect(await contract.getBitwiseAndAug(1n, 3n)).toBe(1n); + expect(await contract.getBitwiseXorAug(1n, 2n)).toBe(3n); + expect(await contract.getBitwiseXorAug(3n, 1n)).toBe(2n); + expect(await contract.getBitwiseXorAug(2n, 0n)).toBe(2n); // Basic Compare expect(await contract.getCompare1(1n, 2n)).toBe(false); diff --git a/src/test/features/math.tact b/src/test/features/math.tact index c2ee908ae..99a5c4ddb 100644 --- a/src/test/features/math.tact +++ b/src/test/features/math.tact @@ -74,6 +74,21 @@ contract MathTester with Deployable { return a; } + get fun bitwiseOrAug(a: Int, b: Int): Int { + a |= b; + return a; + } + + get fun bitwiseAndAug(a: Int, b: Int): Int { + a &= b; + return a; + } + + get fun bitwiseXorAug(a: Int, b: Int): Int { + a ^= b; + return a; + } + // // Int Compare // @@ -371,4 +386,4 @@ contract MathTester with Deployable { get fun precedence12(): Int { return 5 ^ (6 | 7) & 8; } -} \ No newline at end of file +}