Chapter 10
Complete interactive calculator
10.1 Introduction
This chapter presents an extention of the calculator described in the tutorial (see 3). This calculator has
more functions and a memory.
10.2 New functions
10.2.1 Trigonometric and other functions
This calculator can compute some numerical functions (sin, cos, sqrt, ...). The make_op function
(see figure 3.4) has been extended to return these functions. Tokens must also be defined
to scan function names. funct1 defines the name of unaries functions and funct2 defines
the name of binaries functions. Finally the grammar rule of the atoms has been added a
branch to parse functions. The Function non terminal symbol parser unaries and binaries
functions.
10.2.2 Memories
The calculator has memories. A memory cell is identified by a name. For example, if the user types
pi = 4 * atan(1), the memory cell named pi will contain the value of π and cos(pi) will return
-1.
To display the content of the whole memory, the user can type vars.
The variables are saved in a dictionnary. In fact the parser itself is a dictionnary (the parser inherits
from the dict class).
The START symbol parses a variable creation or a single expression and the Atom parses variable
names (the Var symbol parses a variable name and returns its value).
10.3 Source code
Here is the complete source code (calc.pyg):
#!/usr/bin/env python
import math
import operator
import string
import tpg
def make_op(op):
return {
’+’ : operator.add,
’-’ : operator.sub,
’*’ : operator.mul,
’/’ : operator.div,
’%’ : operator.mod,
’ˆ’ : lambda x,y:x**y,
’**’ : lambda x,y:x**y,
’cos’ : math.cos,
’sin’ : math.sin,
’tan’ : math.tan,
’acos’: math.acos,
’asin’: math.asin,
’atan’: math.atan,
’sqr’ : lambda x:x*x,
’sqrt’: math.sqrt,
’abs’ : abs,
’norm’: lambda x,y:math.sqrt(x*x+y*y),
}[op]
class Calc(tpg.Parser, dict):
r"""
separator space ’\s+’ ;
token pow_op ’\ˆ|\*\*’ $ make_op
token add_op ’[+-]’ $ make_op
token mul_op ’[*/%]’ $ make_op
token funct1 ’(cos|sin|tan|acos|asin|atan|sqr|sqrt|abs)\b’ $ make_op
token funct2 ’(norm)\b’ $ make_op
token real ’(\d+\.\d*|\d*\.\d+)([eE][-+]?\d+)?|\d+[eE][-+]?\d+’ $ string.atof
token integer ’\d+’ $ string.atol
token VarId ’[a-zA-Z_]\w*’ ;
START/e ->
’vars’ $ e=self.mem()
| VarId/v ’=’ Expr/e $ self[v]=e
| Expr/e
;
Var/$self.get(v,0)$ -> VarId/v ;
Expr/e -> Term/e ( add_op/op Term/t $ e=op(e,t)
)*
;
Term/t -> Fact/t ( mul_op/op Fact/f $ t=op(t,f)
)*
;
Fact/f ->
add_op/op Fact/f $ f=op(0,f)
| Pow/f
;
Pow/f -> Atom/f ( pow_op/op Fact/e $ f=op(f,e)
)?
;
Atom/a ->
real/a
| integer/a
| Function/a
| Var/a
| ’\(’ Expr/a ’\)’
;
Function/y ->
funct1/f ’\(’ Expr/x ’\)’ $ y = f(x)
| funct2/f ’\(’ Expr/x1 ’,’ Expr/x2 ’\)’ $ y = f(x1,x2)
;
"""
def mem(self):
vars = self.items()
vars.sort()
memory = [ "%s = %s"%(var, val) for (var, val) in vars ]
return "\n\t" + "\n\t".join(memory)
print "Calc (TPG example)"
calc = Calc()
while 1:
l = raw_input("\n:")
if l:
try:
print calc(l)
except Exception, e:
print e
else:
break