CODE-0006 · deepseek-v4-flash (default)
def tokenize(s):
    """Generate tokens from the expression string."""
    i = 0
    n = len(s)
    while i < n:
        c = s[i]
        if c.isspace():
            i += 1
            continue
        if c.isdigit() or c == '.':
            start = i
            has_dot = False
            i += 1
            while i < n and (s[i].isdigit() or (s[i] == '.' and not has_dot)):
                if s[i] == '.':
                    has_dot = True
                i += 1
            num_str = s[start:i]
            if has_dot:
                yield ('num', float(num_str))
            else:
                yield ('num', int(num_str))
            continue
        if c in '+-*/':
            yield ('op', c)
            i += 1
            continue
        if c == '(':
            yield ('(', None)
            i += 1
            continue
        if c == ')':
            yield (')', None)
            i += 1
            continue
        raise ValueError(f"unknown token: {c!r}")


def parse_expr(tokens, idx):
    left, idx = parse_term(tokens, idx)
    while idx < len(tokens) and tokens[idx][0] == 'op' and tokens[idx][1] in '+-':
        op = tokens[idx][1]
        idx += 1
        right, idx = parse_term(tokens, idx)
        if op == '+':
            left += right
        else:
            left -= right
    return left, idx


def parse_term(tokens, idx):
    left, idx = parse_factor(tokens, idx)
    while idx < len(tokens) and tokens[idx][0] == 'op' and tokens[idx][1] in '*/':
        op = tokens[idx][1]
        idx += 1
        right, idx = parse_factor(tokens, idx)
        if op == '*':
            left *= right
        else:
            if isinstance(right, (int, float)) and right == 0:
                raise ValueError("division by zero")
            left /= right
    return left, idx


def parse_factor(tokens, idx):
    if idx >= len(tokens):
        raise ValueError("unexpected end of expression")
    token = tokens[idx]
    if token[0] == 'op' and token[1] in '+-':
        op = token[1]
        idx += 1
        inner, idx = parse_factor(tokens, idx)
        return -inner if op == '-' else inner, idx
    return parse_atom(tokens, idx)


def parse_atom(tokens, idx):
    if idx >= len(tokens):
        raise ValueError("unexpected end of expression")
    token = tokens[idx]
    if token[0] == 'num':
        return token[1], idx + 1
    if token[0] == '(':
        idx += 1
        inner, idx = parse_expr(tokens, idx)
        if idx >= len(tokens) or tokens[idx][0] != ')':
            raise ValueError("missing closing parenthesis")
        idx += 1
        return inner, idx
    raise ValueError("expected number or '('")


def eval_expr(s):
    """Evaluate an arithmetic expression string."""
    tokens = list(tokenize(s))
    if not tokens:
        raise ValueError("empty expression")
    result, idx = parse_expr(tokens, 0)
    if idx != len(tokens):
        raise ValueError("unexpected trailing tokens")
    # Return exact integer when possible
    if isinstance(result, float) and result.is_integer():
        return int(result)
    return result