70 lines
1.7 KiB
Python
70 lines
1.7 KiB
Python
from enum import Enum
|
|||
|
|
|
||
|
|
class Tokens(Enum):
|
||
|
|
QUOTE = 1,
|
||
|
|
CHAR = 2,
|
||
|
|
NEWLINE = 3
|
||
|
|
|
||
|
|
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)
|
||
|
|
elif c == "\n":
|
||
|
|
self.tokens.append(Tokens.NEWLINE)
|
||
|
|
else:
|
||
|
|
self.tokens.append(Tokens.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:
|
||
|
|
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)
|
||
|
|
|
||
|
|
|