from enum import Enum class Tokens(Enum): QUOTE = 1, CHAR = 2, NEWLINE = 3, SPACE = 4 class Lexer: tokens = list() def __init__(self): pass def lex(self, str): for c in str: if c == "'" or c == '"': self.tokens.append(Tokens.QUOTE) print("Token: QUOTE") elif c == "\n": self.tokens.append(Tokens.NEWLINE) print("Token: NEWLINE") elif c == " ": self.tokens.append(Tokens.SPACE) print("Token: SPACE") else: self.tokens.append(Tokens.CHAR) print("Token: CHAR") def __str__(self): return ",".join(Tokens(token).name for token in self.tokens) class States(Enum): INITIAL = 0, BEGINQUOTE = 1 class Parser: def __init__(self): self.state = States.INITIAL self.line = 1 def transition(self, state): print(f"{States(self.state).name} -> {States(state).name}") self.state = state def parse(self, tokens): for token in tokens: if self.state == States.INITIAL: if token == Tokens.QUOTE: self.transition(States.BEGINQUOTE) elif token == Tokens.NEWLINE: self.line += 1 continue if self.state == States.BEGINQUOTE: if token == Tokens.QUOTE: self.transition(States.INITIAL) elif token == Tokens.CHAR or token == Tokens.SPACE: pass elif token == Tokens.NEWLINE: raise ValueError(f"Unterminated string on line {self.line}") continue if __name__ == "__main__": test1 = """abcdefg "abcde 'a' bareword """ lexer = Lexer() lexer.lex(test1) # print(lexer) parser = Parser() try: parser.parse(lexer.tokens) except ValueError as error: print(error)