CODE-0006 · grok-build-0.1 (default)
import re

def tokenize(s):
    token_spec = [
        ('NUMBER', r'\d+\.?\d*|\.\d+'),
        ('OP', r'[+\-*/]'),
        ('LPAREN', r'\('),
        ('RPAREN', r'\)'),
        ('SKIP', r'\s+'),
    ]
    tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_spec)
    tokens = []
    last_end = 0
    for mo in re.finditer(tok_regex, s):
        if mo.start() != last_end:
            raise ValueError("Unknown token")
        kind = mo.lastgroup
        value = mo.group()
        if kind == 'NUMBER':
            tokens.append(value)
        elif kind in ('OP', 'LPAREN', 'RPAREN'):
            tokens.append(value)
        last_end = mo.end()
    if last_end != len(s):
        raise ValueError("Unknown token")
    return tokens

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

    def parse(self):
        if not self.tokens:
            raise ValueError("Empty input")
        val = self.expr()
        if self.pos != len(self.tokens):
            raise ValueError("Malformed expression")
        return val

    def expr(self):
        val = self.term()
        while self.pos < len(self.tokens) and self.tokens[self.pos] in ('+', '-'):
            op = self.tokens[self.pos]
            self.pos += 1
            right = self.term()
            if op == '+':
                val = val + right
            else:
                val = val - right
        return val

    def term(self):
        val = self.factor()
        while self.pos < len(self.tokens) and self.tokens[self.pos] in ('*', '/'):
            op = self.tokens[self.pos]
            self.pos += 1
            right = self.factor()
            if op == '*':
                val = val * right
            else:
                if right == 0:
                    raise ValueError("Division by zero")
                val = val / right
        return val

    def factor(self):
        if self.pos >= len(self.tokens):
            raise ValueError("Malformed expression")
        tok = self.tokens[self.pos]
        if tok == '(':
            self.pos += 1
            val = self.expr()
            if self.pos >= len(self.tokens) or self.tokens[self.pos] != ')':
                raise ValueError("Mismatched parentheses")
            self.pos += 1
            return val
        elif tok == '+':
            self.pos += 1
            return self.factor()
        elif tok == '-':
            self.pos += 1
            return -self.factor()
        else:
            try:
                num = float(tok)
                self.pos += 1
                return num
            except (ValueError, TypeError):
                raise ValueError("Malformed expression")

def eval_expr(s):
    tokens = tokenize(s)
    parser = Parser(tokens)
    result = parser.parse()
    if isinstance(result, float) and result.is_integer():
        return int(result)
    return result