"""Lexer for arithmetic calculator.""" class LexError(Exception): pass class Token: __slots__ = ("kind", "value") def __init__(self, kind: str, value): self.kind = kind self.value = value def __repr__(self): return f"Token({self.kind}, {self.value!r})" def __eq__(self, other): return isinstance(other, Token) and self.kind == other.kind and self.value == other.value _SINGLE = { "+": "PLUS", "-": "MINUS", "*": "STAR", "/": "SLASH", "(": "LPAREN", ")": "RPAREN", } def tokenize(src: str) -> list: tokens = [] i = 0 n = len(src) while i < n: ch = src[i] if ch in " \t": i += 1 elif ch in _SINGLE: tokens.append(Token(_SINGLE[ch], ch)) i += 1 elif ch.isdigit() or ch == ".": j = i has_dot = False while j < n and (src[j].isdigit() or (src[j] == "." and not has_dot)): if src[j] == ".": has_dot = True j += 1 raw = src[i:j] try: value = float(raw) if has_dot else int(raw) except ValueError: raise LexError(f"invalid number literal {raw!r} at position {i}") tokens.append(Token("NUMBER", value)) i = j else: raise LexError(f"unexpected character {ch!r} at position {i}") tokens.append(Token("EOF", None)) return tokens