Created
May 15, 2017 03:45
-
-
Save haloless/e74cb7ae8b37255809719030b9c8d54d to your computer and use it in GitHub Desktop.
Revisions
-
haloless created this gist
May 15, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,194 @@ # ----------------------------------------------------------------------------- # calc.py # # A simple calculator with variables -- all in one file. # ----------------------------------------------------------------------------- import math import os tokens = ( 'NAME','NUMBER', 'PLUS','MINUS','TIMES','DIVIDE','EQUALS', 'LPAREN','RPAREN', 'COMMA', ) # Tokens t_PLUS = r'\+' t_MINUS = r'-' t_TIMES = r'\*' t_DIVIDE = r'/' t_EQUALS = r'=' t_LPAREN = r'\(' t_RPAREN = r'\)' t_COMMA = r',' t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' def t_NUMBER(t): r'\d+' try: t.value = int(t.value) except ValueError: print("Integer value too large %d", t.value) t.value = 0 return t # Ignored characters t_ignore = " \t" def t_newline(t): r'\n+' t.lexer.lineno += t.value.count("\n") def t_error(t): print("Illegal character '%s'" % t.value[0]) t.lexer.skip(1) # Build the lexer import ply.lex as lex lexer = lex.lex() def test_lexer(): data = '1 , 2,3,,4-1' lexer.input(data) for tok in lexer: print(tok) # Parsing rules precedence = ( ('left','PLUS','MINUS'), ('left','TIMES','DIVIDE'), ('right','UMINUS'), ) # dictionary of names names = { 'pi': math.pi } funcs = { 'sin': math.sin, 'cos': math.cos, 'tan': math.tan, 'atan2': math.atan2, 'rad2deg': math.degrees, 'deg2rad': math.radians, 'sinh': math.sinh, 'cosh': math.cosh, 'tanh': math.tanh, } # print(names) # print(funcs) def p_statement_assign(t): 'statement : NAME EQUALS expression' names[t[1]] = t[3] def p_statement_expr(t): 'statement : expression' print(t[1]) def p_expression_binop(t): '''expression : expression PLUS expression | expression MINUS expression | expression TIMES expression | expression DIVIDE expression''' if t[2] == '+' : t[0] = t[1] + t[3] elif t[2] == '-': t[0] = t[1] - t[3] elif t[2] == '*': t[0] = t[1] * t[3] elif t[2] == '/': t[0] = t[1] / t[3] def p_expression_uminus(t): 'expression : MINUS expression %prec UMINUS' t[0] = -t[2] def p_expression_group(t): 'expression : LPAREN expression RPAREN' t[0] = t[2] def p_expression_number(t): 'expression : NUMBER' t[0] = t[1] def p_expression_name(t): 'expression : NAME' try: t[0] = names[t[1]] except LookupError: print("Undefined name '%s'" % t[1]) t[0] = 0 def p_expression_func(t): 'expression : NAME LPAREN exprlist RPAREN' # print(t[1:]) try: fun = funcs[t[1]] val = fun(*t[3]) t[0] = val except LookupError: print("Undefined func '%s'" % t[1]) t[0] = 0 except TypeError as e: print("Function arguments no match: %s" % e) t[0] = 0 except Excpetion as e: print("Unexpected error: %s" % e) t[0] = 0 return def p_exprlist_list(t): '''exprlist : exprlist COMMA expression''' # print(t[1:]) t[0] = t[1] + (t[3],) def p_exprlist_last(t): '''exprlist : expression''' # print(t[1:]) t[0] = (t[1],) def p_error(t): print("Syntax error at '%s'" % t.value) import ply.yacc as yacc parser = yacc.yacc() def calc_complete(prefix, index): '''Completor for NAMES and FUNCS''' words = list(names.keys()) + list(funcs.keys()) matched = [ w for w in words if w.startswith(prefix) ] try: return matched[index] except IndexError: return None def main_calc(): try: import readline except ImportError: print('Failed to import readline') else: print('Use readline') readline.parse_and_bind("tab: complete") readline.set_completer(calc_complete) while True: try: # s = input('calc > ') # Use raw_input on Python 2 s = raw_input('calc > ') # Use raw_input on Python 2 except EOFError: break parser.parse(s, lexer=lexer) if __name__ == '__main__': # test_lexer() main_calc()