# 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, PRINT, BEGIN, END}
ignore = ' \t'
ignore_comment = r'\#.*'
# 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
NAME['begin'] = BEGIN
NAME['end'] = END
NAME['print'] = PRINT
# 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):
return ('statement-expr', p.expr)
@_('BEGIN statement_list END')
def statement(self, p):
return ('statement-compound', p.statement_list)
@_('statement statement_list')
def statement_list(self, p):
return ('statement-list', p.statement, p.statement_list)
@_('')
def statement_list(self, p):
return ('statement-list-end')
@_('NAME ASSIGN expr')
def statement(self, p):
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)
@_('PRINT expr') # for printing, since we're not always printing the result any more
def statement(self, p):
return ('print', p.expr)
@_('') # basically epsilon, for comment lines that don't parse to anything
def statement(self, p):
return
@_('expr PLUS expr')
def expr(self, p):
return ('plus', p.expr0, p.expr1)
@_('expr MINUS expr')
def expr(self, p):
return ('minus', p.expr0, p.expr1)
@_('expr TIMES expr')
def expr(self, p):
return ('times', p.expr0, p.expr1)
@_('expr DIVIDE expr')
def expr(self, p):
return ('divide', p.expr0, p.expr1)
@_('MINUS expr %prec UMINUS')
def expr(self, p):
return ('uminus', p.expr)
@_('LPAREN expr RPAREN')
def expr(self, p):
return ('paren', p.expr)
@_('NUMBER')
def expr(self, p):
return ('number', p.NUMBER)
@_('NAME')
def expr(self, p):
return ('name', p.NAME)
def evaluate(tree):
global names
if tree == None: return
rule = tree[0]
if rule == 'statement-expr':
value = evaluate(tree[1])
return value
elif rule == 'assign':
value = evaluate(tree[2])
name = tree[1]
names[name] = value
return value
elif rule == 'print':
value = evaluate(tree[1])
print(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 = {}
text = sys.stdin.read()
tree = parser.parse(lexer.tokenize(text))
evaluate(tree)