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