CODE-0006 · claude-sonnet-4-6 (high)def eval_expr(s):
"""Evaluate an arithmetic expression string and return its numeric value."""
# ------------------------------------------------------------------ #
# Tokenizer #
# ------------------------------------------------------------------ #
def tokenize(text):
tokens = []
i = 0
n = len(text)
while i < n:
c = text[i]
if c.isspace():
i += 1
elif c in '+-*/()':
tokens.append(c)
i += 1
elif c.isdigit() or c == '.':
j = i
has_dot = False
while j < n and (text[j].isdigit() or (text[j] == '.' and not has_dot)):
if text[j] == '.':
has_dot = True
j += 1
num_str = text[i:j]
if num_str == '.':
raise ValueError("Invalid token: '.'")
tokens.append(float(num_str) if has_dot else int(num_str))
i = j
else:
raise ValueError(f"Unknown character: {repr(c)}")
if not tokens:
raise ValueError("Empty expression")
return tokens
# ------------------------------------------------------------------ #
# Input validation #
# ------------------------------------------------------------------ #
if not s or not s.strip():
raise ValueError("Empty expression")
tokens = tokenize(s)
pos = [0] # mutable position shared across helpers
# ------------------------------------------------------------------ #
# Parser helpers #
# ------------------------------------------------------------------ #
def peek():
return tokens[pos[0]] if pos[0] < len(tokens) else None
def consume():
tok = tokens[pos[0]]
pos[0] += 1
return tok
# Grammar:
# expr ::= term (('+' | '-') term)*
# term ::= factor (('*' | '/') factor)*
# factor ::= ('+' | '-') factor | atom
# atom ::= number | '(' expr ')'
def parse_expr():
left = parse_term()
while peek() in ('+', '-'):
op = consume()
right = parse_term()
left = left + right if op == '+' else left - right
return left
def parse_term():
left = parse_factor()
while peek() in ('*', '/'):
op = consume()
right = parse_factor()
if op == '*':
left = left * right
else:
if right == 0:
raise ValueError("Division by zero")
left = left / right
return left
def parse_factor():
tok = peek()
if tok == '+':
consume()
return parse_factor()
elif tok == '-':
consume()
return -parse_factor()
else:
return parse_atom()
def parse_atom():
tok = peek()
if tok is None:
raise ValueError("Unexpected end of expression")
if tok == '(':
consume()
val = parse_expr()
if peek() != ')':
raise ValueError("Missing closing parenthesis")
consume()
return val
elif isinstance(tok, (int, float)):
consume()
return tok
else:
raise ValueError(f"Unexpected token: {repr(tok)}")
# ------------------------------------------------------------------ #
# Parse and finalise #
# ------------------------------------------------------------------ #
result = parse_expr()
if pos[0] != len(tokens):
raise ValueError(f"Unexpected token: {repr(tokens[pos[0]])}")
# Return int when the result is an exact integer value.
if isinstance(result, float) and result.is_integer():
return int(result)
return result