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;
}
}