# 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)