CODE-0006 · claude-opus-4-8 (high)
"""Arithmetic expression evaluator.

Hand-written recursive-descent parser for a small arithmetic grammar:

    expr   := term (('+' | '-') term)*
    term   := factor (('*' | '/') factor)*
    factor := ('+' | '-') factor | primary
    primary:= NUMBER | '(' expr ')'

Supports +, -, *, / with standard precedence and left associativity,
parentheses, unary + and -, integer and decimal literals, and arbitrary
whitespace. `/` is true division. An exact integer result is returned as an
`int`, otherwise as a `float`. Malformed input and division by zero raise
`ValueError`.
"""


def _tokenize(s):
    """Turn the input string into a list of tokens.

    Each token is a tuple (kind, value). `kind` is one of
    'NUMBER', '+', '-', '*', '/', '(', ')'. Numbers carry their float value.
    Raises ValueError on an unknown character or a malformed number.
    """
    tokens = []
    i = 0
    n = len(s)
    while i < n:
        c = s[i]
        if c.isspace():
            i += 1
            continue
        if c in '+-*/()':
            tokens.append((c, c))
            i += 1
            continue
        if c.isdigit() or c == '.':
            start = i
            seen_dot = False
            while i < n and (s[i].isdigit() or s[i] == '.'):
                if s[i] == '.':
                    if seen_dot:
                        raise ValueError(
                            "malformed number: multiple decimal points "
                            "in %r" % s[start:i + 1]
                        )
                    seen_dot = True
                i += 1
            text = s[start:i]
            if text == '.':
                raise ValueError("malformed number: lone '.'")
            try:
                value = float(text)
            except ValueError:
                raise ValueError("malformed number: %r" % text)
            tokens.append(('NUMBER', value))
            continue
        raise ValueError("unknown token: %r" % c)
    return tokens


class _Parser:
    def __init__(self, tokens):
        self._tokens = tokens
        self._pos = 0

    def _peek(self):
        if self._pos < len(self._tokens):
            return self._tokens[self._pos]
        return (None, None)

    def _advance(self):
        tok = self._tokens[self._pos]
        self._pos += 1
        return tok

    def parse(self):
        if not self._tokens:
            raise ValueError("empty expression")
        value = self._expr()
        if self._pos != len(self._tokens):
            kind, _ = self._peek()
            raise ValueError("unexpected token: %r" % kind)
        return value

    def _expr(self):
        value = self._term()
        while self._peek()[0] in ('+', '-'):
            op, _ = self._advance()
            rhs = self._term()
            if op == '+':
                value = value + rhs
            else:
                value = value - rhs
        return value

    def _term(self):
        value = self._factor()
        while self._peek()[0] in ('*', '/'):
            op, _ = self._advance()
            rhs = self._factor()
            if op == '*':
                value = value * rhs
            else:
                if rhs == 0:
                    raise ValueError("division by zero")
                value = value / rhs
        return value

    def _factor(self):
        kind, _ = self._peek()
        if kind == '+':
            self._advance()
            return self._factor()
        if kind == '-':
            self._advance()
            return -self._factor()
        return self._primary()

    def _primary(self):
        kind, value = self._peek()
        if kind == 'NUMBER':
            self._advance()
            return value
        if kind == '(':
            self._advance()
            inner = self._expr()
            if self._peek()[0] != ')':
                raise ValueError("mismatched parentheses: expected ')'")
            self._advance()
            return inner
        if kind is None:
            raise ValueError("unexpected end of expression")
        raise ValueError("unexpected token: %r" % kind)


def eval_expr(s):
    """Evaluate the arithmetic expression `s` and return its numeric value.

    Returns an `int` when the result is an exact integer value, otherwise a
    `float`. Raises `ValueError` for malformed input or division by zero.
    """
    if not isinstance(s, str):
        raise ValueError("expression must be a string")
    tokens = _tokenize(s)
    result = _Parser(tokens).parse()

    # Normalise an exact integer result to int.
    if isinstance(result, float):
        if result != result:  # NaN guard (cannot arise here, defensive)
            return result
        if result == int(result) and result not in (float('inf'), float('-inf')):
            return int(result)
    return result