From 88e3c381357a43a68de7dd230993760d67e65ee3 Mon Sep 17 00:00:00 2001 From: Daniil Sedov <42098239+Gusarich@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:47:50 +0800 Subject: [PATCH] fix: operations precendence levels (#265) --- CHANGELOG.md | 1 + src/grammar/grammar.ohm | 43 +++++++++++++++++++++++------------ src/grammar/grammar.ts | 12 +++++----- src/test/feature-math.spec.ts | 9 ++++++++ src/test/features/math.tact | 24 +++++++++++++++++++ 5 files changed, 69 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba52d4359..03a3f71a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Escape backticks in error messages for generated TypeScript code: PR [#192](https://github.com/tact-lang/tact/pull/192) - Empty inherited trait lists after `with` keyword are now disallowed: PR [#246](https://github.com/tact-lang/tact/pull/246) - Allow chaining method calls with `!!`, for instance, `map.asCell()!!.hash()` is grammatically correct now: PR [#257](ttps://github.com/tact-lang/tact/pull/257) +- Operation precendence for bitwise operators, equality and comparisons now matches common languages, like JavaScript: PR [#265](https://github.com/tact-lang/tact/pull/265) ## [1.2.0] - 2024-02-29 diff --git a/src/grammar/grammar.ohm b/src/grammar/grammar.ohm index 84798e523..272fda7f2 100644 --- a/src/grammar/grammar.ohm +++ b/src/grammar/grammar.ohm @@ -126,35 +126,50 @@ Tact { // Expressions Expression = ExpressionConditional + ExpressionConditional = ExpressionOr "?" ExpressionOr ":" ExpressionConditional --ternary | ExpressionOr + ExpressionOr = ExpressionOr "||" ExpressionAnd --or | ExpressionAnd - ExpressionAnd = ExpressionAnd "&&" ExpressionCompare --and - | ExpressionCompare - ExpressionCompare = ExpressionCompare "!=" ExpressionBinary --not - | ExpressionCompare "==" ExpressionBinary --eq - | ExpressionCompare ">" ExpressionBinary --gt - | ExpressionCompare ">=" ExpressionBinary --gte - | ExpressionCompare "<" ExpressionBinary --lt - | ExpressionCompare "<=" ExpressionBinary --lte - | ExpressionBinary - ExpressionBinary = ExpressionBinary ">>" ExpressionAdd --shr - | ExpressionBinary "<<" ExpressionAdd --shl - | ExpressionBinary "&" ExpressionAdd --bin_and - | ExpressionBinary "|" ExpressionAdd --bin_or - | ExpressionAdd + + ExpressionAnd = ExpressionAnd "&&" ExpressionBinaryOr --and + | ExpressionBinaryOr + + ExpressionBinaryOr = ExpressionBinaryOr "|" ExpressionBinaryAnd --bin_or + | ExpressionBinaryAnd + + ExpressionBinaryAnd = ExpressionBinaryAnd "&" ExpressionEquality --bin_and + | ExpressionEquality + + ExpressionEquality = ExpressionEquality "!=" ExpressionCompare --not + | ExpressionEquality "==" ExpressionCompare --eq + | ExpressionCompare + + ExpressionCompare = ExpressionCompare ">" ExpressionBinaryShift --gt + | ExpressionCompare ">=" ExpressionBinaryShift --gte + | ExpressionCompare "<" ExpressionBinaryShift --lt + | ExpressionCompare "<=" ExpressionBinaryShift --lte + | ExpressionBinaryShift + + ExpressionBinaryShift = ExpressionBinaryShift "<<" ExpressionAdd --shl + | ExpressionBinaryShift ">>" ExpressionAdd --shr + | ExpressionAdd + ExpressionAdd = ExpressionAdd "+" ~"+" ExpressionMul --add | ExpressionAdd "-" ~"-" ExpressionMul --sub | ExpressionMul + ExpressionMul = ExpressionMul "*" ExpressionUnary --mul | ExpressionMul "/" ExpressionUnary --div | ExpressionMul "%" ExpressionUnary --rem | ExpressionUnary + ExpressionUnary = "-" ExpressionValue --neg | "+" ExpressionValue --add | "!" ExpressionValue --not | ExpressionValue + ExpressionBracket = "(" Expression ")" // Order is important diff --git a/src/grammar/grammar.ts b/src/grammar/grammar.ts index 40ce407b5..2f74f7509 100644 --- a/src/grammar/grammar.ts +++ b/src/grammar/grammar.ts @@ -973,7 +973,7 @@ semantics.addOperation("resolve_expression", { ref: createRef(this), }); }, - ExpressionCompare_eq(arg0, _arg1, arg2) { + ExpressionEquality_eq(arg0, _arg1, arg2) { return createNode({ kind: "op_binary", op: "==", @@ -982,7 +982,7 @@ semantics.addOperation("resolve_expression", { ref: createRef(this), }); }, - ExpressionCompare_not(arg0, _arg1, arg2) { + ExpressionEquality_not(arg0, _arg1, arg2) { return createNode({ kind: "op_binary", op: "!=", @@ -1045,7 +1045,7 @@ semantics.addOperation("resolve_expression", { ref: createRef(this), }); }, - ExpressionBinary_shr(arg0, _arg1, arg2) { + ExpressionBinaryShift_shr(arg0, _arg1, arg2) { return createNode({ kind: "op_binary", op: ">>", @@ -1054,7 +1054,7 @@ semantics.addOperation("resolve_expression", { ref: createRef(this), }); }, - ExpressionBinary_shl(arg0, _arg1, arg2) { + ExpressionBinaryShift_shl(arg0, _arg1, arg2) { return createNode({ kind: "op_binary", op: "<<", @@ -1063,7 +1063,7 @@ semantics.addOperation("resolve_expression", { ref: createRef(this), }); }, - ExpressionBinary_bin_and(arg0, _arg1, arg2) { + ExpressionBinaryAnd_bin_and(arg0, _arg1, arg2) { return createNode({ kind: "op_binary", op: "&", @@ -1072,7 +1072,7 @@ semantics.addOperation("resolve_expression", { ref: createRef(this), }); }, - ExpressionBinary_bin_or(arg0, _arg1, arg2) { + ExpressionBinaryOr_bin_or(arg0, _arg1, arg2) { return createNode({ kind: "op_binary", op: "|", diff --git a/src/test/feature-math.spec.ts b/src/test/feature-math.spec.ts index 981d80b58..77fb9cc31 100644 --- a/src/test/feature-math.spec.ts +++ b/src/test/feature-math.spec.ts @@ -388,5 +388,14 @@ describe("feature-math", () => { ); } } + + // Test operation precendence + + expect(await contract.getPrecendence1()).toBe(12n); + expect(await contract.getPrecendence2()).toBe(4n); + expect(await contract.getPrecendence3()).toBe(12n); + expect(await contract.getPrecendence4()).toBe(12n); + expect(await contract.getPrecendence5()).toBe(5n); + expect(await contract.getPrecendence6()).toBe(0n); }); }); diff --git a/src/test/features/math.tact b/src/test/features/math.tact index 661762a48..8c828add4 100644 --- a/src/test/features/math.tact +++ b/src/test/features/math.tact @@ -307,4 +307,28 @@ contract MathTester with Deployable { get fun log(num: Int, base: Int): Int { return log(num, base); } + + get fun precendence1(): Int { + return 5 & 6 | 1 << 5 + 11 * 3 % 12 >> 11; + } + + get fun precendence2(): Int { + return 5 & 6 | 1 << (5 + 11) * 3 % 12 >> 11; + } + + get fun precendence3(): Int { + return 5 & 6 | 1 << 5 + 11 * (3 % 12) >> 35; + } + + get fun precendence4(): Int { + return 5 & 6 | 1 << 5 + (11 * 3) % 12 >> 11; + } + + get fun precendence5(): Int { + return 5 | 6 & 8; + } + + get fun precendence6(): Int { + return (5 | 6) & 8; + } } \ No newline at end of file