From 7706712fbc47b30d9274da7e946ff45ebf0862b5 Mon Sep 17 00:00:00 2001 From: beam Date: Thu, 22 Oct 2026 20:50:20 +0000 Subject: [PATCH] beam: fix safe_eval unary minus precedence vs ** --- wizard-work/beam/safe_eval.py | 24 ++++++++++++------------ wizard-work/beam/test_safe_eval.py | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/wizard-work/beam/safe_eval.py b/wizard-work/beam/safe_eval.py index 5d26b79..cda4dd0 100644 --- a/wizard-work/beam/safe_eval.py +++ b/wizard-work/beam/safe_eval.py @@ -141,10 +141,10 @@ class _Parser: return value def term(self): - value = self.power() + value = self.unary() while self.peek()[0] == _OP and self.peek()[1] in ("*", "/", "//", "%"): op = self.advance()[1] - rhs = self.power() + rhs = self.unary() if op == "*": value = value * rhs elif op == "/": @@ -161,20 +161,20 @@ class _Parser: value = value % rhs return value - def power(self): - base = self.unary() - if self.peek()[0] == _OP and self.peek()[1] == "**": - self.advance() - exp = self.power() # right-assoc - return base ** exp - return base - def unary(self): if self.peek()[0] == _OP and self.peek()[1] in ("+", "-"): op = self.advance()[1] - val = self.unary() + val = self.power() # unary lower precedence than ** (python semantics) return -val if op == "-" else val - return self.primary() + return self.power() + + def power(self): + base = self.primary() + if self.peek()[0] == _OP and self.peek()[1] == "**": + self.advance() + exp = self.unary() # right-assoc, exponent may be signed + return base ** exp + return base def primary(self): t = self.peek() diff --git a/wizard-work/beam/test_safe_eval.py b/wizard-work/beam/test_safe_eval.py index 708dbfd..cb54824 100644 --- a/wizard-work/beam/test_safe_eval.py +++ b/wizard-work/beam/test_safe_eval.py @@ -4,6 +4,8 @@ cases = [ ("2+3*4", 14), ("(2+3)*4", 20), ("2**3**2", 512), # right assoc + ("-2**2", -4), # unary lower than ** + ("2**-2", 0.25), # signed exponent ("10//3", 3), ("10%3", 1), ("-5+2", -3),