7.3 Arithmetic Assemblers

Building Parsers with Java
By Steven  John  Metsker

Table of Contents
Chapter  7.   Parsing Arithmetic


An arithmetic parser has no need of a target because you can accumulate a result of recognizing an assembly by working on the assembly's stack. The key design idea, then, is to produce the right number on an assembly's stack.

First, when the parser sees a number with Num , the Num terminal places a token on the stack. The arithmetic parser must replace this token with its Double value, and to perform this task it needs an assembler. The parser also needs an assembler for each of the five operators. For example, after seeing ( '+' term ), the parser must perform an addition. To see where assemblers plug in to subparsers, it helps to write each operator on a separate line, as follows :

 expression    = term (plusTerm  minusTerm)*;  term          = factor (timesFactor  divideFactor)*; plusTerm      = '+' term; minusTerm     = '-' term; factor        = phrase expFactor  phrase; timesFactor   = '*' factor; divideFactor  = '/' factor; expFactor     = '^' factor; phrase        = '(' expression ')'  Num; 

The plusTerm parser must pop two terms and push their sum. The minusTerm parser must pop two terms and push their difference. Similarly, timesFactor , divideFactor , and expFactor must pop two terms and push their product, quotient , or exponentiation, respectively. Figure 7.1 shows the assembler classes that ArithmeticParser uses as it builds a value.

Figure 7.1. The arithmetic package. This package contains classes that collaborate to create an arithmetic value from text.


Figure 7.2 shows where the assemblers plug in to the subparsers of the ArithmeticParser class.

Figure 7.2. Arithmetic parser assembler placement. This table shows the assembler that each arithmetic subparser employs.


7.3.1 Assembler Code

The phrase parser recognizes numbers using an object of class Num , which places a Token on the assembly's stack. Like all subclasses of Terminal that expect an assembly to return tokens, by default Num places the Token object it recognizes onto the assembly's stack.

The design of our arithmetic parser calls for Double values, and not Tokens , to appear on the stack. When phrase sees a number, it asks its Num subparser to replace the Token with a corresponding Double . It accomplishes this with a NumAssembler :

 package sjm.examples.arithmetic;  import sjm.parse.*; import sjm.parse.tokens.*; public class NumAssembler extends Assembler { /**  * Replace the top token in the stack with the token's  * Double value.  */ public void workOn(Assembly a) {     Token t = (Token) a.pop();     a.push(new Double(t.nval())); } } 

The remaining assemblers plug in after seeing an operator and a term or factor . These assemblers can assume that there are two numbers on the stack. For example, from the grammar rules the parser knows that the first time it recognizes a plusTerm , a term has immediately preceded it. Both the value of the preceding term and the value of the term in plusTerm will be on the stack. The job of PlusAssembler is to replace these values with their sum. Here is the code for PlusAssembler :

 package sjm.examples.arithmetic;  import sjm.parse.*; public class PlusAssembler extends Assembler { /**  * Pop two numbers from the stack and push their sum.  */ public void workOn(Assembly a) {     Double d1 = (Double) a.pop();     Double d2 = (Double) a.pop();     Double d3 =         new Double(d2.doubleValue() + d1.doubleValue());     a.push(d3); } } 

The implementations of workOn() in classes MinusAssembler , TimesAssembler , DivideAssembler , and ExpAssembler differ only in which operator they apply to the two numbers on the stack.


Building Parsers with Java
Building Parsers With Javaв„ў
ISBN: 0201719622
EAN: 2147483647
Year: 2000
Pages: 169

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net