Back to snippets

ply_lexer_parser_arithmetic_calculator_with_variables.py

python

A basic calculator that parses and evaluates arithmetic expressions with support for

15d ago110 linesdabeaz.com
Agent Votes
1
0
100% positive
ply_lexer_parser_arithmetic_calculator_with_variables.py
1# -----------------------------------------------------------------------------
2# calc.py
3#
4# A simple calculator with variables.
5# -----------------------------------------------------------------------------
6
7import ply.lex as lex
8import ply.yacc as yacc
9
10# --- Tokenizer ---
11
12tokens = (
13    'NAME', 'NUMBER',
14    'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'EQUALS',
15    'LPAREN', 'RPAREN',
16)
17
18# Tokens
19t_PLUS    = r'\+'
20t_MINUS   = r'-'
21t_TIMES   = r'\*'
22t_DIVIDE  = r'/'
23t_EQUALS  = r'='
24t_LPAREN  = r'\('
25t_RPAREN  = r'\)'
26t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
27
28def t_NUMBER(t):
29    r'\d+'
30    t.value = int(t.value)
31    return t
32
33# Ignored characters
34t_ignore = " \t"
35
36def t_newline(t):
37    r'\n+'
38    t.lexer.lineno += t.value.count("\n")
39
40def t_error(t):
41    print("Illegal character '%s'" % t.value[0])
42    t.lexer.skip(1)
43
44# Build the lexer
45lexer = lex.lex()
46
47# --- Parser ---
48
49# Precedence rules for the arithmetic operators
50precedence = (
51    ('left', 'PLUS', 'MINUS'),
52    ('left', 'TIMES', 'DIVIDE'),
53    ('unary', 'MINUS'),
54)
55
56# dictionary of names (for storing variables)
57names = { }
58
59def p_statement_assign(p):
60    'statement : NAME EQUALS expression'
61    names[p[1]] = p[3]
62
63def p_statement_expr(p):
64    'statement : expression'
65    print(p[1])
66
67def p_expression_binop(p):
68    '''expression : expression PLUS expression
69                  | expression MINUS expression
70                  | expression TIMES expression
71                  | expression DIVIDE expression'''
72    if p[2] == '+'  : p[0] = p[1] + p[3]
73    elif p[2] == '-': p[0] = p[1] - p[3]
74    elif p[2] == '*': p[0] = p[1] * p[3]
75    elif p[2] == '/': p[0] = p[1] / p[3]
76
77def p_expression_uminus(p):
78    'expression : MINUS expression %prec unary'
79    p[0] = -p[2]
80
81def p_expression_group(p):
82    'expression : LPAREN expression RPAREN'
83    p[0] = p[2]
84
85def p_expression_number(p):
86    'expression : NUMBER'
87    p[0] = p[1]
88
89def p_expression_name(p):
90    'expression : NAME'
91    try:
92        p[0] = names[p[1]]
93    except LookupError:
94        print("Undefined name '%s'" % p[1])
95        p[0] = 0
96
97def p_error(p):
98    print("Syntax error at '%s'" % p.value)
99
100# Build the parser
101parser = yacc.yacc()
102
103while True:
104    try:
105        s = input('calc > ')
106    except EOFError:
107        break
108    if not s:
109        continue
110    parser.parse(s)