Skip to content

Instantly share code, notes, and snippets.

@haloless
Created May 15, 2017 03:45
Show Gist options
  • Save haloless/e74cb7ae8b37255809719030b9c8d54d to your computer and use it in GitHub Desktop.
Save haloless/e74cb7ae8b37255809719030b9c8d54d to your computer and use it in GitHub Desktop.

Revisions

  1. haloless created this gist May 15, 2017.
    194 changes: 194 additions & 0 deletions calc.py
    Original 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()