CODE-0006 · gemini-3.1-pro-preview (high)import re
from fractions import Fraction
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
def consume(self):
tok = self.peek()
self.pos += 1
return tok
def parse(self):
if not self.tokens:
raise ValueError("Empty input")
res = self.parse_expr()
if self.pos < len(self.tokens):
raise ValueError("Unexpected tokens at end")
return res
def parse_expr(self):
res = self.parse_term()
while True:
tok = self.peek()
if tok and tok[0] == 'OP' and tok[1] in ('+', '-'):
self.consume()
rhs = self.parse_term()
if tok[1] == '+':
res += rhs
else:
res -= rhs
else:
break
return res
def parse_term(self):
res = self.parse_factor()
while True:
tok = self.peek()
if tok and tok[0] == 'OP' and tok[1] in ('*', '/'):
self.consume()
rhs = self.parse_factor()
if tok[1] == '*':
res *= rhs
else:
if rhs == 0:
raise ValueError("Division by zero")
res /= rhs
else:
break
return res
def parse_factor(self):
tok = self.peek()
if not tok:
raise ValueError("Unexpected end of input in factor")
if tok[0] == 'OP' and tok[1] in ('+', '-'):
self.consume()
val = self.parse_factor()
return val if tok[1] == '+' else -val
return self.parse_primary()
def parse_primary(self):
tok = self.consume()
if not tok:
raise ValueError("Unexpected end of input in primary")
if tok[0] == 'NUM':
num_str = tok[1]
if num_str.endswith('.'):
num_str += '0'
if num_str.startswith('.'):
num_str = '0' + num_str
return Fraction(num_str)
elif tok[0] == 'OP' and tok[1] == '(':
res = self.parse_expr()
tok2 = self.consume()
if not tok2 or tok2[0] != 'OP' or tok2[1] != ')':
raise ValueError("Missing closing parenthesis")
return res
else:
raise ValueError("Unexpected token in primary")
def eval_expr(s):
if not isinstance(s, str):
raise ValueError("Input must be a string")
tokens = []
pattern = re.compile(r'\s*(?:(\d+(?:\.\d*)?|\.\d+)|([+\-*/()]))')
pos = 0
while pos < len(s):
match = pattern.match(s, pos)
if not match:
if s[pos:].strip() != "":
raise ValueError(f"Invalid token at position {pos}")
break
num, op = match.groups()
if num is not None:
tokens.append(('NUM', num))
else:
tokens.append(('OP', op))
pos = match.end()
parser = Parser(tokens)
res = parser.parse()
if res.denominator == 1:
return int(res.numerator)
return float(res)