CODE-0006 · claude-opus-4-8 (xhigh)
"""Evaluate arithmetic expressions with a hand-written recursive descent parser.

Supports + - * / with standard precedence and left associativity, parentheses,
unary + and -, integer and decimal literals, and arbitrary whitespace.
"""


def _tokenize(s):
    """Split the input string into a list of (kind, value) tokens.

    Kinds are: 'NUM' (value is the parsed float), 'OP' (value is one of
    '+', '-', '*', '/'), 'LPAREN', 'RPAREN'. Whitespace is skipped.
    Raises ValueError on any character that cannot start a valid token.
    """
    tokens = []
    i = 0
    n = len(s)
    while i < n:
        c = s[i]
        if c.isspace():
            i += 1
            continue
        if c in '+-*/':
            tokens.append(('OP', c))
            i += 1
            continue
        if c == '(':
            tokens.append(('LPAREN', c))
            i += 1
            continue
        if c == ')':
            tokens.append(('RPAREN', 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:
                        # A second dot in the same number literal is malformed.
                        raise ValueError("malformed number literal: %r" % s[start:i + 1])
                    seen_dot = True
                i += 1
            literal = s[start:i]
            # A lone '.' is not a valid number.
            if literal == '.':
                raise ValueError("malformed number literal: %r" % literal)
            try:
                value = float(literal)
            except ValueError:
                raise ValueError("malformed number literal: %r" % literal)
            tokens.append(('NUM', value))
            continue
        raise ValueError("unknown token: %r" % c)
    return tokens


class _Parser:
    """Recursive descent parser over a flat token list.

    Grammar (precedence from lowest to highest):
        expr   := term (('+' | '-') term)*
        term   := factor (('*' | '/') factor)*
        factor := ('+' | '-') factor | primary
        primary := NUM | '(' expr ')'
    """

    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

    def _advance(self):
        tok = self.tokens[self.pos]
        self.pos += 1
        return tok

    def parse(self):
        value = self._expr()
        if self.pos != len(self.tokens):
            # Trailing tokens that were not consumed, e.g. two adjacent
            # numbers ("3 4") or a stray ')'.
            raise ValueError("unexpected trailing input")
        return value

    def _expr(self):
        value = self._term()
        while True:
            tok = self._peek()
            if tok is not None and tok[0] == 'OP' and tok[1] in '+-':
                op = self._advance()[1]
                right = self._term()
                if op == '+':
                    value = value + right
                else:
                    value = value - right
            else:
                break
        return value

    def _term(self):
        value = self._factor()
        while True:
            tok = self._peek()
            if tok is not None and tok[0] == 'OP' and tok[1] in '*/':
                op = self._advance()[1]
                right = self._factor()
                if op == '*':
                    value = value * right
                else:
                    if right == 0:
                        raise ValueError("division by zero")
                    value = value / right
            else:
                break
        return value

    def _factor(self):
        tok = self._peek()
        if tok is None:
            raise ValueError("unexpected end of expression")
        if tok[0] == 'OP' and tok[1] in '+-':
            op = self._advance()[1]
            operand = self._factor()
            if op == '-':
                return -operand
            return operand
        return self._primary()

    def _primary(self):
        tok = self._peek()
        if tok is None:
            raise ValueError("unexpected end of expression")
        if tok[0] == 'NUM':
            self._advance()
            return tok[1]
        if tok[0] == 'LPAREN':
            self._advance()
            value = self._expr()
            closing = self._peek()
            if closing is None or closing[0] != 'RPAREN':
                raise ValueError("mismatched parentheses: missing ')'")
            self._advance()
            return value
        # An OP in operator position (e.g. '*' with no left operand) or a
        # stray ')' lands here.
        raise ValueError("unexpected token: %r" % (tok[1],))


def _normalize(value):
    """Return an int when the value is an exact integer, else a float."""
    if isinstance(value, float):
        if value == int(value) and value not in (float('inf'), float('-inf')):
            return int(value)
        return value
    return value


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

    Returns an ``int`` when the result is an exact integer value, otherwise a
    ``float``. Raises ``ValueError`` for any malformed expression (empty input,
    a dangling operator, mismatched parentheses, two adjacent numbers, an
    unknown token) and for division by zero.
    """
    if not isinstance(s, str):
        raise ValueError("expected a string expression")
    tokens = _tokenize(s)
    if not tokens:
        raise ValueError("empty expression")
    parser = _Parser(tokens)
    value = parser.parse()
    return _normalize(value)