CODE-0006 · claude-haiku-4-5 (high)Perfect. Now I'll deliver the solution as requested - the complete source code file:
def eval_expr(s):
"""
Evaluate an arithmetic expression given as a string.
Supports: +, -, *, / (with standard precedence), parentheses, unary +/-,
integer and decimal literals, and whitespace.
Returns int for exact integer results, float otherwise.
Raises ValueError for malformed expressions or division by zero.
"""
tokens = tokenize(s)
if not tokens:
raise ValueError("Empty expression")
parser = Parser(tokens)
result = parser.parse_expr()
if parser.pos < len(tokens):
raise ValueError("Unexpected token after expression")
# Convert to int if it's an exact integer
if isinstance(result, float) and result.is_integer():
return int(result)
return result
def tokenize(s):
"""Tokenize the input string into a list of tokens."""
tokens = []
i = 0
while i < len(s):
# Skip whitespace
if s[i].isspace():
i += 1
continue
# Numbers (integer or decimal)
if s[i].isdigit() or (s[i] == '.' and i + 1 < len(s) and s[i + 1].isdigit()):
j = i
has_dot = False
while j < len(s) and (s[j].isdigit() or (s[j] == '.' and not has_dot)):
if s[j] == '.':
has_dot = True
j += 1
tokens.append(('NUMBER', float(s[i:j])))
i = j
continue
# Operators and parentheses
if s[i] in '+-*/()':
tokens.append((s[i], s[i]))
i += 1
continue
# Unknown character
raise ValueError(f"Unknown character: {s[i]}")
return tokens
class Parser:
"""Recursive descent parser for arithmetic expressions."""
def __init__(self, tokens):
self.tokens = tokens
self.pos = 0
def current_token(self):
"""Return the current token, or None if at end."""
if self.pos < len(self.tokens):
return self.tokens[self.pos]
return None
def consume(self, expected=None):
"""Consume and return the current token."""
token = self.current_token()
if token is None:
raise ValueError("Unexpected end of expression")
if expected is not None and token[0] != expected:
raise ValueError(f"Expected {expected}, got {token[0]}")
self.pos += 1
return token
def parse_expr(self):
"""Parse addition and subtraction (lowest precedence)."""
left = self.parse_term()
while self.current_token() is not None and self.current_token()[0] in ('+', '-'):
op_token = self.consume()
right = self.parse_term()
if op_token[0] == '+':
left = left + right
else:
left = left - right
return left
def parse_term(self):
"""Parse multiplication and division (higher precedence)."""
left = self.parse_unary()
while self.current_token() is not None and self.current_token()[0] in ('*', '/'):
op_token = self.consume()
right = self.parse_unary()
if op_token[0] == '*':
left = left * right
else:
if right == 0:
raise ValueError("Division by zero")
left = left / right
return left
def parse_unary(self):
"""Parse unary plus and minus, and primary expressions."""
token = self.current_token()
if token is not None and token[0] in ('+', '-'):
op_token = self.consume()
value = self.parse_unary()
if op_token[0] == '-':
return -value
else:
return value
return self.parse_primary()
def parse_primary(self):
"""Parse primary expressions: numbers and parenthesized expressions."""
token = self.current_token()
if token is None:
raise ValueError("Unexpected end of expression")
if token[0] == 'NUMBER':
self.consume()
return token[1]
if token[0] == '(':
self.consume()
result = self.parse_expr()
self.consume(')')
return result
raise ValueError(f"Unexpected token: {token[0]}")