This commit is contained in:
David Beazley 2018-01-30 07:42:27 -06:00
parent 51b01d8335
commit d0e34417bc
2 changed files with 88 additions and 63 deletions

View File

@ -85,20 +85,27 @@ expressions and store variables:
from sly import Lexer, Parser from sly import Lexer, Parser
class CalcLexer(Lexer): class CalcLexer(Lexer):
tokens = { NAME, NUMBER } tokens = { NAME, NUMBER, PLUS, TIMES, MINUS, DIVIDE, ASSIGN, LPAREN, RPAREN }
ignore = ' \t' ignore = ' \t'
literals = { '=', '+', '-', '*', '/', '(', ')' }
# Tokens # Tokens
NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
NUMBER = r'\d+'
@_(r'\d+') # Special symbols
def NUMBER(self, t): PLUS = r'\+'
t.value = int(t.value) MINUS = r'-'
return t TIMES = r'\*'
DIVIDE = r'/'
ASSIGN = r'='
LPAREN = r'\('
RPAREN = r'\)'
@_(r'\n+') # Ignored pattern
def newline(self, t): ignore_newline = r'\n+'
# Extra action for newlines
def ignore_newline(self, t):
self.lineno += t.value.count('\n') self.lineno += t.value.count('\n')
def error(self, t): def error(self, t):
@ -109,15 +116,15 @@ expressions and store variables:
tokens = CalcLexer.tokens tokens = CalcLexer.tokens
precedence = ( precedence = (
('left', '+', '-'), ('left', PLUS, MINUS),
('left', '*', '/'), ('left', TIMES, DIVIDE),
('right', 'UMINUS'), ('right', UMINUS),
) )
def __init__(self): def __init__(self):
self.names = { } self.names = { }
@_('NAME "=" expr') @_('NAME ASSIGN expr')
def statement(self, p): def statement(self, p):
self.names[p.NAME] = p.expr self.names[p.NAME] = p.expr
@ -125,27 +132,27 @@ expressions and store variables:
def statement(self, p): def statement(self, p):
print(p.expr) print(p.expr)
@_('expr "+" expr') @_('expr PLUS expr')
def expr(self, p): def expr(self, p):
return p.expr0 + p.expr1 return p.expr0 + p.expr1
@_('expr "-" expr') @_('expr MINUS expr')
def expr(self, p): def expr(self, p):
return p.expr0 - p.expr1 return p.expr0 - p.expr1
@_('expr "*" expr') @_('expr TIMES expr')
def expr(self, p): def expr(self, p):
return p.expr0 * p.expr1 return p.expr0 * p.expr1
@_('expr "/" expr') @_('expr DIVIDE expr')
def expr(self, p): def expr(self, p):
return p.expr0 / p.expr1 return p.expr0 / p.expr1
@_('"-" expr %prec UMINUS') @_('MINUS expr %prec UMINUS')
def expr(self, p): def expr(self, p):
return -p.expr return -p.expr
@_('"(" expr ")"') @_('LPAREN expr RPAREN')
def expr(self, p): def expr(self, p):
return p.expr return p.expr
@ -158,7 +165,7 @@ expressions and store variables:
try: try:
return self.names[p.NAME] return self.names[p.NAME]
except LookupError: except LookupError:
print("Undefined name '%s'" % p.NAME) print(f'Undefined name {p.NAME!r}')
return 0 return 0
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -3,32 +3,32 @@
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
import sys import sys
sys.path.insert(0, "../..") sys.path.append('../..')
from sly import Lexer, Parser from sly import Lexer, Parser
class CalcLexer(Lexer): class CalcLexer(Lexer):
# Set of token names. This is always required tokens = { NAME, NUMBER, PLUS, TIMES, MINUS, DIVIDE, ASSIGN, LPAREN, RPAREN }
tokens = { NUMBER, PLUS, MINUS, TIMES, DIVIDE, LPAREN, RPAREN }
# String containing ignored characters between tokens
ignore = ' \t' ignore = ' \t'
# Regular expression rules for tokens # Tokens
PLUS = r'\+' NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
MINUS = r'-' NUMBER = r'\d+'
TIMES = r'\*'
DIVIDE = r'/'
LPAREN = r'\('
RPAREN = r'\)'
@_(r'\d+') # Special symbols
def NUMBER(self, t): PLUS = r'\+'
t.value = int(t.value) MINUS = r'-'
return t TIMES = r'\*'
DIVIDE = r'/'
ASSIGN = r'='
LPAREN = r'\('
RPAREN = r'\)'
@_(r'\n+') # Ignored pattern
def newline(self, t): ignore_newline = r'\n+'
# Extra action for newlines
def ignore_newline(self, t):
self.lineno += t.value.count('\n') self.lineno += t.value.count('\n')
def error(self, t): def error(self, t):
@ -36,50 +36,68 @@ class CalcLexer(Lexer):
self.index += 1 self.index += 1
class CalcParser(Parser): class CalcParser(Parser):
# Get the token list from the lexer (required)
tokens = CalcLexer.tokens tokens = CalcLexer.tokens
# Grammar rules and actions precedence = (
@_('expr PLUS term') ('left', PLUS, MINUS),
('left', TIMES, DIVIDE),
('right', UMINUS)
)
def __init__(self):
self.names = { }
@_('NAME ASSIGN expr')
def statement(self, p):
self.names[p.NAME] = p.expr
@_('expr')
def statement(self, p):
print(p.expr)
@_('expr PLUS expr')
def expr(self, p): def expr(self, p):
return p.expr + p.term return p.expr0 + p.expr1
@_('expr MINUS term') @_('expr MINUS expr')
def expr(self, p): def expr(self, p):
return p.expr - p.term return p.expr0 - p.expr1
@_('term') @_('expr TIMES expr')
def expr(self, p): def expr(self, p):
return p.term return p.expr0 * p.expr1
@_('term TIMES factor') @_('expr DIVIDE expr')
def term(self, p): def expr(self, p):
return p.term * p.factor return p.expr0 / p.expr1
@_('term DIVIDE factor') @_('MINUS expr %prec UMINUS')
def term(self, p): def expr(self, p):
return p.term / p.factor return -p.expr
@_('factor')
def term(self, p):
return p.factor
@_('NUMBER')
def factor(self, p):
return p.NUMBER
@_('LPAREN expr RPAREN') @_('LPAREN expr RPAREN')
def factor(self, p): def expr(self, p):
return p.expr return p.expr
@_('NUMBER')
def expr(self, p):
return int(p.NUMBER)
@_('NAME')
def expr(self, p):
try:
return self.names[p.NAME]
except LookupError:
print(f'Undefined name {p.NAME!r}')
return 0
if __name__ == '__main__': if __name__ == '__main__':
lexer = CalcLexer() lexer = CalcLexer()
parser = CalcParser() parser = CalcParser()
while True: while True:
try: try:
text = input('calc > ') text = input('calc > ')
result = parser.parse(lexer.tokenize(text))
print(result)
except EOFError: except EOFError:
break break
if text:
parser.parse(lexer.tokenize(text))