CODE-0006 · kimi-k3 (default)import re
_NUMBER_RE = re.compile(r"\d+(?:\.\d*)?|\.\d+")
def _tokenize(s):
tokens = []
i = 0
n = len(s)
while i < n:
ch = s[i]
if ch.isspace():
i += 1
elif ch.isdigit() or ch == ".":
m = _NUMBER_RE.match(s, i)
if m is None:
raise ValueError("invalid number literal at position %d" % i)
tokens.append(("NUM", float(m.group())))
i = m.end()
elif ch in "+-*/()":
tokens.append((ch, ch))
i += 1
else:
raise ValueError("unknown character %r at position %d" % (ch, 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][0]
return None
def _advance(self):
tok = self.tokens[self.pos]
self.pos += 1
return tok
def parse(self):
value = self._parse_expr()
if self.pos != len(self.tokens):
raise ValueError("unexpected token %r" % (self.tokens[self.pos][1],))
return value
def _parse_expr(self):
value = self._parse_term()
while self._peek() in ("+", "-"):
op = self._advance()[0]
rhs = self._parse_term()
if op == "+":
value = value + rhs
else:
value = value - rhs
return value
def _parse_term(self):
value = self._parse_factor()
while self._peek() in ("*", "/"):
op = self._advance()[0]
rhs = self._parse_factor()
if op == "*":
value = value * rhs
else:
if rhs == 0:
raise ValueError("division by zero")
value = value / rhs
return value
def _parse_factor(self):
tok = self._peek()
if tok == "+":
self._advance()
return +self._parse_factor()
if tok == "-":
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 == "NUM":
return self._advance()[1]
if tok == "(":
self._advance()
value = self._parse_expr()
if self._peek() != ")":
raise ValueError("mismatched parentheses")
self._advance()
return value
raise ValueError("unexpected token %r" % (self.tokens[self.pos][1],))
def eval_expr(s):
if not isinstance(s, str):
raise ValueError("expression must be a string")
result = _Parser(_tokenize(s)).parse()
if isinstance(result, float) and result.is_integer():
return int(result)
return result