CODE-0006 · grok-4.3 (default)
import re

class Parser:
    def __init__(self, s):
        self.tokens = self.tokenize(s)
        self.pos = 0

    def tokenize(self, s):
        if not s or not s.strip():
            raise ValueError("empty")
        pattern = r'\s*((\d+\.?\d*|\.\d+)|[+\-*/()])\s*'
        tokens = []
        last_end = 0
        for m in re.finditer(pattern, s):
            if m.start() != last_end:
                raise ValueError("invalid")
            last_end = m.end()
            token = m.group(1)
            tokens.append(token)
        if last_end != len(s):
            raise ValueError("invalid")
        if not tokens:
            raise ValueError("empty")
        return tokens

    def peek(self):
        if self.pos < len(self.tokens):
            return self.tokens[self.pos]
        return None

    def consume(self):
        tok = self.peek()
        self.pos += 1
        return tok

    def parse(self):
        val = self.expr()
        if self.pos != len(self.tokens):
            raise ValueError("extra")
        if isinstance(val, float) and val.is_integer():
            return int(val)
        return val

    def expr(self):
        val = self.term()
        while self.peek() in ('+', '-'):
            op = self.consume()
            right = self.term()
            if op == '+':
                val = val + right
            else:
                val = val - right
        return val

    def term(self):
        val = self.factor()
        while self.peek() in ('*', '/'):
            op = self.consume()
            right = self.factor()
            if op == '*':
                val = val * right
            else:
                if right == 0:
                    raise ValueError("div0")
                val = val / right
        return val

    def factor(self):
        tok = self.peek()
        if tok is None:
            raise ValueError("dangling")
        if tok == '(':
            self.consume()
            val = self.expr()
            if self.consume() != ')':
                raise ValueError("mismatch")
            return val
        elif tok in ('+', '-'):
            self.consume()
            val = self.factor()
            return val if tok == '+' else -val
        elif re.match(r'^[\d.]', tok):
            self.consume()
            try:
                return float(tok)
            except ValueError:
                raise ValueError("badnum")
        else:
            raise ValueError("unknown")

def eval_expr(s):
    try:
        p = Parser(s)
        return p.parse()
    except Exception:
        raise ValueError