PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1

APPENDIX B ■ A SIMPLE PARSER


->setHandler( new StringLiteralHandler() );
$comp->add( $this->variable() );
$this->operand->add( $comp );
$this->operand->add( new \gi\parse\RepetitionParse( ) )
->add($this->eqExpr());
}
return $this->operand;
}


function eqExpr() {
$equals = new \gi\parse\SequenceParse();
$equals->add( new \gi\parse\WordParse('equals') )->discard();
$equals->add( $this->operand() );
$equals->setHandler( new EqualsHandler() );
return $equals;
}


function variable() {
$variable = new \gi\parse\SequenceParse();
$variable->add( new \gi\parse\CharacterParse( '$' ))->discard();
$variable->add( new \gi\parse\WordParse());
$variable->setHandler( new VariableHandler() );
return $variable;
}
}


This may seem like a complicated class, but all it is doing is building up the grammar I have already
defined. Most of the methods are analogous to production names (that is, the names that begin each
production line in EBNF, such as eqExpr and andExpr). If you look at the expression() method, you
should see that I am building up the same rule as I defined in EBNF earlier:


// expr ::= operand (orExpr | andExpr )*
function expression() {
if (! isset( $this->expression ) ) {
$this->expression = new \gi\parse\SequenceParse();
$this->expression->add( $this->operand() );
$bools = new \gi\parse\RepetitionParse( );
$whichbool = new \gi\parse\AlternationParse();
$whichbool->add( $this->orExpr() );
$whichbool->add( $this->andExpr() );
$bools->add( $whichbool );
$this->expression->add( $bools );
}
return $this->expression;
}


In both the code and the EBNF notation, I define a sequence that consists of a reference to an
operand, followed by zero or more instances of an alternation between orExpr and andExpr. Notice that I
am storing the Parser returned by this method in a property variable. This is to prevent infinite loops, as
methods invoked from expression() themselves reference expression().
The only methods that are doing more than just building the grammar are compile() and
evaluate(). compile() can be called directly or automatically via the constructor, which accepts a
statement string and uses it to create a Scanner object. It calls the expression() method, which returns a
tree of Parser objects that make up the grammar. It then calls Parser::scan(), passing it the Scanner

Free download pdf