CODE-0006 · grok-composer-2.5-fast (default)def eval_expr(s: str):
tokens = _tokenize(s)
parser = _Parser(tokens)
result = parser.parse_expr()
if parser._peek() is not None:
raise ValueError("malformed expression")
return _normalize(result)
def _normalize(value):
if isinstance(value, float) and value.is_integer():
return int(value)
if isinstance(value, int):
return value
return float(value)
def _tokenize(s: str):
if not s or not s.strip():
raise ValueError("empty expression")
tokens = []
i = 0
n = len(s)
while i < n:
c = s[i]
if c.isspace():
i += 1
continue
if c in "+-*/()":
tokens.append(c)
i += 1
continue
if c.isdigit() or c == ".":
start = i
if c == ".":
i += 1
if i >= n or not s[i].isdigit():
raise ValueError("malformed number")
while i < n and s[i].isdigit():
i += 1
if i < n and s[i] == ".":
i += 1
if i < n and s[i] == ".":
raise ValueError("malformed number")
while i < n and s[i].isdigit():
i += 1
num_str = s[start:i]
if num_str == ".":
raise ValueError("malformed number")
tokens.append(float(num_str) if "." in num_str else int(num_str))
continue
raise ValueError("unknown token")
if not tokens:
raise ValueError("empty expression")
return tokens
class _Parser:
def __init__(self, tokens):
self._tokens = tokens
self._pos = 0
def _peek(self):
if self._pos >= len(self._tokens):
return None
return self._tokens[self._pos]
def _advance(self):
tok = self._peek()
if tok is None:
raise ValueError("malformed expression")
self._pos += 1
return tok
def parse_expr(self):
left = self.parse_term()
while True:
op = self._peek()
if op not in ("+", "-"):
break
self._advance()
right = self.parse_term()
if op == "+":
left = left + right
else:
left = left - right
return left
def parse_term(self):
left = self.parse_unary()
while True:
op = self._peek()
if op not in ("*", "/"):
break
self._advance()
right = self.parse_unary()
if op == "*":
left = left * right
else:
if right == 0:
raise ValueError("division by zero")
left = left / right
return left
def parse_unary(self):
op = self._peek()
if op == "+":
self._advance()
return self.parse_unary()
if op == "-":
self._advance()
return -self.parse_unary()
return self.parse_primary()
def parse_primary(self):
tok = self._peek()
if isinstance(tok, (int, float)):
self._advance()
return tok
if tok == "(":
self._advance()
value = self.parse_expr()
if self._peek() != ")":
raise ValueError("mismatched parentheses")
self._advance()
return value
if tok in ("+", "-", "*", "/"):
raise ValueError("dangling operator")
if tok == ")":
raise ValueError("mismatched parentheses")
raise ValueError("malformed expression")