PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1

APPENDIX B ■ A SIMPLE PARSER


function term() {
return true;
}


// private/protected


protected function invokeHandler(
Scanner $scanner ) {
if (! empty( $this->handler ) ) {
$this->report( "calling handler: ".get_class( $this->handler ) );
$this->handler->handleMatch( $this, $scanner );
}
}


protected function report( $msg ) {
if ( self::$debug ) {
print "<{$this->name}> ".get_class( $this ).": $msg\n";
}
}


protected function push( Scanner $scanner ) {
$context = $scanner->getContext();
$context->pushResult( $scanner->token() );
}


abstract protected function doScan( Scanner $scan );
}


The place to start with this class is the scan() method. It is here that most of the logic resides. scan()
is given a Scanner object to work with. The first thing that the Parser does is defer to a concrete child
class, calling the abstract doScan() method. doScan() returns true or false; you will see a concrete
example later in the section.
If doScan() reports success, and a couple of other conditions are fulfilled, then the results of the
parse are pushed to the Context object’s result stack. The Scanner object holds the Context that is used by
Parser objects to communicate results. The actual pushing of the successful parse takes place in the
Parser::push() method.


protected function push( Scanner $scanner ) {
$context = $scanner->getContext();
$context->pushResult( $scanner->token() );
}


In addition to a parse failure, there are two conditions that might prevent the result from being
pushed to the scanner’s stack. First, client code can ask a parser to discard a successful match by calling
the discard() method. This toggles a property called $discard to true. Second, only terminal parsers
(that is, parsers that are not composed of other parsers) should push their result to the stack. Composite
parsers (instances of CollectionParser, often referred to in the following text as collection parsers) will
instead let their successful children push their results. I test whether or not a parser is terminal using the
term() method, which is overridden to return false by collection parsers.
If the concrete parser has been successful in its matching then I call another method:
invokeHandler(). This is passed the Scanner object. If a Handler (that is, an object that implements the
Handler interface) has been attached to Parser (using the setHandler() method), then its handleMatch()

Free download pdf