CODE-0006 · claude-sonnet-5 (high)def _tokenize(s):
tokens = []
i = 0
n = len(s)
while i < n:
c = s[i]
if c.isspace():
i += 1
continue
if c.isdigit():
j = i
while j < n and s[j].isdigit():
j += 1
if j < n and s[j] == '.':
j += 1
if j >= n or not s[j].isdigit():
raise ValueError(
"malformed number literal at position {}".format(i)
)
while j < n and s[j].isdigit():
j += 1
tokens.append(('NUMBER', s[i:j]))
i = j
continue
if c in '+-*/()':
tokens.append((c, c))
i += 1
continue
raise ValueError("unexpected character {!r} at position {}".format(c, i))
return tokens
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 _advance(self):
tok = self.tokens[self.pos]
self.pos += 1
return tok
def parse_expr(self):
value = self.parse_term()
while True:
tok = self._peek()
if tok is not None and tok[0] in ('+', '-'):
self._advance()
rhs = self.parse_term()
if tok[0] == '+':
value = value + rhs
else:
value = value - rhs
else:
break
return value
def parse_term(self):
value = self.parse_factor()
while True:
tok = self._peek()
if tok is not None and tok[0] in ('*', '/'):
self._advance()
rhs = self.parse_factor()
if tok[0] == '*':
value = value * rhs
else:
if rhs == 0:
raise ValueError("division by zero")
value = value / rhs
else:
break
return value
def parse_factor(self):
tok = self._peek()
if tok is None:
raise ValueError("unexpected end of expression")
if tok[0] == '+':
self._advance()
return +self.parse_factor()
if tok[0] == '-':
self._advance()
return -self.parse_factor()
return self.parse_primary()
def parse_primary(self):
tok = self._peek()
if tok is None:
raise ValueError("unexpected end of expression")
if tok[0] == 'NUMBER':
self._advance()
return float(tok[1])
if tok[0] == '(':
self._advance()
value = self.parse_expr()
tok2 = self._peek()
if tok2 is None or tok2[0] != ')':
raise ValueError("mismatched parentheses")
self._advance()
return value
raise ValueError("unexpected token {!r}".format(tok[1]))
def eval_expr(s):
if not isinstance(s, str):
raise ValueError("expression must be a string")
tokens = _tokenize(s)
if not tokens:
raise ValueError("empty expression")
parser = _Parser(tokens)
value = parser.parse_expr()
if parser.pos != len(tokens):
extra = parser.tokens[parser.pos]
raise ValueError("unexpected token {!r}".format(extra[1]))
if isinstance(value, float) and value.is_integer():
return int(value)
return value