4 * Exception from ExpParser
6 class ExpParserException
extends Exception
{}
9 * Base class to handle the logic and lexing in a descendent recursive parser
11 abstract class ExpParser
{
13 * The expression to parse
15 protected $expr; /* Protected, just so we can add custom acceptors... */
17 * The current position during parsing, as an integer
22 * Start parsing of an expression
24 public function parse( $expr ) {
28 $result = $this->run();
30 $curptr = $this->ptr
; /* Keep $this->ptr for error message */
31 while( ctype_space( substr( $this->expr
, $curptr, 1 ) ) ) $this->ptr++
;
32 if( $curptr < strlen( $this->expr
) ) {
33 $this->error( 'Expected end' );
40 * Entry point to start parsing. Runned when the state is set up correctly
42 abstract protected function run();
45 * Trim whitespaces at the current pointer
47 protected function trimLeft() {
48 while( ctype_space( substr( $this->expr
, $this->ptr
, 1 ) ) ) $this->ptr++
;
54 * Takes an array of possible symbols, must be ordered from the longest to the shortest.
56 protected function acceptSym( $tokenlist ) {
60 foreach( $tokenlist as $token ) {
61 if( substr( $this->expr
, $this->ptr
, strlen( $token ) ) == $token ) {
62 $this->ptr +
= strlen( $token );
70 * Excpect a symbol, but fail if not availible
74 protected function expectSym( $tokenlist ) {
75 $sym = $this->acceptSym( $tokenlist );
77 $this->error('Unexpected token, expected '.implode(',',$tokenlist));
82 * Accept a keyword, finished by a whitespace.
84 protected function acceptKeyword( $keywordlist = false, $case_insensitive = false, $numeric = false ) {
87 /* Peek at next keyword */
90 $c = substr( $this->expr
, $curptr, 1 );
91 while( ctype_alpha( $c ) ||
$c == '_' ||
($buffer !== '' && ctype_digit($c)) ) {
94 $c = substr( $this->expr
, $curptr, 1 );
96 if( $case_insensitive )
97 $buffer = strtolower( $buffer );
98 if( $keywordlist === false ||
in_array( $buffer, $keywordlist ) ) {
110 protected function expectKeyword( $keywordlist = false, $case_insensitive = false, $numeric = false ) {
111 $sym = $this->acceptKeyword( $keywordlist, $case_insensitive, $numeric );
113 $this->error('Unexpected token, expected '.(($keywordlist===false)?
('keyword'):implode(',',$keywordlist)));
120 * Strings can be quoted by doublequotes, and may have backslash escape sequences
122 protected function acceptString( ) {
126 if( substr( $this->expr
, $this->ptr
, 1 ) != '"' ) {
132 while( ($c = substr( $this->expr
, $this->ptr++
, 1 )) != '"' ) {
134 $c = substr( $this->expr
, $this->ptr++
, 1 );
146 protected function expectString() {
147 $sym = $this->acceptString();
149 $this->error('Unexpected token, expected string');
154 * Accept a number, (only digits)
156 protected function acceptNum() {
159 /* Peek at next integer */
160 $curptr = $this->ptr
;
162 while( ctype_digit( $c = substr( $this->expr
, $curptr, 1 ) ) ) {
167 if( strlen( $buffer ) > 0 ) {
168 $this->ptr
= $curptr;
169 return intval($buffer);
179 protected function expectNum() {
180 $sym = $this->acceptNum();
182 $this->error('Unexpected token, expected number');
187 * Trigger a parser error, format the current position and throw an exception.
189 protected function error( $msg ) {
190 throw new ExpParserException($msg . ' at ' . $this->ptr
);