APPENDIX B ■ A SIMPLE PARSER
return $char;
}
function getPos() {
return $this->r->getPos();
}
}
class ScannerState {
public $line_no;
public $char_no;
public $token;
public $token_type;
public $r;
}
First off, I set up constants for the tokens that interest me. I am going to match characters, words,
whitespace, and quote characters. I test for these types in methods dedicated to each token:
isWordChar(), isSpaceChar(), and so on. The heart of the class is the nextToken() method. This attempts
to match the next token in a given string. The Scanner stores a Context object. Parser objects use this to
share results as they work through the target text.
Note also a second class: ScannerState. The Scanner is designed so that Parser objects can save
state, try stuff out, and restore if they’ve gone down a blind alley. The getState() method populates and
returns a ScannerState object. setState() uses a ScannerState object to revert state if required.
Here is the Context class:
namespace gi\parse;
//...
class Context {
public $resultstack = array();
function pushResult( $mixed ) {
array_push( $this->resultstack, $mixed );
}
function popResult( ) {
return array_pop( $this->resultstack );
}
function resultCount() {
return count( $this->resultstack );
}
function peekResult( ) {
if ( empty( $this->resultstack ) ) {
throw new Exception( "empty resultstack" );
}
return $this->resultstack[count( $this->resultstack ) -1 ];
}
}