77 lines
2.3 KiB
Python
77 lines
2.3 KiB
Python
from .tokens import Token, TokenType
|
|
from string import digits
|
|
|
|
WHITESPACE = " \r\n\t"
|
|
|
|
|
|
class Lexer:
|
|
def __init__(self, text):
|
|
self.text = iter(text)
|
|
self.advance()
|
|
|
|
def advance(self):
|
|
try:
|
|
self.current_char = next(self.text)
|
|
except StopIteration:
|
|
self.current_char = None
|
|
|
|
def generate_tokens(self):
|
|
while self.current_char is not None:
|
|
match self.current_char:
|
|
case x if x in WHITESPACE:
|
|
self.advance()
|
|
case x if x in digits or x == ".":
|
|
yield self.generate_number()
|
|
case "+":
|
|
self.advance()
|
|
yield Token(TokenType.PLUS)
|
|
case "-":
|
|
self.advance()
|
|
yield Token(TokenType.MINUS)
|
|
case "*":
|
|
self.advance()
|
|
yield Token(TokenType.MULTIPLY)
|
|
case "/":
|
|
self.advance()
|
|
yield Token(TokenType.DIVIDE)
|
|
case "(":
|
|
self.advance()
|
|
yield Token(TokenType.LPAREN)
|
|
case ")":
|
|
self.advance()
|
|
yield Token(TokenType.RPAREN)
|
|
case "^":
|
|
self.advance()
|
|
yield Token(TokenType.POWER)
|
|
case "%":
|
|
self.advance()
|
|
yield Token(TokenType.MODULO)
|
|
case _:
|
|
raise Exception(f"Illegal Character: '{self.current_char}'")
|
|
|
|
def generate_number(self):
|
|
decimal_pt_count = 0
|
|
number_str = self.current_char
|
|
self.advance()
|
|
|
|
while self.current_char is not None and (
|
|
self.current_char == "." or self.current_char in digits
|
|
):
|
|
if self.current_char == ".":
|
|
decimal_pt_count += 1
|
|
if decimal_pt_count > 1:
|
|
break
|
|
|
|
number_str += self.current_char
|
|
self.advance()
|
|
|
|
if number_str.startswith("."):
|
|
number_str = "0" + number_str
|
|
|
|
if number_str.endswith("."):
|
|
number_str += "0"
|
|
|
|
return Token(
|
|
TokenType.NUMBER,
|
|
float(number_str) if "." in number_str else int(number_str),
|
|
)
|