CODE-0006 · grok-composer-2.5-fast (default)
def eval_expr(s: str):
    tokens = _tokenize(s)
    parser = _Parser(tokens)
    result = parser.parse_expr()
    if parser._peek() is not None:
        raise ValueError("malformed expression")
    return _normalize(result)


def _normalize(value):
    if isinstance(value, float) and value.is_integer():
        return int(value)
    if isinstance(value, int):
        return value
    return float(value)


def _tokenize(s: str):
    if not s or not s.strip():
        raise ValueError("empty expression")
    tokens = []
    i = 0
    n = len(s)
    while i < n:
        c = s[i]
        if c.isspace():
            i += 1
            continue
        if c in "+-*/()":
            tokens.append(c)
            i += 1
            continue
        if c.isdigit() or c == ".":
            start = i
            if c == ".":
                i += 1
                if i >= n or not s[i].isdigit():
                    raise ValueError("malformed number")
            while i < n and s[i].isdigit():
                i += 1
            if i < n and s[i] == ".":
                i += 1
                if i < n and s[i] == ".":
                    raise ValueError("malformed number")
                while i < n and s[i].isdigit():
                    i += 1
            num_str = s[start:i]
            if num_str == ".":
                raise ValueError("malformed number")
            tokens.append(float(num_str) if "." in num_str else int(num_str))
            continue
        raise ValueError("unknown token")
    if not tokens:
        raise ValueError("empty expression")
    return tokens


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

    def _peek(self):
        if self._pos >= len(self._tokens):
            return None
        return self._tokens[self._pos]

    def _advance(self):
        tok = self._peek()
        if tok is None:
            raise ValueError("malformed expression")
        self._pos += 1
        return tok

    def parse_expr(self):
        left = self.parse_term()
        while True:
            op = self._peek()
            if op not in ("+", "-"):
                break
            self._advance()
            right = self.parse_term()
            if op == "+":
                left = left + right
            else:
                left = left - right
        return left

    def parse_term(self):
        left = self.parse_unary()
        while True:
            op = self._peek()
            if op not in ("*", "/"):
                break
            self._advance()
            right = self.parse_unary()
            if op == "*":
                left = left * right
            else:
                if right == 0:
                    raise ValueError("division by zero")
                left = left / right
        return left

    def parse_unary(self):
        op = self._peek()
        if op == "+":
            self._advance()
            return self.parse_unary()
        if op == "-":
            self._advance()
            return -self.parse_unary()
        return self.parse_primary()

    def parse_primary(self):
        tok = self._peek()
        if isinstance(tok, (int, float)):
            self._advance()
            return tok
        if tok == "(":
            self._advance()
            value = self.parse_expr()
            if self._peek() != ")":
                raise ValueError("mismatched parentheses")
            self._advance()
            return value
        if tok in ("+", "-", "*", "/"):
            raise ValueError("dangling operator")
        if tok == ")":
            raise ValueError("mismatched parentheses")
        raise ValueError("malformed expression")