CODE-0006 · claude-opus-4-8 (medium)
I'll write a recursive descent parser. Let me deliver the solution.

The final message is the deliverable.

import re


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

    Supports + - * / with standard precedence and left associativity,
    parentheses, unary +/-, integer and decimal literals, and ignores
    whitespace. True division is used; results that are exact integers are
    returned as int, otherwise float. Raises ValueError on malformed input
    or division by zero.
    """
    tokens = _tokenize(s)
    parser = _Parser(tokens)
    value = parser.parse()
    return _normalize(value)


_TOKEN_RE = re.compile(r"""
    \s*(?:
        (?P<number>(?:\d+\.\d*|\.\d+|\d+))
      | (?P<op>[+\-*/()])
    )
""", re.VERBOSE)


def _tokenize(s):
    if s is None:
        raise ValueError("expression must be a string")
    tokens = []
    pos = 0
    n = len(s)
    while pos < n:
        # Skip leading whitespace explicitly to detect trailing junk cleanly.
        m = _TOKEN_RE.match(s, pos)
        if not m or m.end() == pos:
            # Check whether the remainder is only whitespace.
            if s[pos:].strip() == "":
                break
            raise ValueError("unknown token at position {}".format(pos))
        if m.group("number") is not None:
            text = m.group("number")
            try:
                if "." in text:
                    value = float(text)
                else:
                    value = int(text)
            except ValueError:
                raise ValueError("invalid number literal: {!r}".format(text))
            tokens.append(("number", value))
        else:
            tokens.append(("op", m.group("op")))
        pos = m.end()
    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):
            raise ValueError("unexpected trailing token")
        return value

    def _expr(self):
        # Addition and subtraction, left associative.
        value = self._term()
        while True:
            kind, tok = self._peek()
            if kind == "op" and tok in ("+", "-"):
                self._advance()
                right = self._term()
                if tok == "+":
                    value = value + right
                else:
                    value = value - right
            else:
                break
        return value

    def _term(self):
        # Multiplication and division, left associative.
        value = self._factor()
        while True:
            kind, tok = self._peek()
            if kind == "op" and tok in ("*", "/"):
                self._advance()
                right = self._factor()
                if tok == "*":
                    value = value * right
                else:
                    if right == 0:
                        raise ValueError("division by zero")
                    value = value / right
            else:
                break
        return value

    def _factor(self):
        # Unary operators, then a primary.
        kind, tok = self._peek()
        if kind == "op" and tok in ("+", "-"):
            self._advance()
            operand = self._factor()
            return operand if tok == "+" else -operand
        return self._primary()

    def _primary(self):
        kind, tok = self._peek()
        if kind == "number":
            self._advance()
            return tok
        if kind == "op" and tok == "(":
            self._advance()
            value = self._expr()
            kind2, tok2 = self._peek()
            if not (kind2 == "op" and tok2 == ")"):
                raise ValueError("mismatched parentheses")
            self._advance()
            return value
        if kind is None:
            raise ValueError("unexpected end of expression")
        raise ValueError("unexpected token: {!r}".format(tok))


def _normalize(value):
    if isinstance(value, int):
        return value
    if isinstance(value, float):
        if value == int(value) and value not in (float("inf"), float("-inf")):
            return int(value)
        return value
    raise ValueError("non-numeric result")