Chapter 11
Infix/Prefix/Postfix notation converter
11.1 Introduction
In the previous example, the parser computes the value of the expression on the fly, while parsing. It is
also possible to build an abstract syntax tree to store an abstract representation of the input. This may
be usefull when several passes are necessary.
This example shows how to parse an expression (infix, prefix or postfix) and convert it in
infix, prefix and postfix notation. The expression is saved in a tree. Each node of the tree
correspond to an operator in the expression. Each leave is a number. Then to write the expression
in infix, prefix or postfix notation, we just need to walk throught the tree in a particular
order.
11.2 Abstract syntax trees
The AST of this converter has two types of node:
-
class Op
- is used to store operators (+, -, *, /, ^). It has two sons associated to the sub expressions.
-
class Atom
- is an atomic expression (a number or a symbolic name).
-
class Func
- is used to store functions.
These classes are instanciated by the __init__ method. The infix, prefix and postfix methods return
strings containing the representation of the node in infix, prefix and postfix notation.
11.3 Grammar
11.3.1 Infix expressions
The grammar for infix expressions is similar to the grammar used in the previous example.
EXPR/e -> TERM/e ( '[+-]'/op TERM/t $e=Op(op,e,t,1)$ )* ;
TERM/t -> FACT/t ( '[*/]'/op FACT/f $t=Op(op,t,f,2)$ )* ;
FACT/f -> ATOM/f ( '\^'/op FACT/e $f=Op(op,f,e,3)$ )? ;
ATOM/a -> ident/s $a=Atom(s)$ | '\(' EXPR/a '\)'
| func1/f '\(' EXPR/x '\)' $a=Func(f,x)
| func2/f '\(' EXPR/x ',' EXPR/y '\)' $a=Func(f,x,y)
;
|
11.3.2 Prefix expressions
The grammar for prefix expressions is very simple. A compound prefix expression is an operator followed
by two subexpressions.
EXPR_PRE/e ->
ident/s $ e=Atom(s)
| '\(' EXPR_PRE/e '\)'
| OP/<op,prec> EXPR_PRE/a EXPR_PRE/b $ e=Op(op,a,b,prec)
| func1/f EXPR/x $ e=Func(f,x)
| func1/f EXPR/x EXPR/y $ e=Func(f,x,y)
;
OP/<op,prec> ->
'[+-]'/op $ prec=1
| '[*/]'/op $ prec=2
| '\^'/op $ prec=3
;
|
11.3.3 Postfix expressions
At first sight postfix and infix grammars may be very similar. Only the position of the operators changes.
So a compound postfix expression is a first expression followed by a second and an operator. This rule is
left recursive. As TPG is a descendant recursive parser, such rules are forbidden to avoid infinite
recursion. To remove the left recursion a classical solution is to rewrite the grammar like
this:
EXPR_POST/e -> ATOM_POST/a SEXPR_POST<a>/e ;
ATOM_POST/a ->
ident/s $ a=Atom(s)
| '\(' EXPR_POST/a '\)'
;
SEXPR_POST<e>/e ->
EXPR_POST/e2
( OP/<op,prec> SEXPR_POST<$Op(op,e,e2,prec)$>/e
| func2/f SEXPR_POST<$Func(f, e, e2)$>/e
)
| func1/f SEXPR_POST<$Func(f, e)$>/e
| ;
|
The parser first searches for an atomic expression and then builds the AST by passing partial
expressions by the attributes of the SEXPR_POST symbol.
11.4 Source code
Here is the complete source code (notation.py):
#!/usr/bin/env python
# Infix/prefix/postfix expression conversion
import tpg
class Op:
""" Binary operator """
def __init__(self, op, a, b, prec):
self.op = op # operator ("+", "-", "*", "/", "^")
self.prec = prec # precedence of the operator
self.a, self.b = a, b # operands
def infix(self):
a = self.a.infix()
if self.a.prec < self.prec: a = "(%s)"%a
b = self.b.infix()
if self.b.prec <= self.prec: b = "(%s)"%b
return "%s %s %s"%(a, self.op, b)
def prefix(self):
a = self.a.prefix()
b = self.b.prefix()
return "%s %s %s"%(self.op, a, b)
def postfix(self):
a = self.a.postfix()
b = self.b.postfix()
return "%s %s %s"%(a, b, self.op)
class Atom:
""" Atomic expression """
def __init__(self, s):
self.a = s
self.prec = 99
def infix(self): return self.a
def prefix(self): return self.a
def postfix(self): return self.a
class Func:
""" Function expression """
def __init__(self, name, *args):
self.name = name
self.args = args
self.prec = 99
def infix(self):
args = [a.infix() for a in self.args]
return "%s(%s)"%(self.name, ",".join(args))
def prefix(self):
args = [a.prefix() for a in self.args]
return "%s %s"%(self.name, " ".join(args))
def postfix(self):
args = [a.postfix() for a in self.args]
return "%s %s"%(" ".join(args), self.name)
# Grammar for arithmetic expressions
class ExpressionParser(tpg.Parser):
r"""
separator space '\s+';
token func1 '\b(sin|cos|tan)\b' ;
token func2 '\b(min|max)\b' ;
token ident '\w+';
START/<e,t> ->
EXPR/e '\n' $ t = 'infix'
| EXPR_PRE/e '\n' $ t = 'prefix'
| EXPR_POST/e '\n' $ t = 'postfix'
;
# Infix expressions
EXPR/e -> TERM/e ( '[+-]'/op TERM/t $ e=Op(op,e,t,1)
)*
;
TERM/t -> FACT/t ( '[*/]'/op FACT/f $ t=Op(op,t,f,2)
)*
;
FACT/f -> ATOM/f ( '\^'/op FACT/e $ f=Op(op,f,e,3)
)?
;
ATOM/a ->
ident/s $ a=Atom(s)
| '\(' EXPR/a '\)'
| func1/f '\(' EXPR/x '\)' $ a=Func(f,x)
| func2/f '\(' EXPR/x ',' EXPR/y '\)' $ a=Func(f,x,y)
;
# Prefix expressions
EXPR_PRE/e ->
ident/s $ e=Atom(s)
| '\(' EXPR_PRE/e '\)'
| OP/<op,prec> EXPR_PRE/a EXPR_PRE/b $ e=Op(op,a,b,prec)
| func1/f EXPR/x $ e=Func(f,x)
| func1/f EXPR/x EXPR/y $ e=Func(f,x,y)
;
# Postfix expressions
EXPR_POST/e -> ATOM_POST/a SEXPR_POST<a>/e ;
ATOM_POST/a ->
ident/s $ a=Atom(s)
| '\(' EXPR_POST/a '\)'
;
SEXPR_POST<e>/e ->
EXPR_POST/e2
( OP/<op,prec> SEXPR_POST<$Op(op,e,e2,prec)$>/e
| func2/f SEXPR_POST<$Func(f, e, e2)$>/e
)
| func1/f SEXPR_POST<$Func(f, e)$>/e
| ;
OP/<op,prec> ->
'[+-]'/op $ prec=1
| '[*/]'/op $ prec=2
| '\^'/op $ prec=3
;
"""
parser = ExpressionParser()
while 1:
e = raw_input(":")
if e == "": break
try:
expr, t = parser(e+"\n")
except tpg.Error, e:
print e
else:
print e, "is a", t, "expression"
print "\tinfix :", expr.infix()
print "\tprefix :", expr.prefix()
print "\tpostfix :", expr.postfix()