109 $this->end = strlen(
$rule );
118 $expectOperator =
true;
123 while (
false !== ( $token = $this->
nextToken() ) ) {
126 $expectOperator = !$expectOperator;
130 if ( $expectOperator ) {
131 $token->error(
'unexpected operand' );
133 $this->operands[] = $token;
137 if ( !$expectOperator ) {
138 $token->error(
'unexpected operator' );
142 while ( $lastOp && self::$precedence[$token->name] <= self::$precedence[$lastOp->name] ) {
152 while ( $op = array_pop( $this->
operators ) ) {
158 if ( !count( $this->operands ) ) {
159 $this->
error(
'condition expected' );
160 } elseif ( count( $this->operands ) > 1 ) {
161 $this->
error(
'missing operator or too many operands' );
164 $value = $this->operands[0];
165 if (
$value->type !==
'boolean' ) {
166 $this->
error(
'the result must have a boolean type' );
169 return $this->operands[0]->rpn;
178 if ( $this->pos >= $this->end ) {
183 $length = strspn( $this->rule, self::WHITESPACE_CLASS, $this->pos );
184 $this->pos += $length;
186 if ( $this->pos >= $this->end ) {
191 $length = strspn( $this->rule, self::NUMBER_CLASS, $this->pos );
192 if ( $length !== 0 ) {
193 $token = $this->
newNumber( substr( $this->rule, $this->pos, $length ), $this->pos );
194 $this->pos += $length;
199 $op2 = substr( $this->rule, $this->pos, 2 );
200 if ( $op2 ===
'..' || $op2 ===
'!=' ) {
201 $token = $this->
newOperator( $op2, $this->pos, 2 );
208 if ( $op1 ===
',' || $op1 ===
'=' || $op1 ===
'%' ) {
209 $token = $this->
newOperator( $op1, $this->pos, 1 );
215 if ( !preg_match( self::WORD_REGEX, $this->rule, $m, 0, $this->pos ) ) {
216 $this->
error(
'unexpected character "' . $this->rule[$this->pos] .
'"' );
218 $word1 = strtolower( $m[0] );
220 $nextTokenPos = $this->pos + strlen( $word1 );
221 if ( $word1 ===
'not' || $word1 ===
'is' ) {
223 $nextTokenPos += strspn( $this->rule, self::WHITESPACE_CLASS, $nextTokenPos );
224 if ( $nextTokenPos < $this->end
225 && preg_match( self::WORD_REGEX, $this->rule, $m, 0, $nextTokenPos )
227 $word2 = strtolower( $m[0] );
228 $nextTokenPos += strlen( $word2 );
233 if ( $word2 !==
'' ) {
234 $bothWords =
"{$word1}-{$word2}";
235 if ( isset( self::$precedence[$bothWords] ) ) {
236 $token = $this->
newOperator( $bothWords, $this->pos, $nextTokenPos - $this->pos );
237 $this->pos = $nextTokenPos;
243 if ( isset( self::$precedence[$word1] ) ) {
244 $token = $this->
newOperator( $word1, $this->pos, strlen( $word1 ) );
245 $this->pos += strlen( $word1 );
250 if ( strpos( self::OPERAND_SYMBOLS, $word1 ) !==
false ) {
251 $token = $this->
newNumber( $word1, $this->pos );
257 if ( $word1 ===
'@integer' || $word1 ===
'@decimal' ) {
264 $this->
error(
'unrecognised word' );
275 if ( count( $this->operands ) < 2 ) {
276 $op->error(
'missing operand' );
278 $right = array_pop( $this->operands );
279 $left = array_pop( $this->operands );
310 protected function error( $message ) {