From e88cfba167c478b3d2346a7167f34e283c2ff46c Mon Sep 17 00:00:00 2001 From: CornerPin <7669876+CornerPin@users.noreply.github.com> Date: Thu, 18 Jun 2026 18:35:51 +0300 Subject: [PATCH 1/2] Add new compound and modulo operators to E2 --- .../tests/compiler/parser/assign.txt | 8 +- .../tests/runtime/types/number/compound.txt | 18 ++++ .../tests/runtime/types/vector4.txt | 3 + .../gmod_wire_expression2/base/compiler.lua | 18 +++- .../gmod_wire_expression2/base/parser.lua | 46 ++++++++-- .../gmod_wire_expression2/core/angle.lua | 12 +++ .../gmod_wire_expression2/core/e2lib.lua | 83 ++++++++++++------- .../gmod_wire_expression2/core/vector.lua | 12 +++ .../gmod_wire_expression2/core/vector2.lua | 32 ++++++- 9 files changed, 191 insertions(+), 41 deletions(-) diff --git a/data_static/expression2/tests/compiler/parser/assign.txt b/data_static/expression2/tests/compiler/parser/assign.txt index 63cdf9a3f3..92ed450861 100644 --- a/data_static/expression2/tests/compiler/parser/assign.txt +++ b/data_static/expression2/tests/compiler/parser/assign.txt @@ -11,4 +11,10 @@ A-- A += 1 A -= 1 A *= 1 -A /= 1 \ No newline at end of file +A /= 1 +A %= 1 +A &&= 1 +A ||= 1 +A ^^= 1 +A >>= 1 +A <<= 1 \ No newline at end of file diff --git a/data_static/expression2/tests/runtime/types/number/compound.txt b/data_static/expression2/tests/runtime/types/number/compound.txt index 5789a5bca6..fb8ba42b89 100644 --- a/data_static/expression2/tests/runtime/types/number/compound.txt +++ b/data_static/expression2/tests/runtime/types/number/compound.txt @@ -14,6 +14,24 @@ assert(X == 8) X /= 2 assert(X == 4) +X %= 3 +assert(X == 1) + +X ||= 10 +assert(X == 11) + +X &&= 8 +assert(X == 8) + +X ^^= 12 +assert(X == 4) + +X >>= 2 +assert(X == 1) + +X <<= 2 +assert(X == 4) + X++ assert(X == 5) diff --git a/data_static/expression2/tests/runtime/types/vector4.txt b/data_static/expression2/tests/runtime/types/vector4.txt index cf344f2ac1..b8c207b545 100644 --- a/data_static/expression2/tests/runtime/types/vector4.txt +++ b/data_static/expression2/tests/runtime/types/vector4.txt @@ -34,11 +34,13 @@ assert(vec4(1, 2, 3, 4) + 1 == vec4(2, 3, 4, 5)) assert(vec4(1, 2, 3, 4) - 1 == vec4(0, 1, 2, 3)) assert(vec4(1, 2, 3, 4) * 2 == vec4(2, 4, 6, 8)) assert(vec4(1, 2, 3, 4) / 2 == vec4(0.5, 1, 1.5, 2)) +assert(vec4(1, 2, 3, 4) % 2 == vec4(1, 0, 1, 0)) assert(vec4(1, 2, 3, 4) + vec4(1, 1, 1, 1) == vec4(2, 3, 4, 5)) assert(vec4(1, 2, 3, 4) - vec4(1, 1, 1, 1) == vec4(0, 1, 2, 3)) assert(vec4(1, 2, 3, 4) * vec4(2, 2, 2, 2) == vec4(2, 4, 6, 8)) assert(vec4(1, 2, 3, 4) / vec4(2, 2, 2, 2) == vec4(0.5, 1, 1.5, 2)) +assert(vec4(1, 2, 3, 4) % vec4(2, 2, 2, 2) == vec4(1, 0, 1, 0)) assert(vec4(1, 2, 3, 4) + vec4(1, 2, 3, 4) == vec4(2, 4, 6, 8)) @@ -50,5 +52,6 @@ W += 1 W -= 1 W /= 1 W *= 1 +W %= 1 W++ W-- \ No newline at end of file diff --git a/lua/entities/gmod_wire_expression2/base/compiler.lua b/lua/entities/gmod_wire_expression2/base/compiler.lua index b9ff290753..f40f78a369 100644 --- a/lua/entities/gmod_wire_expression2/base/compiler.lua +++ b/lua/entities/gmod_wire_expression2/base/compiler.lua @@ -231,6 +231,20 @@ local function empty_array() return {} end +---@type table +local CompoundAssignmentVariants = { + [Operator.Add] = NodeVariant.ExprArithmetic, + [Operator.Sub] = NodeVariant.ExprArithmetic, + [Operator.Mul] = NodeVariant.ExprArithmetic, + [Operator.Div] = NodeVariant.ExprArithmetic, + [Operator.Mod] = NodeVariant.ExprArithmetic, + [Operator.Band] = NodeVariant.ExprBinaryOp, + [Operator.Bor] = NodeVariant.ExprBinaryOp, + [Operator.Bxor] = NodeVariant.ExprBinaryOp, + [Operator.Bshr] = NodeVariant.ExprBitShift, + [Operator.Bshl] = NodeVariant.ExprBitShift +} + ---@type table local CompileVisitors = { ---@param data Node[] @@ -1178,10 +1192,10 @@ local CompileVisitors = { end, ---@param data { [1]: Token, [2]: Operator, [3]: Node } - [NodeVariant.CompoundArithmetic] = function(self, trace, data) + [NodeVariant.CompoundAssignment] = function(self, trace, data) -- Transform V = E -> V = V E local result = Node.new( - NodeVariant.ExprArithmetic, + self:Assert(CompoundAssignmentVariants[data[2]], "Unrecognized compound assignment", trace), { Node.new(NodeVariant.ExprIdent, data[1], data[1].trace), data[2], data[3] }, trace ) diff --git a/lua/entities/gmod_wire_expression2/base/parser.lua b/lua/entities/gmod_wire_expression2/base/parser.lua index 3bc618ee0c..58899c8efa 100644 --- a/lua/entities/gmod_wire_expression2/base/parser.lua +++ b/lua/entities/gmod_wire_expression2/base/parser.lua @@ -68,7 +68,7 @@ local NodeVariant = { Increment = 9, -- `++` Decrement = 10, -- `--` - CompoundArithmetic = 11, -- `+=`, `-=`, `*=`, `/=` + CompoundAssignment = 11, -- `+=`, `-=`, `*=`, `/=` Assignment = 12, -- `X = Y[2, number] = Z[2] = 5` or `local X = 5` Const = 13, -- const X = 5 @@ -354,13 +354,25 @@ function Parser:Stmt() --- Compound Assignment if self:ConsumeValue(TokenVariant.Operator, Operator.Aadd) then - return Node.new(NodeVariant.CompoundArithmetic, { var, Operator.Add, self:Expr() }, var.trace:stitch(self:Prev().trace)) + return Node.new(NodeVariant.CompoundAssignment, { var, Operator.Add, self:Expr() }, var.trace:stitch(self:Prev().trace)) elseif self:ConsumeValue(TokenVariant.Operator, Operator.Asub) then - return Node.new(NodeVariant.CompoundArithmetic, { var, Operator.Sub, self:Expr() }, var.trace:stitch(self:Prev().trace)) + return Node.new(NodeVariant.CompoundAssignment, { var, Operator.Sub, self:Expr() }, var.trace:stitch(self:Prev().trace)) elseif self:ConsumeValue(TokenVariant.Operator, Operator.Amul) then - return Node.new(NodeVariant.CompoundArithmetic, { var, Operator.Mul, self:Expr() }, var.trace:stitch(self:Prev().trace)) + return Node.new(NodeVariant.CompoundAssignment, { var, Operator.Mul, self:Expr() }, var.trace:stitch(self:Prev().trace)) elseif self:ConsumeValue(TokenVariant.Operator, Operator.Adiv) then - return Node.new(NodeVariant.CompoundArithmetic, { var, Operator.Div, self:Expr() }, var.trace:stitch(self:Prev().trace)) + return Node.new(NodeVariant.CompoundAssignment, { var, Operator.Div, self:Expr() }, var.trace:stitch(self:Prev().trace)) + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Amod) then + return Node.new(NodeVariant.CompoundAssignment, { var, Operator.Mod, self:Expr() }, var.trace:stitch(self:Prev().trace)) + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Aband) then + return Node.new(NodeVariant.CompoundAssignment, { var, Operator.Band, self:Expr() }, var.trace:stitch(self:Prev().trace)) + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abor) then + return Node.new(NodeVariant.CompoundAssignment, { var, Operator.Bor, self:Expr() }, var.trace:stitch(self:Prev().trace)) + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abxor) then + return Node.new(NodeVariant.CompoundAssignment, { var, Operator.Bxor, self:Expr() }, var.trace:stitch(self:Prev().trace)) + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abshr) then + return Node.new(NodeVariant.CompoundAssignment, { var, Operator.Bshr, self:Expr() }, var.trace:stitch(self:Prev().trace)) + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abshl) then + return Node.new(NodeVariant.CompoundAssignment, { var, Operator.Bshl, self:Expr() }, var.trace:stitch(self:Prev().trace)) end -- Didn't match anything. Might be something else. @@ -694,6 +706,18 @@ function Parser:Expr(ignore_assign) self:Error("Multiplicative assignment operator (*=) must not be part of equation") elseif self:ConsumeValue(TokenVariant.Operator, Operator.Adiv) then self:Error("Divisive assignment operator (/=) must not be part of equation") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Amod) then + self:Error("Modular assignment operator (%=) must not be part of equation") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Aband) then + self:Error("Bitwise AND assignment operator (&&=) must not be part of equation") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abor) then + self:Error("Bitwise OR assignment operator (||=) must not be part of equation") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abxor) then + self:Error("Bitwise XOR assignment operator (^^=) must not be part of equation") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abshr) then + self:Error("Right bitwise shift assignment operator (>>=) must not be part of equation") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abshl) then + self:Error("Left bitwise shift assignment operator (<<=) must not be part of equation") end self.index = self.index - 1 @@ -1060,6 +1084,18 @@ function Parser:Expr15() self:Error("Multiplicative assignment operator (*=) must be preceded by variable") elseif self:ConsumeValue(TokenVariant.Operator, Operator.Adiv) then self:Error("Divisive assignment operator (/=) must be preceded by variable") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Amod) then + self:Error("Modular assignment operator (%=) must be preceded by variable") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Aband) then + self:Error("Bitwise AND assignment operator (&&=) must be preceded by variable") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abor) then + self:Error("Bitwise OR assignment operator (||=) must be preceded by variable") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abxor) then + self:Error("Bitwise XOR assignment operator (^^=) must be preceded by variable") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abshr) then + self:Error("Right bitwise shift assignment operator (>>=) must be preceded by variable") + elseif self:ConsumeValue(TokenVariant.Operator, Operator.Abshl) then + self:Error("Left bitwise shift assignment operator (<<=) must be preceded by variable") elseif self:ConsumeValue(TokenVariant.Operator, Operator.And) then self:Error("Logical and operator (&) must be preceded by equation or value") diff --git a/lua/entities/gmod_wire_expression2/core/angle.lua b/lua/entities/gmod_wire_expression2/core/angle.lua index 19b44def4f..fb66d0a827 100644 --- a/lua/entities/gmod_wire_expression2/core/angle.lua +++ b/lua/entities/gmod_wire_expression2/core/angle.lua @@ -125,6 +125,18 @@ e2function angle operator/(angle rv1, angle rv2) return Angle( rv1[1] / rv2[1], rv1[2] / rv2[2], rv1[3] / rv2[3] ) end +e2function angle operator%(rv1, angle rv2) + return Angle( rv1 % rv2[1], rv1 % rv2[2], rv1 % rv2[3] ) +end + +e2function angle operator%(angle rv1, rv2) + return Angle( rv1[1] % rv2, rv1[2] % rv2, rv1[3] % rv2 ) +end + +e2function angle operator%(angle rv1, angle rv2) + return Angle( rv1[1] % rv2[1], rv1[2] % rv2[2], rv1[3] % rv2[3] ) +end + registerOperator("indexget", "an", "n", function(state, this, index) return this[floor(math.Clamp(index, 1, 3) + 0.5)] end) diff --git a/lua/entities/gmod_wire_expression2/core/e2lib.lua b/lua/entities/gmod_wire_expression2/core/e2lib.lua index 0ea5cd693f..9b136bb6a7 100644 --- a/lua/entities/gmod_wire_expression2/core/e2lib.lua +++ b/lua/entities/gmod_wire_expression2/core/e2lib.lua @@ -550,6 +550,12 @@ E2Lib.optable_inv = { asub = "-=", amul = "*=", adiv = "/=", + amod = "%=", + aband = "&&=", + abor = "||=", + abxor = "^^=", + abshr = ">>=", + abshl = "<<=", inc = "++", dec = "--", eq = "==", @@ -723,60 +729,72 @@ local Operator = { Exp = 6, -- `=` Ass = 7, - -- += + -- `+=` Aadd = 8, - -- -= + -- `-=` Asub = 9, -- `*=` Amul = 10, -- `/=` Adiv = 11, + -- `%=` + Amod = 12, + -- ``&&=` + Aband = 13, + -- `||=` + Abor = 14, + -- `^^=` + Abxor = 15, + -- `>>=` + Abshr = 16, + -- `<<=` + Abshl = 17, -- `++` - Inc = 12, + Inc = 18, -- `--` - Dec = 13, + Dec = 19, -- `==` - Eq = 14, + Eq = 20, -- `!=` - Neq = 15, + Neq = 21, -- `<` - Lth = 16, + Lth = 22, -- `>=` - Geq = 17, + Geq = 23, -- `<=` - Leq = 18, + Leq = 24, -- `>` - Gth = 19, + Gth = 25, -- `&&` - Band = 20, + Band = 26, -- `||` - Bor = 21, + Bor = 27, -- `^^` - Bxor = 22, + Bxor = 28, -- `>>` - Bshr = 23, + Bshr = 29, -- `<<` - Bshl = 24, + Bshl = 30, -- `!` - Not = 25, + Not = 31, -- `&` - And = 26, + And = 32, -- `|` - Or = 27, + Or = 33, -- `?` - Qsm = 28, + Qsm = 34, -- `:` - Col = 29, + Col = 35, -- `?:` - Def = 30, + Def = 36, -- `$` - Dlt = 31, + Dlt = 37, -- `~` - Trg = 32, + Trg = 38, -- `->` - Imp = 33, + Imp = 39, -- `...` - Spread = 34 + Spread = 40 } E2Lib.Operator = Operator @@ -791,13 +809,14 @@ E2Lib.OperatorNames = OperatorNames local OperatorLookup = { ["+"] = Operator.Add, ["-"] = Operator.Sub, ["*"] = Operator.Mul, ["/"] = Operator.Div, ["%"] = Operator.Mod, ["^"] = Operator.Exp, ["="] = Operator.Ass, ["+="] = Operator.Aadd, - ["-="] = Operator.Asub, ["*="] = Operator.Amul, ["/="] = Operator.Adiv, ["++"] = Operator.Inc, - ["--"] = Operator.Dec, ["=="] = Operator.Eq, ["!="] = Operator.Neq, ["<"] = Operator.Lth, - [">="] = Operator.Geq, ["<="] = Operator.Leq, [">"] = Operator.Gth, ["&&"] = Operator.Band, - ["||"] = Operator.Bor, ["^^"] = Operator.Bxor, [">>"] = Operator.Bshr, ["<<"] = Operator.Bshl, - ["!"] = Operator.Not, ["&"] = Operator.And, ["|"] = Operator.Or, ["?"] = Operator.Qsm, - [":"] = Operator.Col, ["?:"] = Operator.Def, ["$"] = Operator.Dlt, ["~"] = Operator.Trg, - ["->"] = Operator.Imp, ["..."] = Operator.Spread + ["-="] = Operator.Asub, ["*="] = Operator.Amul, ["/="] = Operator.Adiv, ["%="] = Operator.Amod, + ["&&="] = Operator.Aband, ["||="] = Operator.Abor, ["^^="] = Operator.Abxor, [">>="] = Operator.Abshr, + ["<<="] = Operator.Abshl, ["++"] = Operator.Inc, ["--"] = Operator.Dec, ["=="] = Operator.Eq, + ["!="] = Operator.Neq, ["<"] = Operator.Lth, [">="] = Operator.Geq, ["<="] = Operator.Leq, + [">"] = Operator.Gth, ["&&"] = Operator.Band, ["||"] = Operator.Bor, ["^^"] = Operator.Bxor, + [">>"] = Operator.Bshr, ["<<"] = Operator.Bshl, ["!"] = Operator.Not, ["&"] = Operator.And, + ["|"] = Operator.Or, ["?"] = Operator.Qsm, [":"] = Operator.Col, ["?:"] = Operator.Def, + ["$"] = Operator.Dlt, ["~"] = Operator.Trg, ["->"] = Operator.Imp, ["..."] = Operator.Spread } E2Lib.OperatorLookup = OperatorLookup diff --git a/lua/entities/gmod_wire_expression2/core/vector.lua b/lua/entities/gmod_wire_expression2/core/vector.lua index 06a05f2e56..8cc7aeb007 100644 --- a/lua/entities/gmod_wire_expression2/core/vector.lua +++ b/lua/entities/gmod_wire_expression2/core/vector.lua @@ -133,6 +133,18 @@ e2function vector operator/(vector lhs, vector rhs) return Vector( lhs[1] / rhs[1], lhs[2] / rhs[2], lhs[3] / rhs[3] ) end +e2function vector operator%(lhs, vector rhs) + return Vector( lhs % rhs[1], lhs % rhs[2], lhs % rhs[3] ) +end + +e2function vector operator%(vector lhs, rhs) + return Vector( lhs[1] % rhs, lhs[2] % rhs, lhs[3] % rhs ) +end + +e2function vector operator%(vector lhs, vector rhs) + return Vector( lhs[1] % rhs[1], lhs[2] % rhs[2], lhs[3] % rhs[3] ) +end + registerOperator("indexget", "vn", "n", function(state, this, index) return this[floor(math.Clamp(index, 1, 3) + 0.5)] end) diff --git a/lua/entities/gmod_wire_expression2/core/vector2.lua b/lua/entities/gmod_wire_expression2/core/vector2.lua index c5a7cedf9a..2a04e21bec 100644 --- a/lua/entities/gmod_wire_expression2/core/vector2.lua +++ b/lua/entities/gmod_wire_expression2/core/vector2.lua @@ -99,6 +99,18 @@ e2function vector2 operator/(vector2 lhs, vector2 rhs) return { lhs[1] / rhs[1], lhs[2] / rhs[2] } end +e2function vector2 operator%(lhs, vector2 rhs) + return { lhs % rhs[1], lhs % rhs[2] } +end + +e2function vector2 operator%(vector2 lhs, rhs) + return { lhs[1] % rhs, lhs[2] % rhs } +end + +e2function vector2 operator%(vector2 lhs, vector2 rhs) + return { lhs[1] % rhs[1], lhs[2] % rhs[2] } +end + registerOperator("indexget", "xv2n", "n", function(state, this, index) return this[floor(math.Clamp(index, 1, 2) + 0.5)] end) @@ -421,7 +433,7 @@ end 4D Vector support \******************************************************************************/ --- NOTE: These are purely cartesian 4D vectors, so "w" denotes the 4th coordinate rather than a scaling factor as with an homogeneous coordinate system +-- NOTE: These are purely cartesian 4D vectors, so "w" denotes the 4th coordinate rather than a scaling factor as with a homogeneous coordinate system /******************************************************************************/ @@ -568,6 +580,24 @@ registerOperator("div", "xv4xv4", "xv4", function(self, args) return { rv1[1] / rv2[1], rv1[2] / rv2[2], rv1[3] / rv2[3], rv1[4] / rv2[4] } end) +registerOperator("mod", "nxv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 % rv2[1], rv1 % rv2[2], rv1 % rv2[3], rv1 % rv2[4] } +end) + +registerOperator("mod", "xv4n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] % rv2, rv1[2] % rv2, rv1[3] % rv2, rv1[4] % rv2 } +end) + +registerOperator("mod", "xv4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] % rv2[1], rv1[2] % rv2[2], rv1[3] % rv2[3], rv1[4] % rv2[4] } +end) + registerOperator("indexget", "xv4n", "n", function(state, this, index) return this[floor(math.Clamp(index, 1, 4) + 0.5)] end) From 02180f211d1772734b04276e63ab3a5ba7bbf25d Mon Sep 17 00:00:00 2001 From: CornerPin <7669876+CornerPin@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:33:11 +0300 Subject: [PATCH 2/2] Add modulo operator for `complex` and `quaternion` --- lua/entities/gmod_wire_expression2/core/complex.lua | 4 ++++ lua/entities/gmod_wire_expression2/core/quaternion.lua | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/lua/entities/gmod_wire_expression2/core/complex.lua b/lua/entities/gmod_wire_expression2/core/complex.lua index 4f38ebea92..0ffe7de051 100644 --- a/lua/entities/gmod_wire_expression2/core/complex.lua +++ b/lua/entities/gmod_wire_expression2/core/complex.lua @@ -164,6 +164,10 @@ e2function complex operator/(complex lhs, number rhs) return {lhs[1]/rhs, lhs[2]/rhs} end +e2function complex operator%(complex lhs, number rhs) + return {lhs[1]%rhs, lhs[2]%rhs} +end + e2function complex operator^(complex lhs, complex rhs) local l = clog(lhs[1], lhs[2]) local e = {rhs[1]*l[1] - rhs[2]*l[2], rhs[1]*l[2] + rhs[2]*l[1]} diff --git a/lua/entities/gmod_wire_expression2/core/quaternion.lua b/lua/entities/gmod_wire_expression2/core/quaternion.lua index 66646ee118..d6669e4ad6 100644 --- a/lua/entities/gmod_wire_expression2/core/quaternion.lua +++ b/lua/entities/gmod_wire_expression2/core/quaternion.lua @@ -420,6 +420,16 @@ end __e2setcost(4) +e2function quaternion operator%(quaternion lhs, number rhs) + local lhs1, lhs2, lhs3, lhs4 = lhs[1], lhs[2], lhs[3], lhs[4] + return { + lhs1%rhs, + lhs2%rhs, + lhs3%rhs, + lhs4%rhs + } +end + e2function quaternion operator^(number lhs, quaternion rhs) if lhs == 0 then return { 0, 0, 0, 0 } end local l = log(lhs)