"http://www.w3.org/TR/html4/loose.dtd"> >

Chapter 9
Complete interactive calculator

9.1 Introduction

This chapter presents an extention of the calculator described in the tutorial (see 3). This calculator has more functions and a memory.

9.2 New functions

9.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.

9.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 p 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).

9.3 Source code

9.3.1 TPG grammar

The calculator source code can be a grammar for TPG. I.e. the calc.g file is translated into a calc.py script by TPG. Just type in:
    tpg calc.g

Here is the complete source code (calc.g):

set magic = "/usr/bin/env python"  
 
{{  
import math  
import operator  
import string  
}}  
 
parser Calc(dict):  
 
{{  
    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)  
 
    def make_op(self, 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]  
}}  
 
separator space: '\s+' ;  
 
token pow_op: '\^|\*\*' self.make_op ;  
token add_op: '[+-]' self.make_op ;  
token mul_op: '[*/%]' self.make_op ;  
token funct1: '(cos|sin|tan|acos|asin|atan|sqr|sqrt|abs)\b' self.make_op ;  
token funct2: '(norm)\b' self.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>  
    ;  
 
main:  
 
{{  
    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  
}}

9.3.2 Python script

The calculator can be directly embeded in a Python script. The grammar is in a string and compiled using the tpg module.

Here is the complete source code (calc2.py):

#!/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]  
 
exec(tpg.compile(r"""  
 
parser Calc(dict):  
 
{{  
    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)  
}}  
 
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>  
    ;  
 
"""))  
 
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