|
Symbolic Differentiation Part II Terry Brugger
[email: zow@acm.org] A previous entry in the Field Guide explained how the Polynomial Parser from Language Translation Using PCCTS & C++ could be constructed in ANTLR 2. This Field Guide entry expands upon that to construct the AST for the Poly Parser. The first advantage to ANTLR 2 that appears in this section is that a assign
: ID "="^ poly ";"!
{ System.out.println( #assign.toStringList() ); }
;
The only change here is in the action. Rules can be referred to using the syntax #name. The term
:! coeff:coefficient
( var:reg
( "^" pow:exp
{ #term = #(#[MULT, "MULT"], #coeff,
#(#[EXP, "EXP"], #var, #pow)); }
| { #term = #(#[MULT, "MULT"], #coeff, #var); }
)
| { #term = #coeff; }
)
|! var2:reg ( "^" pow2:exp
{ #term = #(#[EXP, "EXP"], #var2, #pow2); }
| { #term = #var2; }
)
;
The two main changes that aren't related to variable name changes stem from the combination of We add DummyTokens : MULT | EXP ; We add the // Poly.g
// Defines a grammar for a Polynomial Parser
class PolyParser extends Parser;
options
{
buildAST = true;
}
// The entry point for an interpreter
interp!
: (assign )+
;
// An assignment expression
assign
: ID "="^ poly ";"!
{ System.out.println(#assign.toStringList()); }
;
// A polynomial
poly
: term ( "+"^ term )*
;
// A term in the polynomial
term
:! coeff:coefficient
( var:reg
( "^" pow:exp
{ #term = #(#[MULT, "MULT"], #coeff,
#(#[EXP, "EXP"], #var, #pow)); }
| { #term = #(#[MULT, "MULT"], #coeff, #var); }
)
| { #term = #coeff; }
)
|! var2:reg ( "^" pow2:exp
{ #term = #(#[EXP, "EXP"], #var2, #pow2); }
| { #term = #var2; }
)
;
// The coefficient of the term
coefficient
: FLOAT
;
// A register or variable
reg
: ID
;
// An exponent in a term
exp
: reg
| FLOAT
;
dummyTokens
: MULT
| EXP
;
class PolyLex extends Lexer;
// An ID for a register
ID
: 'a'..'z'
;
// A floating point number
FLOAT
: ( '0'..'9' )+ ( '.' ( '0'..'9' )* )?
;
// Operators
OPERATORS
: '='
| '+'
| '^'
| ';'
;
// Eat the white space
WS
: ( ' '
| '\t'
| '\r' '\n' { newline(); }
| '\n' { newline(); }
)
/* All the characters are in a subrule so that this
action will apply to them: */
{ $setType(Token.SKIP); } // ignore this token
;
Walking the ASTWalking the AST that we just built is very easy in ANTLR 2. We'll start with defining a TreeParser in Poly.g: class PolyWalker extends TreeParser; Our assign and poly rules from page 60 have only a slight modification: assign : #( "=" ID poly) ; poly : #( MULT poly poly ) | #( "+" poly poly ) | #( EXP poly poly ) | ID | FLOAT ; that is, the ASSIGN and ADD tokens have been changed to their string forms. Using real tokens would require going back and changing the entire Poly grammar. The conversion to using Java syntax for the actions is equally straightforward: assign
{ double dbl=0.0; }
: #( "=" id:ID dbl=poly )
{ regs.store(id.getText(), dbl); }
;
poly returns [double ret]
{ double lhs = 0.0, rhs = 0.0; ret = -1.0; }
: #( MULT lhs=poly rhs=poly )
{ ret = lhs * rhs; }
| #( "+" lhs=poly rhs=poly )
{ ret = lhs + rhs; }
| #( EXP lhs=poly rhs=poly )
{ ret = java.lang.Math.pow(lhs, rhs); }
| id:ID
{ ret = regs.value(id.getText()); }
| flt:FLOAT
{ ret = (new Double(flt.getText())).doubleValue(); }
;
We add in our registers and a specialized constructor for the register behaviour, just like we did for PolyParser when we had it perform semantic actions: {
private DblStorage regs;
public PolyWalker (DblStorage regs)
{
this();
this.regs = regs;
} // end specialized constructor
}
And tell PolyParser to use the PolyWalker: {
protected PolyWalker walker;
public PolyParser(Tokenizer tokenBuf, PolyWalker walker)
{
this(tokenBuf);
this.walker = walker;
} // end specialized constructor
}
assign
: ID "="^ poly ";"!
{ walker.assign(#assign); }
;
Our resulting grammar is: // Poly.g
// Defines a grammar for a Polynomial Parser
class PolyParser extends Parser;
options
{
buildAST = true;
}
{
protected PolyWalker walker;
public PolyParser(Tokenizer tokenBuf, PolyWalker walker)
{
this(tokenBuf);
this.walker = walker;
} // end specialized constructor
}
// The entry point for an interpreter
interp!
: (assign )+
;
// An assignment expression
assign
: ID "="^ poly ";"!
{ walker.assign(#assign); }
;
// A polynomial
poly
: term ( "+"^ term )*
;
// A term in the polynomial
term
:! coeff:coefficient
( var:reg
( "^" pow:exp
{ #term = #(#[MULT, "MULT"], #coeff,
#(#[EXP, "EXP"], #var, #pow)); }
| { #term = #(#[MULT, "MULT"], #coeff, #var); }
)
| { #term = #coeff; }
)
|! var2:reg ( "^" pow2:exp
{ #term = #(#[EXP, "EXP"], #var2, #pow2); }
| { #term = #var2; }
)
;
// The coefficient of the term
coefficient
: FLOAT
;
// A register or variable
reg
: ID
;
// An exponent in a term
exp
: reg
| FLOAT
;
dummyTokens
: MULT
| EXP
;
class PolyWalker extends TreeParser;
{
private DblStorage regs;
public PolyWalker (DblStorage regs)
{
this();
this.regs = regs;
} // end specialized constructor
}
// Assignment of the result of a polynomial to a register
assign
{ double dbl=0.0; }
: #( "=" id:ID dbl=poly )
{ regs.store(id.getText(), dbl); }
;
// computation of polynomials
poly returns [double ret]
{ double lhs = 0.0, rhs = 0.0; ret = -1.0; }
: #( MULT lhs=poly rhs=poly )
{ ret = lhs * rhs; }
| #( "+" lhs=poly rhs=poly )
{ ret = lhs + rhs; }
| #( EXP lhs=poly rhs=poly )
{ ret = java.lang.Math.pow(lhs, rhs); }
| id:ID
{ ret = regs.value(id.getText()); }
| flt:FLOAT
{ ret = (new Double(flt.getText())).doubleValue(); }
;
class PolyLex extends Lexer;
// An ID for a register
ID
: 'a'..'z'
;
// A floating point number
FLOAT
: ( '0'..'9' )+ ( '.' ( '0'..'9' )* )?
;
// Operators
OPERATORS
: '='
| '+'
| '^'
| ';'
;
// Eat the white space
WS
: ( ' '
| '\t'
| '\r' '\n' { newline(); }
| '\n' { newline(); }
)
/* All the characters are in a subrule so that this
action will apply to them: */
{ $setType(Token.SKIP); } // ignore this token
;
We also need a new main in order to construct everything with the proper behaviour: import java.io.*;
import antlr.CommonAST;
import antlr.collections.AST;
class PolyMain
{
public static void main(String[] args)
{
try {
DblStorage regs = new Registers();
PolyWalker walker = new PolyWalker(regs);
PolyLex lexer = new PolyLex(new DataInputStream(System.in));
PolyParser parser = new PolyParser(lexer, walker);
parser.interp();
} catch(Exception exception) {
System.err.println("exception: "+exception);
} // end try
} // end main()
} // end Main
|