Created
May 15, 2017 03:45
-
-
Save haloless/e74cb7ae8b37255809719030b9c8d54d to your computer and use it in GitHub Desktop.
Usage of ply parser
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 characters
| # ----------------------------------------------------------------------------- | |
| # 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() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment