# note - use python3
# note - example from https://github.com/dabeaz/sly
# note - type ctrl-d as the end of input.
# notes on lexer
# - tokens are compiled with re.verbose flag
# (comments with #, gets rid of white space).
# If you need to match # use \#
# - tokens matched in order in your file.
# If you want to use == and =, put the rule for == first.
# - sometimes there is more than one way to do something in the
# lexer. for example, can have a separate rule for each keyword
# or catch them under a generic "ID" or "NAME" rule and use "token remapping"
# -----------------------------------------------------------------------------
# calc.py
# -----------------------------------------------------------------------------
# package isn't installed at the moment, so add the path so python can find it
import sys
slyPath = "/u1/h0/jkinne/public_html/cs420-s2019/code/sly-0.4"
sys.path.append(slyPath)
from sly import Lexer, Parser
class CalcLexer(Lexer):
tokens = { NAME, NUMBER, PLUS, TIMES, MINUS, DIVIDE, ASSIGN, LPAREN, RPAREN,
IF, THEN, WHILE, DO}
ignore = ' \t'
# Tokens
NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
NUMBER = r'\d+'
NAME['if'] = IF
NAME['then'] = THEN
NAME['while'] = WHILE
NAME['do'] = DO
# Special symbols
PLUS = r'\+'
MINUS = r'-'
TIMES = r'\*'
DIVIDE = r'/'
ASSIGN = r'='
LPAREN = r'\('
RPAREN = r'\)'
# Ignored pattern
ignore_newline = r'\n+'
# Extra action for newlines
def ignore_newline(self, t):
self.lineno += t.value.count('\n')
def error(self, t):
print("Illegal character '%s'" % t.value[0])
self.index += 1
class CalcParser(Parser):
tokens = CalcLexer.tokens
precedence = (
# ('left', IF, THEN), # note - if-then will be in sly-calc2.py
('left', PLUS, MINUS),
('left', TIMES, DIVIDE),
('right', UMINUS),
)
def __init__(self):
self.names = { }
self.prompt = True
@_('expr')
def statement(self, p):
#print(p.expr)
return ('statement-expr', p.expr)
@_('NAME ASSIGN expr')
def statement(self, p):
#self.names[p.NAME] = p.expr
return ('assign', p.NAME, p.expr)
@_('IF expr THEN statement')
def statement(self, p):
return ('if-then', p.expr, p.statement)
@_('WHILE expr DO statement')
def statement(self, p):
return ('while', p.expr, p.statement)
@_('expr PLUS expr')
def expr(self, p):
#return p.expr0 + p.expr1
return ('plus', p.expr0, p.expr1)
@_('expr MINUS expr')
def expr(self, p):
#return p.expr0 - p.expr1
return ('minus', p.expr0, p.expr1)
@_('expr TIMES expr')
def expr(self, p):
#return p.expr0 * p.expr1
return ('times', p.expr0, p.expr1)
@_('expr DIVIDE expr')
def expr(self, p):
#return p.expr0 / p.expr1
return ('divide', p.expr0, p.expr1)
@_('MINUS expr %prec UMINUS')
def expr(self, p):
#return -p.expr
return ('uminus', p.expr)
@_('LPAREN expr RPAREN')
def expr(self, p):
#return p.expr
return ('paren', p.expr)
@_('NUMBER')
def expr(self, p):
#return int(p.NUMBER)
return ('number', p.NUMBER)
@_('NAME')
def expr(self, p):
return ('name', p.NAME)
#try:
# return self.names[p.NAME]
#except LookupError:
# print(f'Undefined name {p.NAME!r}')
# return 0
def evaluate(tree):
global names
rule = tree[0]
if rule == 'statement-expr':
value = evaluate(tree[1])
print(value)
return value
elif rule == 'assign':
value = evaluate(tree[2])
name = tree[1]
names[name] = value
return value
elif rule == 'times':
return evaluate(tree[1]) * evaluate(tree[2])
elif rule == 'plus':
return evaluate(tree[1]) + evaluate(tree[2])
elif rule == 'minus':
return evaluate(tree[1]) - evaluate(tree[2])
elif rule == 'divide':
return evaluate(tree[1]) / evaluate(tree[2])
elif rule == 'uminus':
return -evaluate(tree[1])
elif rule == 'number':
return int(tree[1])
elif rule == 'name':
return names[tree[1]]
elif rule == 'paren':
return evaluate(tree[1])
elif rule == 'if-then':
value = evaluate(tree[1])
if value:
return evaluate(tree[2])
else:
return 0
elif rule == 'while':
while evaluate(tree[1]):
evaluate(tree[2])
if __name__ == '__main__':
lexer = CalcLexer()
parser = CalcParser()
names = {}
while True:
try:
text = input('calc > ')
except EOFError:
break
tree = parser.parse(lexer.tokenize(text))
evaluate(tree)