PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1
APPENDIX B ■ A SIMPLE PARSER

method is invoked here. I use handlers to make a successful grammar actually do something, as you will
see shortly.
Back in the scan() method, I call on the Scanner object (via the next() method) to advance its
position by calling its nextToken() and eatWhiteSpace() methods. Finally, I return the value that was
provided by doScan().
In addition to doScan(), notice the abstract trigger() method. This is used to determine whether a
parser should bother to attempt a match. If trigger() returns false then the conditions are not right for
parsing. Let’s take a look at a concrete terminal Parser. CharacterParse is designed to match a particular
character:


namespace gi\parse;


class CharacterParse extends Parser {
private $char;


function construct( $char, $name=null, $options=null ) {
parent::
construct( $name, $options );
$this->char = $char;
}


function trigger( Scanner $scanner ) {
return ( $scanner->token() == $this->char );
}


protected function doScan( Scanner $scanner ) {
return ( $this->trigger( $scanner ) );
}
}


The constructor accepts a character to match and an optional parser name for debugging purposes.
The trigger() method simply checks whether the scanner is pointing to a character token that matches
the sought character. Because no further scanning than this is required, the doScan() method simply
invokes trigger().
Terminal matching is a reasonably simple affair, as you can see. Let’s look now at a collection
parser. First I'll define a common superclass, and then go on to create a concrete example.


namespace gi/parse;


// This abstract class holds subparsers
abstract class CollectionParse extends Parser {
protected $parsers = array();


function add( Parser $p ) {
if ( is_null( $p ) ) {
throw new Exception( "argument is null" );
}
$this->parsers[]= $p;
return $p;
}


function term() {
return false;
}
}

Free download pdf