CODE-0006 · glm-5.2 (default)def eval_expr(s):
tokens = _tokenize(s)
if not tokens:
raise ValueError("empty input")
parser = _Parser(tokens)
result = parser.parse()
if parser.pos != len(parser.tokens):
raise ValueError("unexpected trailing tokens")
if isinstance(result, float) and result.is_integer():
return int(result)
return result
def _tokenize(s):
tokens = []
i = 0
n = len(s)
while i < n:
c = s[i]
if c.isspace():
i += 1
continue
if c in '+-*/()':
tokens.append(c)
i += 1
continue
if c.isdigit() or c == '.':
start = i
seen_dot = False
seen_digit = False
while i < n and (s[i].isdigit() or s[i] == '.'):
if s[i] == '.':
if seen_dot:
raise ValueError("malformed number")
seen_dot = True
else:
seen_digit = True
i += 1
if not seen_digit:
raise ValueError("malformed number")
tokens.append(s[start:i])
continue
raise ValueError("unknown token: %r" % c)
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(self):
return self.parse_expr()
def parse_expr(self):
left = self.parse_term()
while self.peek() in ('+', '-'):
op = self.advance()
right = self.parse_term()
if op == '+':
left = left + right
else:
left = left - right
return left
def parse_term(self):
left = self.parse_factor()
while self.peek() in ('*', '/'):
op = self.advance()
right = self.parse_factor()
if op == '*':
left = left * right
else:
if right == 0:
raise ValueError("division by zero")
left = left / right
return left
def parse_factor(self):
tok = self.peek()
if tok == '+':
self.advance()
return self.parse_factor()
if tok == '-':
self.advance()
return -self.parse_factor()
if tok == '(':
self.advance()
result = self.parse_expr()
if self.peek() != ')':
raise ValueError("mismatched parentheses")
self.advance()
return result
if tok is None:
raise ValueError("unexpected end of input")
if tok in ('+', '-', '*', '/', ')'):
raise ValueError("dangling operator")
self.advance()
if '.' in tok:
return float(tok)
return int(tok)