Index: trunk/extensions/InlineScripts/interpreter/ParserShuntingYard.php |
— | — | @@ -1,461 +0,0 @@ |
2 | | -<?php |
3 | | - |
4 | | -class ISCodeParserShuntingYard { |
5 | | - const ExpectingData = 1; |
6 | | - const ExpectingOperator = 2; |
7 | | - |
8 | | - var $mCode, $mPos, $mCur, $mPrev, $mFunctions, $mInterpreter, $mTokensCount; |
9 | | - |
10 | | - public function __construct( &$interpreter ) { |
11 | | - $this->resetState(); |
12 | | - $this->mFunctions = array_keys( InlineScriptInterpreter::$mFunctions ); |
13 | | - $this->mInterpreter = $interpreter; |
14 | | - } |
15 | | - |
16 | | - public function resetState() { |
17 | | - $this->mCode = ''; |
18 | | - $this->mPos = $this->mTokensCount = 0; |
19 | | - $this->mCur = $this->mPrev = null; |
20 | | - } |
21 | | - |
22 | | - public function parse( $code ) { |
23 | | - $this->resetState(); |
24 | | - $this->mCode = $code; |
25 | | - $ast = $this->parseToAST(); |
26 | | - return new ISParserOutput( $ast, $this->mTokensCount ); |
27 | | - } |
28 | | - |
29 | | - function nextToken() { |
30 | | - $tok = ''; |
31 | | - |
32 | | - // Spaces |
33 | | - $matches = array(); |
34 | | - if ( preg_match( '/\s+/uA', $this->mCode, $matches, 0, $this->mPos ) ) |
35 | | - $this->mPos += strlen($matches[0]); |
36 | | - |
37 | | - if( $this->mPos >= strlen($this->mCode) ) |
38 | | - return array( '', ISToken::TNone, $this->mCode, $this->mPos ); |
39 | | - |
40 | | - // Comments |
41 | | - if ( substr($this->mCode, $this->mPos, 2) == '/*' ) { |
42 | | - $this->mPos = strpos( $this->mCode, '*/', $this->mPos ) + 2; |
43 | | - return self::nextToken(); |
44 | | - } |
45 | | - |
46 | | - // Braces |
47 | | - if( $this->mCode[$this->mPos] == ')' ) { |
48 | | - return array( $this->mCode[$this->mPos], ISToken::TBraceClose, $this->mCode, $this->mPos + 1 ); |
49 | | - } |
50 | | - |
51 | | - // Square brackets |
52 | | - if( $this->mCode[$this->mPos] == ']' ) { |
53 | | - return array( $this->mCode[$this->mPos], ISToken::TSquareClose, $this->mCode, $this->mPos + 1 ); |
54 | | - } |
55 | | - |
56 | | - // Curly brackets |
57 | | - if( $this->mCode[$this->mPos] == '}' ) { |
58 | | - return array( $this->mCode[$this->mPos], ISToken::TCurlyClose, $this->mCode, $this->mPos + 1 ); |
59 | | - } |
60 | | - |
61 | | - // Strings |
62 | | - if( $this->mCode[$this->mPos] == '"' || $this->mCode[$this->mPos] == "'" ) { |
63 | | - $type = $this->mCode[$this->mPos]; |
64 | | - $this->mPos++; |
65 | | - $strLen = strlen($this->mCode); |
66 | | - while( $this->mPos < $strLen ) { |
67 | | - if( $this->mCode[$this->mPos] == $type ) { |
68 | | - $this->mPos++; |
69 | | - return array( $tok, ISToken::TString, $this->mCode, $this->mPos ); |
70 | | - } |
71 | | - |
72 | | - // Performance: Use a PHP function (implemented in C) |
73 | | - // to scan ahead. |
74 | | - $addLength = strcspn( $this->mCode, $type."\\", $this->mPos ); |
75 | | - if ($addLength) { |
76 | | - $tok .= substr( $this->mCode, $this->mPos, $addLength ); |
77 | | - $this->mPos += $addLength; |
78 | | - } elseif( $this->mCode[$this->mPos] == '\\' ) { |
79 | | - switch( $this->mCode[$this->mPos + 1] ) { |
80 | | - case '\\': |
81 | | - $tok .= '\\'; |
82 | | - break; |
83 | | - case $type: |
84 | | - $tok .= $type; |
85 | | - break; |
86 | | - case 'n'; |
87 | | - $tok .= "\n"; |
88 | | - break; |
89 | | - case 'r': |
90 | | - $tok .= "\r"; |
91 | | - break; |
92 | | - case 't': |
93 | | - $tok .= "\t"; |
94 | | - break; |
95 | | - case 'x': |
96 | | - $chr = substr( $this->mCode, $this->mPos + 2, 2 ); |
97 | | - |
98 | | - if ( preg_match( '/^[0-9A-Fa-f]{2}$/', $chr ) ) { |
99 | | - $chr = base_convert( $chr, 16, 10 ); |
100 | | - $tok .= chr($chr); |
101 | | - $this->mPos += 2; # \xXX -- 2 done later |
102 | | - } else { |
103 | | - $tok .= 'x'; |
104 | | - } |
105 | | - break; |
106 | | - default: |
107 | | - $tok .= "\\" . $this->mCode[$this->mPos + 1]; |
108 | | - } |
109 | | - $this->mPos+=2; |
110 | | - } else { |
111 | | - $tok .= $this->mCode[$this->mPos]; |
112 | | - $this->mPos++; |
113 | | - } |
114 | | - } |
115 | | - throw new ISUserVisibleException( 'unclosedstring', $this->mPos, array() );; |
116 | | - } |
117 | | - |
118 | | - // Find operators |
119 | | - static $operator_regex = null; |
120 | | - // Match using a regex. Regexes are faster than PHP |
121 | | - if (!$operator_regex) { |
122 | | - $quoted_operators = array(); |
123 | | - |
124 | | - foreach( InlineScriptInterpreter::$mOps as $op ) |
125 | | - $quoted_operators[] = preg_quote( $op, '/' ); |
126 | | - $operator_regex = '/('.implode('|', $quoted_operators).')/A'; |
127 | | - } |
128 | | - |
129 | | - $matches = array(); |
130 | | - |
131 | | - preg_match( $operator_regex, $this->mCode, $matches, 0, $this->mPos ); |
132 | | - |
133 | | - if( count( $matches ) ) { |
134 | | - $tok = $matches[0]; |
135 | | - $this->mPos += strlen( $tok ); |
136 | | - return array( $tok, ISToken::TOp, $this->mCode, $this->mPos ); |
137 | | - } |
138 | | - |
139 | | - // Find bare numbers |
140 | | - $bases = array( 'b' => 2, |
141 | | - 'x' => 16, |
142 | | - 'o' => 8 ); |
143 | | - $baseChars = array( |
144 | | - 2 => '[01]', |
145 | | - 16 => '[0-9A-Fa-f]', |
146 | | - 8 => '[0-8]', |
147 | | - 10 => '[0-9.]', |
148 | | - ); |
149 | | - $baseClass = '['.implode('', array_keys($bases)).']'; |
150 | | - $radixRegex = "/([0-9A-Fa-f]+(?:\.\d*)?|\.\d+)($baseClass)?/Au"; |
151 | | - $matches = array(); |
152 | | - |
153 | | - if ( preg_match( $radixRegex, $this->mCode, $matches, 0, $this->mPos ) ) { |
154 | | - $input = $matches[1]; |
155 | | - $baseChar = @$matches[2]; |
156 | | - $num = null; |
157 | | - // Sometimes the base char gets mixed in with the rest of it because |
158 | | - // the regex targets hex, too. |
159 | | - // This mostly happens with binary |
160 | | - if (!$baseChar && !empty( $bases[ substr( $input, -1 ) ] ) ) { |
161 | | - $baseChar = substr( $input, -1, 1 ); |
162 | | - $input = substr( $input, 0, -1 ); |
163 | | - } |
164 | | - |
165 | | - if( $baseChar ) |
166 | | - $base = $bases[$baseChar]; |
167 | | - else |
168 | | - $base = 10; |
169 | | - |
170 | | - // Check against the appropriate character class for input validation |
171 | | - $baseRegex = "/^".$baseChars[$base]."+$/"; |
172 | | - |
173 | | - if ( preg_match( $baseRegex, $input ) ) { |
174 | | - if ($base != 10) { |
175 | | - $num = base_convert( $input, $base, 10 ); |
176 | | - } else { |
177 | | - $num = $input; |
178 | | - } |
179 | | - |
180 | | - $this->mPos += strlen( $matches[0] ); |
181 | | - |
182 | | - $float = in_string( '.', $input ); |
183 | | - |
184 | | - return array( |
185 | | - $float |
186 | | - ? doubleval( $num ) |
187 | | - : intval( $num ), |
188 | | - $float |
189 | | - ? ISToken::TFloat |
190 | | - : ISToken::TInt, |
191 | | - $this->mCode, |
192 | | - $this->mPos |
193 | | - ); |
194 | | - } |
195 | | - } |
196 | | - |
197 | | - // The rest are considered IDs |
198 | | - |
199 | | - // Regex match > PHP |
200 | | - $idSymbolRegex = '/[0-9A-Za-z_]+/A'; |
201 | | - $matches = array(); |
202 | | - |
203 | | - if ( preg_match( $idSymbolRegex, $this->mCode, $matches, 0, $this->mPos ) ) { |
204 | | - $tok = strtolower( $matches[0] ); |
205 | | - |
206 | | - $type = in_array( $tok, InlineScriptInterpreter::$mKeywords ) |
207 | | - ? ISToken::TKeyword |
208 | | - : ( in_array( $tok, $this->mFunctions ) |
209 | | - ? ISToken::TFunction : ISToken::TID); |
210 | | - |
211 | | - return array( $tok, $type, $this->mCode, $this->mPos + strlen($tok) ); |
212 | | - } |
213 | | - |
214 | | - throw new ISUserVisibleException( |
215 | | - 'unrecognisedtoken', $this->mPos, array( substr( $this->mCode, $this->mPos ) ) ); |
216 | | - } |
217 | | - |
218 | | - protected function move() { |
219 | | - wfProfileIn( __METHOD__ ); |
220 | | - list( $val, $type, $code, $offset ) = |
221 | | - $this->nextToken(); |
222 | | - |
223 | | - if( $type != ISToken::TNone ) { |
224 | | - $this->mTokensCount++; |
225 | | - if( !$this->mInterpreter->increaseTokensCount() ) |
226 | | - throw new ISUserVisibleException( 'toomanytokens', $this->mPos ); |
227 | | - } |
228 | | - |
229 | | - $token = new ISToken( $type, $val, $this->mPos ); |
230 | | - $this->mPos = $offset; |
231 | | - wfProfileOut( __METHOD__ ); |
232 | | - $this->mPrev = $this->mCur; |
233 | | - return $this->mCur = $token; |
234 | | - } |
235 | | - |
236 | | - protected function parseToAST() { |
237 | | - $outputQueue = array(); |
238 | | - $opStack = array(); |
239 | | - $expecting = self::ExpectingData; |
240 | | - |
241 | | - while( $this->move()->type != ISToken::TNone ) { |
242 | | - /* Handling of all constants */ |
243 | | - if( $this->mCur->isDataToken() ) { |
244 | | - if( $expecting != self::ExpectingData ) |
245 | | - throw new ISUserVisibleException( 'expectingdata', $this->mPos ); |
246 | | - switch( $this->mCur->type ) { |
247 | | - case ISToken::TID: |
248 | | - $outputQueue[] = new ISDataNode( $this->mCur->value, $this->mPos ); |
249 | | - break; |
250 | | - case ISToken::TKeyword: |
251 | | - switch( $this->mCur->value ) { |
252 | | - case 'true': |
253 | | - $outputQueue[] = new ISDataNode( new ISData( ISData::DBool, true ), $this->mPos ); |
254 | | - break; |
255 | | - case 'false': |
256 | | - $outputQueue[] = new ISDataNode( new ISData( ISData::DBool, false ), $this->mPos ); |
257 | | - break; |
258 | | - case 'null': |
259 | | - $outputQueue[] = new ISDataNode( new ISData(), $this->mPos ); |
260 | | - break; |
261 | | - } |
262 | | - break; |
263 | | - default: |
264 | | - $outputQueue[] = new ISDataNode( ISData::newFromPHPVar( $this->mCur->value ), $this->mPos ); |
265 | | - } |
266 | | - $expecting = self::ExpectingOperator; |
267 | | - } |
268 | | - |
269 | | - /* Foreach handling */ |
270 | | - elseif( $this->mCur->isOp( 'foreach' ) ) { |
271 | | - if( $expecting != self::ExpectingData ) |
272 | | - throw new ISUserVisibleException( 'expectingdata', $this->mPos ); |
273 | | - $this->move(); |
274 | | - if( $this->mCur->type != ISToken::TID ) |
275 | | - throw new ISUserVisibleException( 'cantchangeconst', $pos ); |
276 | | - $varname = $this->mCur->value; |
277 | | - $this->move(); |
278 | | - if( !$this->mCur->isOp( 'in' ) ) |
279 | | - throw new ISUserVisibleException( 'expectednotfound', $pos, array( 'in' ) ); |
280 | | - array_push( $opStack, new ISOperatorNode( ISOperatorNode::OForeach, $this->mPos, $varname ) ); |
281 | | - } |
282 | | - |
283 | | - /* Handling of other operators */ |
284 | | - elseif( $this->mCur->type == ISToken::TOp || $this->mCur->type == ISToken::TKeyword ) { |
285 | | - $op1 = ISOperatorNode::parseOperator( $this->mCur->value, $expecting, $this->mPos ); |
286 | | - if( !$op1 ) |
287 | | - throw new ISUserVisibleException( 'expectingoperator', $this->mPos ); |
288 | | - if( $expecting == self::ExpectingOperator ) { |
289 | | - while( !empty( $opStack ) ) { |
290 | | - $op2 = end( $opStack ); |
291 | | - if( $op1->isRightAssociative() ? |
292 | | - ($op1->getPrecedence() < $op2->getPrecedence()) : |
293 | | - ($op1->getPrecedence() <= $op2->getPrecedence()) ) |
294 | | - $this->pushOp( $outputQueue, $opStack ); |
295 | | - else |
296 | | - break; |
297 | | - } |
298 | | - } |
299 | | - |
300 | | - if( $this->mCur->isOp( 'catch' ) ) { |
301 | | - $this->move(); |
302 | | - if( $this->mCur->type != ISToken::TID ) |
303 | | - throw new ISUserVisibleException( 'cantchangeconst', $pos ); |
304 | | - $op1->setData( $this->mCur->value ); |
305 | | - } |
306 | | - |
307 | | - if( $op1->getArgsNumber() ) { |
308 | | - array_push( $opStack, $op1 ); |
309 | | - $expecting = self::ExpectingData; |
310 | | - } else { |
311 | | - $outputQueue[] = $op1; |
312 | | - $expecting = self::ExpectingOperator; |
313 | | - } |
314 | | - } |
315 | | - |
316 | | - /* Functions */ |
317 | | - elseif( $this->mCur->type == ISToken::TFunction ) { |
318 | | - if( $expecting != self::ExpectingData ) |
319 | | - throw new ISUserVisibleException( 'expectingdata', $this->mPos ); |
320 | | - array_push( $opStack, new ISOperatorNode( ISOperatorNode::OFunction, $this->mPos, |
321 | | - $this->mCur->value ) ); |
322 | | - } |
323 | | - |
324 | | - /* Different right parenthesis */ |
325 | | - elseif( $this->mCur->type == ISToken::TSquareClose ) { |
326 | | - if( $this->mPrev->isOp( '[' ) ) { |
327 | | - $topToken = array_pop( $opStack ); |
328 | | - if( $topToken->getOperator() == ISOperatorNode::OArrayElement ) { |
329 | | - array_push( $opStack, new ISOperatorNode( ISOperatorNode::OArrayElementSingle, $this->mPos ) ); |
330 | | - $this->pushOp( $outputQueue, $opStack ); |
331 | | - } else { |
332 | | - $outputQueue[] = new ISDataNode( |
333 | | - new ISData( ISData::DList, array() ), $this->mPos ); |
334 | | - } |
335 | | - $expecting = self::ExpectingOperator; |
336 | | - continue; |
337 | | - } |
338 | | - if( $expecting != self::ExpectingOperator ) |
339 | | - throw new ISUserVisibleException( 'expectingoperator', $this->mPos ); |
340 | | - for(;;) { |
341 | | - if( empty( $opStack ) ) |
342 | | - throw new ISUserVisibleException( 'unbalancedbraces', $this->mPos );; |
343 | | - $op = end( $opStack ); |
344 | | - $this->pushOp( $outputQueue, $opStack, true ); |
345 | | - if( $op->isLeftSquare() ) |
346 | | - break; |
347 | | - } |
348 | | - } elseif( $this->mCur->type == ISToken::TCurlyClose ) { |
349 | | - if( $this->mPrev->isOp( ';' ) ) |
350 | | - array_pop( $opStack ); |
351 | | - elseif( $expecting != self::ExpectingOperator ) |
352 | | - throw new ISUserVisibleException( 'expectingoperator', $this->mPos ); |
353 | | - for(;;) { |
354 | | - if( empty( $opStack ) ) |
355 | | - throw new ISUserVisibleException( 'unbalancedbraces', $this->mPos );; |
356 | | - $op = end( $opStack ); |
357 | | - if( $op->getOperator() == '{' ) { |
358 | | - array_pop( $opStack ); |
359 | | - break; |
360 | | - } else { |
361 | | - $this->pushOp( $outputQueue, $opStack ); |
362 | | - } |
363 | | - } |
364 | | - $expecting = self::ExpectingOperator; |
365 | | - } elseif( $this->mCur->type == ISToken::TBraceClose ) { |
366 | | - // Handle no-argument function |
367 | | - if( $this->mPrev->isOp( '(' ) && count( $opStack ) >= 2 && $opStack[count($opStack) - 2]->isOp( ISOperatorNode::OFunction ) ) { |
368 | | - array_pop( $opStack ); |
369 | | - $outputQueue[] = array_pop( $opStack ); |
370 | | - $expecting = self::ExpectingOperator; |
371 | | - continue; |
372 | | - } |
373 | | - |
374 | | - if( $expecting != self::ExpectingOperator ) |
375 | | - throw new ISUserVisibleException( 'expectingoperator', $this->mPos ); |
376 | | - for(;;) { |
377 | | - if( empty( $opStack ) ) |
378 | | - throw new ISUserVisibleException( 'unbalancedbraces', $this->mPos );; |
379 | | - $op = end( $opStack ); |
380 | | - if( $op->getOperator() == '(' ) { |
381 | | - array_pop( $opStack ); |
382 | | - $topToken = end( $opStack ); |
383 | | - if( $topToken && $topToken->getOperator() == ISOperatorNode::OFunction ) { |
384 | | - $this->pushOp( $outputQueue, $opStack ); |
385 | | - } |
386 | | - break; |
387 | | - } else { |
388 | | - $this->pushOp( $outputQueue, $opStack ); |
389 | | - } |
390 | | - } |
391 | | - } |
392 | | - } |
393 | | - |
394 | | - while( !empty( $opStack ) ) { |
395 | | - $this->pushOp( $outputQueue, $opStack ); |
396 | | - } |
397 | | - if( count( $outputQueue ) != 1 ) |
398 | | - throw new ISException( 'Invalid size of output queue' ); // Should never happen |
399 | | - return $outputQueue[0]; |
400 | | - } |
401 | | - |
402 | | - function pushOp( &$queue, &$stack, $allowSquare = false ) { |
403 | | - $node = array_pop( $stack ); |
404 | | - if( $node->isLeftSquare() && !$allowSquare ) { |
405 | | - throw new ISUserVisibleException( 'unbalancedbraces', $this->mPos ); |
406 | | - } |
407 | | - if( $node->getOperator() == ISOperatorNode::OLeftBrace || |
408 | | - $node->getOperator() == ISOperatorNode::OLeftCurly ) { |
409 | | - throw new ISUserVisibleException( 'unbalancedbraces', $this->mPos ); |
410 | | - } |
411 | | - for( $i = 0; $i < $node->getArgsNumber(); $i++ ) { |
412 | | - if( $queue ) { |
413 | | - $node->addChild( array_pop( $queue ) ); |
414 | | - } else { |
415 | | - if( !($node->getOperator() == ';' && $i == 1) ) |
416 | | - throw new ISUserVisibleException( 'notenoughopargs', $this->mPos ); |
417 | | - } |
418 | | - } |
419 | | - $queue[] = $node; |
420 | | - } |
421 | | -} |
422 | | - |
423 | | -class ISToken { |
424 | | - //Types of token |
425 | | - const TNone = 'T_NONE'; |
426 | | - const TID = 'T_ID'; |
427 | | - const TFunction = 'T_FUNCTION'; |
428 | | - const TKeyword = 'T_KEYWORD'; |
429 | | - const TString = 'T_STRING'; |
430 | | - const TInt = 'T_INT'; |
431 | | - const TFloat = 'T_FLOAT'; |
432 | | - const TOp = 'T_OP'; |
433 | | - const TBraceClose = 'T_BRACE_CLOSE'; |
434 | | - const TSquareClose = 'T_SQUARE_CURLY'; |
435 | | - const TCurlyClose = 'T_CURLY_CLOSE'; |
436 | | - |
437 | | - var $type; |
438 | | - var $value; |
439 | | - var $pos; |
440 | | - |
441 | | - public function __construct( $type = self::TNone, $value = null, $pos = 0 ) { |
442 | | - $this->type = $type; |
443 | | - $this->value = $value; |
444 | | - $this->pos = $pos; |
445 | | - } |
446 | | - |
447 | | - public function isOp( $op ) { |
448 | | - return ( $this->type == self::TOp || $this->type == self::TKeyword ) |
449 | | - && $this->value == $op; |
450 | | - } |
451 | | - |
452 | | - public function isDataToken() { |
453 | | - $types = array( self::TID, self::TString, self::TInt, self::TFloat ); |
454 | | - $keywords = array( 'true', 'false', 'null' ); |
455 | | - return in_array( $this->type, $types ) || |
456 | | - ( $this->type == self::TKeyword && in_array( $this->value, $keywords ) ); |
457 | | - } |
458 | | - |
459 | | - function __toString() { |
460 | | - return "{$this->value}"; |
461 | | - } |
462 | | -} |
Index: trunk/extensions/InlineScripts/interpreter/Utils.php |
— | — | @@ -1,554 +0,0 @@ |
2 | | -<?php |
3 | | - |
4 | | -class ISASTNode { |
5 | | - const NodeData = 1; |
6 | | - const NodeOperator = 2; |
7 | | - |
8 | | - var $mChildren, $mType, $mPos; |
9 | | - |
10 | | - public function __construct( $type, $pos ) { |
11 | | - $this->mChildren = array(); |
12 | | - $this->mType = $type; |
13 | | - $this->mPos = $pos; |
14 | | - } |
15 | | - |
16 | | - public function addChild( $child ) { |
17 | | - array_unshift( $this->mChildren, $child ); |
18 | | - } |
19 | | - |
20 | | - public function addChildren( $children ) { |
21 | | - $this->mChildren = array_merge( $this->mChildren, $children ); |
22 | | - } |
23 | | - |
24 | | - public function getChildren() { |
25 | | - return $this->mChildren; |
26 | | - } |
27 | | - |
28 | | - public function getType() { |
29 | | - return $this->mType; |
30 | | - } |
31 | | - |
32 | | - public function getPos() { |
33 | | - return $this->mPos; |
34 | | - } |
35 | | - |
36 | | - public function isOp( $op ) { |
37 | | - return $this->mType == self::NodeOperator && |
38 | | - $this->getOperator() == $op; |
39 | | - } |
40 | | - |
41 | | - protected function childrenToString() { |
42 | | - $s = array(); |
43 | | - foreach( $this->mChildren as $child ) { |
44 | | - $lines = explode( "\n", $child->__toString() ); |
45 | | - foreach( $lines as $line ) |
46 | | - $s[] = " {$line}"; |
47 | | - } |
48 | | - return implode( "\n", $s ); |
49 | | - } |
50 | | -} |
51 | | - |
52 | | -class ISOperatorNode extends ISASTNode { |
53 | | - const OFunction = 'FUNCTION'; |
54 | | - const OArrayElement = 'AELEMENT'; |
55 | | - const OArray = 'ARRAY'; |
56 | | - const OArrayElementSingle = 'AELEMENT_S'; // array[] |
57 | | - const OPositive = 'POSITIVE'; // not to be confused with sum |
58 | | - const ONegative = 'NEGATIVE'; // not to be confused with sub |
59 | | - const OIf = 'if'; |
60 | | - const OThen = 'then'; |
61 | | - const OElse = 'else'; |
62 | | - const ODo = 'do'; |
63 | | - const OForeach = 'foreach'; |
64 | | - const OTry = 'try'; |
65 | | - const OCatch = 'catch'; |
66 | | - const OBreak = 'break'; |
67 | | - const OContinue = 'continue'; |
68 | | - const OIsset = 'isset'; |
69 | | - const OUnset = 'unset'; |
70 | | - const OIn = 'in'; |
71 | | - const OInvert = '!'; |
72 | | - const OPow = '**'; |
73 | | - const OMul = '*'; |
74 | | - const ODiv = '/'; |
75 | | - const OMod = '%'; |
76 | | - const OSum = 'SUM'; |
77 | | - const OSub = 'SUB'; |
78 | | - const OEqualsTo = '=='; |
79 | | - const ONotEqualsTo = '!='; |
80 | | - const OEqualsToStrict = '==='; |
81 | | - const ONotEqualsToStrict = '!=='; |
82 | | - const OGreater = '>'; |
83 | | - const OLess = '<'; |
84 | | - const OGreaterOrEq = '>='; |
85 | | - const OLessOrEq = '<='; |
86 | | - const OAnd = '&'; |
87 | | - const OOr = '|'; |
88 | | - const OXor = '^'; |
89 | | - const OTrinary = '?'; |
90 | | - const OColon = ':'; |
91 | | - const OSet = '='; |
92 | | - const OSetAdd = '+='; |
93 | | - const OSetSub = '-='; |
94 | | - const OSetMul = '*='; |
95 | | - const OSetDiv = '/='; |
96 | | - const OComma = ','; |
97 | | - const OStatementSeperator = ';'; |
98 | | - const OLeftBrace = '('; |
99 | | - const OLeftCurly = '{'; |
100 | | - |
101 | | - static $precedence = array( |
102 | | - self::OFunction => 20, self::OIsset => 20, self::OUnset => 20, |
103 | | - self::OArrayElement => 19, self::OPositive => 19, self::ONegative => 19, |
104 | | - self::OIn => 18, |
105 | | - self::OInvert => 17, self::OPow => 17, |
106 | | - self::OMul => 16, self::ODiv => 16, self::OMod => 16, |
107 | | - self::OSum => 15, self::OSub => 15, |
108 | | - self::OEqualsTo => 14, self::ONotEqualsTo => 14, self::OEqualsToStrict => 14, |
109 | | - self::ONotEqualsToStrict => 13, self::OGreater => 13, self::OLess => 13, |
110 | | - self::OGreaterOrEq => 13, self::OLessOrEq => 13, |
111 | | - self::OAnd => 12, self::OOr => 12, self::OXor => 12, |
112 | | - self::OColon => 11, self::OTrinary => 10, |
113 | | - self::OSet => 9, self::OSetAdd => 9, self::OSetSub => 9, |
114 | | - self::OSetMul => 9, self::OSetDiv => 9, |
115 | | - self::OIf => 6, self::OThen => 7, self::OElse => 8, |
116 | | - self::OForeach => 6, self::ODo => 7, |
117 | | - self::OTry => 6, self::OCatch => 7, |
118 | | - self::OComma => 8, self::OStatementSeperator => 0, |
119 | | - ); |
120 | | - |
121 | | - static $unaryOperators = array( |
122 | | - self::OPositive, self::ONegative, self::OFunction, self::OArray, self::OTry, |
123 | | - self::OIf, self::OForeach, self::OInvert, self::OArrayElementSingle, |
124 | | - self::OIsset, self::OUnset |
125 | | - ); |
126 | | - |
127 | | - static function parseOperator( $op, $expecting, $pos ) { |
128 | | - if( $expecting == ISCodeParserShuntingYard::ExpectingData ) { |
129 | | - $ops = array( '(', '{', 'if', '!', 'try', 'break', 'continue', |
130 | | - 'isset', 'unset' ); |
131 | | - if( $op == '+' ) return new self( self::OPositive, $pos ); |
132 | | - if( $op == '-' ) return new self( self::ONegative, $pos ); |
133 | | - if( $op == '[' ) return new self( self::OArray, $pos ); |
134 | | - if( in_array( $op, $ops ) ) |
135 | | - return new self( $op, $pos ); |
136 | | - return null; |
137 | | - } else { |
138 | | - if( $op == '+' ) return new self( self::OSum, $pos ); |
139 | | - if( $op == '-' ) return new self( self::OSub, $pos ); |
140 | | - if( $op == '[' ) return new self( self::OArrayElement, $pos ); |
141 | | - return new self( $op, $pos ); |
142 | | - } |
143 | | - } |
144 | | - |
145 | | - var $mOperator, $mData, $mNumArgs; |
146 | | - |
147 | | - public function __construct( $op, $pos, $data = '' ) { |
148 | | - parent::__construct( ISASTNode::NodeOperator, $pos ); |
149 | | - $this->mOperator = $op; |
150 | | - $this->mData = $data; |
151 | | - $this->mNumArgs = null; |
152 | | - } |
153 | | - |
154 | | - public function getOperator() { |
155 | | - return $this->mOperator; |
156 | | - } |
157 | | - |
158 | | - public function getData() { |
159 | | - return $this->mData; |
160 | | - } |
161 | | - |
162 | | - public function getPrecedence() { |
163 | | - return isset( self::$precedence[$this->mOperator] ) ? |
164 | | - self::$precedence[$this->mOperator] : -1; |
165 | | - } |
166 | | - |
167 | | - public function getArgsNumber() { |
168 | | - if( $this->mNumArgs !== null ) |
169 | | - return $this->mNumArgs; |
170 | | - if( in_array( $this->mOperator, self::$unaryOperators ) ) |
171 | | - return 1; |
172 | | - elseif( $this->mOperator == self::OBreak || |
173 | | - $this->mOperator == self::OContinue ) |
174 | | - return 0; |
175 | | - else |
176 | | - return 2; |
177 | | - } |
178 | | - |
179 | | - public function setArgsNumber( $num ) { |
180 | | - $this->mNumArgs = $num; |
181 | | - } |
182 | | - |
183 | | - public function setData( $data ) { |
184 | | - $this->mData = $data; |
185 | | - } |
186 | | - |
187 | | - public function isRightAssociative() { |
188 | | - return $this->mOperator == self::OPow || |
189 | | - $this->mOperator == self::OSet; |
190 | | - } |
191 | | - |
192 | | - public function isLeftSquare() { |
193 | | - return $this->mOperator == self::OArrayElement || |
194 | | - $this->mOperator == self::OArray; |
195 | | - } |
196 | | - |
197 | | - public function __toString() { |
198 | | - return "<operator value=\"{$this->mOperator}\" data=\"{$this->mData}\">\n" . |
199 | | - $this->childrenToString() . "\n</operator>"; |
200 | | - } |
201 | | -} |
202 | | - |
203 | | -class ISDataNode extends ISASTNode { |
204 | | - const DNData = 0; |
205 | | - const DNVariable = 1; |
206 | | - |
207 | | - var $mDataType, $mData, $mVar; |
208 | | - |
209 | | - public function __construct( $dataOrVar, $pos ) { |
210 | | - parent::__construct( ISASTNode::NodeData, $pos ); |
211 | | - if( $dataOrVar instanceof ISData ) { |
212 | | - $this->mDataType = self::DNData; |
213 | | - $this->mData = $dataOrVar; |
214 | | - } |
215 | | - if( is_string( $dataOrVar ) ) { |
216 | | - $this->mDataType = self::DNVariable; |
217 | | - $this->mVar = $dataOrVar; |
218 | | - } |
219 | | - // Some whining here? |
220 | | - } |
221 | | - |
222 | | - public function getDataType() { |
223 | | - return $this->mDataType; |
224 | | - } |
225 | | - |
226 | | - public function getData() { |
227 | | - return $this->mData; |
228 | | - } |
229 | | - |
230 | | - public function getVar() { |
231 | | - return $this->mVar; |
232 | | - } |
233 | | - |
234 | | - public function __toString() { |
235 | | - $type = ''; |
236 | | - switch( $this->mDataType ) { |
237 | | - case self::DNData: |
238 | | - return "<datanode nodetype=\"data\" type=\"{$this->mData->type}\" value=\"{$this->mData->data}\" />"; |
239 | | - case self::DNVariable: |
240 | | - return "<datanode nodetype=\"var\" name=\"{$this->mVar}\" />"; |
241 | | - break; |
242 | | - } |
243 | | - } |
244 | | -} |
245 | | - |
246 | | -class ISData { |
247 | | - // Data types |
248 | | - const DInt = 'int'; |
249 | | - const DString = 'string'; |
250 | | - const DNull = 'null'; |
251 | | - const DBool = 'bool'; |
252 | | - const DFloat = 'float'; |
253 | | - const DList = 'list'; |
254 | | - |
255 | | - var $type; |
256 | | - var $data; |
257 | | - |
258 | | - public function __construct( $type = self::DNull, $val = null ) { |
259 | | - $this->type = $type; |
260 | | - $this->data = $val; |
261 | | - } |
262 | | - |
263 | | - public static function newFromPHPVar( $var ) { |
264 | | - if( is_string( $var ) ) |
265 | | - return new ISData( self::DString, $var ); |
266 | | - elseif( is_int( $var ) ) |
267 | | - return new ISData( self::DInt, $var ); |
268 | | - elseif( is_float( $var ) ) |
269 | | - return new ISData( self::DFloat, $var ); |
270 | | - elseif( is_bool( $var ) ) |
271 | | - return new ISData( self::DBool, $var ); |
272 | | - elseif( is_array( $var ) ) { |
273 | | - $result = array(); |
274 | | - foreach( $var as $item ) |
275 | | - $result[] = self::newFromPHPVar( $item ); |
276 | | - return new ISData( self::DList, $result ); |
277 | | - } |
278 | | - elseif( is_null( $var ) ) |
279 | | - return new ISData(); |
280 | | - else |
281 | | - throw new ISException( |
282 | | - "Data type " . gettype( $var ) . " is not supported by InlineScrtips" ); |
283 | | - } |
284 | | - |
285 | | - public function dup() { |
286 | | - return new ISData( $this->type, $this->data ); |
287 | | - } |
288 | | - |
289 | | - public static function castTypes( $orig, $target ) { |
290 | | - if( $orig->type == $target ) |
291 | | - return $orig->dup(); |
292 | | - if( $target == self::DNull ) { |
293 | | - return new ISData(); |
294 | | - } |
295 | | - |
296 | | - if( $orig->type == self::DList ) { |
297 | | - if( $target == self::DBool ) |
298 | | - return new ISData( self::DBool, (bool)count( $orig->data ) ); |
299 | | - if( $target == self::DFloat ) { |
300 | | - return new ISData( self::DFloat, doubleval( count( $orig->data ) ) ); |
301 | | - } |
302 | | - if( $target == self::DInt ) { |
303 | | - return new ISData( self::DInt, intval( count( $orig->data ) ) ); |
304 | | - } |
305 | | - if( $target == self::DString ) { |
306 | | - $s = ''; |
307 | | - foreach( $orig->data as $item ) |
308 | | - $s .= $item->toString()."\n"; |
309 | | - return new ISData( self::DString, $s ); |
310 | | - } |
311 | | - } |
312 | | - |
313 | | - if( $target == self::DBool ) { |
314 | | - return new ISData( self::DBool, (bool)$orig->data ); |
315 | | - } |
316 | | - if( $target == self::DFloat ) { |
317 | | - return new ISData( self::DFloat, doubleval( $orig->data ) ); |
318 | | - } |
319 | | - if( $target == self::DInt ) { |
320 | | - return new ISData( self::DInt, intval( $orig->data ) ); |
321 | | - } |
322 | | - if( $target == self::DString ) { |
323 | | - return new ISData( self::DString, strval( $orig->data ) ); |
324 | | - } |
325 | | - if( $target == self::DList ) { |
326 | | - return new ISData( self::DList, array( $orig ) ); |
327 | | - } |
328 | | - } |
329 | | - |
330 | | - public static function boolInvert( $value ) { |
331 | | - return new ISData( self::DBool, !$value->toBool() ); |
332 | | - } |
333 | | - |
334 | | - public static function pow( $base, $exponent ) { |
335 | | - if( $base->type == self::DInt && $exponent->type == self::DInt ) |
336 | | - return new ISData( self::DInt, pow( $base->toInt(), $exponent->toInt() ) ); |
337 | | - else |
338 | | - return new ISData( self::DFloat, pow( $base->toFloat(), $exponent->toFloat() ) ); |
339 | | - } |
340 | | - |
341 | | - public static function keywordIn( $a, $b ) { |
342 | | - $a = $a->toString(); |
343 | | - $b = $b->toString(); |
344 | | - |
345 | | - if ($a == '' || $b == '') { |
346 | | - return new ISData( self::DBool, false ); |
347 | | - } |
348 | | - |
349 | | - return new ISData( self::DBool, in_string( $a, $b ) ); |
350 | | - } |
351 | | - |
352 | | - public static function keywordContains( $a, $b ) { |
353 | | - $a = $a->toString(); |
354 | | - $b = $b->toString(); |
355 | | - |
356 | | - if ($a == '' || $b == '') { |
357 | | - return new ISData( self::DBool, false ); |
358 | | - } |
359 | | - |
360 | | - return new ISData( self::DBool, in_string( $b, $a ) ); |
361 | | - } |
362 | | - |
363 | | - public static function listContains( $value, $list ) { |
364 | | - // Should use built-in PHP function somehow |
365 | | - foreach( $list->data as $item ) { |
366 | | - if( self::equals( $value, $item ) ) |
367 | | - return true; |
368 | | - } |
369 | | - return false; |
370 | | - } |
371 | | - |
372 | | - public static function equals( $d1, $d2 ) { |
373 | | - return $d1->type != self::DList && $d2->type != self::DList && |
374 | | - $d1->data == $d2->data; |
375 | | - } |
376 | | - |
377 | | - public static function unaryMinus( $data ) { |
378 | | - if ($data->type == self::DInt) { |
379 | | - return new ISData( $data->type, -$data->toInt() ); |
380 | | - } else { |
381 | | - return new ISData( $data->type, -$data->toFloat() ); |
382 | | - } |
383 | | - } |
384 | | - |
385 | | - public static function compareOp( $a, $b, $op ) { |
386 | | - if( $op == '==' ) |
387 | | - return new ISData( self::DBool, self::equals( $a, $b ) ); |
388 | | - if( $op == '!=' ) |
389 | | - return new ISData( self::DBool, !self::equals( $a, $b ) ); |
390 | | - if( $op == '===' ) |
391 | | - return new ISData( self::DBool, $a->type == $b->type && self::equals( $a, $b ) ); |
392 | | - if( $op == '!==' ) |
393 | | - return new ISData( self::DBool, $a->type != $b->type || !self::equals( $a, $b ) ); |
394 | | - $a = $a->toString(); |
395 | | - $b = $b->toString(); |
396 | | - if( $op == '>' ) |
397 | | - return new ISData( self::DBool, $a > $b ); |
398 | | - if( $op == '<' ) |
399 | | - return new ISData( self::DBool, $a < $b ); |
400 | | - if( $op == '>=' ) |
401 | | - return new ISData( self::DBool, $a >= $b ); |
402 | | - if( $op == '<=' ) |
403 | | - return new ISData( self::DBool, $a <= $b ); |
404 | | - throw new ISException( "Invalid comparison operation: {$op}" ); // Should never happen |
405 | | - } |
406 | | - |
407 | | - public static function mulRel( $a, $b, $op, $pos ) { |
408 | | - // Figure out the type. |
409 | | - if( ( $a->type == self::DFloat || $b->type == self::DFloat ) && |
410 | | - $op != '/' ) { |
411 | | - $type = self::DInt; |
412 | | - $a = $a->toInt(); |
413 | | - $b = $b->toInt(); |
414 | | - } else { |
415 | | - $type = self::DFloat; |
416 | | - $a = $a->toFloat(); |
417 | | - $b = $b->toFloat(); |
418 | | - } |
419 | | - |
420 | | - if ($op != '*' && $b == 0) { |
421 | | - throw new ISUserVisibleException( 'dividebyzero', $pos, array($a) ); |
422 | | - } |
423 | | - |
424 | | - $data = null; |
425 | | - if( $op == '*' ) |
426 | | - $data = $a * $b; |
427 | | - elseif( $op == '/' ) |
428 | | - $data = $a / $b; |
429 | | - elseif( $op == '%' ) |
430 | | - $data = $a % $b; |
431 | | - else |
432 | | - throw new ISException( "Invalid multiplication-related operation: {$op}" ); // Should never happen |
433 | | - |
434 | | - if ($type == self::DInt) |
435 | | - $data = intval($data); |
436 | | - else |
437 | | - $data = doubleval($data); |
438 | | - |
439 | | - return new ISData( $type, $data ); |
440 | | - } |
441 | | - |
442 | | - public static function sum( $a, $b ) { |
443 | | - if( $a->type == self::DString || $b->type == self::DString ) |
444 | | - return new ISData( self::DString, $a->toString() . $b->toString() ); |
445 | | - elseif( $a->type == self::DList && $b->type == self::DList ) |
446 | | - return new ISData( self::DList, array_merge( $a->toList(), $b->toList() ) ); |
447 | | - elseif( $a->type == self::DInt && $b->type == self::DInt ) |
448 | | - return new ISData( self::DInt, $a->toInt() + $b->toInt() ); |
449 | | - else |
450 | | - return new ISData( self::DFloat, $a->toFloat() + $b->toFloat() ); |
451 | | - } |
452 | | - |
453 | | - public static function sub( $a, $b ) { |
454 | | - if( $a->type == self::DInt && $b->type == self::DInt ) |
455 | | - return new ISData( self::DInt, $a->toInt() - $b->toInt() ); |
456 | | - else |
457 | | - return new ISData( self::DFloat, $a->toFloat() - $b->toFloat() ); |
458 | | - } |
459 | | - |
460 | | - public function setValueByIndices( $val, $indices ) { |
461 | | - if( $this->type == self::DNull && $indices[0] === null ) { |
462 | | - $this->type = self::DList; |
463 | | - $this->value = array(); |
464 | | - $this->setValueByIndices( $val, $indices ); |
465 | | - } elseif( $this->type == self::DList ) { |
466 | | - if( $indices[0] === null ) { |
467 | | - $this->data[] = $val; |
468 | | - } else { |
469 | | - $idx = $indices[0]->toInt(); |
470 | | - if( $idx < 0 || $idx >= count( $this->data ) ) |
471 | | - throw new ISUserVisibleException( 'outofbounds', 0, array( count( $this->data ), $index ) ); |
472 | | - if( count( $indices ) > 1 ) |
473 | | - $this->data[$idx]->setValueByIndices( $val, array_slice( $indices, 1 ) ); |
474 | | - else |
475 | | - $this->data[$idx] = $val; |
476 | | - } |
477 | | - } |
478 | | - } |
479 | | - |
480 | | - public function checkIssetByIndices( $indices ) { |
481 | | - if( $indices ) { |
482 | | - $idx = array_shift( $indices ); |
483 | | - if( $this->type != self::DList || $idx >= count( $this->data ) ) |
484 | | - return false; |
485 | | - return $this->checkIssetByIndices( $indices ); |
486 | | - } else { |
487 | | - return true; |
488 | | - } |
489 | | - } |
490 | | - |
491 | | - /** Convert shorteners */ |
492 | | - public function toBool() { |
493 | | - return self::castTypes( $this, self::DBool )->data; |
494 | | - } |
495 | | - |
496 | | - public function toString() { |
497 | | - return self::castTypes( $this, self::DString )->data; |
498 | | - } |
499 | | - |
500 | | - public function toFloat() { |
501 | | - return self::castTypes( $this, self::DFloat )->data; |
502 | | - } |
503 | | - |
504 | | - public function toInt() { |
505 | | - return self::castTypes( $this, self::DInt )->data; |
506 | | - } |
507 | | - |
508 | | - public function toList() { |
509 | | - return self::castTypes( $this, self::DList )->data; |
510 | | - } |
511 | | -} |
512 | | - |
513 | | -class ISParserOutput { |
514 | | - var $mAST, $mTokensCount, $mVersion; |
515 | | - |
516 | | - public function __construct( $ast, $tokens ) { |
517 | | - $this->mAST = $ast; |
518 | | - $this->mTokensCount = $tokens; |
519 | | - $this->mVersion = InlineScriptInterpreter::ParserVersion; |
520 | | - } |
521 | | - |
522 | | - public function getAST() { |
523 | | - return $this->mAST; |
524 | | - } |
525 | | - |
526 | | - public function isOutOfDate() { |
527 | | - return InlineScriptInterpreter::ParserVersion > $this->mVersion; |
528 | | - } |
529 | | - |
530 | | - public function appendTokenCount( &$interpr ) { |
531 | | - global $wgInlineScriptsParserParams; |
532 | | - $interpr->mParser->is_tokensCount += $this->mTokensCount; |
533 | | - if( $interpr->mParser->is_tokensCount > $wgInlineScriptsParserParams['limits']['tokens'] ) |
534 | | - throw new ISUserVisibleException( 'toomanytokens', 0 ); |
535 | | - } |
536 | | -} |
537 | | - |
538 | | -class ISException extends MWException {} |
539 | | - |
540 | | -// Exceptions that we might conceivably want to report to ordinary users |
541 | | -// (i.e. exceptions that don't represent bugs in the extension itself) |
542 | | -class ISUserVisibleException extends ISException { |
543 | | - function __construct( $exception_id, $position, $params = array() ) { |
544 | | - $msg = wfMsgExt( 'inlinescripts-exception-' . $exception_id, array(), array_merge( array($position), $params ) ); |
545 | | - parent::__construct( $msg ); |
546 | | - |
547 | | - $this->mExceptionID = $exception_id; |
548 | | - $this->mPosition = $position; |
549 | | - $this->mParams = $params; |
550 | | - } |
551 | | - |
552 | | - public function getExceptionID() { |
553 | | - return $this->mExceptionID; |
554 | | - } |
555 | | -} |
Index: trunk/extensions/InlineScripts/interpreter/Data.php |
— | — | @@ -0,0 +1,258 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class ISData { |
| 5 | + // Data types |
| 6 | + const DInt = 'int'; |
| 7 | + const DString = 'string'; |
| 8 | + const DNull = 'null'; |
| 9 | + const DBool = 'bool'; |
| 10 | + const DFloat = 'float'; |
| 11 | + const DList = 'list'; // int -> value |
| 12 | + |
| 13 | + var $type; |
| 14 | + var $data; |
| 15 | + |
| 16 | + public function __construct( $type = self::DNull, $val = null ) { |
| 17 | + $this->type = $type; |
| 18 | + $this->data = $val; |
| 19 | + } |
| 20 | + |
| 21 | + public static function newFromPHPVar( $var ) { |
| 22 | + if( is_string( $var ) ) |
| 23 | + return new ISData( self::DString, $var ); |
| 24 | + elseif( is_int( $var ) ) |
| 25 | + return new ISData( self::DInt, $var ); |
| 26 | + elseif( is_float( $var ) ) |
| 27 | + return new ISData( self::DFloat, $var ); |
| 28 | + elseif( is_bool( $var ) ) |
| 29 | + return new ISData( self::DBool, $var ); |
| 30 | + elseif( is_array( $var ) ) { |
| 31 | + if( !$var ) |
| 32 | + return new ISData( self::DList, array() ); |
| 33 | + $result = array(); |
| 34 | + foreach( $var as $item ) |
| 35 | + $result[] = self::newFromPHPVar( $item ); |
| 36 | + return new ISData( self::DList, $result ); |
| 37 | + } |
| 38 | + elseif( is_null( $var ) ) |
| 39 | + return new ISData(); |
| 40 | + else |
| 41 | + throw new ISException( |
| 42 | + "Data type " . gettype( $var ) . " is not supported by InlineScrtips" ); |
| 43 | + } |
| 44 | + |
| 45 | + public function dup() { |
| 46 | + return new ISData( $this->type, $this->data ); |
| 47 | + } |
| 48 | + |
| 49 | + public static function castTypes( $orig, $target ) { |
| 50 | + if( $orig->type == $target ) |
| 51 | + return $orig->dup(); |
| 52 | + if( $target == self::DNull ) { |
| 53 | + return new ISData(); |
| 54 | + } |
| 55 | + |
| 56 | + if( $orig->type == self::DList ) { |
| 57 | + if( $target == self::DBool ) |
| 58 | + return new ISData( self::DBool, (bool)count( $orig->data ) ); |
| 59 | + if( $target == self::DFloat ) { |
| 60 | + return new ISData( self::DFloat, doubleval( count( $orig->data ) ) ); |
| 61 | + } |
| 62 | + if( $target == self::DInt ) { |
| 63 | + return new ISData( self::DInt, intval( count( $orig->data ) ) ); |
| 64 | + } |
| 65 | + if( $target == self::DString ) { |
| 66 | + $s = array(); |
| 67 | + foreach( $orig->data as $item ) |
| 68 | + $s[] = $item->toString(); |
| 69 | + return new ISData( self::DString, implode( "\n", $s ) ); |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + if( $target == self::DBool ) { |
| 74 | + return new ISData( self::DBool, (bool)$orig->data ); |
| 75 | + } |
| 76 | + if( $target == self::DFloat ) { |
| 77 | + return new ISData( self::DFloat, doubleval( $orig->data ) ); |
| 78 | + } |
| 79 | + if( $target == self::DInt ) { |
| 80 | + return new ISData( self::DInt, intval( $orig->data ) ); |
| 81 | + } |
| 82 | + if( $target == self::DString ) { |
| 83 | + return new ISData( self::DString, strval( $orig->data ) ); |
| 84 | + } |
| 85 | + if( $target == self::DList ) { |
| 86 | + return new ISData( self::DList, array( $orig ) ); |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + public static function boolInvert( $value ) { |
| 91 | + return new ISData( self::DBool, !$value->toBool() ); |
| 92 | + } |
| 93 | + |
| 94 | + public static function pow( $base, $exponent ) { |
| 95 | + if( $base->type == self::DInt && $exponent->type == self::DInt ) |
| 96 | + return new ISData( self::DInt, pow( $base->toInt(), $exponent->toInt() ) ); |
| 97 | + else |
| 98 | + return new ISData( self::DFloat, pow( $base->toFloat(), $exponent->toFloat() ) ); |
| 99 | + } |
| 100 | + |
| 101 | + // Checks whether a is in b |
| 102 | + public static function keywordIn( $a, $b ) { |
| 103 | + if( $b->type == self::DList ) { |
| 104 | + foreach( $b->data as $elem ) { |
| 105 | + if( self::equals( $elem, $a ) ) |
| 106 | + return new ISData( self::DBool, true ); |
| 107 | + } |
| 108 | + return new ISData( self::DBool, false ); |
| 109 | + } else { |
| 110 | + $a = $a->toString(); |
| 111 | + $b = $b->toString(); |
| 112 | + |
| 113 | + if ($a == '' || $b == '') { |
| 114 | + return new ISData( self::DBool, false ); |
| 115 | + } |
| 116 | + |
| 117 | + return new ISData( self::DBool, in_string( $a, $b ) ); |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + public static function equals( $d1, $d2 ) { |
| 122 | + return $d1->data == $d2->data; |
| 123 | + } |
| 124 | + |
| 125 | + public static function unaryMinus( $data ) { |
| 126 | + if( $data->type == self::DInt ) { |
| 127 | + return new ISData( $data->type, -$data->toInt() ); |
| 128 | + } else { |
| 129 | + return new ISData( $data->type, -$data->toFloat() ); |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + public static function compareOp( $a, $b, $op ) { |
| 134 | + if( $op == '==' ) |
| 135 | + return new ISData( self::DBool, self::equals( $a, $b ) ); |
| 136 | + if( $op == '!=' ) |
| 137 | + return new ISData( self::DBool, !self::equals( $a, $b ) ); |
| 138 | + if( $op == '===' ) |
| 139 | + return new ISData( self::DBool, $a->type == $b->type && self::equals( $a, $b ) ); |
| 140 | + if( $op == '!==' ) |
| 141 | + return new ISData( self::DBool, $a->type != $b->type || !self::equals( $a, $b ) ); |
| 142 | + $a = $a->toString(); |
| 143 | + $b = $b->toString(); |
| 144 | + if( $op == '>' ) |
| 145 | + return new ISData( self::DBool, $a > $b ); |
| 146 | + if( $op == '<' ) |
| 147 | + return new ISData( self::DBool, $a < $b ); |
| 148 | + if( $op == '>=' ) |
| 149 | + return new ISData( self::DBool, $a >= $b ); |
| 150 | + if( $op == '<=' ) |
| 151 | + return new ISData( self::DBool, $a <= $b ); |
| 152 | + throw new ISException( "Invalid comparison operation: {$op}" ); // Should never happen |
| 153 | + } |
| 154 | + |
| 155 | + public static function mulRel( $a, $b, $op, $pos ) { |
| 156 | + // Figure out the type. |
| 157 | + if( ( $a->type == self::DFloat || $b->type == self::DFloat ) && |
| 158 | + $op != '/' ) { |
| 159 | + $type = self::DInt; |
| 160 | + $a = $a->toInt(); |
| 161 | + $b = $b->toInt(); |
| 162 | + } else { |
| 163 | + $type = self::DFloat; |
| 164 | + $a = $a->toFloat(); |
| 165 | + $b = $b->toFloat(); |
| 166 | + } |
| 167 | + |
| 168 | + if ($op != '*' && $b == 0) { |
| 169 | + throw new ISUserVisibleException( 'dividebyzero', $pos, array($a) ); |
| 170 | + } |
| 171 | + |
| 172 | + $data = null; |
| 173 | + if( $op == '*' ) |
| 174 | + $data = $a * $b; |
| 175 | + elseif( $op == '/' ) |
| 176 | + $data = $a / $b; |
| 177 | + elseif( $op == '%' ) |
| 178 | + $data = $a % $b; |
| 179 | + else |
| 180 | + throw new ISException( "Invalid multiplication-related operation: {$op}" ); // Should never happen |
| 181 | + |
| 182 | + if ($type == self::DInt) |
| 183 | + $data = intval($data); |
| 184 | + else |
| 185 | + $data = doubleval($data); |
| 186 | + |
| 187 | + return new ISData( $type, $data ); |
| 188 | + } |
| 189 | + |
| 190 | + public static function sum( $a, $b ) { |
| 191 | + if( $a->type == self::DString || $b->type == self::DString ) |
| 192 | + return new ISData( self::DString, $a->toString() . $b->toString() ); |
| 193 | + elseif( $a->type == self::DList && $b->type == self::DList ) |
| 194 | + return new ISData( self::DList, array_merge( $a->toList(), $b->toList() ) ); |
| 195 | + elseif( $a->type == self::DInt && $b->type == self::DInt ) |
| 196 | + return new ISData( self::DInt, $a->toInt() + $b->toInt() ); |
| 197 | + else |
| 198 | + return new ISData( self::DFloat, $a->toFloat() + $b->toFloat() ); |
| 199 | + } |
| 200 | + |
| 201 | + public static function sub( $a, $b ) { |
| 202 | + if( $a->type == self::DInt && $b->type == self::DInt ) |
| 203 | + return new ISData( self::DInt, $a->toInt() - $b->toInt() ); |
| 204 | + else |
| 205 | + return new ISData( self::DFloat, $a->toFloat() - $b->toFloat() ); |
| 206 | + } |
| 207 | + |
| 208 | + public function setValueByIndices( $val, $indices, $line ) { |
| 209 | + if( $this->type == self::DNull && $indices[0] === null ) { |
| 210 | + $this->type = self::DList; |
| 211 | + $this->value = array(); |
| 212 | + $this->setValueByIndices( $val, $indices ); |
| 213 | + } elseif( $this->type == self::DList ) { |
| 214 | + if( $indices[0] === null ) { |
| 215 | + $this->data[] = $val; |
| 216 | + } else { |
| 217 | + $idx = $indices[0]->toInt(); |
| 218 | + if( $idx < 0 || $idx >= count( $this->data ) ) |
| 219 | + throw new ISUserVisibleException( 'outofbounds', $line, array( count( $this->data ), $idx ) ); |
| 220 | + if( count( $indices ) > 1 ) |
| 221 | + $this->data[$idx]->setValueByIndices( $val, array_slice( $indices, 1 ) ); |
| 222 | + else |
| 223 | + $this->data[$idx] = $val; |
| 224 | + } |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | + public function checkIssetByIndices( $indices ) { |
| 229 | + if( $indices ) { |
| 230 | + $idx = array_shift( $indices ); |
| 231 | + if( $this->type != self::DList || $idx >= count( $this->data ) ) |
| 232 | + return false; |
| 233 | + return $this->checkIssetByIndices( $indices ); |
| 234 | + } else { |
| 235 | + return true; |
| 236 | + } |
| 237 | + } |
| 238 | + |
| 239 | + /** Convert shorteners */ |
| 240 | + public function toBool() { |
| 241 | + return self::castTypes( $this, self::DBool )->data; |
| 242 | + } |
| 243 | + |
| 244 | + public function toString() { |
| 245 | + return self::castTypes( $this, self::DString )->data; |
| 246 | + } |
| 247 | + |
| 248 | + public function toFloat() { |
| 249 | + return self::castTypes( $this, self::DFloat )->data; |
| 250 | + } |
| 251 | + |
| 252 | + public function toInt() { |
| 253 | + return self::castTypes( $this, self::DInt )->data; |
| 254 | + } |
| 255 | + |
| 256 | + public function toList() { |
| 257 | + return self::castTypes( $this, self::DList )->data; |
| 258 | + } |
| 259 | +} |
Property changes on: trunk/extensions/InlineScripts/interpreter/Data.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 260 | + native |
Index: trunk/extensions/InlineScripts/interpreter/Scanner.php |
— | — | @@ -0,0 +1,304 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Lexical analizator for inline scripts. Splits strings to tokens. |
| 6 | + */ |
| 7 | + |
| 8 | +require_once( 'Shared.php' ); |
| 9 | + |
| 10 | +class ISScanner implements Iterator { |
| 11 | + var $mCode, $mPos, $mCur, $mEof; |
| 12 | + |
| 13 | + // Order is important. The punctuation-matching regex requires that |
| 14 | + // ** comes before *, etc. They are sorted to make it easy to spot |
| 15 | + // such errors. |
| 16 | + static $mOps = array( |
| 17 | + '!==', '!=', '!', // Inequality |
| 18 | + '+=', '-=', // Setting 1 |
| 19 | + '*=', '/=', // Setting 2 |
| 20 | + '**', '*', // Multiplication/exponentiation |
| 21 | + '/', '+', '-', '%', // Other arithmetic |
| 22 | + '&', '|', '^', // Logic |
| 23 | + '?', ':', // Ternery |
| 24 | + '<=','<', // Less than |
| 25 | + '>=', '>', // Greater than |
| 26 | + '===', '==', '=', // Equality |
| 27 | + ',', ';', // Comma, semicolon |
| 28 | + '(', '[', '{', // Braces |
| 29 | + ')', ']', '}', // Braces |
| 30 | + ); |
| 31 | + |
| 32 | + static $mKeywords = array( |
| 33 | + 'in', 'true', 'false', 'null', 'contains', 'break', |
| 34 | + 'if', 'then', 'else', 'foreach', 'do', 'try', 'catch', |
| 35 | + 'continue', 'isset', 'unset', |
| 36 | + ); |
| 37 | + |
| 38 | + public function __construct( $code ) { |
| 39 | + $this->mCode = $code; |
| 40 | + $this->rewind(); |
| 41 | + } |
| 42 | + |
| 43 | + public function rewind() { |
| 44 | + $this->mPos = 0; |
| 45 | + $this->mCur = null; |
| 46 | + $this->mEof = false; |
| 47 | + $this->move(); |
| 48 | + } |
| 49 | + |
| 50 | + public function current() { |
| 51 | + return $this->mCur; |
| 52 | + } |
| 53 | + |
| 54 | + public function key() { |
| 55 | + return $this->mPos; |
| 56 | + } |
| 57 | + |
| 58 | + public function next() { |
| 59 | + return $this->move(); |
| 60 | + } |
| 61 | + |
| 62 | + public function valid() { |
| 63 | + return !$this->mEof; |
| 64 | + } |
| 65 | + |
| 66 | + private function move() { |
| 67 | + if( $this->mEof || ( $this->mCur && $this->mCur->type == ISToken::TEnd ) ) { |
| 68 | + $this->mEof = true; |
| 69 | + return $this->mCur = null; |
| 70 | + } |
| 71 | + list( $val, $type ) = $this->nextToken(); |
| 72 | + |
| 73 | + $lineno = count( explode( "\n", substr( $this->mCode, 0, $this->mPos ) ) ); |
| 74 | + return $this->mCur = new ISToken( $type, $val, $lineno ); |
| 75 | + } |
| 76 | + |
| 77 | + private function nextToken() { |
| 78 | + $tok = ''; |
| 79 | + |
| 80 | + // Spaces |
| 81 | + $matches = array(); |
| 82 | + if ( preg_match( '/\s+/uA', $this->mCode, $matches, 0, $this->mPos ) ) |
| 83 | + $this->mPos += strlen($matches[0]); |
| 84 | + |
| 85 | + if( $this->mPos >= strlen($this->mCode) ) |
| 86 | + return array( null, ISToken::TEnd ); |
| 87 | + |
| 88 | + // Comments |
| 89 | + if ( substr($this->mCode, $this->mPos, 2) == '/*' ) { |
| 90 | + $this->mPos = strpos( $this->mCode, '*/', $this->mPos ) + 2; |
| 91 | + return self::nextToken(); |
| 92 | + } |
| 93 | + |
| 94 | + // Strings |
| 95 | + if( $this->mCode[$this->mPos] == '"' || $this->mCode[$this->mPos] == "'" ) { |
| 96 | + $type = $this->mCode[$this->mPos]; |
| 97 | + $this->mPos++; |
| 98 | + $strLen = strlen($this->mCode); |
| 99 | + while( $this->mPos < $strLen ) { |
| 100 | + if( $this->mCode[$this->mPos] == $type ) { |
| 101 | + $this->mPos++; |
| 102 | + return array( $tok, ISToken::TString ); |
| 103 | + } |
| 104 | + |
| 105 | + // Performance: Use a PHP function (implemented in C) |
| 106 | + // to scan ahead. |
| 107 | + $addLength = strcspn( $this->mCode, $type."\\", $this->mPos ); |
| 108 | + if ($addLength) { |
| 109 | + $tok .= substr( $this->mCode, $this->mPos, $addLength ); |
| 110 | + $this->mPos += $addLength; |
| 111 | + } elseif( $this->mCode[$this->mPos] == '\\' ) { |
| 112 | + switch( $this->mCode[$this->mPos + 1] ) { |
| 113 | + case '\\': |
| 114 | + $tok .= '\\'; |
| 115 | + break; |
| 116 | + case $type: |
| 117 | + $tok .= $type; |
| 118 | + break; |
| 119 | + case 'n'; |
| 120 | + $tok .= "\n"; |
| 121 | + break; |
| 122 | + case 'r': |
| 123 | + $tok .= "\r"; |
| 124 | + break; |
| 125 | + case 't': |
| 126 | + $tok .= "\t"; |
| 127 | + break; |
| 128 | + case 'x': |
| 129 | + $chr = substr( $this->mCode, $this->mPos + 2, 2 ); |
| 130 | + |
| 131 | + if ( preg_match( '/^[0-9A-Fa-f]{2}$/', $chr ) ) { |
| 132 | + $chr = base_convert( $chr, 16, 10 ); |
| 133 | + $tok .= chr($chr); |
| 134 | + $this->mPos += 2; # \xXX -- 2 done later |
| 135 | + } else { |
| 136 | + $tok .= 'x'; |
| 137 | + } |
| 138 | + break; |
| 139 | + default: |
| 140 | + $tok .= "\\" . $this->mCode[$this->mPos + 1]; |
| 141 | + } |
| 142 | + $this->mPos+=2; |
| 143 | + } else { |
| 144 | + $tok .= $this->mCode[$this->mPos]; |
| 145 | + $this->mPos++; |
| 146 | + } |
| 147 | + } |
| 148 | + throw new ISUserVisibleException( 'unclosedstring', $this->mPos, array() );; |
| 149 | + } |
| 150 | + |
| 151 | + // Find operators |
| 152 | + static $operator_regex = null; |
| 153 | + // Match using a regex. Regexes are faster than PHP |
| 154 | + if (!$operator_regex) { |
| 155 | + $quoted_operators = array(); |
| 156 | + |
| 157 | + foreach( self::$mOps as $op ) |
| 158 | + $quoted_operators[] = preg_quote( $op, '/' ); |
| 159 | + $operator_regex = '/('.implode('|', $quoted_operators).')/A'; |
| 160 | + } |
| 161 | + |
| 162 | + $matches = array(); |
| 163 | + |
| 164 | + preg_match( $operator_regex, $this->mCode, $matches, 0, $this->mPos ); |
| 165 | + |
| 166 | + if( count( $matches ) ) { |
| 167 | + $tok = $matches[0]; |
| 168 | + $this->mPos += strlen( $tok ); |
| 169 | + return array( $tok, $this->getOperatorType( $tok ) ); |
| 170 | + } |
| 171 | + |
| 172 | + // Find bare numbers |
| 173 | + $bases = array( 'b' => 2, |
| 174 | + 'x' => 16, |
| 175 | + 'o' => 8 ); |
| 176 | + $baseChars = array( |
| 177 | + 2 => '[01]', |
| 178 | + 16 => '[0-9A-Fa-f]', |
| 179 | + 8 => '[0-8]', |
| 180 | + 10 => '[0-9.]', |
| 181 | + ); |
| 182 | + $baseClass = '['.implode('', array_keys($bases)).']'; |
| 183 | + $radixRegex = "/([0-9A-Fa-f]+(?:\.\d*)?|\.\d+)($baseClass)?/Au"; |
| 184 | + $matches = array(); |
| 185 | + |
| 186 | + if ( preg_match( $radixRegex, $this->mCode, $matches, 0, $this->mPos ) ) { |
| 187 | + $input = $matches[1]; |
| 188 | + $baseChar = @$matches[2]; |
| 189 | + $num = null; |
| 190 | + // Sometimes the base char gets mixed in with the rest of it because |
| 191 | + // the regex targets hex, too. |
| 192 | + // This mostly happens with binary |
| 193 | + if (!$baseChar && !empty( $bases[ substr( $input, -1 ) ] ) ) { |
| 194 | + $baseChar = substr( $input, -1, 1 ); |
| 195 | + $input = substr( $input, 0, -1 ); |
| 196 | + } |
| 197 | + |
| 198 | + if( $baseChar ) |
| 199 | + $base = $bases[$baseChar]; |
| 200 | + else |
| 201 | + $base = 10; |
| 202 | + |
| 203 | + // Check against the appropriate character class for input validation |
| 204 | + $baseRegex = "/^".$baseChars[$base]."+$/"; |
| 205 | + |
| 206 | + if ( preg_match( $baseRegex, $input ) ) { |
| 207 | + if ($base != 10) { |
| 208 | + $num = base_convert( $input, $base, 10 ); |
| 209 | + } else { |
| 210 | + $num = $input; |
| 211 | + } |
| 212 | + |
| 213 | + $this->mPos += strlen( $matches[0] ); |
| 214 | + |
| 215 | + $float = in_string( '.', $input ); |
| 216 | + |
| 217 | + return array( |
| 218 | + $float |
| 219 | + ? doubleval( $num ) |
| 220 | + : intval( $num ), |
| 221 | + $float |
| 222 | + ? ISToken::TFloat |
| 223 | + : ISToken::TInt, |
| 224 | + ); |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | + // The rest are considered IDs |
| 229 | + |
| 230 | + // Regex match > PHP |
| 231 | + $idSymbolRegex = '/[0-9A-Za-z_]+/A'; |
| 232 | + $matches = array(); |
| 233 | + |
| 234 | + if ( preg_match( $idSymbolRegex, $this->mCode, $matches, 0, $this->mPos ) ) { |
| 235 | + $tok = strtolower( $matches[0] ); |
| 236 | + |
| 237 | + $type = in_array( $tok, self::$mKeywords ) |
| 238 | + ? $tok : ISToken::TID; |
| 239 | + |
| 240 | + $this->mPos += strlen( $tok ); |
| 241 | + return array( $tok, $type ); |
| 242 | + } |
| 243 | + |
| 244 | + throw new ISUserVisibleException( |
| 245 | + 'unrecognisedtoken', $this->mPos, array( substr( $this->mCode, $this->mPos ) ) ); |
| 246 | + } |
| 247 | + |
| 248 | + private static function getOperatorType( $op ) { |
| 249 | + switch( $op ) { |
| 250 | + case ':': |
| 251 | + return ISToken::TColon; |
| 252 | + case ',': |
| 253 | + return ISToken::TComma; |
| 254 | + case '>': |
| 255 | + case '<': |
| 256 | + case '>=': |
| 257 | + case '<=': |
| 258 | + return ISToken::TCompareOperator; |
| 259 | + case '==': |
| 260 | + case '!=': |
| 261 | + case '===': |
| 262 | + case '!==': |
| 263 | + return ISToken::TEqualsToOperator; |
| 264 | + case '!': |
| 265 | + return ISToken::TBoolInvert; |
| 266 | + case '(': |
| 267 | + return ISToken::TLeftBrace; |
| 268 | + case '{': |
| 269 | + return ISToken::TLeftCurly; |
| 270 | + case '[': |
| 271 | + return ISToken::TLeftSquare; |
| 272 | + case '&': |
| 273 | + case '|': |
| 274 | + case '^': |
| 275 | + return ISToken::TLogicalOperator; |
| 276 | + case '*': |
| 277 | + case '/': |
| 278 | + case '%': |
| 279 | + return ISToken::TMulOperator; |
| 280 | + case '**': |
| 281 | + return ISToken::TPow; |
| 282 | + case ')': |
| 283 | + return ISToken::TRightBrace; |
| 284 | + case '}': |
| 285 | + return ISToken::TRightCurly; |
| 286 | + case ']': |
| 287 | + return ISToken::TRightSquare; |
| 288 | + case ';': |
| 289 | + return ISToken::TSemicolon; |
| 290 | + case '=': |
| 291 | + case '+=': |
| 292 | + case '-=': |
| 293 | + case '*=': |
| 294 | + case '/=': |
| 295 | + return ISToken::TSet; |
| 296 | + case '+': |
| 297 | + case '-': |
| 298 | + return ISToken::TSumOperator; |
| 299 | + case '?': |
| 300 | + return ISToken::TTrinary; |
| 301 | + default: |
| 302 | + throw new ISException( "Invalid operator: {$op}" ); |
| 303 | + } |
| 304 | + } |
| 305 | +} |
Property changes on: trunk/extensions/InlineScripts/interpreter/Scanner.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 306 | + native |
Index: trunk/extensions/InlineScripts/interpreter/LRParser.php |
— | — | @@ -0,0 +1,83 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * LR parser for inline scripts. |
| 6 | + * Inputs tokens and LR table (ACTION/GOTO). |
| 7 | + * Outputs parser tree. |
| 8 | + */ |
| 9 | + |
| 10 | +class ISLRParser implements ISParser { |
| 11 | + const Version = 1; |
| 12 | + |
| 13 | + const Shift = 0; |
| 14 | + const Reduce = 1; |
| 15 | + const Accept = 2; |
| 16 | + |
| 17 | + var $mNonterminals, $mProductions, $mAction, $mGoto; |
| 18 | + |
| 19 | + public function __construct() { |
| 20 | + $this->loadGrammar(); |
| 21 | + } |
| 22 | + |
| 23 | + private function loadGrammar() { |
| 24 | + require_once( 'LRTable.php' ); |
| 25 | + |
| 26 | + $this->mNonterminals = ISLRTable::$nonterminals; |
| 27 | + $this->mProductions = ISLRTable::$productions; |
| 28 | + $this->mAction = ISLRTable::$action; |
| 29 | + $this->mGoto = ISLRTable::$goto; |
| 30 | + } |
| 31 | + |
| 32 | + public function needsScanner() { |
| 33 | + return true; |
| 34 | + } |
| 35 | + |
| 36 | + public function parse( $scanner, $maxTokens ) { |
| 37 | + $states = array( array( null, 0 ) ); |
| 38 | + $scanner->rewind(); |
| 39 | + $tokenCount = 0; |
| 40 | + |
| 41 | + for( ; ; ) { |
| 42 | + $token = $scanner->current(); |
| 43 | + $cur = $token->type; |
| 44 | + if( !$token ) |
| 45 | + throw new ISException( 'Non-token input in LRParser::parse' ); |
| 46 | + |
| 47 | + $tokenCount++; |
| 48 | + if( $tokenCount > $maxTokens ) |
| 49 | + throw new ISUserVisibleException( 'toomanytokens', $token->line ); |
| 50 | + |
| 51 | + list( $stateval, $state ) = end( $states ); |
| 52 | + $act = @$this->mAction[$state][$cur]; |
| 53 | + if( !$act ) { |
| 54 | + throw new ISUserVisibleException( 'unexceptedtoken', $token->line, |
| 55 | + array( $token, implode( ', ', array_keys( @$this->mAction[$state] ) ) ) ); |
| 56 | + } |
| 57 | + if( $act[0] == self::Shift ) { |
| 58 | + $states[] = array( $token, $act[1] ); |
| 59 | + $scanner->next(); |
| 60 | + } elseif( $act[0] == self::Reduce ) { |
| 61 | + list( $nonterm, $prod ) = $this->mProductions[$act[1]]; |
| 62 | + $len = count( $prod ); |
| 63 | + |
| 64 | + // Change state |
| 65 | + $str = array(); |
| 66 | + for( $i = 0; $i < $len; $i++ ) |
| 67 | + $str[] = array_pop( $states ); |
| 68 | + $str = array_reverse( $str ); |
| 69 | + list( $stateval, $state ) = end( $states ); |
| 70 | + |
| 71 | + $node = new ISParserTreeNode( $this, $nonterm ); |
| 72 | + foreach( $str as $symbol ) { |
| 73 | + list( $val ) = $symbol; |
| 74 | + $node->addChild( $val ); |
| 75 | + } |
| 76 | + $states[] = array( $node, $this->mGoto[$state][$nonterm] ); |
| 77 | + } elseif( $act[0] == self::Accept ) { |
| 78 | + break; |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + return new ISParserOutput( $states[1][0], $tokenCount ); |
| 83 | + } |
| 84 | +} |
Property changes on: trunk/extensions/InlineScripts/interpreter/LRParser.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 85 | + native |
Index: trunk/extensions/InlineScripts/interpreter/syntax.txt |
— | — | @@ -0,0 +1,36 @@ |
| 2 | +<program> ::= <stmts> |
| 3 | +<stmts> ::= <stmts> <stmt> | <stmt> |
| 4 | + |
| 5 | +<stmt> ::= <expr> semicolon |
| 6 | +<stmt> ::= if leftbrace <expr> rightbrace <stmt> |
| 7 | +<stmt> ::= if leftbrace <expr> rightbrace <stmt> else <stmt> |
| 8 | +<stmt> ::= foreach leftbrace <lvalue> in <expr> rightbrace <stmt> |
| 9 | +<stmt> ::= try <stmt> catch leftbrace <lvalue> rightbrace <stmt> |
| 10 | +<stmt> ::= leftcurly <stmts> rightcurly |
| 11 | + |
| 12 | +<expr> ::= <exprSet> |
| 13 | +<exprSet> ::= <lvalue> setto <exprSet> | <exprTrinary> |
| 14 | +<exprTrinary> ::= <exprLogical> trinary <exprTrinary> colon <exprTrinary> | <exprLogical> |
| 15 | +<exprLogical> ::= <exprLogical> logicop <exprCompare> | <exprCompare> |
| 16 | +<exprCompare> ::= <exprCompare> comareop <exprEquals> | <exprEquals> |
| 17 | +<exprEquals> ::= <exprSum> equalsto <exprSum> | <exprSum> |
| 18 | +<exprSum> ::= <exprSum> sum <exprMul> | <exprMul> |
| 19 | +<exprMul> ::= <exprMul> mul <exprPow> | <exprPow> |
| 20 | +<exprPow> ::= <exprInvert> pow <exprPow> | <exprInvert> |
| 21 | +<exprInvert> ::= invert <exprKeyword> | <exprKeyword> |
| 22 | +<exprKeyword> ::= <exprUnary> in <exprUnary> | <exprUnary> contains <exprUnary> | <exprUnary> |
| 23 | +<exprUnary> ::= sum <exprFunction> | <exprFunction> |
| 24 | +<exprFunction> ::= id leftbrace <commaListPlain> rightbrace | id leftbrace rightbrace |
| 25 | +<exprFunction> ::= <varfunc> leftbrace <lvalue> rightbrace | <exprAtom> |
| 26 | +<exprAtom> ::= <lvalue> | <atom> | break | continue |
| 27 | +<exprAtom> ::= leftbrace <expr> rightbrace | leftsquare <commaList> rightsquare |
| 28 | + |
| 29 | +<varfunc> ::= isset | unset |
| 30 | +<commaList> ::= <commaListAssoc> | <commaListPlain> |
| 31 | +<commaListPlain> ::= <commaListPlain> comma <expr> | <expr> |
| 32 | +<commaListAssoc> ::= <commaListAssoc> comma <keyValue> | <keyValue> |
| 33 | +<keyValue> ::= <expr> colon <expr> |
| 34 | +<atom> ::= string | int | float | true | false | null |
| 35 | +<lvalue> ::= id <arrayIdxs> | id |
| 36 | +<arrayIdxs> ::= <arrayIdx> <arrayIdxs> | <arrayIdx> |
| 37 | +<arrayIdx> ::= leftsquare <expr> rightsquare | leftsquare rightsquare |
Property changes on: trunk/extensions/InlineScripts/interpreter/syntax.txt |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 38 | + native |
Index: trunk/extensions/InlineScripts/interpreter/Interpreter.php |
— | — | @@ -1,22 +1,18 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * Interpreter for MediaWiki inline scripts |
| 4 | + * Interpreter for MediaWiki inline scripts. |
5 | 5 | * Copyright (C) Victor Vasiliev, Andrew Garrett, 2008-2009. |
6 | 6 | * Distributed under GNU GPL v2 or later terms. |
7 | 7 | */ |
8 | 8 | |
9 | | -require_once( 'Utils.php' ); |
| 9 | +require_once( 'Shared.php' ); |
| 10 | +require_once( 'Data.php' ); |
10 | 11 | |
11 | 12 | class InlineScriptInterpreter { |
12 | | - /** |
13 | | - * Used to invalidate AST cache. Increment whenever you change |
14 | | - * code parser or $mFunctions/$mOps |
15 | | - */ |
16 | 13 | const ParserVersion = 1; |
17 | 14 | |
18 | 15 | var $mVars, $mOut, $mParser, $mFrame, $mCodeParser; |
19 | 16 | |
20 | | - // length,lcase,ccnorm,rmdoubles,specialratio,rmspecials,norm,count |
21 | 17 | static $mFunctions = array( |
22 | 18 | 'out' => 'funcOut', |
23 | 19 | |
— | — | @@ -50,33 +46,10 @@ |
51 | 47 | 'bool' => 'castBool', |
52 | 48 | ); |
53 | 49 | |
54 | | - // Order is important. The punctuation-matching regex requires that |
55 | | - // ** comes before *, etc. They are sorted to make it easy to spot |
56 | | - // such errors. |
57 | | - static $mOps = array( |
58 | | - '!==', '!=', '!', // Inequality |
59 | | - '+=', '-=', // Setting 1 |
60 | | - '*=', '/=', // Setting 2 |
61 | | - '**', '*', // Multiplication/exponentiation |
62 | | - '/', '+', '-', '%', // Other arithmetic |
63 | | - '&', '|', '^', // Logic |
64 | | - '?', ':', // Ternery |
65 | | - '<=','<', // Less than |
66 | | - '>=', '>', // Greater than |
67 | | - '===', '==', '=', // Equality |
68 | | - ',', ';', // Comma, semicolon |
69 | | - '(', '[', '{', // Braces |
70 | | - ); |
71 | | - static $mKeywords = array( |
72 | | - 'in', 'true', 'false', 'null', 'contains', 'break', |
73 | | - 'if', 'then', 'else', 'foreach', 'do', 'try', 'catch', |
74 | | - 'continue', 'isset', 'unset', |
75 | | - ); |
76 | | - |
77 | 50 | public function __construct() { |
78 | | - global $wgInlineScriptsParserParams; |
| 51 | + global $wgInlineScriptsParserClass; |
79 | 52 | $this->resetState(); |
80 | | - $this->mCodeParser = new $wgInlineScriptsParserParams['parserClass']( $this ); |
| 53 | + $this->mCodeParser = new $wgInlineScriptsParserClass( $this ); |
81 | 54 | } |
82 | 55 | |
83 | 56 | public function resetState() { |
— | — | @@ -85,33 +58,32 @@ |
86 | 59 | } |
87 | 60 | |
88 | 61 | protected function checkRecursionLimit( $rec ) { |
89 | | - global $wgInlineScriptsParserParams; |
| 62 | + global $wgInlineScriptsLimits; |
90 | 63 | if( $rec > $this->mParser->is_maxDepth ) |
91 | 64 | $this->mParser->is_maxDepth = $rec; |
92 | | - return $rec <= $wgInlineScriptsParserParams['limits']['depth']; |
| 65 | + return $rec <= $wgInlineScriptsLimits['depth']; |
93 | 66 | } |
94 | 67 | |
95 | 68 | protected function increaseEvaluationsCount() { |
96 | | - global $wgInlineScriptsParserParams; |
| 69 | + global $wgInlineScriptsLimits; |
97 | 70 | $this->mParser->is_evalsCount++; |
98 | | - return $this->mParser->is_evalsCount <= $wgInlineScriptsParserParams['limits']['evaluations']; |
| 71 | + return $this->mParser->is_evalsCount <= $wgInlineScriptsLimits['evaluations']; |
99 | 72 | } |
100 | 73 | |
101 | | - public function increaseTokensCount() { |
102 | | - global $wgInlineScriptsParserParams; |
103 | | - $this->mParser->is_tokensCount++; |
104 | | - return $this->mParser->is_tokensCount <= $wgInlineScriptsParserParams['limits']['tokens']; |
| 74 | + public function getMaxTokensLeft() { |
| 75 | + global $wgInlineScriptsLimits; |
| 76 | + return $wgInlineScriptsLimits['tokens'] - $this->mParser->is_tokensCount; |
105 | 77 | } |
106 | 78 | |
107 | | - public function evaluateForOutput( $code, $parser, $frame, $resetState = true ) { |
| 79 | + public function execute( $code, $parser, $frame, $resetState = true ) { |
108 | 80 | wfProfileIn( __METHOD__ ); |
109 | 81 | if( $resetState ) |
110 | 82 | $this->resetState(); |
111 | 83 | $this->mParser = $parser; |
112 | 84 | $this->mFrame = $frame; |
113 | 85 | |
114 | | - $ast = $this->getCodeAST( $code ); |
115 | | - $this->evaluateASTNode( $ast ); |
| 86 | + $ast = $this->parseCode( $code ); |
| 87 | + $this->evaluateNode( $ast, 0 ); |
116 | 88 | wfProfileOut( __METHOD__ ); |
117 | 89 | return $this->mOut; |
118 | 90 | } |
— | — | @@ -123,330 +95,356 @@ |
124 | 96 | $this->mParser = $parser; |
125 | 97 | $this->mFrame = $frame; |
126 | 98 | |
127 | | - $ast = $this->getCodeAST( $code ); |
| 99 | + $ast = $this->parseCode( $code ); |
128 | 100 | wfProfileOut( __METHOD__ ); |
129 | | - return $this->evaluateASTNode( $ast )->toString(); |
| 101 | + return $this->evaluateNode( $ast, 0 )->toString(); |
130 | 102 | } |
131 | 103 | |
132 | | - public function getCodeAST( $code ) { |
| 104 | + public function parseCode( $code ) { |
133 | 105 | global $parserMemc; |
134 | | - static $ASTCache; |
| 106 | + static $parserCache; // Unserializing can be expensive as well |
135 | 107 | |
136 | 108 | wfProfileIn( __METHOD__ ); |
137 | 109 | $code = trim( $code ); |
138 | 110 | |
139 | 111 | $memcKey = 'isparser:ast:' . md5( $code ); |
140 | | - if( isset( $ASTCache[$memcKey] ) ) { |
| 112 | + |
| 113 | + if( isset( $parserCache[$memcKey] ) ) { |
141 | 114 | wfProfileOut( __METHOD__ ); |
142 | | - return $ASTCache[$memcKey]; |
| 115 | + return $parserCache[$memcKey]; |
143 | 116 | } |
144 | 117 | |
145 | 118 | $cached = $parserMemc->get( $memcKey ); |
146 | 119 | if( @$cached instanceof ISParserOutput && !$cached->isOutOfDate() ) { |
147 | 120 | $cached->appendTokenCount( $this ); |
148 | | - $ASTCache[$memcKey] = $cached->getAST(); |
| 121 | + $parserCache[$memcKey] = $cached->getParserTree(); |
149 | 122 | wfProfileOut( __METHOD__ ); |
150 | | - return $cached->getAST(); |
| 123 | + return $cached->getParserTree(); |
151 | 124 | } |
152 | 125 | |
153 | | - $out = $this->mCodeParser->parse( $code ); |
| 126 | + $scanner = new ISScanner( $code ); |
| 127 | + $out = $this->mCodeParser->parse( $scanner, $this->getMaxTokensLeft() ); |
| 128 | + |
| 129 | + $out->appendTokenCount( $this ); |
154 | 130 | $parserMemc->set( $memcKey, $out ); |
155 | | - $ASTCache[$memcKey] = $out->getAST(); |
| 131 | + $parserCache[$memcKey] = $out->getParserTree(); |
| 132 | + |
156 | 133 | wfProfileOut( __METHOD__ ); |
157 | | - return $out->getAST(); |
| 134 | + return $out->getParserTree(); |
158 | 135 | } |
159 | 136 | |
160 | | - public function evaluateASTNode( $ast, $rec = 0 ) { |
161 | | - if( $ast instanceof ISDataNode ) { |
162 | | - return $this->getDataNodeValue( $ast ); |
| 137 | + public function evaluateNode( $node, $rec ) { |
| 138 | + if( !$node instanceof ISParserTreeNode ) { |
| 139 | + throw new ISException( 'evaluateNode() accepts only nonterminals' ); |
163 | 140 | } |
164 | 141 | |
165 | | - if( !$this->checkRecursionLimit( $rec ) ) |
166 | | - throw new ISUserVisibleException( 'recoverflow', $ast->getPos() ); |
167 | | - if( !$this->increaseEvaluationsCount() ) |
168 | | - throw new ISUserVisibleException( 'toomanyevals', $ast->getPos() ); |
169 | | - |
170 | | - @list( $l, $r ) = $ast->getChildren(); |
171 | | - $op = $ast->getOperator(); |
172 | | - switch( $op ) { |
173 | | - /* Math */ |
174 | | - case ISOperatorNode::OMul: |
175 | | - case ISOperatorNode::ODiv: |
176 | | - case ISOperatorNode::OMod: |
177 | | - $ldata = $this->evaluateASTNode( $l, $rec + 1 ); |
178 | | - $rdata = $this->evaluateASTNode( $r, $rec + 1 ); |
179 | | - return ISData::mulRel( $ldata, $rdata, $op, $ast->getPos() ); |
180 | | - case ISOperatorNode::OSum: |
181 | | - $ldata = $this->evaluateASTNode( $l, $rec + 1 ); |
182 | | - $rdata = $this->evaluateASTNode( $r, $rec + 1 ); |
183 | | - return ISData::sum( $ldata, $rdata ); |
184 | | - case ISOperatorNode::OSub: |
185 | | - $ldata = $this->evaluateASTNode( $l, $rec + 1 ); |
186 | | - $rdata = $this->evaluateASTNode( $r, $rec + 1 ); |
187 | | - return ISData::sub( $ldata, $rdata ); |
188 | | - case ISOperatorNode::OPow: |
189 | | - $ldata = $this->evaluateASTNode( $l, $rec + 1 ); |
190 | | - $rdata = $this->evaluateASTNode( $r, $rec + 1 ); |
191 | | - return ISData::pow( $ldata, $rdata ); |
192 | | - case ISOperatorNode::OPositive: |
193 | | - return $this->evaluateASTNode( $l, $rec + 1 ); |
194 | | - case ISOperatorNode::ONegative: |
195 | | - $data = $this->evaluateASTNode( $l, $rec + 1 ); |
196 | | - return ISData::unaryMinus( $data ); |
197 | | - |
198 | | - /* Statement seperator */ |
199 | | - case ISOperatorNode::OStatementSeperator: |
200 | | - // Linearize tree for ";" to allow code with many statements |
201 | | - $statements = array(); |
202 | | - if( $r ) |
203 | | - array_unshift( $statements, $r ); |
204 | | - while( $l->isOp( ';' ) ) { |
205 | | - list( $l, $r ) = $l->getChildren(); |
206 | | - array_unshift( $statements, $r ); |
| 142 | + $c = $node->getChildren(); |
| 143 | + switch( $node->getType() ) { |
| 144 | + case 'stmts': |
| 145 | + $stmts = array(); |
| 146 | + while( isset( $c[1] ) ) { |
| 147 | + array_unshift( $stmts, $c[1] ); |
| 148 | + $c = $c[0]->getChildren(); |
207 | 149 | } |
208 | | - array_unshift( $statements, $l ); |
209 | | - |
210 | | - foreach( $statements as $node ) |
211 | | - $result = $this->evaluateASTNode( $node, $rec + 1 ); |
212 | | - return $result; |
213 | | - |
214 | | - /* Logic */ |
215 | | - case ISOperatorNode::OInvert: |
216 | | - $data = $this->evaluateASTNode( $l, $rec + 1 ); |
217 | | - return ISData::boolInvert( $data ); |
218 | | - case ISOperatorNode::OAnd: |
219 | | - $ldata = $this->evaluateASTNode( $l, $rec + 1 ); |
220 | | - if( $ldata->toBool() ) { |
221 | | - return ISData::castTypes( $this->evaluateASTNode( $r, $rec + 1 ), ISData::DBool ); |
| 150 | + array_unshift( $stmts, $c[0] ); |
| 151 | + foreach( $stmts as $stmt ) |
| 152 | + $res = $this->evaluateNode( $stmt, $rec + 1 ); |
| 153 | + return $res; |
| 154 | + case 'stmt': |
| 155 | + if( $c[0] instanceof ISToken ) { |
| 156 | + switch( $c[0]->type ) { |
| 157 | + case 'leftcurly': |
| 158 | + return $this->evaluateNode( $c[1], $rec + 1 ); |
| 159 | + case 'if': |
| 160 | + $cond = $this->evaluateNode( $c[2], $rec + 1 ); |
| 161 | + if( $cond->toBool() ) { |
| 162 | + return $this->evaluateNode( $c[4], $rec + 1 ); |
| 163 | + } else { |
| 164 | + if( isset( $c[6] ) ) { |
| 165 | + return $this->evaluateNode( $c[6], $rec + 1 ); |
| 166 | + } else { |
| 167 | + return new ISData(); |
| 168 | + } |
| 169 | + } |
| 170 | + case 'foreach': |
| 171 | + $array = $this->evaluateNode( $c[4], $rec + 1 ); |
| 172 | + if( $array->type != ISData::DList ) |
| 173 | + throw new ISException( 'invalidforeach', $c[0]->type ); |
| 174 | + $last = new ISData(); |
| 175 | + foreach( $array->data as $item ) { |
| 176 | + $this->setVar( $c[2], $item, $rec ); |
| 177 | + try { |
| 178 | + $last = $this->evaluateNode( $c[6], $rec + 1 ); |
| 179 | + } catch( ISUserVisibleException $e ) { |
| 180 | + if( $e->getExceptionID() == 'break' ) |
| 181 | + break; |
| 182 | + elseif( $e->getExceptionID() == 'continue' ) |
| 183 | + continue; |
| 184 | + else |
| 185 | + throw $e; |
| 186 | + } |
| 187 | + } |
| 188 | + return $last; |
| 189 | + case 'try': |
| 190 | + try { |
| 191 | + return $this->evaluateNode( $c[1], $rec + 1 ); |
| 192 | + } catch( ISUserVisibleException $e ) { |
| 193 | + if( $e->getExceptionID() == 'break' || $e->getExceptionID() == 'continue' ) { |
| 194 | + throw $e; |
| 195 | + } else { |
| 196 | + $this->setVar( $c[4], new ISData( ISData::DString, $e->getExceptionID() ), $rec ); |
| 197 | + return $this->evaluateNode( $c[6], $rec + 1 ); |
| 198 | + } |
| 199 | + } |
| 200 | + default: |
| 201 | + throw new ISException( "Unknown keyword: {$c[0]->type}" ); |
| 202 | + } |
222 | 203 | } else { |
223 | | - return new ISData( ISData::DBool, false ); |
| 204 | + return $this->evaluateNode( $c[0], $rec + 1 ); |
224 | 205 | } |
225 | | - case ISOperatorNode::OOr: |
226 | | - $ldata = $this->evaluateASTNode( $l, $rec + 1 ); |
227 | | - if( !$ldata->toBool() ) { |
228 | | - return ISData::castTypes( $this->evaluateASTNode( $r, $rec + 1 ), ISData::DBool ); |
| 206 | + case 'exprset': |
| 207 | + if( $c[1]->value == '=' ) { |
| 208 | + $new = $this->evaluateNode( $c[2], $rec + 1 ); |
| 209 | + $this->setVar( $c[0], $new, $rec ); |
| 210 | + return $new; |
229 | 211 | } else { |
230 | | - return new ISData( ISData::DBool, true ); |
| 212 | + $old = $this->getVar( $c[0], $rec, false ); |
| 213 | + $new = $this->evaluateNode( $c[2], $rec + 1 ); |
| 214 | + $new = $this->getValueForSetting( $old, $new, |
| 215 | + $c[1]->value, $c[1]->line ); |
| 216 | + $this->setVar( $c[0], $new, $rec ); |
| 217 | + return $new; |
231 | 218 | } |
232 | | - case ISOperatorNode::OXor: |
233 | | - $ldata = $this->evaluateASTNode( $l, $rec + 1 ); |
234 | | - $rdata = $this->evaluateASTNode( $r, $rec + 1 ); |
235 | | - return new ISData( ISData::DBool, $ldata->toBool() xor $rdata->toBool() ); |
236 | | - |
237 | | - /* Comparsions */ |
238 | | - case ISOperatorNode::OEqualsTo: |
239 | | - case ISOperatorNode::ONotEqualsTo: |
240 | | - case ISOperatorNode::OEqualsToStrict: |
241 | | - case ISOperatorNode::ONotEqualsToStrict: |
242 | | - case ISOperatorNode::OGreater: |
243 | | - case ISOperatorNode::OGreaterOrEq: |
244 | | - case ISOperatorNode::OLess: |
245 | | - case ISOperatorNode::OLessOrEq: |
246 | | - $ldata = $this->evaluateASTNode( $l, $rec + 1 ); |
247 | | - $rdata = $this->evaluateASTNode( $r, $rec + 1 ); |
248 | | - return ISData::compareOp( $ldata, $rdata, $op ); |
249 | | - |
250 | | - /* Variable assignment */ |
251 | | - case ISOperatorNode::OSet: |
252 | | - case ISOperatorNode::OSetAdd: |
253 | | - case ISOperatorNode::OSetSub: |
254 | | - case ISOperatorNode::OSetMul: |
255 | | - case ISOperatorNode::OSetDiv: |
256 | | - if( $l->isOp( ISOperatorNode::OArrayElement ) || $l->isOp( ISOperatorNode::OArrayElementSingle ) ) { |
257 | | - $datanode = $r; |
258 | | - $keys = array(); |
259 | | - while( $l->isOp( ISOperatorNode::OArrayElement ) || $l->isOp( ISOperatorNode::OArrayElementSingle ) ) { |
260 | | - @list( $l, $r ) = $l->getChildren(); |
261 | | - array_unshift( $keys, $r ? $r : null ); |
262 | | - } |
263 | | - if( $l->getType() != ISASTNode::NodeData || $l->getType() == ISDataNode::DNData ) |
264 | | - throw new ISUserVisibleException( 'cantchangeconst', $pos ); |
265 | | - $array = $this->getDataNodeValue( new ISDataNode( $l->getVar(), 0 ) ); |
266 | | - foreach( $keys as &$key ) |
267 | | - if( $key ) |
268 | | - $key = $this->evaluateASTNode( $key, $rec + 1 ); |
269 | | - $val = $this->evaluateASTNode( $datanode, $rec + 1 ); |
270 | | - $array->setValueByIndices( $val, $keys ); |
271 | | - $this->mVars[$l->getVar()] = $array; |
272 | | - return $val; |
| 219 | + case 'exprtrinary': |
| 220 | + $cond = $this->evaluateNode( $c[0], $rec + 1 ); |
| 221 | + if( $cond->toBool() ) { |
| 222 | + return $this->evaluateNode( $c[2], $rec + 1 ); |
273 | 223 | } else { |
274 | | - if( $l->getType() != ISASTNode::NodeData || $l->getType() == ISDataNode::DNData ) |
275 | | - throw new ISUserVisibleException( 'cantchangeconst', $pos ); |
276 | | - $val = $this->getValueForSetting( @$this->mVars[$l->getVar()], |
277 | | - $this->evaluateASTNode( $r, $rec + 1 ), $op ); |
278 | | - return $this->mVars[$l->getVar()] = $val; |
| 224 | + return $this->evaluateNode( $c[4], $rec + 1 ); |
279 | 225 | } |
280 | | - |
281 | | - /* Arrays */ |
282 | | - case ISOperatorNode::OArray: |
283 | | - $array = array(); |
284 | | - while( $l->isOp( ',' ) ) { |
285 | | - list( $l, $r ) = $l->getChildren(); |
286 | | - $array[] = $r; |
| 226 | + case 'exprlogical': |
| 227 | + $arg1 = $this->evaluateNode( $c[0], $rec + 1 ); |
| 228 | + switch( $c[1]->value ) { |
| 229 | + case '&': |
| 230 | + if( !$arg1->toBool() ) |
| 231 | + return new ISData( ISData::DBool, false ); |
| 232 | + else |
| 233 | + return $this->evaluateNode( $c[2], $rec + 1 ); |
| 234 | + case '|': |
| 235 | + if( $arg1->toBool() ) |
| 236 | + return new ISData( ISData::DBool, true ); |
| 237 | + else |
| 238 | + return $this->evaluateNode( $c[2], $rec + 1 ); |
| 239 | + case '^': |
| 240 | + $arg2 = $this->evaluateNode( $c[2], $rec + 1 ); |
| 241 | + return new ISData( ISData::DBool, $arg1->toBool() xor $arg2->toBool() ); |
| 242 | + default: |
| 243 | + throw new ISException( "Invalid logical operation: {$c[1]->value}" ); |
287 | 244 | } |
288 | | - $array[] = $l; |
289 | | - $array = array_reverse( $array ); |
290 | | - foreach( $array as &$element ) |
291 | | - $element = $this->evaluateASTNode( $element, $rec + 1 ); |
292 | | - return new ISData( ISData::DList, $array ); |
293 | | - case ISOperatorNode::OArrayElement: |
294 | | - $array = $this->evaluateASTNode( $l, $rec + 1 ); |
295 | | - $index = $this->evaluateASTNode( $r, $rec + 1 )->toInt(); |
296 | | - if( $array->type != ISData::DList ) |
297 | | - throw new ISUserVisibleException( 'notanarray', $ast->getPos(), array( $array->type ) ); |
298 | | - if( count( $array->data ) <= $index ) |
299 | | - throw new ISUserVisibleException( 'outofbounds', $ast->getPos(), array( count( $array->data ), $index ) ); |
300 | | - return $array->data[$index]; |
301 | | - |
302 | | - /* Flow control (if, foreach, etc) */ |
303 | | - case ISOperatorNode::OTrinary: |
304 | | - if( !$r->isOp( ':' ) ) |
305 | | - throw new ISUserVisibleException( 'expectednotfound', $pos, array( ':' ) ); |
306 | | - $cond = $this->evaluateASTNode( $l, $rec + 1 ); |
307 | | - list( $onTrue, $onFalse ) = $r->getChildren(); |
308 | | - if( $cond->toBool() ) |
309 | | - return $this->evaluateASTNode( $onTrue, $rec + 1 ); |
| 245 | + case 'exprequals': |
| 246 | + case 'exprcompare': |
| 247 | + $arg1 = $this->evaluateNode( $c[0], $rec + 1 ); |
| 248 | + $arg2 = $this->evaluateNode( $c[2], $rec + 1 ); |
| 249 | + return ISData::compareOp( $arg1, $arg2, $c[1]->value ); |
| 250 | + case 'exprsum': |
| 251 | + $arg1 = $this->evaluateNode( $c[0], $rec + 1 ); |
| 252 | + $arg2 = $this->evaluateNode( $c[2], $rec + 1 ); |
| 253 | + switch( $c[1]->value ) { |
| 254 | + case '+': |
| 255 | + return ISData::sum( $arg1, $arg2 ); |
| 256 | + case '-': |
| 257 | + return ISData::sub( $arg1, $arg2 ); |
| 258 | + } |
| 259 | + case 'exprmul': |
| 260 | + $arg1 = $this->evaluateNode( $c[0], $rec + 1 ); |
| 261 | + $arg2 = $this->evaluateNode( $c[2], $rec + 1 ); |
| 262 | + return ISData::mulRel( $arg1, $arg2, $c[1]->value, $c[1]->line ); |
| 263 | + case 'exprpow': |
| 264 | + $arg1 = $this->evaluateNode( $c[0], $rec + 1 ); |
| 265 | + $arg2 = $this->evaluateNode( $c[2], $rec + 1 ); |
| 266 | + return ISData::pow( $arg1, $arg2 ); |
| 267 | + case 'exprkeyword': |
| 268 | + $arg1 = $this->evaluateNode( $c[0], $rec + 1 ); |
| 269 | + $arg2 = $this->evaluateNode( $c[2], $rec + 1 ); |
| 270 | + switch( $c[1]->value ) { |
| 271 | + case 'in': |
| 272 | + return ISData::keywordIn( $arg1, $arg2 ); |
| 273 | + case 'contains': |
| 274 | + return ISData::keywordIn( $arg2, $arg1 ); |
| 275 | + default: |
| 276 | + throw new ISException( "Invalid keyword: {$c[1]->value}" ); |
| 277 | + } |
| 278 | + case 'exprinvert': |
| 279 | + $arg = $this->evaluateNode( $c[1], $rec + 1 ); |
| 280 | + return ISData::boolInvert( $arg ); |
| 281 | + case 'exprunary': |
| 282 | + $arg = $this->evaluateNode( $c[1], $rec + 1 ); |
| 283 | + if( $c[0]->value == '-' ) |
| 284 | + return ISData::unaryMinus( $arg ); |
310 | 285 | else |
311 | | - return $this->evaluateASTNode( $onFalse, $rec + 1 ); |
312 | | - case ISOperatorNode::OIf: |
313 | | - if( !$l->isOp( ISOperatorNode::OThen ) ) |
314 | | - throw new ISUserVisibleException( 'exceptednotfound', $ast->getPos(), array( 'then' ) ); |
315 | | - list( $l, $r ) = $l->getChildren(); |
316 | | - if( $r->isOp( ISOperatorNode::OElse ) ) { |
317 | | - list( $onTrue, $onFalse ) = $r->getChildren(); |
318 | | - if( $this->evaluateASTNode( $l, $rec + 1 )->toBool() ) |
319 | | - $this->evaluateASTNode( $onTrue, $rec + 1 ); |
320 | | - else |
321 | | - $this->evaluateASTNode( $onFalse, $rec + 1 ); |
| 286 | + return $arg; |
| 287 | + case 'exprfunction': |
| 288 | + if( $c[0] instanceof ISToken ) { |
| 289 | + $funcname = $c[0]->value; |
| 290 | + if( !isset( self::$mFunctions[$funcname] ) ) |
| 291 | + throw new ISUserVisibleException( 'unknownfunction', $c[0]->line ); |
| 292 | + $func = self::$mFunctions[$funcname]; |
| 293 | + if( $c[2] instanceof ISParserTreeNode ) { |
| 294 | + $args = $this->parseArray( $c[2], $rec ); |
| 295 | + } else { |
| 296 | + $args = array(); |
| 297 | + } |
| 298 | + return $this->$func( $args, $c[0]->line ); |
322 | 299 | } else { |
323 | | - if( $this->evaluateASTNode( $l, $rec + 1 )->toBool() ) |
324 | | - $this->evaluateASTNode( $r, $rec + 1 ); |
325 | | - } |
326 | | - return new ISData(); |
327 | | - case ISOperatorNode::OForeach: |
328 | | - if( !$l->isOp( ISOperatorNode::ODo ) ) |
329 | | - throw new ISUserVisibleException( 'exceptednotfound', $ast->getPos(), array( 'do' ) ); |
330 | | - list( $l, $r ) = $l->getChildren(); |
331 | | - $array = $this->evaluateASTNode( $l, $rec + 1 ); |
332 | | - if( $array->type != ISData::DList ) |
333 | | - throw new ISUserVisibleException( 'invalidforeach', $ast->getPos(), array( $array->type ) ); |
334 | | - foreach( $array->data as $element ) { |
335 | | - try { |
336 | | - $this->mVars[$ast->getData()] = $element; |
337 | | - $this->evaluateASTNode( $r, $rec + 1 ); |
338 | | - } catch( ISUserVisibleException $e ) { |
339 | | - if( $e->getExceptionID() == 'break' ) |
340 | | - break; |
341 | | - elseif( $e->getExceptionID() == 'continue' ) |
342 | | - continue; |
343 | | - else |
344 | | - throw $e; |
| 300 | + $type = $c[0]->mChildren[0]->value; |
| 301 | + switch( $type ) { |
| 302 | + case 'isset': |
| 303 | + return new ISData( ISData::DBool, $this->checkIsset( $c[2], $rec ) ); |
| 304 | + case 'unset': |
| 305 | + $this->unsetVar( $c[2], $rec ); |
| 306 | + return new ISData(); |
| 307 | + default: |
| 308 | + throw new ISException( "Unknown keyword: {$type}" ); |
345 | 309 | } |
346 | 310 | } |
347 | | - return new ISData(); |
348 | | - case ISOperatorNode::OTry: |
349 | | - if( $l->isOp( ISOperatorNode::OCatch ) ) { |
350 | | - list( $code, $errorHandler ) = $l->getChildren(); |
351 | | - try { |
352 | | - $val = $this->evaluateASTNode( $code, $rec + 1 ); |
353 | | - } catch( ISUserVisibleException $e ) { |
354 | | - if( in_array( $e->getExceptionID(), array( 'break', 'continue' ) ) ) |
355 | | - throw $e; |
356 | | - $varname = $l->getData(); |
357 | | - $old = wfSetVar( $this->mVars[$varname], |
358 | | - new ISData( ISData::DString, $e->getExceptionID() ) ); |
359 | | - $val = $this->evaluateASTNode( $errorHandler, $rec + 1 ); |
360 | | - $this->mVars[$varname] = $old; |
| 311 | + case 'expratom': |
| 312 | + if( $c[0] instanceof ISParserTreeNode ) { |
| 313 | + if( $c[0]->getType() == 'atom' ) { |
| 314 | + list( $val ) = $c[0]->getChildren(); |
| 315 | + switch( $val->type ) { |
| 316 | + case 'string': |
| 317 | + return new ISData( ISData::DString, $val->value ); |
| 318 | + case 'int': |
| 319 | + return new ISData( ISData::DInt, $val->value ); |
| 320 | + case 'float': |
| 321 | + return new ISData( ISData::DFloat, $val->value ); |
| 322 | + case 'true': |
| 323 | + return new ISData( ISData::DBool, true ); |
| 324 | + case 'false': |
| 325 | + return new ISData( ISData::DBool, false ); |
| 326 | + case 'null': |
| 327 | + return new ISData(); |
| 328 | + } |
| 329 | + } else { |
| 330 | + return $this->getVar( $c[0], $rec ); |
361 | 331 | } |
362 | | - return $val; |
363 | 332 | } else { |
364 | | - try { |
365 | | - return $this->evaluateASTNode( $l, $rec + 1 ); |
366 | | - } catch( ISUserVisibleException $e ) { |
367 | | - return new ISData(); |
| 333 | + switch( $c[0]->type ) { |
| 334 | + case 'leftbrace': |
| 335 | + return $this->evaluateNode( $c[1], $rec + 1 ); |
| 336 | + case 'leftsquare': |
| 337 | + return new ISData( ISData::DList, $this->parseArray( $c[1], $rec + 1 ) ); |
| 338 | + case 'break': |
| 339 | + throw new ISUserVisibleException( 'break', $c[0]->line ); |
| 340 | + case 'continue': |
| 341 | + throw new ISUserVisibleException( 'continue', $c[0]->line ); |
368 | 342 | } |
369 | 343 | } |
| 344 | + default: |
| 345 | + $type = $node->getType(); |
| 346 | + throw new ISException( "Invalid node type passed to evaluateNode(): {$type}" ); |
| 347 | + } |
| 348 | + } |
370 | 349 | |
371 | | - /* break/continue */ |
372 | | - case ISOperatorNode::OBreak: |
373 | | - throw new ISUserVisibleException( 'break', $ast->getPos() ); |
374 | | - case ISOperatorNode::OContinue: |
375 | | - throw new ISUserVisibleException( 'continue', $ast->getPos() ); |
376 | | - |
377 | | - /* isset/unset */ |
378 | | - case ISOperatorNode::OUnset: |
379 | | - if( $l->getType() == ISASTNode::NodeData && $l->getDataType() == ISDataNode::DNVariable ) { |
380 | | - if( isset( $this->mVars[$l->getVar()] ) ) |
381 | | - unset( $this->mVars[$l->getVar()] ); |
382 | | - break; |
383 | | - } else { |
384 | | - throw new ISUserVisibleException( 'cantchangeconst', $ast->getPos() ); |
| 350 | + /* |
| 351 | + * Converts commaList* to a PHP array. |
| 352 | + */ |
| 353 | + protected function parseArray( $node, $rec ) { |
| 354 | + $c = $node->getChildren(); |
| 355 | + switch( $node->getType() ) { |
| 356 | + case 'commalist': |
| 357 | + return $this->parseArray( $c[0], $rec ); |
| 358 | + case 'commalistplain': |
| 359 | + $elements = $result = array(); |
| 360 | + while( isset( $c[2] ) ) { |
| 361 | + array_unshift( $elements, $c[2] ); |
| 362 | + $c = $c[0]->getChildren(); |
385 | 363 | } |
386 | | - case ISOperatorNode::OIsset: |
387 | | - if( $l->getType() == ISASTNode::NodeData && $l->getDataType() == ISDataNode::DNVariable ) { |
388 | | - return new ISData( ISData::DBool, isset( $this->mVars[$l->getVar()] ) ); |
389 | | - } elseif( $l->isOp( ISOperatorNode::OArrayElement ) ) { |
390 | | - $indices = array(); |
391 | | - while( $l->isOp( ISOperatorNode::OArrayElement ) ) { |
392 | | - list( $l, $r ) = $l->getChildren(); |
393 | | - array_unshift( $indices, $r ); |
394 | | - } |
395 | | - if( !($l->getType() == ISASTNode::NodeData && $l->getDataType() == ISDataNode::DNVariable) ) |
396 | | - throw new ISUserVisibleException( 'cantchangeconst', $ast->getPos() ); |
397 | | - foreach( $indices as &$idx ) |
398 | | - $idx = $this->evaluateASTNode( $idx )->toInt(); |
399 | | - $var = $l->getVar(); |
| 364 | + array_unshift( $elements, $c[0] ); |
| 365 | + foreach( $elements as $elem ) |
| 366 | + $result[] = $this->evaluateNode( $elem, $rec + 1 ); |
| 367 | + return $result; |
| 368 | + case 'commalistassoc': |
| 369 | + throw new ISException( 'Not implemented' ); |
| 370 | + } |
| 371 | + } |
400 | 372 | |
401 | | - if( !isset( $this->mVars[$var] ) ) |
402 | | - return new ISData( ISData::DBool, false ); |
403 | | - return new ISData( ISData::DBool, $this->mVars[$var]->checkIssetByIndices( $indices ) ); |
| 373 | + protected function getVar( $lval, $rec ) { |
| 374 | + $c = $lval->getChildren(); |
| 375 | + $line = $c[0]->line; |
| 376 | + $varname = $c[0]->value; |
| 377 | + if( !isset( $this->mVars[$varname] ) ) { |
| 378 | + throw new ISUserVisibleException( 'unknownvar', $line, array( $varname ) ); |
| 379 | + } |
| 380 | + if( isset( $c[1] ) ) { |
| 381 | + $indices = array(); |
| 382 | + $c = $c[1]->getChildren(); |
| 383 | + while( isset( $c[1] ) ) { |
| 384 | + array_unshift( $indices, $c[1] ); |
| 385 | + $c = $c[0]->getChildren(); |
| 386 | + } |
| 387 | + array_unshift( $indices, $c[0] ); |
| 388 | + foreach( $indices as &$idx ) { |
| 389 | + $c = $idx->getChildren(); |
| 390 | + if( !$c[1] instanceof ISParserTreeNode ) |
| 391 | + throw new ISUserVisibleException( 'emptyidx', $line ); |
| 392 | + $idx = $this->evaluateNode( $c[1], $rec + 1 ); |
| 393 | + } |
| 394 | + |
| 395 | + $val = $this->mVars[$varname]; |
| 396 | + foreach( $indices as $idx ) { |
| 397 | + if( $val->type == ISData::DList ) { |
| 398 | + $idx = $idx->toInt(); |
| 399 | + if( count( $val->data ) <= $idx ) |
| 400 | + throw new ISUserVisibleException( 'outofbounds', $line ); |
| 401 | + $val = $val->data[$idx]; |
404 | 402 | } else { |
405 | | - throw new ISUserVisibleException( 'cantchangeconst', $ast->getPos() ); |
| 403 | + throw new ISUserVisibleException( 'notanarray', $line ); |
406 | 404 | } |
407 | | - |
408 | | - /* Functions */ |
409 | | - case ISOperatorNode::OFunction: |
410 | | - $args = array(); |
411 | | - if( $l ) { |
412 | | - while( $l->isOp( ',' ) ) { |
413 | | - @list( $l, $r ) = $l->getChildren(); |
414 | | - array_unshift( $args, $r ); |
415 | | - } |
416 | | - array_unshift( $args, $l ); |
417 | | - } |
418 | | - foreach( $args as &$arg ) |
419 | | - $arg = $this->evaluateASTNode( $arg, $rec + 1 ); |
420 | | - $funcName = self::$mFunctions[$ast->getData()]; |
421 | | - $result = $this->$funcName( $args, $ast->getPos() ); |
422 | | - return $result; |
423 | | - default: |
424 | | - throw new ISUserVisibleException( 'unexceptedop', $ast->getPos(), array( $op ) ); |
| 405 | + } |
| 406 | + return $val; |
| 407 | + } else { |
| 408 | + return $this->mVars[$varname]; |
425 | 409 | } |
426 | 410 | } |
427 | 411 | |
428 | | - protected function getDataNodeValue( $node ) { |
429 | | - switch( $node->getDataType() ) { |
430 | | - case ISDataNode::DNData: |
431 | | - return $node->getData(); |
432 | | - case ISDataNode::DNVariable: |
433 | | - $varname = $node->getVar(); |
434 | | - if( isset( $this->mVars[$varname] ) ) |
435 | | - return $this->mVars[$varname]; |
| 412 | + protected function setVar( $lval, $newval, $rec ) { |
| 413 | + $c = $lval->getChildren(); |
| 414 | + $varname = $c[0]->value; |
| 415 | + if( isset( $c[1] ) ) { |
| 416 | + $lineno = 0; |
| 417 | + $idxs = array(); |
| 418 | + while( isset( $c[1] ) && $c[1]->getType() == 'arrayidxs' ) { |
| 419 | + $c = $c[1]->getChildren(); |
| 420 | + $idxs[] = $c[0]; |
| 421 | + } |
| 422 | + if( !isset( $this->mVars[$varname] ) ) |
| 423 | + $this->mVars[$varname] = new ISData( ISData::DList, array() ); |
| 424 | + foreach( $idxs as &$idx ) { |
| 425 | + $idxchildren = $idx->getChildren(); |
| 426 | + if( !$lineno ) |
| 427 | + $lineno = $idxchildren[0]->line; |
| 428 | + if( count( $idxchildren ) > 2 ) |
| 429 | + $idx = $this->evaluateNode( $idxchildren[1], $rec + 1 ); |
436 | 430 | else |
437 | | - return new ISData(); |
| 431 | + $idx = null; |
| 432 | + } |
| 433 | + $this->mVars[$varname]->setValueByIndices( $newval, $idxs, $lineno ); |
| 434 | + } else { |
| 435 | + $this->mVars[$varname] = $newval; |
438 | 436 | } |
439 | 437 | } |
440 | 438 | |
441 | | - protected function getValueForSetting( $old, $new, $set ) { |
| 439 | + protected function getValueForSetting( $old, $new, $set, $line ) { |
442 | 440 | switch( $set ) { |
443 | | - case ISOperatorNode::OSetAdd: |
| 441 | + case '+=': |
444 | 442 | return ISData::sum( $old, $new ); |
445 | | - case ISOperatorNode::OSetSub: |
| 443 | + case '-=': |
446 | 444 | return ISData::sub( $old, $new ); |
447 | | - case ISOperatorNode::OSetMul: |
448 | | - return ISData::mulRel( $old, $new, '*', 0 ); |
449 | | - case ISOperatorNode::OSetDiv: |
450 | | - return ISData::mulRel( $old, $new, '/', 0 ); |
| 445 | + case '*=': |
| 446 | + return ISData::mulRel( $old, $new, '*', $line ); |
| 447 | + case '/=': |
| 448 | + return ISData::mulRel( $old, $new, '/', $line ); |
451 | 449 | default: |
452 | 450 | return $new; |
453 | 451 | } |
— | — | @@ -457,6 +455,55 @@ |
458 | 456 | throw new ISUserVisibleException( 'notenoughargs', $pos ); |
459 | 457 | } |
460 | 458 | |
| 459 | + protected function checkIsset( $lval, $rec ) { |
| 460 | + $c = $lval->getChildren(); |
| 461 | + $line = $c[0]->line; |
| 462 | + $varname = $c[0]->value; |
| 463 | + if( !isset( $this->mVars[$varname] ) ) { |
| 464 | + return false; |
| 465 | + } |
| 466 | + if( isset( $c[1] ) ) { |
| 467 | + $indices = array(); |
| 468 | + $c = $c[1]->getChildren(); |
| 469 | + while( isset( $c[1] ) ) { |
| 470 | + array_unshift( $indices, $c[1] ); |
| 471 | + $c = $c[0]->getChildren(); |
| 472 | + } |
| 473 | + array_unshift( $indices, $c[0] ); |
| 474 | + foreach( $indices as &$idx ) { |
| 475 | + $c = $idx->getChildren(); |
| 476 | + if( !$c[1] instanceof ISParserTreeNode ) |
| 477 | + throw new ISUserVisibleException( 'emptyidx', $line ); |
| 478 | + $idx = $this->evaluateNode( $c[1], $rec + 1 ); |
| 479 | + } |
| 480 | + |
| 481 | + $val = $this->mVars[$varname]; |
| 482 | + foreach( $indices as $idx ) { |
| 483 | + if( $val->type == ISData::DList ) { |
| 484 | + $idx = $idx->toInt(); |
| 485 | + if( count( $val->data ) <= $idx ) |
| 486 | + return false; |
| 487 | + $val = $val->data[$idx]; |
| 488 | + } else { |
| 489 | + return false; |
| 490 | + } |
| 491 | + } |
| 492 | + return true; |
| 493 | + } else { |
| 494 | + return true; |
| 495 | + } |
| 496 | + } |
| 497 | + |
| 498 | + protected function unsetVar( $lval, $rec ) { |
| 499 | + $c = $lval->getChildren(); |
| 500 | + $line = $c[0]->line; |
| 501 | + $varname = $c[0]->value; |
| 502 | + if( isset( $c[1] ) ) { |
| 503 | + throw new ISException( 'unset() is not usable for array elements' ); |
| 504 | + } |
| 505 | + unset( $this->mVars[$varname] ); |
| 506 | + } |
| 507 | + |
461 | 508 | /** Functions */ |
462 | 509 | protected function funcOut( $args, $pos ) { |
463 | 510 | $this->checkParamsCount( $args, $pos, 1 ); |
Index: trunk/extensions/InlineScripts/interpreter/LRTable.php |
— | — | @@ -0,0 +1,1808 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Autogenerated SLR-table for inline scripts language. |
| 6 | + * |
| 7 | + * You should not try to modify it manually (it's very easy to break). |
| 8 | + * Use syntax.txt and buildLRTables.php insteaed. |
| 9 | + * |
| 10 | + * Actions have following syntax |
| 11 | + * array( 0, N ) means "shift and go to state N" |
| 12 | + * array( 1, N ) means "reduce to production N" |
| 13 | + * array( 2 ) means "accept" |
| 14 | + * null means "error" |
| 15 | + * |
| 16 | + * Terminals are referred by names, nonterminals - by ids. |
| 17 | + * |
| 18 | + * Variables has following format: |
| 19 | + * * $nonterminals is a nonterminal ID -> name map. |
| 20 | + * * $productions is a ID -> array( nonterminal, body ) map. |
| 21 | + * * Production body is an array of production symbols |
| 22 | + * |
| 23 | + * Generated on 2009-09-05 14:15. |
| 24 | + */ |
| 25 | + |
| 26 | +class ISLRTable { |
| 27 | + |
| 28 | +static $nonterminals = array( |
| 29 | + 0 => 'program', |
| 30 | + 1 => 'stmts', |
| 31 | + 2 => 'stmt', |
| 32 | + 3 => 'expr', |
| 33 | + 4 => 'lvalue', |
| 34 | + 5 => 'exprset', |
| 35 | + 6 => 'exprtrinary', |
| 36 | + 7 => 'exprlogical', |
| 37 | + 8 => 'exprcompare', |
| 38 | + 9 => 'exprequals', |
| 39 | + 10 => 'exprsum', |
| 40 | + 11 => 'exprmul', |
| 41 | + 12 => 'exprpow', |
| 42 | + 13 => 'exprinvert', |
| 43 | + 14 => 'exprkeyword', |
| 44 | + 15 => 'exprunary', |
| 45 | + 16 => 'exprfunction', |
| 46 | + 17 => 'commalistplain', |
| 47 | + 18 => 'varfunc', |
| 48 | + 19 => 'expratom', |
| 49 | + 20 => 'atom', |
| 50 | + 21 => 'commalist', |
| 51 | + 22 => 'commalistassoc', |
| 52 | + 23 => 'keyvalue', |
| 53 | + 24 => 'arrayidxs', |
| 54 | + 25 => 'arrayidx', |
| 55 | +); |
| 56 | + |
| 57 | +static $productions = array( |
| 58 | + 0 => array( 0, array( 1 ) ), |
| 59 | + 1 => array( 1, array( 1, 2 ) ), |
| 60 | + 2 => array( 1, array( 2 ) ), |
| 61 | + 3 => array( 2, array( 3, 'semicolon' ) ), |
| 62 | + 4 => array( 2, array( 'if', 'leftbrace', 3, 'rightbrace', 2 ) ), |
| 63 | + 5 => array( 2, array( 'if', 'leftbrace', 3, 'rightbrace', 2, 'else', 2 ) ), |
| 64 | + 6 => array( 2, array( 'foreach', 'leftbrace', 4, 'in', 3, 'rightbrace', 2 ) ), |
| 65 | + 7 => array( 2, array( 'try', 2, 'catch', 'leftbrace', 4, 'rightbrace', 2 ) ), |
| 66 | + 8 => array( 2, array( 'leftcurly', 1, 'rightcurly' ) ), |
| 67 | + 9 => array( 3, array( 5 ) ), |
| 68 | + 10 => array( 5, array( 4, 'setto', 5 ) ), |
| 69 | + 11 => array( 5, array( 6 ) ), |
| 70 | + 12 => array( 6, array( 7, 'trinary', 6, 'colon', 6 ) ), |
| 71 | + 13 => array( 6, array( 7 ) ), |
| 72 | + 14 => array( 7, array( 7, 'logicop', 8 ) ), |
| 73 | + 15 => array( 7, array( 8 ) ), |
| 74 | + 16 => array( 8, array( 8, 'comareop', 9 ) ), |
| 75 | + 17 => array( 8, array( 9 ) ), |
| 76 | + 18 => array( 9, array( 10, 'equalsto', 10 ) ), |
| 77 | + 19 => array( 9, array( 10 ) ), |
| 78 | + 20 => array( 10, array( 10, 'sum', 11 ) ), |
| 79 | + 21 => array( 10, array( 11 ) ), |
| 80 | + 22 => array( 11, array( 11, 'mul', 12 ) ), |
| 81 | + 23 => array( 11, array( 12 ) ), |
| 82 | + 24 => array( 12, array( 13, 'pow', 12 ) ), |
| 83 | + 25 => array( 12, array( 13 ) ), |
| 84 | + 26 => array( 13, array( 'invert', 14 ) ), |
| 85 | + 27 => array( 13, array( 14 ) ), |
| 86 | + 28 => array( 14, array( 15, 'in', 15 ) ), |
| 87 | + 29 => array( 14, array( 15, 'contains', 15 ) ), |
| 88 | + 30 => array( 14, array( 15 ) ), |
| 89 | + 31 => array( 15, array( 'sum', 16 ) ), |
| 90 | + 32 => array( 15, array( 16 ) ), |
| 91 | + 33 => array( 16, array( 'id', 'leftbrace', 17, 'rightbrace' ) ), |
| 92 | + 34 => array( 16, array( 'id', 'leftbrace', 'rightbrace' ) ), |
| 93 | + 35 => array( 16, array( 18, 'leftbrace', 4, 'rightbrace' ) ), |
| 94 | + 36 => array( 16, array( 19 ) ), |
| 95 | + 37 => array( 19, array( 4 ) ), |
| 96 | + 38 => array( 19, array( 20 ) ), |
| 97 | + 39 => array( 19, array( 'break' ) ), |
| 98 | + 40 => array( 19, array( 'continue' ) ), |
| 99 | + 41 => array( 19, array( 'leftbrace', 3, 'rightbrace' ) ), |
| 100 | + 42 => array( 19, array( 'leftsquare', 21, 'rightsquare' ) ), |
| 101 | + 43 => array( 18, array( 'isset' ) ), |
| 102 | + 44 => array( 18, array( 'unset' ) ), |
| 103 | + 45 => array( 21, array( 22 ) ), |
| 104 | + 46 => array( 21, array( 17 ) ), |
| 105 | + 47 => array( 17, array( 17, 'comma', 3 ) ), |
| 106 | + 48 => array( 17, array( 3 ) ), |
| 107 | + 49 => array( 22, array( 22, 'comma', 23 ) ), |
| 108 | + 50 => array( 22, array( 23 ) ), |
| 109 | + 51 => array( 23, array( 3, 'colon', 3 ) ), |
| 110 | + 52 => array( 20, array( 'string' ) ), |
| 111 | + 53 => array( 20, array( 'int' ) ), |
| 112 | + 54 => array( 20, array( 'float' ) ), |
| 113 | + 55 => array( 20, array( 'true' ) ), |
| 114 | + 56 => array( 20, array( 'false' ) ), |
| 115 | + 57 => array( 20, array( 'null' ) ), |
| 116 | + 58 => array( 4, array( 'id', 24 ) ), |
| 117 | + 59 => array( 4, array( 'id' ) ), |
| 118 | + 60 => array( 24, array( 25, 24 ) ), |
| 119 | + 61 => array( 24, array( 25 ) ), |
| 120 | + 62 => array( 25, array( 'leftsquare', 3, 'rightsquare' ) ), |
| 121 | + 63 => array( 25, array( 'leftsquare', 'rightsquare' ) ), |
| 122 | +); |
| 123 | + |
| 124 | +static $action = array( |
| 125 | + 0 => array( |
| 126 | + 'if' => array( 0, 1 ), |
| 127 | + 'foreach' => array( 0, 3 ), |
| 128 | + 'try' => array( 0, 4 ), |
| 129 | + 'leftcurly' => array( 0, 5 ), |
| 130 | + 'id' => array( 0, 8 ), |
| 131 | + 'invert' => array( 0, 7 ), |
| 132 | + 'sum' => array( 0, 6 ), |
| 133 | + 'isset' => array( 0, 12 ), |
| 134 | + 'unset' => array( 0, 13 ), |
| 135 | + 'break' => array( 0, 9 ), |
| 136 | + 'continue' => array( 0, 10 ), |
| 137 | + 'leftbrace' => array( 0, 2 ), |
| 138 | + 'leftsquare' => array( 0, 11 ), |
| 139 | + 'string' => array( 0, 14 ), |
| 140 | + 'int' => array( 0, 15 ), |
| 141 | + 'float' => array( 0, 16 ), |
| 142 | + 'true' => array( 0, 17 ), |
| 143 | + 'false' => array( 0, 18 ), |
| 144 | + 'null' => array( 0, 19 ), |
| 145 | + ), |
| 146 | + 1 => array( |
| 147 | + 'leftbrace' => array( 0, 39 ), |
| 148 | + ), |
| 149 | + 2 => array( |
| 150 | + 'id' => array( 0, 8 ), |
| 151 | + 'invert' => array( 0, 7 ), |
| 152 | + 'sum' => array( 0, 6 ), |
| 153 | + 'isset' => array( 0, 12 ), |
| 154 | + 'unset' => array( 0, 13 ), |
| 155 | + 'break' => array( 0, 9 ), |
| 156 | + 'continue' => array( 0, 10 ), |
| 157 | + 'leftbrace' => array( 0, 2 ), |
| 158 | + 'leftsquare' => array( 0, 11 ), |
| 159 | + 'string' => array( 0, 14 ), |
| 160 | + 'int' => array( 0, 15 ), |
| 161 | + 'float' => array( 0, 16 ), |
| 162 | + 'true' => array( 0, 17 ), |
| 163 | + 'false' => array( 0, 18 ), |
| 164 | + 'null' => array( 0, 19 ), |
| 165 | + ), |
| 166 | + 3 => array( |
| 167 | + 'leftbrace' => array( 0, 41 ), |
| 168 | + ), |
| 169 | + 4 => array( |
| 170 | + 'if' => array( 0, 1 ), |
| 171 | + 'foreach' => array( 0, 3 ), |
| 172 | + 'try' => array( 0, 4 ), |
| 173 | + 'leftcurly' => array( 0, 5 ), |
| 174 | + 'id' => array( 0, 8 ), |
| 175 | + 'invert' => array( 0, 7 ), |
| 176 | + 'sum' => array( 0, 6 ), |
| 177 | + 'isset' => array( 0, 12 ), |
| 178 | + 'unset' => array( 0, 13 ), |
| 179 | + 'break' => array( 0, 9 ), |
| 180 | + 'continue' => array( 0, 10 ), |
| 181 | + 'leftbrace' => array( 0, 2 ), |
| 182 | + 'leftsquare' => array( 0, 11 ), |
| 183 | + 'string' => array( 0, 14 ), |
| 184 | + 'int' => array( 0, 15 ), |
| 185 | + 'float' => array( 0, 16 ), |
| 186 | + 'true' => array( 0, 17 ), |
| 187 | + 'false' => array( 0, 18 ), |
| 188 | + 'null' => array( 0, 19 ), |
| 189 | + ), |
| 190 | + 5 => array( |
| 191 | + 'if' => array( 0, 1 ), |
| 192 | + 'foreach' => array( 0, 3 ), |
| 193 | + 'try' => array( 0, 4 ), |
| 194 | + 'leftcurly' => array( 0, 5 ), |
| 195 | + 'id' => array( 0, 8 ), |
| 196 | + 'invert' => array( 0, 7 ), |
| 197 | + 'sum' => array( 0, 6 ), |
| 198 | + 'isset' => array( 0, 12 ), |
| 199 | + 'unset' => array( 0, 13 ), |
| 200 | + 'break' => array( 0, 9 ), |
| 201 | + 'continue' => array( 0, 10 ), |
| 202 | + 'leftbrace' => array( 0, 2 ), |
| 203 | + 'leftsquare' => array( 0, 11 ), |
| 204 | + 'string' => array( 0, 14 ), |
| 205 | + 'int' => array( 0, 15 ), |
| 206 | + 'float' => array( 0, 16 ), |
| 207 | + 'true' => array( 0, 17 ), |
| 208 | + 'false' => array( 0, 18 ), |
| 209 | + 'null' => array( 0, 19 ), |
| 210 | + ), |
| 211 | + 6 => array( |
| 212 | + 'id' => array( 0, 44 ), |
| 213 | + 'isset' => array( 0, 12 ), |
| 214 | + 'unset' => array( 0, 13 ), |
| 215 | + 'break' => array( 0, 9 ), |
| 216 | + 'continue' => array( 0, 10 ), |
| 217 | + 'leftbrace' => array( 0, 2 ), |
| 218 | + 'leftsquare' => array( 0, 11 ), |
| 219 | + 'string' => array( 0, 14 ), |
| 220 | + 'int' => array( 0, 15 ), |
| 221 | + 'float' => array( 0, 16 ), |
| 222 | + 'true' => array( 0, 17 ), |
| 223 | + 'false' => array( 0, 18 ), |
| 224 | + 'null' => array( 0, 19 ), |
| 225 | + ), |
| 226 | + 7 => array( |
| 227 | + 'sum' => array( 0, 6 ), |
| 228 | + 'id' => array( 0, 44 ), |
| 229 | + 'isset' => array( 0, 12 ), |
| 230 | + 'unset' => array( 0, 13 ), |
| 231 | + 'break' => array( 0, 9 ), |
| 232 | + 'continue' => array( 0, 10 ), |
| 233 | + 'leftbrace' => array( 0, 2 ), |
| 234 | + 'leftsquare' => array( 0, 11 ), |
| 235 | + 'string' => array( 0, 14 ), |
| 236 | + 'int' => array( 0, 15 ), |
| 237 | + 'float' => array( 0, 16 ), |
| 238 | + 'true' => array( 0, 17 ), |
| 239 | + 'false' => array( 0, 18 ), |
| 240 | + 'null' => array( 0, 19 ), |
| 241 | + ), |
| 242 | + 8 => array( |
| 243 | + 'in' => array( 1, 59 ), |
| 244 | + 'rightbrace' => array( 1, 59 ), |
| 245 | + 'setto' => array( 1, 59 ), |
| 246 | + 'pow' => array( 1, 59 ), |
| 247 | + 'equalsto' => array( 1, 59 ), |
| 248 | + 'trinary' => array( 1, 59 ), |
| 249 | + 'semicolon' => array( 1, 59 ), |
| 250 | + 'colon' => array( 1, 59 ), |
| 251 | + 'logicop' => array( 1, 59 ), |
| 252 | + 'comareop' => array( 1, 59 ), |
| 253 | + 'sum' => array( 1, 59 ), |
| 254 | + 'mul' => array( 1, 59 ), |
| 255 | + 'contains' => array( 1, 59 ), |
| 256 | + 'rightsquare' => array( 1, 59 ), |
| 257 | + 'comma' => array( 1, 59 ), |
| 258 | + 'leftbrace' => array( 0, 48 ), |
| 259 | + 'leftsquare' => array( 0, 49 ), |
| 260 | + ), |
| 261 | + 9 => array( |
| 262 | + 'in' => array( 1, 39 ), |
| 263 | + 'pow' => array( 1, 39 ), |
| 264 | + 'equalsto' => array( 1, 39 ), |
| 265 | + 'trinary' => array( 1, 39 ), |
| 266 | + 'semicolon' => array( 1, 39 ), |
| 267 | + 'rightbrace' => array( 1, 39 ), |
| 268 | + 'colon' => array( 1, 39 ), |
| 269 | + 'logicop' => array( 1, 39 ), |
| 270 | + 'comareop' => array( 1, 39 ), |
| 271 | + 'sum' => array( 1, 39 ), |
| 272 | + 'mul' => array( 1, 39 ), |
| 273 | + 'contains' => array( 1, 39 ), |
| 274 | + 'rightsquare' => array( 1, 39 ), |
| 275 | + 'comma' => array( 1, 39 ), |
| 276 | + ), |
| 277 | + 10 => array( |
| 278 | + 'in' => array( 1, 40 ), |
| 279 | + 'pow' => array( 1, 40 ), |
| 280 | + 'equalsto' => array( 1, 40 ), |
| 281 | + 'trinary' => array( 1, 40 ), |
| 282 | + 'semicolon' => array( 1, 40 ), |
| 283 | + 'rightbrace' => array( 1, 40 ), |
| 284 | + 'colon' => array( 1, 40 ), |
| 285 | + 'logicop' => array( 1, 40 ), |
| 286 | + 'comareop' => array( 1, 40 ), |
| 287 | + 'sum' => array( 1, 40 ), |
| 288 | + 'mul' => array( 1, 40 ), |
| 289 | + 'contains' => array( 1, 40 ), |
| 290 | + 'rightsquare' => array( 1, 40 ), |
| 291 | + 'comma' => array( 1, 40 ), |
| 292 | + ), |
| 293 | + 11 => array( |
| 294 | + 'id' => array( 0, 8 ), |
| 295 | + 'invert' => array( 0, 7 ), |
| 296 | + 'sum' => array( 0, 6 ), |
| 297 | + 'isset' => array( 0, 12 ), |
| 298 | + 'unset' => array( 0, 13 ), |
| 299 | + 'break' => array( 0, 9 ), |
| 300 | + 'continue' => array( 0, 10 ), |
| 301 | + 'leftbrace' => array( 0, 2 ), |
| 302 | + 'leftsquare' => array( 0, 11 ), |
| 303 | + 'string' => array( 0, 14 ), |
| 304 | + 'int' => array( 0, 15 ), |
| 305 | + 'float' => array( 0, 16 ), |
| 306 | + 'true' => array( 0, 17 ), |
| 307 | + 'false' => array( 0, 18 ), |
| 308 | + 'null' => array( 0, 19 ), |
| 309 | + ), |
| 310 | + 12 => array( |
| 311 | + 'leftbrace' => array( 1, 43 ), |
| 312 | + ), |
| 313 | + 13 => array( |
| 314 | + 'leftbrace' => array( 1, 44 ), |
| 315 | + ), |
| 316 | + 14 => array( |
| 317 | + 'in' => array( 1, 52 ), |
| 318 | + 'pow' => array( 1, 52 ), |
| 319 | + 'equalsto' => array( 1, 52 ), |
| 320 | + 'trinary' => array( 1, 52 ), |
| 321 | + 'semicolon' => array( 1, 52 ), |
| 322 | + 'rightbrace' => array( 1, 52 ), |
| 323 | + 'colon' => array( 1, 52 ), |
| 324 | + 'logicop' => array( 1, 52 ), |
| 325 | + 'comareop' => array( 1, 52 ), |
| 326 | + 'sum' => array( 1, 52 ), |
| 327 | + 'mul' => array( 1, 52 ), |
| 328 | + 'contains' => array( 1, 52 ), |
| 329 | + 'rightsquare' => array( 1, 52 ), |
| 330 | + 'comma' => array( 1, 52 ), |
| 331 | + ), |
| 332 | + 15 => array( |
| 333 | + 'in' => array( 1, 53 ), |
| 334 | + 'pow' => array( 1, 53 ), |
| 335 | + 'equalsto' => array( 1, 53 ), |
| 336 | + 'trinary' => array( 1, 53 ), |
| 337 | + 'semicolon' => array( 1, 53 ), |
| 338 | + 'rightbrace' => array( 1, 53 ), |
| 339 | + 'colon' => array( 1, 53 ), |
| 340 | + 'logicop' => array( 1, 53 ), |
| 341 | + 'comareop' => array( 1, 53 ), |
| 342 | + 'sum' => array( 1, 53 ), |
| 343 | + 'mul' => array( 1, 53 ), |
| 344 | + 'contains' => array( 1, 53 ), |
| 345 | + 'rightsquare' => array( 1, 53 ), |
| 346 | + 'comma' => array( 1, 53 ), |
| 347 | + ), |
| 348 | + 16 => array( |
| 349 | + 'in' => array( 1, 54 ), |
| 350 | + 'pow' => array( 1, 54 ), |
| 351 | + 'equalsto' => array( 1, 54 ), |
| 352 | + 'trinary' => array( 1, 54 ), |
| 353 | + 'semicolon' => array( 1, 54 ), |
| 354 | + 'rightbrace' => array( 1, 54 ), |
| 355 | + 'colon' => array( 1, 54 ), |
| 356 | + 'logicop' => array( 1, 54 ), |
| 357 | + 'comareop' => array( 1, 54 ), |
| 358 | + 'sum' => array( 1, 54 ), |
| 359 | + 'mul' => array( 1, 54 ), |
| 360 | + 'contains' => array( 1, 54 ), |
| 361 | + 'rightsquare' => array( 1, 54 ), |
| 362 | + 'comma' => array( 1, 54 ), |
| 363 | + ), |
| 364 | + 17 => array( |
| 365 | + 'in' => array( 1, 55 ), |
| 366 | + 'pow' => array( 1, 55 ), |
| 367 | + 'equalsto' => array( 1, 55 ), |
| 368 | + 'trinary' => array( 1, 55 ), |
| 369 | + 'semicolon' => array( 1, 55 ), |
| 370 | + 'rightbrace' => array( 1, 55 ), |
| 371 | + 'colon' => array( 1, 55 ), |
| 372 | + 'logicop' => array( 1, 55 ), |
| 373 | + 'comareop' => array( 1, 55 ), |
| 374 | + 'sum' => array( 1, 55 ), |
| 375 | + 'mul' => array( 1, 55 ), |
| 376 | + 'contains' => array( 1, 55 ), |
| 377 | + 'rightsquare' => array( 1, 55 ), |
| 378 | + 'comma' => array( 1, 55 ), |
| 379 | + ), |
| 380 | + 18 => array( |
| 381 | + 'in' => array( 1, 56 ), |
| 382 | + 'pow' => array( 1, 56 ), |
| 383 | + 'equalsto' => array( 1, 56 ), |
| 384 | + 'trinary' => array( 1, 56 ), |
| 385 | + 'semicolon' => array( 1, 56 ), |
| 386 | + 'rightbrace' => array( 1, 56 ), |
| 387 | + 'colon' => array( 1, 56 ), |
| 388 | + 'logicop' => array( 1, 56 ), |
| 389 | + 'comareop' => array( 1, 56 ), |
| 390 | + 'sum' => array( 1, 56 ), |
| 391 | + 'mul' => array( 1, 56 ), |
| 392 | + 'contains' => array( 1, 56 ), |
| 393 | + 'rightsquare' => array( 1, 56 ), |
| 394 | + 'comma' => array( 1, 56 ), |
| 395 | + ), |
| 396 | + 19 => array( |
| 397 | + 'in' => array( 1, 57 ), |
| 398 | + 'pow' => array( 1, 57 ), |
| 399 | + 'equalsto' => array( 1, 57 ), |
| 400 | + 'trinary' => array( 1, 57 ), |
| 401 | + 'semicolon' => array( 1, 57 ), |
| 402 | + 'rightbrace' => array( 1, 57 ), |
| 403 | + 'colon' => array( 1, 57 ), |
| 404 | + 'logicop' => array( 1, 57 ), |
| 405 | + 'comareop' => array( 1, 57 ), |
| 406 | + 'sum' => array( 1, 57 ), |
| 407 | + 'mul' => array( 1, 57 ), |
| 408 | + 'contains' => array( 1, 57 ), |
| 409 | + 'rightsquare' => array( 1, 57 ), |
| 410 | + 'comma' => array( 1, 57 ), |
| 411 | + ), |
| 412 | + 20 => array( |
| 413 | + '$' => array( 2, null ), |
| 414 | + 'if' => array( 0, 1 ), |
| 415 | + 'foreach' => array( 0, 3 ), |
| 416 | + 'try' => array( 0, 4 ), |
| 417 | + 'leftcurly' => array( 0, 5 ), |
| 418 | + 'id' => array( 0, 8 ), |
| 419 | + 'invert' => array( 0, 7 ), |
| 420 | + 'sum' => array( 0, 6 ), |
| 421 | + 'isset' => array( 0, 12 ), |
| 422 | + 'unset' => array( 0, 13 ), |
| 423 | + 'break' => array( 0, 9 ), |
| 424 | + 'continue' => array( 0, 10 ), |
| 425 | + 'leftbrace' => array( 0, 2 ), |
| 426 | + 'leftsquare' => array( 0, 11 ), |
| 427 | + 'string' => array( 0, 14 ), |
| 428 | + 'int' => array( 0, 15 ), |
| 429 | + 'float' => array( 0, 16 ), |
| 430 | + 'true' => array( 0, 17 ), |
| 431 | + 'false' => array( 0, 18 ), |
| 432 | + 'null' => array( 0, 19 ), |
| 433 | + ), |
| 434 | + 21 => array( |
| 435 | + '$' => array( 1, 2 ), |
| 436 | + 'if' => array( 1, 2 ), |
| 437 | + 'foreach' => array( 1, 2 ), |
| 438 | + 'try' => array( 1, 2 ), |
| 439 | + 'leftcurly' => array( 1, 2 ), |
| 440 | + 'id' => array( 1, 2 ), |
| 441 | + 'invert' => array( 1, 2 ), |
| 442 | + 'sum' => array( 1, 2 ), |
| 443 | + 'isset' => array( 1, 2 ), |
| 444 | + 'unset' => array( 1, 2 ), |
| 445 | + 'break' => array( 1, 2 ), |
| 446 | + 'continue' => array( 1, 2 ), |
| 447 | + 'leftbrace' => array( 1, 2 ), |
| 448 | + 'leftsquare' => array( 1, 2 ), |
| 449 | + 'string' => array( 1, 2 ), |
| 450 | + 'int' => array( 1, 2 ), |
| 451 | + 'float' => array( 1, 2 ), |
| 452 | + 'true' => array( 1, 2 ), |
| 453 | + 'false' => array( 1, 2 ), |
| 454 | + 'null' => array( 1, 2 ), |
| 455 | + 'rightcurly' => array( 1, 2 ), |
| 456 | + ), |
| 457 | + 22 => array( |
| 458 | + 'semicolon' => array( 0, 58 ), |
| 459 | + ), |
| 460 | + 23 => array( |
| 461 | + 'setto' => array( 0, 59 ), |
| 462 | + 'in' => array( 1, 37 ), |
| 463 | + 'pow' => array( 1, 37 ), |
| 464 | + 'equalsto' => array( 1, 37 ), |
| 465 | + 'trinary' => array( 1, 37 ), |
| 466 | + 'semicolon' => array( 1, 37 ), |
| 467 | + 'rightbrace' => array( 1, 37 ), |
| 468 | + 'colon' => array( 1, 37 ), |
| 469 | + 'logicop' => array( 1, 37 ), |
| 470 | + 'comareop' => array( 1, 37 ), |
| 471 | + 'sum' => array( 1, 37 ), |
| 472 | + 'mul' => array( 1, 37 ), |
| 473 | + 'contains' => array( 1, 37 ), |
| 474 | + 'rightsquare' => array( 1, 37 ), |
| 475 | + 'comma' => array( 1, 37 ), |
| 476 | + ), |
| 477 | + 24 => array( |
| 478 | + 'semicolon' => array( 1, 9 ), |
| 479 | + 'rightbrace' => array( 1, 9 ), |
| 480 | + 'rightsquare' => array( 1, 9 ), |
| 481 | + 'comma' => array( 1, 9 ), |
| 482 | + 'colon' => array( 1, 9 ), |
| 483 | + ), |
| 484 | + 25 => array( |
| 485 | + 'semicolon' => array( 1, 11 ), |
| 486 | + 'rightbrace' => array( 1, 11 ), |
| 487 | + 'rightsquare' => array( 1, 11 ), |
| 488 | + 'comma' => array( 1, 11 ), |
| 489 | + 'colon' => array( 1, 11 ), |
| 490 | + ), |
| 491 | + 26 => array( |
| 492 | + 'trinary' => array( 0, 60 ), |
| 493 | + 'semicolon' => array( 1, 13 ), |
| 494 | + 'rightbrace' => array( 1, 13 ), |
| 495 | + 'colon' => array( 1, 13 ), |
| 496 | + 'rightsquare' => array( 1, 13 ), |
| 497 | + 'comma' => array( 1, 13 ), |
| 498 | + 'logicop' => array( 0, 61 ), |
| 499 | + ), |
| 500 | + 27 => array( |
| 501 | + 'trinary' => array( 1, 15 ), |
| 502 | + 'semicolon' => array( 1, 15 ), |
| 503 | + 'rightbrace' => array( 1, 15 ), |
| 504 | + 'colon' => array( 1, 15 ), |
| 505 | + 'logicop' => array( 1, 15 ), |
| 506 | + 'rightsquare' => array( 1, 15 ), |
| 507 | + 'comma' => array( 1, 15 ), |
| 508 | + 'comareop' => array( 0, 62 ), |
| 509 | + ), |
| 510 | + 28 => array( |
| 511 | + 'trinary' => array( 1, 17 ), |
| 512 | + 'semicolon' => array( 1, 17 ), |
| 513 | + 'rightbrace' => array( 1, 17 ), |
| 514 | + 'colon' => array( 1, 17 ), |
| 515 | + 'logicop' => array( 1, 17 ), |
| 516 | + 'comareop' => array( 1, 17 ), |
| 517 | + 'rightsquare' => array( 1, 17 ), |
| 518 | + 'comma' => array( 1, 17 ), |
| 519 | + ), |
| 520 | + 29 => array( |
| 521 | + 'equalsto' => array( 0, 63 ), |
| 522 | + 'trinary' => array( 1, 19 ), |
| 523 | + 'semicolon' => array( 1, 19 ), |
| 524 | + 'rightbrace' => array( 1, 19 ), |
| 525 | + 'colon' => array( 1, 19 ), |
| 526 | + 'logicop' => array( 1, 19 ), |
| 527 | + 'comareop' => array( 1, 19 ), |
| 528 | + 'rightsquare' => array( 1, 19 ), |
| 529 | + 'comma' => array( 1, 19 ), |
| 530 | + 'sum' => array( 0, 64 ), |
| 531 | + ), |
| 532 | + 30 => array( |
| 533 | + 'equalsto' => array( 1, 21 ), |
| 534 | + 'trinary' => array( 1, 21 ), |
| 535 | + 'semicolon' => array( 1, 21 ), |
| 536 | + 'rightbrace' => array( 1, 21 ), |
| 537 | + 'colon' => array( 1, 21 ), |
| 538 | + 'logicop' => array( 1, 21 ), |
| 539 | + 'comareop' => array( 1, 21 ), |
| 540 | + 'sum' => array( 1, 21 ), |
| 541 | + 'rightsquare' => array( 1, 21 ), |
| 542 | + 'comma' => array( 1, 21 ), |
| 543 | + 'mul' => array( 0, 65 ), |
| 544 | + ), |
| 545 | + 31 => array( |
| 546 | + 'equalsto' => array( 1, 23 ), |
| 547 | + 'trinary' => array( 1, 23 ), |
| 548 | + 'semicolon' => array( 1, 23 ), |
| 549 | + 'rightbrace' => array( 1, 23 ), |
| 550 | + 'colon' => array( 1, 23 ), |
| 551 | + 'logicop' => array( 1, 23 ), |
| 552 | + 'comareop' => array( 1, 23 ), |
| 553 | + 'sum' => array( 1, 23 ), |
| 554 | + 'mul' => array( 1, 23 ), |
| 555 | + 'rightsquare' => array( 1, 23 ), |
| 556 | + 'comma' => array( 1, 23 ), |
| 557 | + ), |
| 558 | + 32 => array( |
| 559 | + 'pow' => array( 0, 66 ), |
| 560 | + 'equalsto' => array( 1, 25 ), |
| 561 | + 'trinary' => array( 1, 25 ), |
| 562 | + 'semicolon' => array( 1, 25 ), |
| 563 | + 'rightbrace' => array( 1, 25 ), |
| 564 | + 'colon' => array( 1, 25 ), |
| 565 | + 'logicop' => array( 1, 25 ), |
| 566 | + 'comareop' => array( 1, 25 ), |
| 567 | + 'sum' => array( 1, 25 ), |
| 568 | + 'mul' => array( 1, 25 ), |
| 569 | + 'rightsquare' => array( 1, 25 ), |
| 570 | + 'comma' => array( 1, 25 ), |
| 571 | + ), |
| 572 | + 33 => array( |
| 573 | + 'pow' => array( 1, 27 ), |
| 574 | + 'equalsto' => array( 1, 27 ), |
| 575 | + 'trinary' => array( 1, 27 ), |
| 576 | + 'semicolon' => array( 1, 27 ), |
| 577 | + 'rightbrace' => array( 1, 27 ), |
| 578 | + 'colon' => array( 1, 27 ), |
| 579 | + 'logicop' => array( 1, 27 ), |
| 580 | + 'comareop' => array( 1, 27 ), |
| 581 | + 'sum' => array( 1, 27 ), |
| 582 | + 'mul' => array( 1, 27 ), |
| 583 | + 'rightsquare' => array( 1, 27 ), |
| 584 | + 'comma' => array( 1, 27 ), |
| 585 | + ), |
| 586 | + 34 => array( |
| 587 | + 'in' => array( 0, 67 ), |
| 588 | + 'contains' => array( 0, 68 ), |
| 589 | + 'pow' => array( 1, 30 ), |
| 590 | + 'equalsto' => array( 1, 30 ), |
| 591 | + 'trinary' => array( 1, 30 ), |
| 592 | + 'semicolon' => array( 1, 30 ), |
| 593 | + 'rightbrace' => array( 1, 30 ), |
| 594 | + 'colon' => array( 1, 30 ), |
| 595 | + 'logicop' => array( 1, 30 ), |
| 596 | + 'comareop' => array( 1, 30 ), |
| 597 | + 'sum' => array( 1, 30 ), |
| 598 | + 'mul' => array( 1, 30 ), |
| 599 | + 'rightsquare' => array( 1, 30 ), |
| 600 | + 'comma' => array( 1, 30 ), |
| 601 | + ), |
| 602 | + 35 => array( |
| 603 | + 'in' => array( 1, 32 ), |
| 604 | + 'pow' => array( 1, 32 ), |
| 605 | + 'equalsto' => array( 1, 32 ), |
| 606 | + 'trinary' => array( 1, 32 ), |
| 607 | + 'semicolon' => array( 1, 32 ), |
| 608 | + 'rightbrace' => array( 1, 32 ), |
| 609 | + 'colon' => array( 1, 32 ), |
| 610 | + 'logicop' => array( 1, 32 ), |
| 611 | + 'comareop' => array( 1, 32 ), |
| 612 | + 'sum' => array( 1, 32 ), |
| 613 | + 'mul' => array( 1, 32 ), |
| 614 | + 'contains' => array( 1, 32 ), |
| 615 | + 'rightsquare' => array( 1, 32 ), |
| 616 | + 'comma' => array( 1, 32 ), |
| 617 | + ), |
| 618 | + 36 => array( |
| 619 | + 'leftbrace' => array( 0, 69 ), |
| 620 | + ), |
| 621 | + 37 => array( |
| 622 | + 'in' => array( 1, 36 ), |
| 623 | + 'pow' => array( 1, 36 ), |
| 624 | + 'equalsto' => array( 1, 36 ), |
| 625 | + 'trinary' => array( 1, 36 ), |
| 626 | + 'semicolon' => array( 1, 36 ), |
| 627 | + 'rightbrace' => array( 1, 36 ), |
| 628 | + 'colon' => array( 1, 36 ), |
| 629 | + 'logicop' => array( 1, 36 ), |
| 630 | + 'comareop' => array( 1, 36 ), |
| 631 | + 'sum' => array( 1, 36 ), |
| 632 | + 'mul' => array( 1, 36 ), |
| 633 | + 'contains' => array( 1, 36 ), |
| 634 | + 'rightsquare' => array( 1, 36 ), |
| 635 | + 'comma' => array( 1, 36 ), |
| 636 | + ), |
| 637 | + 38 => array( |
| 638 | + 'in' => array( 1, 38 ), |
| 639 | + 'pow' => array( 1, 38 ), |
| 640 | + 'equalsto' => array( 1, 38 ), |
| 641 | + 'trinary' => array( 1, 38 ), |
| 642 | + 'semicolon' => array( 1, 38 ), |
| 643 | + 'rightbrace' => array( 1, 38 ), |
| 644 | + 'colon' => array( 1, 38 ), |
| 645 | + 'logicop' => array( 1, 38 ), |
| 646 | + 'comareop' => array( 1, 38 ), |
| 647 | + 'sum' => array( 1, 38 ), |
| 648 | + 'mul' => array( 1, 38 ), |
| 649 | + 'contains' => array( 1, 38 ), |
| 650 | + 'rightsquare' => array( 1, 38 ), |
| 651 | + 'comma' => array( 1, 38 ), |
| 652 | + ), |
| 653 | + 39 => array( |
| 654 | + 'id' => array( 0, 8 ), |
| 655 | + 'invert' => array( 0, 7 ), |
| 656 | + 'sum' => array( 0, 6 ), |
| 657 | + 'isset' => array( 0, 12 ), |
| 658 | + 'unset' => array( 0, 13 ), |
| 659 | + 'break' => array( 0, 9 ), |
| 660 | + 'continue' => array( 0, 10 ), |
| 661 | + 'leftbrace' => array( 0, 2 ), |
| 662 | + 'leftsquare' => array( 0, 11 ), |
| 663 | + 'string' => array( 0, 14 ), |
| 664 | + 'int' => array( 0, 15 ), |
| 665 | + 'float' => array( 0, 16 ), |
| 666 | + 'true' => array( 0, 17 ), |
| 667 | + 'false' => array( 0, 18 ), |
| 668 | + 'null' => array( 0, 19 ), |
| 669 | + ), |
| 670 | + 40 => array( |
| 671 | + 'rightbrace' => array( 0, 71 ), |
| 672 | + ), |
| 673 | + 41 => array( |
| 674 | + 'id' => array( 0, 72 ), |
| 675 | + ), |
| 676 | + 42 => array( |
| 677 | + 'catch' => array( 0, 74 ), |
| 678 | + ), |
| 679 | + 43 => array( |
| 680 | + 'rightcurly' => array( 0, 75 ), |
| 681 | + 'if' => array( 0, 1 ), |
| 682 | + 'foreach' => array( 0, 3 ), |
| 683 | + 'try' => array( 0, 4 ), |
| 684 | + 'leftcurly' => array( 0, 5 ), |
| 685 | + 'id' => array( 0, 8 ), |
| 686 | + 'invert' => array( 0, 7 ), |
| 687 | + 'sum' => array( 0, 6 ), |
| 688 | + 'isset' => array( 0, 12 ), |
| 689 | + 'unset' => array( 0, 13 ), |
| 690 | + 'break' => array( 0, 9 ), |
| 691 | + 'continue' => array( 0, 10 ), |
| 692 | + 'leftbrace' => array( 0, 2 ), |
| 693 | + 'leftsquare' => array( 0, 11 ), |
| 694 | + 'string' => array( 0, 14 ), |
| 695 | + 'int' => array( 0, 15 ), |
| 696 | + 'float' => array( 0, 16 ), |
| 697 | + 'true' => array( 0, 17 ), |
| 698 | + 'false' => array( 0, 18 ), |
| 699 | + 'null' => array( 0, 19 ), |
| 700 | + ), |
| 701 | + 44 => array( |
| 702 | + 'leftbrace' => array( 0, 48 ), |
| 703 | + 'in' => array( 1, 59 ), |
| 704 | + 'rightbrace' => array( 1, 59 ), |
| 705 | + 'setto' => array( 1, 59 ), |
| 706 | + 'pow' => array( 1, 59 ), |
| 707 | + 'equalsto' => array( 1, 59 ), |
| 708 | + 'trinary' => array( 1, 59 ), |
| 709 | + 'semicolon' => array( 1, 59 ), |
| 710 | + 'colon' => array( 1, 59 ), |
| 711 | + 'logicop' => array( 1, 59 ), |
| 712 | + 'comareop' => array( 1, 59 ), |
| 713 | + 'sum' => array( 1, 59 ), |
| 714 | + 'mul' => array( 1, 59 ), |
| 715 | + 'contains' => array( 1, 59 ), |
| 716 | + 'rightsquare' => array( 1, 59 ), |
| 717 | + 'comma' => array( 1, 59 ), |
| 718 | + 'leftsquare' => array( 0, 49 ), |
| 719 | + ), |
| 720 | + 45 => array( |
| 721 | + 'in' => array( 1, 37 ), |
| 722 | + 'pow' => array( 1, 37 ), |
| 723 | + 'equalsto' => array( 1, 37 ), |
| 724 | + 'trinary' => array( 1, 37 ), |
| 725 | + 'semicolon' => array( 1, 37 ), |
| 726 | + 'rightbrace' => array( 1, 37 ), |
| 727 | + 'colon' => array( 1, 37 ), |
| 728 | + 'logicop' => array( 1, 37 ), |
| 729 | + 'comareop' => array( 1, 37 ), |
| 730 | + 'sum' => array( 1, 37 ), |
| 731 | + 'mul' => array( 1, 37 ), |
| 732 | + 'contains' => array( 1, 37 ), |
| 733 | + 'rightsquare' => array( 1, 37 ), |
| 734 | + 'comma' => array( 1, 37 ), |
| 735 | + ), |
| 736 | + 46 => array( |
| 737 | + 'in' => array( 1, 31 ), |
| 738 | + 'pow' => array( 1, 31 ), |
| 739 | + 'equalsto' => array( 1, 31 ), |
| 740 | + 'trinary' => array( 1, 31 ), |
| 741 | + 'semicolon' => array( 1, 31 ), |
| 742 | + 'rightbrace' => array( 1, 31 ), |
| 743 | + 'colon' => array( 1, 31 ), |
| 744 | + 'logicop' => array( 1, 31 ), |
| 745 | + 'comareop' => array( 1, 31 ), |
| 746 | + 'sum' => array( 1, 31 ), |
| 747 | + 'mul' => array( 1, 31 ), |
| 748 | + 'contains' => array( 1, 31 ), |
| 749 | + 'rightsquare' => array( 1, 31 ), |
| 750 | + 'comma' => array( 1, 31 ), |
| 751 | + ), |
| 752 | + 47 => array( |
| 753 | + 'pow' => array( 1, 26 ), |
| 754 | + 'equalsto' => array( 1, 26 ), |
| 755 | + 'trinary' => array( 1, 26 ), |
| 756 | + 'semicolon' => array( 1, 26 ), |
| 757 | + 'rightbrace' => array( 1, 26 ), |
| 758 | + 'colon' => array( 1, 26 ), |
| 759 | + 'logicop' => array( 1, 26 ), |
| 760 | + 'comareop' => array( 1, 26 ), |
| 761 | + 'sum' => array( 1, 26 ), |
| 762 | + 'mul' => array( 1, 26 ), |
| 763 | + 'rightsquare' => array( 1, 26 ), |
| 764 | + 'comma' => array( 1, 26 ), |
| 765 | + ), |
| 766 | + 48 => array( |
| 767 | + 'rightbrace' => array( 0, 76 ), |
| 768 | + 'id' => array( 0, 8 ), |
| 769 | + 'invert' => array( 0, 7 ), |
| 770 | + 'sum' => array( 0, 6 ), |
| 771 | + 'isset' => array( 0, 12 ), |
| 772 | + 'unset' => array( 0, 13 ), |
| 773 | + 'break' => array( 0, 9 ), |
| 774 | + 'continue' => array( 0, 10 ), |
| 775 | + 'leftbrace' => array( 0, 2 ), |
| 776 | + 'leftsquare' => array( 0, 11 ), |
| 777 | + 'string' => array( 0, 14 ), |
| 778 | + 'int' => array( 0, 15 ), |
| 779 | + 'float' => array( 0, 16 ), |
| 780 | + 'true' => array( 0, 17 ), |
| 781 | + 'false' => array( 0, 18 ), |
| 782 | + 'null' => array( 0, 19 ), |
| 783 | + ), |
| 784 | + 49 => array( |
| 785 | + 'rightsquare' => array( 0, 79 ), |
| 786 | + 'id' => array( 0, 8 ), |
| 787 | + 'invert' => array( 0, 7 ), |
| 788 | + 'sum' => array( 0, 6 ), |
| 789 | + 'isset' => array( 0, 12 ), |
| 790 | + 'unset' => array( 0, 13 ), |
| 791 | + 'break' => array( 0, 9 ), |
| 792 | + 'continue' => array( 0, 10 ), |
| 793 | + 'leftbrace' => array( 0, 2 ), |
| 794 | + 'leftsquare' => array( 0, 11 ), |
| 795 | + 'string' => array( 0, 14 ), |
| 796 | + 'int' => array( 0, 15 ), |
| 797 | + 'float' => array( 0, 16 ), |
| 798 | + 'true' => array( 0, 17 ), |
| 799 | + 'false' => array( 0, 18 ), |
| 800 | + 'null' => array( 0, 19 ), |
| 801 | + ), |
| 802 | + 50 => array( |
| 803 | + 'in' => array( 1, 58 ), |
| 804 | + 'rightbrace' => array( 1, 58 ), |
| 805 | + 'setto' => array( 1, 58 ), |
| 806 | + 'pow' => array( 1, 58 ), |
| 807 | + 'equalsto' => array( 1, 58 ), |
| 808 | + 'trinary' => array( 1, 58 ), |
| 809 | + 'semicolon' => array( 1, 58 ), |
| 810 | + 'colon' => array( 1, 58 ), |
| 811 | + 'logicop' => array( 1, 58 ), |
| 812 | + 'comareop' => array( 1, 58 ), |
| 813 | + 'sum' => array( 1, 58 ), |
| 814 | + 'mul' => array( 1, 58 ), |
| 815 | + 'contains' => array( 1, 58 ), |
| 816 | + 'rightsquare' => array( 1, 58 ), |
| 817 | + 'comma' => array( 1, 58 ), |
| 818 | + ), |
| 819 | + 51 => array( |
| 820 | + 'in' => array( 1, 61 ), |
| 821 | + 'rightbrace' => array( 1, 61 ), |
| 822 | + 'setto' => array( 1, 61 ), |
| 823 | + 'pow' => array( 1, 61 ), |
| 824 | + 'equalsto' => array( 1, 61 ), |
| 825 | + 'trinary' => array( 1, 61 ), |
| 826 | + 'semicolon' => array( 1, 61 ), |
| 827 | + 'colon' => array( 1, 61 ), |
| 828 | + 'logicop' => array( 1, 61 ), |
| 829 | + 'comareop' => array( 1, 61 ), |
| 830 | + 'sum' => array( 1, 61 ), |
| 831 | + 'mul' => array( 1, 61 ), |
| 832 | + 'contains' => array( 1, 61 ), |
| 833 | + 'rightsquare' => array( 1, 61 ), |
| 834 | + 'comma' => array( 1, 61 ), |
| 835 | + 'leftsquare' => array( 0, 49 ), |
| 836 | + ), |
| 837 | + 52 => array( |
| 838 | + 'rightbrace' => array( 1, 48 ), |
| 839 | + 'rightsquare' => array( 1, 48 ), |
| 840 | + 'comma' => array( 1, 48 ), |
| 841 | + 'colon' => array( 0, 82 ), |
| 842 | + ), |
| 843 | + 53 => array( |
| 844 | + 'rightsquare' => array( 1, 46 ), |
| 845 | + 'comma' => array( 0, 83 ), |
| 846 | + ), |
| 847 | + 54 => array( |
| 848 | + 'rightsquare' => array( 0, 84 ), |
| 849 | + ), |
| 850 | + 55 => array( |
| 851 | + 'rightsquare' => array( 1, 45 ), |
| 852 | + 'comma' => array( 0, 85 ), |
| 853 | + ), |
| 854 | + 56 => array( |
| 855 | + 'rightsquare' => array( 1, 50 ), |
| 856 | + 'comma' => array( 1, 50 ), |
| 857 | + ), |
| 858 | + 57 => array( |
| 859 | + '$' => array( 1, 1 ), |
| 860 | + 'if' => array( 1, 1 ), |
| 861 | + 'foreach' => array( 1, 1 ), |
| 862 | + 'try' => array( 1, 1 ), |
| 863 | + 'leftcurly' => array( 1, 1 ), |
| 864 | + 'id' => array( 1, 1 ), |
| 865 | + 'invert' => array( 1, 1 ), |
| 866 | + 'sum' => array( 1, 1 ), |
| 867 | + 'isset' => array( 1, 1 ), |
| 868 | + 'unset' => array( 1, 1 ), |
| 869 | + 'break' => array( 1, 1 ), |
| 870 | + 'continue' => array( 1, 1 ), |
| 871 | + 'leftbrace' => array( 1, 1 ), |
| 872 | + 'leftsquare' => array( 1, 1 ), |
| 873 | + 'string' => array( 1, 1 ), |
| 874 | + 'int' => array( 1, 1 ), |
| 875 | + 'float' => array( 1, 1 ), |
| 876 | + 'true' => array( 1, 1 ), |
| 877 | + 'false' => array( 1, 1 ), |
| 878 | + 'null' => array( 1, 1 ), |
| 879 | + 'rightcurly' => array( 1, 1 ), |
| 880 | + ), |
| 881 | + 58 => array( |
| 882 | + '$' => array( 1, 3 ), |
| 883 | + 'if' => array( 1, 3 ), |
| 884 | + 'foreach' => array( 1, 3 ), |
| 885 | + 'try' => array( 1, 3 ), |
| 886 | + 'leftcurly' => array( 1, 3 ), |
| 887 | + 'id' => array( 1, 3 ), |
| 888 | + 'invert' => array( 1, 3 ), |
| 889 | + 'sum' => array( 1, 3 ), |
| 890 | + 'isset' => array( 1, 3 ), |
| 891 | + 'unset' => array( 1, 3 ), |
| 892 | + 'break' => array( 1, 3 ), |
| 893 | + 'continue' => array( 1, 3 ), |
| 894 | + 'leftbrace' => array( 1, 3 ), |
| 895 | + 'leftsquare' => array( 1, 3 ), |
| 896 | + 'string' => array( 1, 3 ), |
| 897 | + 'int' => array( 1, 3 ), |
| 898 | + 'float' => array( 1, 3 ), |
| 899 | + 'true' => array( 1, 3 ), |
| 900 | + 'false' => array( 1, 3 ), |
| 901 | + 'null' => array( 1, 3 ), |
| 902 | + 'else' => array( 1, 3 ), |
| 903 | + 'catch' => array( 1, 3 ), |
| 904 | + 'rightcurly' => array( 1, 3 ), |
| 905 | + ), |
| 906 | + 59 => array( |
| 907 | + 'id' => array( 0, 8 ), |
| 908 | + 'invert' => array( 0, 7 ), |
| 909 | + 'sum' => array( 0, 6 ), |
| 910 | + 'isset' => array( 0, 12 ), |
| 911 | + 'unset' => array( 0, 13 ), |
| 912 | + 'break' => array( 0, 9 ), |
| 913 | + 'continue' => array( 0, 10 ), |
| 914 | + 'leftbrace' => array( 0, 2 ), |
| 915 | + 'leftsquare' => array( 0, 11 ), |
| 916 | + 'string' => array( 0, 14 ), |
| 917 | + 'int' => array( 0, 15 ), |
| 918 | + 'float' => array( 0, 16 ), |
| 919 | + 'true' => array( 0, 17 ), |
| 920 | + 'false' => array( 0, 18 ), |
| 921 | + 'null' => array( 0, 19 ), |
| 922 | + ), |
| 923 | + 60 => array( |
| 924 | + 'invert' => array( 0, 7 ), |
| 925 | + 'sum' => array( 0, 6 ), |
| 926 | + 'id' => array( 0, 44 ), |
| 927 | + 'isset' => array( 0, 12 ), |
| 928 | + 'unset' => array( 0, 13 ), |
| 929 | + 'break' => array( 0, 9 ), |
| 930 | + 'continue' => array( 0, 10 ), |
| 931 | + 'leftbrace' => array( 0, 2 ), |
| 932 | + 'leftsquare' => array( 0, 11 ), |
| 933 | + 'string' => array( 0, 14 ), |
| 934 | + 'int' => array( 0, 15 ), |
| 935 | + 'float' => array( 0, 16 ), |
| 936 | + 'true' => array( 0, 17 ), |
| 937 | + 'false' => array( 0, 18 ), |
| 938 | + 'null' => array( 0, 19 ), |
| 939 | + ), |
| 940 | + 61 => array( |
| 941 | + 'invert' => array( 0, 7 ), |
| 942 | + 'sum' => array( 0, 6 ), |
| 943 | + 'id' => array( 0, 44 ), |
| 944 | + 'isset' => array( 0, 12 ), |
| 945 | + 'unset' => array( 0, 13 ), |
| 946 | + 'break' => array( 0, 9 ), |
| 947 | + 'continue' => array( 0, 10 ), |
| 948 | + 'leftbrace' => array( 0, 2 ), |
| 949 | + 'leftsquare' => array( 0, 11 ), |
| 950 | + 'string' => array( 0, 14 ), |
| 951 | + 'int' => array( 0, 15 ), |
| 952 | + 'float' => array( 0, 16 ), |
| 953 | + 'true' => array( 0, 17 ), |
| 954 | + 'false' => array( 0, 18 ), |
| 955 | + 'null' => array( 0, 19 ), |
| 956 | + ), |
| 957 | + 62 => array( |
| 958 | + 'invert' => array( 0, 7 ), |
| 959 | + 'sum' => array( 0, 6 ), |
| 960 | + 'id' => array( 0, 44 ), |
| 961 | + 'isset' => array( 0, 12 ), |
| 962 | + 'unset' => array( 0, 13 ), |
| 963 | + 'break' => array( 0, 9 ), |
| 964 | + 'continue' => array( 0, 10 ), |
| 965 | + 'leftbrace' => array( 0, 2 ), |
| 966 | + 'leftsquare' => array( 0, 11 ), |
| 967 | + 'string' => array( 0, 14 ), |
| 968 | + 'int' => array( 0, 15 ), |
| 969 | + 'float' => array( 0, 16 ), |
| 970 | + 'true' => array( 0, 17 ), |
| 971 | + 'false' => array( 0, 18 ), |
| 972 | + 'null' => array( 0, 19 ), |
| 973 | + ), |
| 974 | + 63 => array( |
| 975 | + 'invert' => array( 0, 7 ), |
| 976 | + 'sum' => array( 0, 6 ), |
| 977 | + 'id' => array( 0, 44 ), |
| 978 | + 'isset' => array( 0, 12 ), |
| 979 | + 'unset' => array( 0, 13 ), |
| 980 | + 'break' => array( 0, 9 ), |
| 981 | + 'continue' => array( 0, 10 ), |
| 982 | + 'leftbrace' => array( 0, 2 ), |
| 983 | + 'leftsquare' => array( 0, 11 ), |
| 984 | + 'string' => array( 0, 14 ), |
| 985 | + 'int' => array( 0, 15 ), |
| 986 | + 'float' => array( 0, 16 ), |
| 987 | + 'true' => array( 0, 17 ), |
| 988 | + 'false' => array( 0, 18 ), |
| 989 | + 'null' => array( 0, 19 ), |
| 990 | + ), |
| 991 | + 64 => array( |
| 992 | + 'invert' => array( 0, 7 ), |
| 993 | + 'sum' => array( 0, 6 ), |
| 994 | + 'id' => array( 0, 44 ), |
| 995 | + 'isset' => array( 0, 12 ), |
| 996 | + 'unset' => array( 0, 13 ), |
| 997 | + 'break' => array( 0, 9 ), |
| 998 | + 'continue' => array( 0, 10 ), |
| 999 | + 'leftbrace' => array( 0, 2 ), |
| 1000 | + 'leftsquare' => array( 0, 11 ), |
| 1001 | + 'string' => array( 0, 14 ), |
| 1002 | + 'int' => array( 0, 15 ), |
| 1003 | + 'float' => array( 0, 16 ), |
| 1004 | + 'true' => array( 0, 17 ), |
| 1005 | + 'false' => array( 0, 18 ), |
| 1006 | + 'null' => array( 0, 19 ), |
| 1007 | + ), |
| 1008 | + 65 => array( |
| 1009 | + 'invert' => array( 0, 7 ), |
| 1010 | + 'sum' => array( 0, 6 ), |
| 1011 | + 'id' => array( 0, 44 ), |
| 1012 | + 'isset' => array( 0, 12 ), |
| 1013 | + 'unset' => array( 0, 13 ), |
| 1014 | + 'break' => array( 0, 9 ), |
| 1015 | + 'continue' => array( 0, 10 ), |
| 1016 | + 'leftbrace' => array( 0, 2 ), |
| 1017 | + 'leftsquare' => array( 0, 11 ), |
| 1018 | + 'string' => array( 0, 14 ), |
| 1019 | + 'int' => array( 0, 15 ), |
| 1020 | + 'float' => array( 0, 16 ), |
| 1021 | + 'true' => array( 0, 17 ), |
| 1022 | + 'false' => array( 0, 18 ), |
| 1023 | + 'null' => array( 0, 19 ), |
| 1024 | + ), |
| 1025 | + 66 => array( |
| 1026 | + 'invert' => array( 0, 7 ), |
| 1027 | + 'sum' => array( 0, 6 ), |
| 1028 | + 'id' => array( 0, 44 ), |
| 1029 | + 'isset' => array( 0, 12 ), |
| 1030 | + 'unset' => array( 0, 13 ), |
| 1031 | + 'break' => array( 0, 9 ), |
| 1032 | + 'continue' => array( 0, 10 ), |
| 1033 | + 'leftbrace' => array( 0, 2 ), |
| 1034 | + 'leftsquare' => array( 0, 11 ), |
| 1035 | + 'string' => array( 0, 14 ), |
| 1036 | + 'int' => array( 0, 15 ), |
| 1037 | + 'float' => array( 0, 16 ), |
| 1038 | + 'true' => array( 0, 17 ), |
| 1039 | + 'false' => array( 0, 18 ), |
| 1040 | + 'null' => array( 0, 19 ), |
| 1041 | + ), |
| 1042 | + 67 => array( |
| 1043 | + 'sum' => array( 0, 6 ), |
| 1044 | + 'id' => array( 0, 44 ), |
| 1045 | + 'isset' => array( 0, 12 ), |
| 1046 | + 'unset' => array( 0, 13 ), |
| 1047 | + 'break' => array( 0, 9 ), |
| 1048 | + 'continue' => array( 0, 10 ), |
| 1049 | + 'leftbrace' => array( 0, 2 ), |
| 1050 | + 'leftsquare' => array( 0, 11 ), |
| 1051 | + 'string' => array( 0, 14 ), |
| 1052 | + 'int' => array( 0, 15 ), |
| 1053 | + 'float' => array( 0, 16 ), |
| 1054 | + 'true' => array( 0, 17 ), |
| 1055 | + 'false' => array( 0, 18 ), |
| 1056 | + 'null' => array( 0, 19 ), |
| 1057 | + ), |
| 1058 | + 68 => array( |
| 1059 | + 'sum' => array( 0, 6 ), |
| 1060 | + 'id' => array( 0, 44 ), |
| 1061 | + 'isset' => array( 0, 12 ), |
| 1062 | + 'unset' => array( 0, 13 ), |
| 1063 | + 'break' => array( 0, 9 ), |
| 1064 | + 'continue' => array( 0, 10 ), |
| 1065 | + 'leftbrace' => array( 0, 2 ), |
| 1066 | + 'leftsquare' => array( 0, 11 ), |
| 1067 | + 'string' => array( 0, 14 ), |
| 1068 | + 'int' => array( 0, 15 ), |
| 1069 | + 'float' => array( 0, 16 ), |
| 1070 | + 'true' => array( 0, 17 ), |
| 1071 | + 'false' => array( 0, 18 ), |
| 1072 | + 'null' => array( 0, 19 ), |
| 1073 | + ), |
| 1074 | + 69 => array( |
| 1075 | + 'id' => array( 0, 72 ), |
| 1076 | + ), |
| 1077 | + 70 => array( |
| 1078 | + 'rightbrace' => array( 0, 97 ), |
| 1079 | + ), |
| 1080 | + 71 => array( |
| 1081 | + 'in' => array( 1, 41 ), |
| 1082 | + 'pow' => array( 1, 41 ), |
| 1083 | + 'equalsto' => array( 1, 41 ), |
| 1084 | + 'trinary' => array( 1, 41 ), |
| 1085 | + 'semicolon' => array( 1, 41 ), |
| 1086 | + 'rightbrace' => array( 1, 41 ), |
| 1087 | + 'colon' => array( 1, 41 ), |
| 1088 | + 'logicop' => array( 1, 41 ), |
| 1089 | + 'comareop' => array( 1, 41 ), |
| 1090 | + 'sum' => array( 1, 41 ), |
| 1091 | + 'mul' => array( 1, 41 ), |
| 1092 | + 'contains' => array( 1, 41 ), |
| 1093 | + 'rightsquare' => array( 1, 41 ), |
| 1094 | + 'comma' => array( 1, 41 ), |
| 1095 | + ), |
| 1096 | + 72 => array( |
| 1097 | + 'in' => array( 1, 59 ), |
| 1098 | + 'rightbrace' => array( 1, 59 ), |
| 1099 | + 'setto' => array( 1, 59 ), |
| 1100 | + 'pow' => array( 1, 59 ), |
| 1101 | + 'equalsto' => array( 1, 59 ), |
| 1102 | + 'trinary' => array( 1, 59 ), |
| 1103 | + 'semicolon' => array( 1, 59 ), |
| 1104 | + 'colon' => array( 1, 59 ), |
| 1105 | + 'logicop' => array( 1, 59 ), |
| 1106 | + 'comareop' => array( 1, 59 ), |
| 1107 | + 'sum' => array( 1, 59 ), |
| 1108 | + 'mul' => array( 1, 59 ), |
| 1109 | + 'contains' => array( 1, 59 ), |
| 1110 | + 'rightsquare' => array( 1, 59 ), |
| 1111 | + 'comma' => array( 1, 59 ), |
| 1112 | + 'leftsquare' => array( 0, 49 ), |
| 1113 | + ), |
| 1114 | + 73 => array( |
| 1115 | + 'in' => array( 0, 98 ), |
| 1116 | + ), |
| 1117 | + 74 => array( |
| 1118 | + 'leftbrace' => array( 0, 99 ), |
| 1119 | + ), |
| 1120 | + 75 => array( |
| 1121 | + '$' => array( 1, 8 ), |
| 1122 | + 'if' => array( 1, 8 ), |
| 1123 | + 'foreach' => array( 1, 8 ), |
| 1124 | + 'try' => array( 1, 8 ), |
| 1125 | + 'leftcurly' => array( 1, 8 ), |
| 1126 | + 'id' => array( 1, 8 ), |
| 1127 | + 'invert' => array( 1, 8 ), |
| 1128 | + 'sum' => array( 1, 8 ), |
| 1129 | + 'isset' => array( 1, 8 ), |
| 1130 | + 'unset' => array( 1, 8 ), |
| 1131 | + 'break' => array( 1, 8 ), |
| 1132 | + 'continue' => array( 1, 8 ), |
| 1133 | + 'leftbrace' => array( 1, 8 ), |
| 1134 | + 'leftsquare' => array( 1, 8 ), |
| 1135 | + 'string' => array( 1, 8 ), |
| 1136 | + 'int' => array( 1, 8 ), |
| 1137 | + 'float' => array( 1, 8 ), |
| 1138 | + 'true' => array( 1, 8 ), |
| 1139 | + 'false' => array( 1, 8 ), |
| 1140 | + 'null' => array( 1, 8 ), |
| 1141 | + 'else' => array( 1, 8 ), |
| 1142 | + 'catch' => array( 1, 8 ), |
| 1143 | + 'rightcurly' => array( 1, 8 ), |
| 1144 | + ), |
| 1145 | + 76 => array( |
| 1146 | + 'in' => array( 1, 34 ), |
| 1147 | + 'pow' => array( 1, 34 ), |
| 1148 | + 'equalsto' => array( 1, 34 ), |
| 1149 | + 'trinary' => array( 1, 34 ), |
| 1150 | + 'semicolon' => array( 1, 34 ), |
| 1151 | + 'rightbrace' => array( 1, 34 ), |
| 1152 | + 'colon' => array( 1, 34 ), |
| 1153 | + 'logicop' => array( 1, 34 ), |
| 1154 | + 'comareop' => array( 1, 34 ), |
| 1155 | + 'sum' => array( 1, 34 ), |
| 1156 | + 'mul' => array( 1, 34 ), |
| 1157 | + 'contains' => array( 1, 34 ), |
| 1158 | + 'rightsquare' => array( 1, 34 ), |
| 1159 | + 'comma' => array( 1, 34 ), |
| 1160 | + ), |
| 1161 | + 77 => array( |
| 1162 | + 'rightbrace' => array( 1, 48 ), |
| 1163 | + 'rightsquare' => array( 1, 48 ), |
| 1164 | + 'comma' => array( 1, 48 ), |
| 1165 | + ), |
| 1166 | + 78 => array( |
| 1167 | + 'rightbrace' => array( 0, 100 ), |
| 1168 | + 'comma' => array( 0, 83 ), |
| 1169 | + ), |
| 1170 | + 79 => array( |
| 1171 | + 'leftsquare' => array( 1, 63 ), |
| 1172 | + 'in' => array( 1, 63 ), |
| 1173 | + 'rightbrace' => array( 1, 63 ), |
| 1174 | + 'setto' => array( 1, 63 ), |
| 1175 | + 'pow' => array( 1, 63 ), |
| 1176 | + 'equalsto' => array( 1, 63 ), |
| 1177 | + 'trinary' => array( 1, 63 ), |
| 1178 | + 'semicolon' => array( 1, 63 ), |
| 1179 | + 'colon' => array( 1, 63 ), |
| 1180 | + 'logicop' => array( 1, 63 ), |
| 1181 | + 'comareop' => array( 1, 63 ), |
| 1182 | + 'sum' => array( 1, 63 ), |
| 1183 | + 'mul' => array( 1, 63 ), |
| 1184 | + 'contains' => array( 1, 63 ), |
| 1185 | + 'rightsquare' => array( 1, 63 ), |
| 1186 | + 'comma' => array( 1, 63 ), |
| 1187 | + ), |
| 1188 | + 80 => array( |
| 1189 | + 'rightsquare' => array( 0, 101 ), |
| 1190 | + ), |
| 1191 | + 81 => array( |
| 1192 | + 'in' => array( 1, 60 ), |
| 1193 | + 'rightbrace' => array( 1, 60 ), |
| 1194 | + 'setto' => array( 1, 60 ), |
| 1195 | + 'pow' => array( 1, 60 ), |
| 1196 | + 'equalsto' => array( 1, 60 ), |
| 1197 | + 'trinary' => array( 1, 60 ), |
| 1198 | + 'semicolon' => array( 1, 60 ), |
| 1199 | + 'colon' => array( 1, 60 ), |
| 1200 | + 'logicop' => array( 1, 60 ), |
| 1201 | + 'comareop' => array( 1, 60 ), |
| 1202 | + 'sum' => array( 1, 60 ), |
| 1203 | + 'mul' => array( 1, 60 ), |
| 1204 | + 'contains' => array( 1, 60 ), |
| 1205 | + 'rightsquare' => array( 1, 60 ), |
| 1206 | + 'comma' => array( 1, 60 ), |
| 1207 | + ), |
| 1208 | + 82 => array( |
| 1209 | + 'id' => array( 0, 8 ), |
| 1210 | + 'invert' => array( 0, 7 ), |
| 1211 | + 'sum' => array( 0, 6 ), |
| 1212 | + 'isset' => array( 0, 12 ), |
| 1213 | + 'unset' => array( 0, 13 ), |
| 1214 | + 'break' => array( 0, 9 ), |
| 1215 | + 'continue' => array( 0, 10 ), |
| 1216 | + 'leftbrace' => array( 0, 2 ), |
| 1217 | + 'leftsquare' => array( 0, 11 ), |
| 1218 | + 'string' => array( 0, 14 ), |
| 1219 | + 'int' => array( 0, 15 ), |
| 1220 | + 'float' => array( 0, 16 ), |
| 1221 | + 'true' => array( 0, 17 ), |
| 1222 | + 'false' => array( 0, 18 ), |
| 1223 | + 'null' => array( 0, 19 ), |
| 1224 | + ), |
| 1225 | + 83 => array( |
| 1226 | + 'id' => array( 0, 8 ), |
| 1227 | + 'invert' => array( 0, 7 ), |
| 1228 | + 'sum' => array( 0, 6 ), |
| 1229 | + 'isset' => array( 0, 12 ), |
| 1230 | + 'unset' => array( 0, 13 ), |
| 1231 | + 'break' => array( 0, 9 ), |
| 1232 | + 'continue' => array( 0, 10 ), |
| 1233 | + 'leftbrace' => array( 0, 2 ), |
| 1234 | + 'leftsquare' => array( 0, 11 ), |
| 1235 | + 'string' => array( 0, 14 ), |
| 1236 | + 'int' => array( 0, 15 ), |
| 1237 | + 'float' => array( 0, 16 ), |
| 1238 | + 'true' => array( 0, 17 ), |
| 1239 | + 'false' => array( 0, 18 ), |
| 1240 | + 'null' => array( 0, 19 ), |
| 1241 | + ), |
| 1242 | + 84 => array( |
| 1243 | + 'in' => array( 1, 42 ), |
| 1244 | + 'pow' => array( 1, 42 ), |
| 1245 | + 'equalsto' => array( 1, 42 ), |
| 1246 | + 'trinary' => array( 1, 42 ), |
| 1247 | + 'semicolon' => array( 1, 42 ), |
| 1248 | + 'rightbrace' => array( 1, 42 ), |
| 1249 | + 'colon' => array( 1, 42 ), |
| 1250 | + 'logicop' => array( 1, 42 ), |
| 1251 | + 'comareop' => array( 1, 42 ), |
| 1252 | + 'sum' => array( 1, 42 ), |
| 1253 | + 'mul' => array( 1, 42 ), |
| 1254 | + 'contains' => array( 1, 42 ), |
| 1255 | + 'rightsquare' => array( 1, 42 ), |
| 1256 | + 'comma' => array( 1, 42 ), |
| 1257 | + ), |
| 1258 | + 85 => array( |
| 1259 | + 'id' => array( 0, 8 ), |
| 1260 | + 'invert' => array( 0, 7 ), |
| 1261 | + 'sum' => array( 0, 6 ), |
| 1262 | + 'isset' => array( 0, 12 ), |
| 1263 | + 'unset' => array( 0, 13 ), |
| 1264 | + 'break' => array( 0, 9 ), |
| 1265 | + 'continue' => array( 0, 10 ), |
| 1266 | + 'leftbrace' => array( 0, 2 ), |
| 1267 | + 'leftsquare' => array( 0, 11 ), |
| 1268 | + 'string' => array( 0, 14 ), |
| 1269 | + 'int' => array( 0, 15 ), |
| 1270 | + 'float' => array( 0, 16 ), |
| 1271 | + 'true' => array( 0, 17 ), |
| 1272 | + 'false' => array( 0, 18 ), |
| 1273 | + 'null' => array( 0, 19 ), |
| 1274 | + ), |
| 1275 | + 86 => array( |
| 1276 | + 'semicolon' => array( 1, 10 ), |
| 1277 | + 'rightbrace' => array( 1, 10 ), |
| 1278 | + 'rightsquare' => array( 1, 10 ), |
| 1279 | + 'comma' => array( 1, 10 ), |
| 1280 | + 'colon' => array( 1, 10 ), |
| 1281 | + ), |
| 1282 | + 87 => array( |
| 1283 | + 'colon' => array( 0, 106 ), |
| 1284 | + ), |
| 1285 | + 88 => array( |
| 1286 | + 'trinary' => array( 1, 14 ), |
| 1287 | + 'semicolon' => array( 1, 14 ), |
| 1288 | + 'rightbrace' => array( 1, 14 ), |
| 1289 | + 'colon' => array( 1, 14 ), |
| 1290 | + 'logicop' => array( 1, 14 ), |
| 1291 | + 'rightsquare' => array( 1, 14 ), |
| 1292 | + 'comma' => array( 1, 14 ), |
| 1293 | + 'comareop' => array( 0, 62 ), |
| 1294 | + ), |
| 1295 | + 89 => array( |
| 1296 | + 'trinary' => array( 1, 16 ), |
| 1297 | + 'semicolon' => array( 1, 16 ), |
| 1298 | + 'rightbrace' => array( 1, 16 ), |
| 1299 | + 'colon' => array( 1, 16 ), |
| 1300 | + 'logicop' => array( 1, 16 ), |
| 1301 | + 'comareop' => array( 1, 16 ), |
| 1302 | + 'rightsquare' => array( 1, 16 ), |
| 1303 | + 'comma' => array( 1, 16 ), |
| 1304 | + ), |
| 1305 | + 90 => array( |
| 1306 | + 'trinary' => array( 1, 18 ), |
| 1307 | + 'semicolon' => array( 1, 18 ), |
| 1308 | + 'rightbrace' => array( 1, 18 ), |
| 1309 | + 'colon' => array( 1, 18 ), |
| 1310 | + 'logicop' => array( 1, 18 ), |
| 1311 | + 'comareop' => array( 1, 18 ), |
| 1312 | + 'rightsquare' => array( 1, 18 ), |
| 1313 | + 'comma' => array( 1, 18 ), |
| 1314 | + 'sum' => array( 0, 64 ), |
| 1315 | + ), |
| 1316 | + 91 => array( |
| 1317 | + 'equalsto' => array( 1, 20 ), |
| 1318 | + 'trinary' => array( 1, 20 ), |
| 1319 | + 'semicolon' => array( 1, 20 ), |
| 1320 | + 'rightbrace' => array( 1, 20 ), |
| 1321 | + 'colon' => array( 1, 20 ), |
| 1322 | + 'logicop' => array( 1, 20 ), |
| 1323 | + 'comareop' => array( 1, 20 ), |
| 1324 | + 'sum' => array( 1, 20 ), |
| 1325 | + 'rightsquare' => array( 1, 20 ), |
| 1326 | + 'comma' => array( 1, 20 ), |
| 1327 | + 'mul' => array( 0, 65 ), |
| 1328 | + ), |
| 1329 | + 92 => array( |
| 1330 | + 'equalsto' => array( 1, 22 ), |
| 1331 | + 'trinary' => array( 1, 22 ), |
| 1332 | + 'semicolon' => array( 1, 22 ), |
| 1333 | + 'rightbrace' => array( 1, 22 ), |
| 1334 | + 'colon' => array( 1, 22 ), |
| 1335 | + 'logicop' => array( 1, 22 ), |
| 1336 | + 'comareop' => array( 1, 22 ), |
| 1337 | + 'sum' => array( 1, 22 ), |
| 1338 | + 'mul' => array( 1, 22 ), |
| 1339 | + 'rightsquare' => array( 1, 22 ), |
| 1340 | + 'comma' => array( 1, 22 ), |
| 1341 | + ), |
| 1342 | + 93 => array( |
| 1343 | + 'equalsto' => array( 1, 24 ), |
| 1344 | + 'trinary' => array( 1, 24 ), |
| 1345 | + 'semicolon' => array( 1, 24 ), |
| 1346 | + 'rightbrace' => array( 1, 24 ), |
| 1347 | + 'colon' => array( 1, 24 ), |
| 1348 | + 'logicop' => array( 1, 24 ), |
| 1349 | + 'comareop' => array( 1, 24 ), |
| 1350 | + 'sum' => array( 1, 24 ), |
| 1351 | + 'mul' => array( 1, 24 ), |
| 1352 | + 'rightsquare' => array( 1, 24 ), |
| 1353 | + 'comma' => array( 1, 24 ), |
| 1354 | + ), |
| 1355 | + 94 => array( |
| 1356 | + 'pow' => array( 1, 28 ), |
| 1357 | + 'equalsto' => array( 1, 28 ), |
| 1358 | + 'trinary' => array( 1, 28 ), |
| 1359 | + 'semicolon' => array( 1, 28 ), |
| 1360 | + 'rightbrace' => array( 1, 28 ), |
| 1361 | + 'colon' => array( 1, 28 ), |
| 1362 | + 'logicop' => array( 1, 28 ), |
| 1363 | + 'comareop' => array( 1, 28 ), |
| 1364 | + 'sum' => array( 1, 28 ), |
| 1365 | + 'mul' => array( 1, 28 ), |
| 1366 | + 'rightsquare' => array( 1, 28 ), |
| 1367 | + 'comma' => array( 1, 28 ), |
| 1368 | + ), |
| 1369 | + 95 => array( |
| 1370 | + 'pow' => array( 1, 29 ), |
| 1371 | + 'equalsto' => array( 1, 29 ), |
| 1372 | + 'trinary' => array( 1, 29 ), |
| 1373 | + 'semicolon' => array( 1, 29 ), |
| 1374 | + 'rightbrace' => array( 1, 29 ), |
| 1375 | + 'colon' => array( 1, 29 ), |
| 1376 | + 'logicop' => array( 1, 29 ), |
| 1377 | + 'comareop' => array( 1, 29 ), |
| 1378 | + 'sum' => array( 1, 29 ), |
| 1379 | + 'mul' => array( 1, 29 ), |
| 1380 | + 'rightsquare' => array( 1, 29 ), |
| 1381 | + 'comma' => array( 1, 29 ), |
| 1382 | + ), |
| 1383 | + 96 => array( |
| 1384 | + 'rightbrace' => array( 0, 107 ), |
| 1385 | + ), |
| 1386 | + 97 => array( |
| 1387 | + 'if' => array( 0, 1 ), |
| 1388 | + 'foreach' => array( 0, 3 ), |
| 1389 | + 'try' => array( 0, 4 ), |
| 1390 | + 'leftcurly' => array( 0, 5 ), |
| 1391 | + 'id' => array( 0, 8 ), |
| 1392 | + 'invert' => array( 0, 7 ), |
| 1393 | + 'sum' => array( 0, 6 ), |
| 1394 | + 'isset' => array( 0, 12 ), |
| 1395 | + 'unset' => array( 0, 13 ), |
| 1396 | + 'break' => array( 0, 9 ), |
| 1397 | + 'continue' => array( 0, 10 ), |
| 1398 | + 'leftbrace' => array( 0, 2 ), |
| 1399 | + 'leftsquare' => array( 0, 11 ), |
| 1400 | + 'string' => array( 0, 14 ), |
| 1401 | + 'int' => array( 0, 15 ), |
| 1402 | + 'float' => array( 0, 16 ), |
| 1403 | + 'true' => array( 0, 17 ), |
| 1404 | + 'false' => array( 0, 18 ), |
| 1405 | + 'null' => array( 0, 19 ), |
| 1406 | + ), |
| 1407 | + 98 => array( |
| 1408 | + 'id' => array( 0, 8 ), |
| 1409 | + 'invert' => array( 0, 7 ), |
| 1410 | + 'sum' => array( 0, 6 ), |
| 1411 | + 'isset' => array( 0, 12 ), |
| 1412 | + 'unset' => array( 0, 13 ), |
| 1413 | + 'break' => array( 0, 9 ), |
| 1414 | + 'continue' => array( 0, 10 ), |
| 1415 | + 'leftbrace' => array( 0, 2 ), |
| 1416 | + 'leftsquare' => array( 0, 11 ), |
| 1417 | + 'string' => array( 0, 14 ), |
| 1418 | + 'int' => array( 0, 15 ), |
| 1419 | + 'float' => array( 0, 16 ), |
| 1420 | + 'true' => array( 0, 17 ), |
| 1421 | + 'false' => array( 0, 18 ), |
| 1422 | + 'null' => array( 0, 19 ), |
| 1423 | + ), |
| 1424 | + 99 => array( |
| 1425 | + 'id' => array( 0, 72 ), |
| 1426 | + ), |
| 1427 | + 100 => array( |
| 1428 | + 'in' => array( 1, 33 ), |
| 1429 | + 'pow' => array( 1, 33 ), |
| 1430 | + 'equalsto' => array( 1, 33 ), |
| 1431 | + 'trinary' => array( 1, 33 ), |
| 1432 | + 'semicolon' => array( 1, 33 ), |
| 1433 | + 'rightbrace' => array( 1, 33 ), |
| 1434 | + 'colon' => array( 1, 33 ), |
| 1435 | + 'logicop' => array( 1, 33 ), |
| 1436 | + 'comareop' => array( 1, 33 ), |
| 1437 | + 'sum' => array( 1, 33 ), |
| 1438 | + 'mul' => array( 1, 33 ), |
| 1439 | + 'contains' => array( 1, 33 ), |
| 1440 | + 'rightsquare' => array( 1, 33 ), |
| 1441 | + 'comma' => array( 1, 33 ), |
| 1442 | + ), |
| 1443 | + 101 => array( |
| 1444 | + 'leftsquare' => array( 1, 62 ), |
| 1445 | + 'in' => array( 1, 62 ), |
| 1446 | + 'rightbrace' => array( 1, 62 ), |
| 1447 | + 'setto' => array( 1, 62 ), |
| 1448 | + 'pow' => array( 1, 62 ), |
| 1449 | + 'equalsto' => array( 1, 62 ), |
| 1450 | + 'trinary' => array( 1, 62 ), |
| 1451 | + 'semicolon' => array( 1, 62 ), |
| 1452 | + 'colon' => array( 1, 62 ), |
| 1453 | + 'logicop' => array( 1, 62 ), |
| 1454 | + 'comareop' => array( 1, 62 ), |
| 1455 | + 'sum' => array( 1, 62 ), |
| 1456 | + 'mul' => array( 1, 62 ), |
| 1457 | + 'contains' => array( 1, 62 ), |
| 1458 | + 'rightsquare' => array( 1, 62 ), |
| 1459 | + 'comma' => array( 1, 62 ), |
| 1460 | + ), |
| 1461 | + 102 => array( |
| 1462 | + 'rightsquare' => array( 1, 51 ), |
| 1463 | + 'comma' => array( 1, 51 ), |
| 1464 | + ), |
| 1465 | + 103 => array( |
| 1466 | + 'rightbrace' => array( 1, 47 ), |
| 1467 | + 'rightsquare' => array( 1, 47 ), |
| 1468 | + 'comma' => array( 1, 47 ), |
| 1469 | + ), |
| 1470 | + 104 => array( |
| 1471 | + 'colon' => array( 0, 82 ), |
| 1472 | + ), |
| 1473 | + 105 => array( |
| 1474 | + 'rightsquare' => array( 1, 49 ), |
| 1475 | + 'comma' => array( 1, 49 ), |
| 1476 | + ), |
| 1477 | + 106 => array( |
| 1478 | + 'invert' => array( 0, 7 ), |
| 1479 | + 'sum' => array( 0, 6 ), |
| 1480 | + 'id' => array( 0, 44 ), |
| 1481 | + 'isset' => array( 0, 12 ), |
| 1482 | + 'unset' => array( 0, 13 ), |
| 1483 | + 'break' => array( 0, 9 ), |
| 1484 | + 'continue' => array( 0, 10 ), |
| 1485 | + 'leftbrace' => array( 0, 2 ), |
| 1486 | + 'leftsquare' => array( 0, 11 ), |
| 1487 | + 'string' => array( 0, 14 ), |
| 1488 | + 'int' => array( 0, 15 ), |
| 1489 | + 'float' => array( 0, 16 ), |
| 1490 | + 'true' => array( 0, 17 ), |
| 1491 | + 'false' => array( 0, 18 ), |
| 1492 | + 'null' => array( 0, 19 ), |
| 1493 | + ), |
| 1494 | + 107 => array( |
| 1495 | + 'in' => array( 1, 35 ), |
| 1496 | + 'pow' => array( 1, 35 ), |
| 1497 | + 'equalsto' => array( 1, 35 ), |
| 1498 | + 'trinary' => array( 1, 35 ), |
| 1499 | + 'semicolon' => array( 1, 35 ), |
| 1500 | + 'rightbrace' => array( 1, 35 ), |
| 1501 | + 'colon' => array( 1, 35 ), |
| 1502 | + 'logicop' => array( 1, 35 ), |
| 1503 | + 'comareop' => array( 1, 35 ), |
| 1504 | + 'sum' => array( 1, 35 ), |
| 1505 | + 'mul' => array( 1, 35 ), |
| 1506 | + 'contains' => array( 1, 35 ), |
| 1507 | + 'rightsquare' => array( 1, 35 ), |
| 1508 | + 'comma' => array( 1, 35 ), |
| 1509 | + ), |
| 1510 | + 108 => array( |
| 1511 | + '$' => array( 1, 4 ), |
| 1512 | + 'if' => array( 1, 4 ), |
| 1513 | + 'foreach' => array( 1, 4 ), |
| 1514 | + 'try' => array( 1, 4 ), |
| 1515 | + 'leftcurly' => array( 1, 4 ), |
| 1516 | + 'id' => array( 1, 4 ), |
| 1517 | + 'invert' => array( 1, 4 ), |
| 1518 | + 'sum' => array( 1, 4 ), |
| 1519 | + 'isset' => array( 1, 4 ), |
| 1520 | + 'unset' => array( 1, 4 ), |
| 1521 | + 'break' => array( 1, 4 ), |
| 1522 | + 'continue' => array( 1, 4 ), |
| 1523 | + 'leftbrace' => array( 1, 4 ), |
| 1524 | + 'leftsquare' => array( 1, 4 ), |
| 1525 | + 'string' => array( 1, 4 ), |
| 1526 | + 'int' => array( 1, 4 ), |
| 1527 | + 'float' => array( 1, 4 ), |
| 1528 | + 'true' => array( 1, 4 ), |
| 1529 | + 'false' => array( 1, 4 ), |
| 1530 | + 'null' => array( 1, 4 ), |
| 1531 | + 'else' => array( 0, 112 ), |
| 1532 | + 'catch' => array( 1, 4 ), |
| 1533 | + 'rightcurly' => array( 1, 4 ), |
| 1534 | + ), |
| 1535 | + 109 => array( |
| 1536 | + 'rightbrace' => array( 0, 113 ), |
| 1537 | + ), |
| 1538 | + 110 => array( |
| 1539 | + 'rightbrace' => array( 0, 114 ), |
| 1540 | + ), |
| 1541 | + 111 => array( |
| 1542 | + 'semicolon' => array( 1, 12 ), |
| 1543 | + 'rightbrace' => array( 1, 12 ), |
| 1544 | + 'colon' => array( 1, 12 ), |
| 1545 | + 'rightsquare' => array( 1, 12 ), |
| 1546 | + 'comma' => array( 1, 12 ), |
| 1547 | + ), |
| 1548 | + 112 => array( |
| 1549 | + 'if' => array( 0, 1 ), |
| 1550 | + 'foreach' => array( 0, 3 ), |
| 1551 | + 'try' => array( 0, 4 ), |
| 1552 | + 'leftcurly' => array( 0, 5 ), |
| 1553 | + 'id' => array( 0, 8 ), |
| 1554 | + 'invert' => array( 0, 7 ), |
| 1555 | + 'sum' => array( 0, 6 ), |
| 1556 | + 'isset' => array( 0, 12 ), |
| 1557 | + 'unset' => array( 0, 13 ), |
| 1558 | + 'break' => array( 0, 9 ), |
| 1559 | + 'continue' => array( 0, 10 ), |
| 1560 | + 'leftbrace' => array( 0, 2 ), |
| 1561 | + 'leftsquare' => array( 0, 11 ), |
| 1562 | + 'string' => array( 0, 14 ), |
| 1563 | + 'int' => array( 0, 15 ), |
| 1564 | + 'float' => array( 0, 16 ), |
| 1565 | + 'true' => array( 0, 17 ), |
| 1566 | + 'false' => array( 0, 18 ), |
| 1567 | + 'null' => array( 0, 19 ), |
| 1568 | + ), |
| 1569 | + 113 => array( |
| 1570 | + 'if' => array( 0, 1 ), |
| 1571 | + 'foreach' => array( 0, 3 ), |
| 1572 | + 'try' => array( 0, 4 ), |
| 1573 | + 'leftcurly' => array( 0, 5 ), |
| 1574 | + 'id' => array( 0, 8 ), |
| 1575 | + 'invert' => array( 0, 7 ), |
| 1576 | + 'sum' => array( 0, 6 ), |
| 1577 | + 'isset' => array( 0, 12 ), |
| 1578 | + 'unset' => array( 0, 13 ), |
| 1579 | + 'break' => array( 0, 9 ), |
| 1580 | + 'continue' => array( 0, 10 ), |
| 1581 | + 'leftbrace' => array( 0, 2 ), |
| 1582 | + 'leftsquare' => array( 0, 11 ), |
| 1583 | + 'string' => array( 0, 14 ), |
| 1584 | + 'int' => array( 0, 15 ), |
| 1585 | + 'float' => array( 0, 16 ), |
| 1586 | + 'true' => array( 0, 17 ), |
| 1587 | + 'false' => array( 0, 18 ), |
| 1588 | + 'null' => array( 0, 19 ), |
| 1589 | + ), |
| 1590 | + 114 => array( |
| 1591 | + 'if' => array( 0, 1 ), |
| 1592 | + 'foreach' => array( 0, 3 ), |
| 1593 | + 'try' => array( 0, 4 ), |
| 1594 | + 'leftcurly' => array( 0, 5 ), |
| 1595 | + 'id' => array( 0, 8 ), |
| 1596 | + 'invert' => array( 0, 7 ), |
| 1597 | + 'sum' => array( 0, 6 ), |
| 1598 | + 'isset' => array( 0, 12 ), |
| 1599 | + 'unset' => array( 0, 13 ), |
| 1600 | + 'break' => array( 0, 9 ), |
| 1601 | + 'continue' => array( 0, 10 ), |
| 1602 | + 'leftbrace' => array( 0, 2 ), |
| 1603 | + 'leftsquare' => array( 0, 11 ), |
| 1604 | + 'string' => array( 0, 14 ), |
| 1605 | + 'int' => array( 0, 15 ), |
| 1606 | + 'float' => array( 0, 16 ), |
| 1607 | + 'true' => array( 0, 17 ), |
| 1608 | + 'false' => array( 0, 18 ), |
| 1609 | + 'null' => array( 0, 19 ), |
| 1610 | + ), |
| 1611 | + 115 => array( |
| 1612 | + '$' => array( 1, 5 ), |
| 1613 | + 'if' => array( 1, 5 ), |
| 1614 | + 'foreach' => array( 1, 5 ), |
| 1615 | + 'try' => array( 1, 5 ), |
| 1616 | + 'leftcurly' => array( 1, 5 ), |
| 1617 | + 'id' => array( 1, 5 ), |
| 1618 | + 'invert' => array( 1, 5 ), |
| 1619 | + 'sum' => array( 1, 5 ), |
| 1620 | + 'isset' => array( 1, 5 ), |
| 1621 | + 'unset' => array( 1, 5 ), |
| 1622 | + 'break' => array( 1, 5 ), |
| 1623 | + 'continue' => array( 1, 5 ), |
| 1624 | + 'leftbrace' => array( 1, 5 ), |
| 1625 | + 'leftsquare' => array( 1, 5 ), |
| 1626 | + 'string' => array( 1, 5 ), |
| 1627 | + 'int' => array( 1, 5 ), |
| 1628 | + 'float' => array( 1, 5 ), |
| 1629 | + 'true' => array( 1, 5 ), |
| 1630 | + 'false' => array( 1, 5 ), |
| 1631 | + 'null' => array( 1, 5 ), |
| 1632 | + 'else' => array( 1, 5 ), |
| 1633 | + 'catch' => array( 1, 5 ), |
| 1634 | + 'rightcurly' => array( 1, 5 ), |
| 1635 | + ), |
| 1636 | + 116 => array( |
| 1637 | + '$' => array( 1, 6 ), |
| 1638 | + 'if' => array( 1, 6 ), |
| 1639 | + 'foreach' => array( 1, 6 ), |
| 1640 | + 'try' => array( 1, 6 ), |
| 1641 | + 'leftcurly' => array( 1, 6 ), |
| 1642 | + 'id' => array( 1, 6 ), |
| 1643 | + 'invert' => array( 1, 6 ), |
| 1644 | + 'sum' => array( 1, 6 ), |
| 1645 | + 'isset' => array( 1, 6 ), |
| 1646 | + 'unset' => array( 1, 6 ), |
| 1647 | + 'break' => array( 1, 6 ), |
| 1648 | + 'continue' => array( 1, 6 ), |
| 1649 | + 'leftbrace' => array( 1, 6 ), |
| 1650 | + 'leftsquare' => array( 1, 6 ), |
| 1651 | + 'string' => array( 1, 6 ), |
| 1652 | + 'int' => array( 1, 6 ), |
| 1653 | + 'float' => array( 1, 6 ), |
| 1654 | + 'true' => array( 1, 6 ), |
| 1655 | + 'false' => array( 1, 6 ), |
| 1656 | + 'null' => array( 1, 6 ), |
| 1657 | + 'else' => array( 1, 6 ), |
| 1658 | + 'catch' => array( 1, 6 ), |
| 1659 | + 'rightcurly' => array( 1, 6 ), |
| 1660 | + ), |
| 1661 | + 117 => array( |
| 1662 | + '$' => array( 1, 7 ), |
| 1663 | + 'if' => array( 1, 7 ), |
| 1664 | + 'foreach' => array( 1, 7 ), |
| 1665 | + 'try' => array( 1, 7 ), |
| 1666 | + 'leftcurly' => array( 1, 7 ), |
| 1667 | + 'id' => array( 1, 7 ), |
| 1668 | + 'invert' => array( 1, 7 ), |
| 1669 | + 'sum' => array( 1, 7 ), |
| 1670 | + 'isset' => array( 1, 7 ), |
| 1671 | + 'unset' => array( 1, 7 ), |
| 1672 | + 'break' => array( 1, 7 ), |
| 1673 | + 'continue' => array( 1, 7 ), |
| 1674 | + 'leftbrace' => array( 1, 7 ), |
| 1675 | + 'leftsquare' => array( 1, 7 ), |
| 1676 | + 'string' => array( 1, 7 ), |
| 1677 | + 'int' => array( 1, 7 ), |
| 1678 | + 'float' => array( 1, 7 ), |
| 1679 | + 'true' => array( 1, 7 ), |
| 1680 | + 'false' => array( 1, 7 ), |
| 1681 | + 'null' => array( 1, 7 ), |
| 1682 | + 'else' => array( 1, 7 ), |
| 1683 | + 'catch' => array( 1, 7 ), |
| 1684 | + 'rightcurly' => array( 1, 7 ), |
| 1685 | + ), |
| 1686 | +); |
| 1687 | + |
| 1688 | +static $goto = array( |
| 1689 | + 0 => array( 1 => 20, 2 => 21, 3 => 22, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1690 | + 1 => array(), |
| 1691 | + 2 => array( 3 => 40, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1692 | + 3 => array(), |
| 1693 | + 4 => array( 2 => 42, 3 => 22, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1694 | + 5 => array( 1 => 43, 2 => 21, 3 => 22, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1695 | + 6 => array( 16 => 46, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1696 | + 7 => array( 14 => 47, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1697 | + 8 => array( 24 => 50, 25 => 51 ), |
| 1698 | + 9 => array(), |
| 1699 | + 10 => array(), |
| 1700 | + 11 => array( 21 => 54, 22 => 55, 17 => 53, 23 => 56, 3 => 52, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1701 | + 12 => array(), |
| 1702 | + 13 => array(), |
| 1703 | + 14 => array(), |
| 1704 | + 15 => array(), |
| 1705 | + 16 => array(), |
| 1706 | + 17 => array(), |
| 1707 | + 18 => array(), |
| 1708 | + 19 => array(), |
| 1709 | + 20 => array( 2 => 57, 3 => 22, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1710 | + 21 => array(), |
| 1711 | + 22 => array(), |
| 1712 | + 23 => array(), |
| 1713 | + 24 => array(), |
| 1714 | + 25 => array(), |
| 1715 | + 26 => array(), |
| 1716 | + 27 => array(), |
| 1717 | + 28 => array(), |
| 1718 | + 29 => array(), |
| 1719 | + 30 => array(), |
| 1720 | + 31 => array(), |
| 1721 | + 32 => array(), |
| 1722 | + 33 => array(), |
| 1723 | + 34 => array(), |
| 1724 | + 35 => array(), |
| 1725 | + 36 => array(), |
| 1726 | + 37 => array(), |
| 1727 | + 38 => array(), |
| 1728 | + 39 => array( 3 => 70, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1729 | + 40 => array(), |
| 1730 | + 41 => array( 4 => 73 ), |
| 1731 | + 42 => array(), |
| 1732 | + 43 => array( 2 => 57, 3 => 22, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1733 | + 44 => array( 24 => 50, 25 => 51 ), |
| 1734 | + 45 => array(), |
| 1735 | + 46 => array(), |
| 1736 | + 47 => array(), |
| 1737 | + 48 => array( 17 => 78, 3 => 77, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1738 | + 49 => array( 3 => 80, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1739 | + 50 => array(), |
| 1740 | + 51 => array( 24 => 81, 25 => 51 ), |
| 1741 | + 52 => array(), |
| 1742 | + 53 => array(), |
| 1743 | + 54 => array(), |
| 1744 | + 55 => array(), |
| 1745 | + 56 => array(), |
| 1746 | + 57 => array(), |
| 1747 | + 58 => array(), |
| 1748 | + 59 => array( 5 => 86, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1749 | + 60 => array( 6 => 87, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1750 | + 61 => array( 8 => 88, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1751 | + 62 => array( 9 => 89, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1752 | + 63 => array( 10 => 90, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1753 | + 64 => array( 11 => 91, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1754 | + 65 => array( 12 => 92, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1755 | + 66 => array( 12 => 93, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1756 | + 67 => array( 15 => 94, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1757 | + 68 => array( 15 => 95, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1758 | + 69 => array( 4 => 96 ), |
| 1759 | + 70 => array(), |
| 1760 | + 71 => array(), |
| 1761 | + 72 => array( 24 => 50, 25 => 51 ), |
| 1762 | + 73 => array(), |
| 1763 | + 74 => array(), |
| 1764 | + 75 => array(), |
| 1765 | + 76 => array(), |
| 1766 | + 77 => array(), |
| 1767 | + 78 => array(), |
| 1768 | + 79 => array(), |
| 1769 | + 80 => array(), |
| 1770 | + 81 => array(), |
| 1771 | + 82 => array( 3 => 102, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1772 | + 83 => array( 3 => 103, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1773 | + 84 => array(), |
| 1774 | + 85 => array( 23 => 105, 3 => 104, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1775 | + 86 => array(), |
| 1776 | + 87 => array(), |
| 1777 | + 88 => array(), |
| 1778 | + 89 => array(), |
| 1779 | + 90 => array(), |
| 1780 | + 91 => array(), |
| 1781 | + 92 => array(), |
| 1782 | + 93 => array(), |
| 1783 | + 94 => array(), |
| 1784 | + 95 => array(), |
| 1785 | + 96 => array(), |
| 1786 | + 97 => array( 2 => 108, 3 => 22, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1787 | + 98 => array( 3 => 109, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1788 | + 99 => array( 4 => 110 ), |
| 1789 | + 100 => array(), |
| 1790 | + 101 => array(), |
| 1791 | + 102 => array(), |
| 1792 | + 103 => array(), |
| 1793 | + 104 => array(), |
| 1794 | + 105 => array(), |
| 1795 | + 106 => array( 6 => 111, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 4 => 45, 20 => 38 ), |
| 1796 | + 107 => array(), |
| 1797 | + 108 => array(), |
| 1798 | + 109 => array(), |
| 1799 | + 110 => array(), |
| 1800 | + 111 => array(), |
| 1801 | + 112 => array( 2 => 115, 3 => 22, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1802 | + 113 => array( 2 => 116, 3 => 22, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1803 | + 114 => array( 2 => 117, 3 => 22, 5 => 24, 4 => 23, 6 => 25, 7 => 26, 8 => 27, 9 => 28, 10 => 29, 11 => 30, 12 => 31, 13 => 32, 14 => 33, 15 => 34, 16 => 35, 18 => 36, 19 => 37, 20 => 38 ), |
| 1804 | + 115 => array(), |
| 1805 | + 116 => array(), |
| 1806 | + 117 => array(), |
| 1807 | +); |
| 1808 | + |
| 1809 | +} |
Property changes on: trunk/extensions/InlineScripts/interpreter/LRTable.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 1810 | + native |
Index: trunk/extensions/InlineScripts/interpreter/Shared.php |
— | — | @@ -0,0 +1,171 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * This file contains implementation-independent interface files for |
| 6 | + * inline scripts interpreter. |
| 7 | + */ |
| 8 | + |
| 9 | +class ISToken { |
| 10 | + // Constant values should match ones in syntax.txt |
| 11 | + const TEnd = '$'; |
| 12 | + const TBreak = 'break'; |
| 13 | + const TCatch = 'catch'; |
| 14 | + const TColon = 'colon'; // : |
| 15 | + const TCompareOperator = 'comareop'; // <, >, <= or >= |
| 16 | + const TComma = 'comma'; // , |
| 17 | + const TContains = 'contains'; |
| 18 | + const TContinue = 'continue'; |
| 19 | + const TElse = 'else'; |
| 20 | + const TEqualsToOperator = 'equalsto'; // ==, ===, != or !== |
| 21 | + const TFalse = 'false'; |
| 22 | + const TFloat = 'float'; |
| 23 | + const TForeach = 'foreach'; |
| 24 | + const TID = 'id'; |
| 25 | + const TIf = 'if'; |
| 26 | + const TIn = 'in'; |
| 27 | + const TInt = 'int'; |
| 28 | + const TBoolInvert = 'invert'; // ! |
| 29 | + const TIsset = 'isset'; |
| 30 | + const TLeftBrace = 'leftbrace'; // ( |
| 31 | + const TLeftCurly = 'leftcurly'; // { |
| 32 | + const TLeftSquare = 'leftsquare'; // [ |
| 33 | + const TLogicalOperator = 'logicop'; // &, | or ^ |
| 34 | + const TMulOperator = 'mul'; // *, / or % |
| 35 | + const TNull = 'null'; |
| 36 | + const TPow = 'pow'; // ** |
| 37 | + const TRightBrace = 'rightbrace'; // ) |
| 38 | + const TRightCurly = 'rightcurly'; // } |
| 39 | + const TRightSquare = 'rightsquare'; // ] |
| 40 | + const TSemicolon = 'semicolon'; // ; |
| 41 | + const TSet = 'setto'; // = |
| 42 | + const TString = 'string'; |
| 43 | + const TSumOperator = 'sum'; // + or - |
| 44 | + const TTrinary = 'trinary'; // ? |
| 45 | + const TTrue = 'true'; |
| 46 | + const TTry = 'try'; |
| 47 | + const TUnset = 'unset'; |
| 48 | + |
| 49 | + var $type; |
| 50 | + var $value; |
| 51 | + var $line; |
| 52 | + |
| 53 | + public function __construct( $type = self::TEnd, $value = null, $line = 0 ) { |
| 54 | + $this->type = $type; |
| 55 | + $this->value = $value; |
| 56 | + $this->line = $line; |
| 57 | + } |
| 58 | + |
| 59 | + function __toString() { |
| 60 | + return "{$this->value}"; |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +class ISParserTreeNode { |
| 65 | + var $mType, $mChildren; |
| 66 | + |
| 67 | + public function __construct( $parser, $id ) { |
| 68 | + $this->mType = $parser->mNonterminals[$id]; |
| 69 | + } |
| 70 | + |
| 71 | + public function addChild( $node ) { |
| 72 | + if( $node instanceof ISParserTreeNode ) { |
| 73 | + $children = $node->getChildren(); |
| 74 | + if( count( $children ) == 1 && strpos( $node->mType, "expr" ) === 0 |
| 75 | + && strpos( @$children[0]->mType, "expr" ) === 0 ) { |
| 76 | + $this->mChildren[] = $children[0]; |
| 77 | + return; |
| 78 | + } |
| 79 | + } |
| 80 | + $this->mChildren[] = $node; |
| 81 | + } |
| 82 | + |
| 83 | + public function getChildren() { |
| 84 | + return $this->mChildren; |
| 85 | + } |
| 86 | + |
| 87 | + public function getType() { |
| 88 | + return $this->mType; |
| 89 | + } |
| 90 | + |
| 91 | + public function __toString() { |
| 92 | + $r = $this->formatStringArray(); |
| 93 | + return implode( "\n", $r ); |
| 94 | + } |
| 95 | + |
| 96 | + public function formatStringArray() { |
| 97 | + $s = array( "<nonterminal type=\"{$this->mType}\">" ); |
| 98 | + foreach( $this->mChildren as $child ) { |
| 99 | + if( $child instanceof ISParserTreeNode ) { |
| 100 | + $sub = $child->formatStringArray(); |
| 101 | + foreach( $sub as $str ) |
| 102 | + $s[] = "\t" . $str; |
| 103 | + } else { |
| 104 | + $s[] = "\t<terminal type=\"{$child->type}\" value=\"{$child->value}\" />"; |
| 105 | + } |
| 106 | + } |
| 107 | + $s[] = "</nonterminal>"; |
| 108 | + return $s; |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +interface ISParser { |
| 113 | + /** |
| 114 | + * If this function returns true, code scanner is passed to parse(). |
| 115 | + * Otherwise, code itself is passed. |
| 116 | + */ |
| 117 | + public function needsScanner(); |
| 118 | + |
| 119 | + /** |
| 120 | + * Parses code (in text or scanner) to parser tree. |
| 121 | + * @param input ISScanner Input (scanner or string) |
| 122 | + * @param maxTokens int Maximal amount of tokens |
| 123 | + * @return ISParserTreeNode |
| 124 | + */ |
| 125 | + public function parse( $input, $maxTokens ); |
| 126 | +} |
| 127 | + |
| 128 | +class ISException extends MWException {} |
| 129 | + |
| 130 | +// Exceptions that we might conceivably want to report to ordinary users |
| 131 | +// (i.e. exceptions that don't represent bugs in the extension itself) |
| 132 | +class ISUserVisibleException extends ISException { |
| 133 | + function __construct( $exception_id, $line, $params = array() ) { |
| 134 | + $msg = wfMsgExt( 'inlinescripts-exception-' . $exception_id, array(), array_merge( array($line), $params ) ); |
| 135 | + parent::__construct( $msg ); |
| 136 | + |
| 137 | + $this->mExceptionID = $exception_id; |
| 138 | + $this->mLine = $line; |
| 139 | + $this->mParams = $params; |
| 140 | + } |
| 141 | + |
| 142 | + public function getExceptionID() { |
| 143 | + return $this->mExceptionID; |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +class ISParserOutput { |
| 148 | + var $mTree, $mTokensCount, $mVersion; |
| 149 | + |
| 150 | + public function __construct( $tree, $tokens ) { |
| 151 | + global $wgInlineScriptsParserClass; |
| 152 | + $this->mTree = $tree; |
| 153 | + $this->mTokensCount = $tokens; |
| 154 | + $this->mVersion = constant( "$wgInlineScriptsParserClass::Version" ); |
| 155 | + } |
| 156 | + |
| 157 | + public function getParserTree() { |
| 158 | + return $this->mTree; |
| 159 | + } |
| 160 | + |
| 161 | + public function isOutOfDate() { |
| 162 | + global $wgInlineScriptsParserClass; |
| 163 | + return constant( "$wgInlineScriptsParserClass::Version" ) > $this->mVersion; |
| 164 | + } |
| 165 | + |
| 166 | + public function appendTokenCount( &$interpr ) { |
| 167 | + global $wgInlineScriptsLimits; |
| 168 | + $interpr->mParser->is_tokensCount += $this->mTokensCount; |
| 169 | + if( $interpr->mParser->is_tokensCount > $wgInlineScriptsLimits['tokens'] ) |
| 170 | + throw new ISUserVisibleException( 'toomanytokens', 0 ); |
| 171 | + } |
| 172 | +} |
Property changes on: trunk/extensions/InlineScripts/interpreter/Shared.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 173 | + native |
Index: trunk/extensions/InlineScripts/interpreter/buildLRTables.php |
— | — | @@ -0,0 +1,536 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * An ugly tool to build SLR-tables. |
| 6 | + * |
| 7 | + * Reads a BNF-ish grammar (strings without <> are terminals) from |
| 8 | + * syntax.txt file and output following files: |
| 9 | + * * LRTableBuildReport.html with misc debug information |
| 10 | + * * LRTable.php, ACTION/GOTO table for grammar in PHP |
| 11 | + * |
| 12 | + * This code requires cleanup, but it's not used outside of development |
| 13 | + * process. |
| 14 | + * |
| 15 | + * This code contains grammar-specific hack to force parser to shift "else" |
| 16 | + * on shift/reduce conflict in "if( ... ) ... (!) else ..." state |
| 17 | + */ |
| 18 | + |
| 19 | +require_once( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) . '/maintenance/commandLine.inc' ); |
| 20 | + |
| 21 | +class Grammar { |
| 22 | + var $mTerminals, $mNonterminals, $mProductions, $mSymbols; |
| 23 | + var $mFirst, $mFollow, $mAction, $mGoto; |
| 24 | + |
| 25 | + private function __construct() { |
| 26 | + $this->mTerminals = |
| 27 | + $this->mNonterminals = |
| 28 | + $this->mProductions = |
| 29 | + array(); |
| 30 | + } |
| 31 | + |
| 32 | + private function getNonterminalID( $name ) { |
| 33 | + if( !in_array( $name, $this->mNonterminals ) ) |
| 34 | + $this->mNonterminals[] = $name; |
| 35 | + return array_search( $name, $this->mNonterminals ); |
| 36 | + } |
| 37 | + |
| 38 | + private function addProduction( $nonterm, $prod ) { |
| 39 | + $this->mProductions[] = array( $nonterm, $prod ); |
| 40 | + } |
| 41 | + |
| 42 | + private function getProdsForNt( $nonterm ) { |
| 43 | + $prods = array(); |
| 44 | + for( $i = 0; $i < count( $this->mProductions ); $i++ ) { |
| 45 | + $prod = $this->mProductions[$i]; |
| 46 | + if( $prod[0] == $nonterm ) |
| 47 | + $prods[$i] = $prod[1]; |
| 48 | + } |
| 49 | + return $prods; |
| 50 | + } |
| 51 | + |
| 52 | + private function getNtName( $id ) { |
| 53 | + return $this->mNonterminals[$id]; |
| 54 | + } |
| 55 | + |
| 56 | + public static function parse( $def ) { |
| 57 | + $g = new Grammar(); |
| 58 | + $def = strtolower( $def ); |
| 59 | + $lines = explode( "\n", $def ); |
| 60 | + for( $i = 1; $i <= count( $lines ); $i++ ) { |
| 61 | + $line = trim( $lines[$i - 1] ); |
| 62 | + if( !$line ) |
| 63 | + continue; |
| 64 | + |
| 65 | + list( $name, $vals ) = self::parseLine( $g, $line, $i ); |
| 66 | + foreach( $vals as $val ) |
| 67 | + $g->addProduction( $name, $val ); |
| 68 | + } |
| 69 | + foreach( $g->mProductions as $prod ) { |
| 70 | + list( $ntid, $prod ) = $prod; |
| 71 | + foreach( $prod as $symbol ) |
| 72 | + if( is_string( $symbol ) && !in_array( $symbol, $g->mTerminals ) ) |
| 73 | + $g->mTerminals[] = $symbol; |
| 74 | + } |
| 75 | + $g->mTerminals[] = '$'; |
| 76 | + $g->mSymbols = array_merge( $g->mTerminals, array_keys( $g->mNonterminals ) ); |
| 77 | + return $g; |
| 78 | + } |
| 79 | + |
| 80 | + private static function parseLine( $g, $line, $lnum ) { |
| 81 | + $i = 0; |
| 82 | + wfSuppressWarnings(); // @ doesn't help to supress "uninitialized string offset" warning |
| 83 | + |
| 84 | + self::skipWhitespace( $line, $i ); |
| 85 | + if( $line[$i] != '<' ) |
| 86 | + die( "Invalid BNF at line $lnum" ); |
| 87 | + $i++; |
| 88 | + |
| 89 | + $end = strpos( $line, '>', $i ); |
| 90 | + if( $end === false ) |
| 91 | + die( "Invalid BNF at line $lnum" ); |
| 92 | + $name = $g->getNonterminalID( substr( $line, $i, $end - $i ) ); |
| 93 | + $i = $end + 1; |
| 94 | + |
| 95 | + self::skipWhitespace( $line, $i ); |
| 96 | + if( substr( $line, $i, 3 ) != '::=' ) |
| 97 | + die( "Invalid BNF at line $lnum" ); |
| 98 | + $i += 3; |
| 99 | + |
| 100 | + $prods = array(); |
| 101 | + $curProd = array(); |
| 102 | + while( $i + 1 < strlen( $line ) ) { |
| 103 | + self::skipWhitespace( $line, $i ); |
| 104 | + if( $line[$i] == '|' ) { |
| 105 | + $prods[] = $curProd; |
| 106 | + $curProd = array(); |
| 107 | + $i++; |
| 108 | + } else if( $line[$i] == '<' ) { |
| 109 | + $i++; |
| 110 | + $end = strpos( $line, '>', $i ); |
| 111 | + if( $end === false ) |
| 112 | + die( "Invalid BNF at line $lnum" ); |
| 113 | + $curProd[] = $g->getNonterminalID( substr( $line, $i, $end - $i ) ); |
| 114 | + $i = $end + 1; |
| 115 | + } else { |
| 116 | + for( $termName = ''; ctype_alnum( $line[$i] ); $i++ ) |
| 117 | + $termName .= $line[$i]; |
| 118 | + if( !$termName ) |
| 119 | + die( "Invalid BNF at line $lnum" ); |
| 120 | + $curProd[] = $termName; |
| 121 | + } |
| 122 | + } |
| 123 | + $prods[] = $curProd; |
| 124 | + wfRestoreWarnings(); |
| 125 | + return array( $name, $prods ); |
| 126 | + } |
| 127 | + |
| 128 | + private static function skipWhitespace( $line, &$pos ) { |
| 129 | + while( ctype_space( $line[$pos] ) && $pos + 1 < strlen( $line ) ) |
| 130 | + $pos++; |
| 131 | + } |
| 132 | + |
| 133 | + private function buildFirstTable() { |
| 134 | + foreach( $this->mSymbols as $symbol ) |
| 135 | + $this->mFirst[$symbol] = array(); |
| 136 | + |
| 137 | + foreach( $this->mTerminals as $t ) |
| 138 | + $this->mFirst[$t][] = $t; |
| 139 | + |
| 140 | + for( ; ; ) { |
| 141 | + $added = 0; |
| 142 | + foreach( $this->mProductions as $prodbundle ) { |
| 143 | + list( $nt, $prod ) = $prodbundle; |
| 144 | + foreach( $this->mFirst[$prod[0]] as $e ) { |
| 145 | + if( !in_array( $e, $this->mFirst[$nt] ) ) { |
| 146 | + $this->mFirst[$nt][] = $e; |
| 147 | + $added++; |
| 148 | + } |
| 149 | + } |
| 150 | + } |
| 151 | + if( !$added ) |
| 152 | + break; |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + private function buildFollowTable() { |
| 157 | + foreach( $this->mSymbols as $symbol ) |
| 158 | + $this->mFollow[$symbol] = array(); |
| 159 | + $this->mFollow[0][] = '$'; |
| 160 | + for( ; ; ) { |
| 161 | + $added = 0; |
| 162 | + foreach( $this->mProductions as $prodbundle ) { |
| 163 | + list( $nt, $prod ) = $prodbundle; |
| 164 | + for( $i = 0; $i < count( $prod ) - 1; $i++ ) { |
| 165 | + $symbol = $prod[$i]; |
| 166 | + if( is_int( $symbol ) ) { |
| 167 | + foreach( $this->mFirst[$prod[$i + 1]] as $fsymbol ) { |
| 168 | + if( !in_array( $fsymbol, $this->mFollow[$symbol] ) ) { |
| 169 | + $this->mFollow[$symbol][] = $fsymbol; |
| 170 | + $added++; |
| 171 | + } |
| 172 | + } |
| 173 | + } |
| 174 | + } |
| 175 | + $last = end( $prod ); |
| 176 | + if( is_int( $last ) ) { |
| 177 | + foreach( $this->mFollow[$nt] as $symbol ) { |
| 178 | + if( !in_array( $symbol, $this->mFollow[$last] ) ) { |
| 179 | + $this->mFollow[$last][] = $symbol; |
| 180 | + } |
| 181 | + } |
| 182 | + } |
| 183 | + } |
| 184 | + if( !$added ) |
| 185 | + break; |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + private function itemsClosure( $items ) { |
| 190 | + for( ; ; ) { |
| 191 | + $oldsize = count( $items ); |
| 192 | + foreach( $items as $item ) { |
| 193 | + list( $prodid, $idx ) = $item; |
| 194 | + list( $unused, $prod ) = $this->mProductions[$prodid]; |
| 195 | + if( is_int( @$prod[$idx] ) ) { |
| 196 | + foreach( $this->getProdsForNt( $prod[$idx] ) as $id => $newProd ) { |
| 197 | + $item = array( $id, 0 ); |
| 198 | + if( !in_array( $item, $items ) ) |
| 199 | + $items[] = $item; |
| 200 | + } |
| 201 | + } |
| 202 | + } |
| 203 | + if( count( $items ) == $oldsize ) |
| 204 | + return $items; |
| 205 | + } |
| 206 | + } |
| 207 | + |
| 208 | + public function itemsGoto( $items, $symbol ) { |
| 209 | + if( is_null( $symbol ) ) |
| 210 | + return array(); |
| 211 | + $result = array(); |
| 212 | + foreach( $items as $item ) { |
| 213 | + list( $prodid, $idx ) = $item; |
| 214 | + $prod = $this->mProductions[$prodid][1]; |
| 215 | + if( @$prod[$idx] === $symbol ) |
| 216 | + $result[] = array( $prodid, $idx + 1 ); |
| 217 | + } |
| 218 | + return $this->itemsClosure( $result ); |
| 219 | + } |
| 220 | + |
| 221 | + public function buildCanonicalSet() { |
| 222 | + $r = array( $this->itemsClosure( array( array( 0, 0 ) ) ) ); |
| 223 | + $symbols = array_merge( $this->mTerminals, array_keys( $this->mNonterminals ) ); |
| 224 | + for( ; ; ) { |
| 225 | + $oldsize = count( $r ); |
| 226 | + foreach( $r as $set ) { |
| 227 | + foreach( $symbols as $symbol ) { |
| 228 | + $goto = $this->itemsGoto( $set, $symbol ); |
| 229 | + if( $goto && !in_array( $goto, $r ) ) |
| 230 | + $r[] = $goto; |
| 231 | + } |
| 232 | + } |
| 233 | + if( $oldsize == count( $r ) ) |
| 234 | + break; |
| 235 | + } |
| 236 | + return $r; |
| 237 | + } |
| 238 | + |
| 239 | + public function buildLRTable() { |
| 240 | + $this->buildFirstTable(); |
| 241 | + $this->buildFollowTable(); |
| 242 | + $canonSet = $this->buildCanonicalSet(); |
| 243 | + $actionTable = array(); |
| 244 | + $gotoTable = array(); |
| 245 | + for( $i = 0; $i < count( $canonSet ); $i++ ) { |
| 246 | + $set = $canonSet[$i]; |
| 247 | + $row = $rowGoto = array(); |
| 248 | + foreach( $set as $item ) { |
| 249 | + list( $prodid, $idx ) = $item; |
| 250 | + list( $nt, $prod ) = $this->mProductions[$prodid]; |
| 251 | + $goto = $this->itemsGoto( $set, @$prod[$idx] ); |
| 252 | + for( $j = 0; $j < count( $canonSet ); $j++ ) { |
| 253 | + if( $goto == $canonSet[$j] ) { |
| 254 | + if( is_string( $prod[$idx] ) ) { |
| 255 | + $act = array( 'shift', $j ); |
| 256 | + if( isset( $row[$prod[$idx]] ) && $row[$prod[$idx]] != $act ) |
| 257 | + if( $prod[0] != 'if' ) // Grammar-specific manual hack for "hanging if" problem |
| 258 | + $this->conflictError( $i, $set, $row[$prod[$idx]], $act, $prod[$idx] ); |
| 259 | + $row[$prod[$idx]] = $act; |
| 260 | + } else { |
| 261 | + $rowGoto[$prod[$idx]] = $j; |
| 262 | + } |
| 263 | + } |
| 264 | + } |
| 265 | + if( $idx == count( $prod ) ) { |
| 266 | + if( $prodid ) { |
| 267 | + foreach( $this->mFollow[$nt] as $symbol ) { |
| 268 | + $act = array( 'reduce', $prodid ); |
| 269 | + if( isset( $row[$symbol] ) && $row[$symbol] != $act ) { |
| 270 | + $this->conflictError( $i, $set, $row[$symbol], $act, $symbol ); |
| 271 | + } |
| 272 | + $row[$symbol] = $act; |
| 273 | + } |
| 274 | + } else { |
| 275 | + $row['$'] = array( 'accept' ); |
| 276 | + } |
| 277 | + } |
| 278 | + } |
| 279 | + $actionTable[$i] = $row; |
| 280 | + $gotoTable[$i] = $rowGoto; |
| 281 | + } |
| 282 | + $this->mAction = $actionTable; |
| 283 | + $this->mGoto = $gotoTable; |
| 284 | + } |
| 285 | + |
| 286 | + /** Debug */ |
| 287 | + public function formatProduction( $prodid ) { |
| 288 | + list( $subj, $val ) = $this->mProductions[$prodid]; |
| 289 | + $s = array( $this->getNtName( $subj ), "->" ); |
| 290 | + foreach( $val as $symbol ) { |
| 291 | + if( is_string( $symbol ) ) |
| 292 | + $s[] = strtoupper( $symbol ); |
| 293 | + else |
| 294 | + $s[] = $this->getNtName( $symbol ); |
| 295 | + } |
| 296 | + return implode( ' ', $s ); |
| 297 | + } |
| 298 | + |
| 299 | + public function formatItem( $item ) { |
| 300 | + list( $prodid, $idx ) = $item; |
| 301 | + list( $subj, $val ) = $this->mProductions[$prodid]; |
| 302 | + $s = array( $this->getNtName( $subj ), "->" ); |
| 303 | + for( $i = 0; $i <= count( $val ); $i++ ) { |
| 304 | + if( $i == $idx ) |
| 305 | + $s[] = '(!)'; |
| 306 | + if( $symbol = @$val[$i] ) { |
| 307 | + if( is_string( $symbol ) ) |
| 308 | + $s[] = strtoupper( $symbol ); |
| 309 | + else |
| 310 | + $s[] = $this->getNtName( $symbol ); |
| 311 | + } |
| 312 | + } |
| 313 | + return implode( ' ', $s ); |
| 314 | + } |
| 315 | + |
| 316 | + public function formatAction( $act ) { |
| 317 | + @list( $name, $arg ) = $act; |
| 318 | + if( $name == 'shift' ) { |
| 319 | + return "Shift to state {$arg}"; |
| 320 | + } |
| 321 | + if( $name == 'reduce' ) { |
| 322 | + $prod = $this->formatProduction( $arg ); |
| 323 | + return "Reduce to production {$arg} ({$prod})"; |
| 324 | + } |
| 325 | + if( $name == 'accept' ) { |
| 326 | + return "Accept"; |
| 327 | + } |
| 328 | + } |
| 329 | + |
| 330 | + public function conflictError( $id, $state, $act1, $act2, $symbol ) { |
| 331 | + echo "Found conflict in state {$id} for symbol {$symbol}.\n"; |
| 332 | + echo "Conflicting actions:\n"; |
| 333 | + foreach( array( $act1, $act2 ) as $act ) { |
| 334 | + $str = $this->formatAction( $act ); |
| 335 | + echo "* {$str}\n"; |
| 336 | + } |
| 337 | + echo "Items of the state:\n"; |
| 338 | + foreach( $state as $item ) { |
| 339 | + $str = $this->formatItem( $item ); |
| 340 | + echo "* {$str}\n"; |
| 341 | + } |
| 342 | + exit; |
| 343 | + } |
| 344 | + |
| 345 | + public function buildHTMLDump() { |
| 346 | + $s = <<<END |
| 347 | +<html> |
| 348 | +<head> |
| 349 | +<title>Inline scripts LR table dump</title> |
| 350 | +<style type="text/css"> |
| 351 | +table { |
| 352 | + margin: 1em 1em 1em 0; |
| 353 | + background: #f9f9f9; |
| 354 | + border: 1px #aaa solid; |
| 355 | + border-collapse: collapse; |
| 356 | +} |
| 357 | +th, td { |
| 358 | + border: 1px #aaa solid; |
| 359 | + padding: 0.2em; |
| 360 | +} |
| 361 | +th { |
| 362 | + background: #f2f2f2; |
| 363 | + text-align: center; |
| 364 | +} |
| 365 | +caption { |
| 366 | + font-weight: bold; |
| 367 | +} |
| 368 | +</style> |
| 369 | +</head> |
| 370 | +<body> |
| 371 | +<p>Here is the dump of LR table itself, as well as data used to build it.</p> |
| 372 | +<p>Navigate: <a href="#first">FIRST()</a> | <a href="#follow">FOLLOW()</a> | <a href="#prods">Productions</a> |
| 373 | + | <a href="#table">ACTION/GOTO</a></p> |
| 374 | +END; |
| 375 | + |
| 376 | + $s .= "<h1><a name='first' id='first'>FIRST()</h1><table><tr><th>Symbol</th><th>FIRST(Symbol)</th></tr>\n"; |
| 377 | + foreach( $this->mFirst as $item => $val ) { |
| 378 | + $itemname = is_int( $item ) ? '<i>' . $this->getNtName( $item ) . '</i>' |
| 379 | + : "<b>{$item}</b>"; |
| 380 | + $s .= "<tr><td>{$itemname}</td><td>" . implode( ', ', $val ) . "</td></tr>\n"; |
| 381 | + } |
| 382 | + $s .= "</table>\n"; |
| 383 | + |
| 384 | + $s .= "<h1><a name='follow' id='follow'>FOLLOW()</h1><table><tr><th>Symbol</th><th>FOLLOW(Symbol)</th></tr>\n"; |
| 385 | + foreach( $this->mFollow as $item => $val ) { |
| 386 | + if( !$val ) continue; |
| 387 | + $itemname = is_int( $item ) ? '<i>' . $this->getNtName( $item ) . '</i>' |
| 388 | + : "<b>{$item}</b>"; |
| 389 | + $s .= "<tr><td>{$itemname}</td><td>" . implode( ', ', $val ) . "</td></tr>\n"; |
| 390 | + } |
| 391 | + $s .= "</table>\n"; |
| 392 | + |
| 393 | + $s .= "<h1><a name='prods' id='prods'>Productions</h1><table><tr><th>ID</th><th>Production</th></tr>\n"; |
| 394 | + foreach( $this->mProductions as $id => $val ) { |
| 395 | + $str = $this->formatProduction( $id ); |
| 396 | + $s .= "<tr><td><b>{$id}</b></td><td>{$str}</td></tr>\n"; |
| 397 | + } |
| 398 | + $s .= "</table>\n"; |
| 399 | + |
| 400 | + $termLen = count( $this->mTerminals ); |
| 401 | + $nontermLen = count( $this->mNonterminals ); |
| 402 | + $s .= "<h1><a name='table' id='action'>LR-table (ACTION/GOTO)</h1><table><tr><th rowspan=2>State ID</th>" . |
| 403 | + "<th colspan={$termLen}>ACTION</th><th colspan={$nontermLen}>GOTO</th></tr><tr><th>" . |
| 404 | + implode( '</th><th>', $this->mTerminals ) . '</th><th>' . implode( '</th><th>', array_values( $this->mNonterminals ) ) . "</th></tr>\n"; |
| 405 | + for( $id = 0; $id < count( $this->mAction ); $id++ ) { |
| 406 | + $row = $this->mAction[$id]; |
| 407 | + $goto = $this->mGoto[$id]; |
| 408 | + $s .= "\t<tr><td><b>{$id}</b></td>"; |
| 409 | + foreach( $this->mTerminals as $t ) { |
| 410 | + $act = @$row[$t]; |
| 411 | + if( $act ) { |
| 412 | + switch( $act[0] ) { |
| 413 | + case 'shift': |
| 414 | + $s .= "<td>s{$act[1]}</td>"; break; |
| 415 | + case 'reduce': |
| 416 | + $s .= "<td>r{$act[1]}</td>"; break; |
| 417 | + case 'accept': |
| 418 | + $s .= "<td>acc</td>"; break; |
| 419 | + } |
| 420 | + } else { |
| 421 | + $s .= "<td></td>"; |
| 422 | + } |
| 423 | + } |
| 424 | + foreach( $this->mNonterminals as $ntid => $ntname ) { |
| 425 | + if( isset( $goto[$ntid] ) ) { |
| 426 | + $s .= "<td>{$goto[$ntid]}</td>"; |
| 427 | + } else { |
| 428 | + $s .= "<td></td>"; |
| 429 | + } |
| 430 | + } |
| 431 | + $s .= "</tr>\n"; |
| 432 | + } |
| 433 | + $s .= "</table>\n"; |
| 434 | + |
| 435 | + $s .= "<hr/><p>Autogenerated on " . gmdate( 'Y-m-d H:i:s' ) . "</p></body></html>"; |
| 436 | + return $s; |
| 437 | + } |
| 438 | + |
| 439 | + private function formatArray( $array ) { |
| 440 | + if( !$array ) |
| 441 | + return 'array()'; |
| 442 | + foreach( $array as &$item ) { |
| 443 | + if( is_string( $item ) ) |
| 444 | + $item = "'{$item}'"; |
| 445 | + } |
| 446 | + return 'array( ' . implode( ', ', $array ) . ' )'; |
| 447 | + } |
| 448 | + |
| 449 | + private function formatAssocArray( $array ) { |
| 450 | + if( !$array ) |
| 451 | + return 'array()'; |
| 452 | + $result = array(); |
| 453 | + foreach( $array as $k => $v ) { |
| 454 | + if( is_string( $v ) ) |
| 455 | + $v = "'{$v}'"; |
| 456 | + $result[] = "{$k} => {$v}"; |
| 457 | + } |
| 458 | + return 'array( ' . implode( ', ', $result ) . ' )'; |
| 459 | + } |
| 460 | + |
| 461 | + public function buildPHPFile() { |
| 462 | + $date = gmdate( 'Y-m-d H:i' ); |
| 463 | + $s = <<<ENDOFHEADER |
| 464 | +<?php |
| 465 | + |
| 466 | +/** |
| 467 | + * Autogenerated SLR-table for inline scripts language. |
| 468 | + * |
| 469 | + * You should not try to modify it manually (it's very easy to break). |
| 470 | + * Use syntax.txt and buildLRTables.php insteaed. |
| 471 | + * |
| 472 | + * Actions have following syntax |
| 473 | + * array( 0, N ) means "shift and go to state N" |
| 474 | + * array( 1, N ) means "reduce to production N" |
| 475 | + * array( 2 ) means "accept" |
| 476 | + * null means "error" |
| 477 | + * |
| 478 | + * Terminals are referred by names, nonterminals - by ids. |
| 479 | + * |
| 480 | + * Variables has following format: |
| 481 | + * * \$nonterminals is a nonterminal ID -> name map. |
| 482 | + * * \$productions is a ID -> array( nonterminal, body ) map. |
| 483 | + * * Production body is an array of production symbols |
| 484 | + * |
| 485 | + * Generated on {$date}. |
| 486 | + */ |
| 487 | + |
| 488 | +class ISLRTable { |
| 489 | + |
| 490 | + |
| 491 | +ENDOFHEADER; |
| 492 | + |
| 493 | + $s .= "static \$nonterminals = array(\n"; |
| 494 | + foreach( $this->mNonterminals as $id => $val ) { |
| 495 | + $s .= "\t{$id} => '{$val}',\n"; |
| 496 | + } |
| 497 | + $s .= ");\n\n"; |
| 498 | + |
| 499 | + $s .= "static \$productions = array(\n"; |
| 500 | + foreach( $this->mProductions as $id => $val ) { |
| 501 | + $body = $this->formatArray( $val[1] ); |
| 502 | + $s .= "\t{$id} => array( {$val[0]}, {$body} ),\n"; |
| 503 | + } |
| 504 | + $s .= ");\n\n"; |
| 505 | + |
| 506 | + $s .= "static \$action = array(\n"; |
| 507 | + foreach( $this->mAction as $id => $row ) { |
| 508 | + $s .= "\t{$id} => array(\n"; |
| 509 | + foreach( $row as $t => $action ) { |
| 510 | + if( $action[0] == 'shift' ) |
| 511 | + $s .= "\t\t'{$t}' => array( 0, {$action[1]} ),\n"; |
| 512 | + if( $action[0] == 'reduce' ) |
| 513 | + $s .= "\t\t'{$t}' => array( 1, {$action[1]} ),\n"; |
| 514 | + if( $action[0] == 'accept' ) |
| 515 | + $s .= "\t\t'{$t}' => array( 2, null ),\n"; |
| 516 | + } |
| 517 | + $s .= "\t),\n"; |
| 518 | + } |
| 519 | + $s .= ");\n\n"; |
| 520 | + |
| 521 | + $s .= "static \$goto = array(\n"; |
| 522 | + foreach( $this->mGoto as $id => $row ) { |
| 523 | + $body = $this->formatAssocArray( $row ); |
| 524 | + $s .= "\t{$id} => {$body},\n"; |
| 525 | + } |
| 526 | + $s .= ");\n\n"; |
| 527 | + |
| 528 | + $s .= "}\n"; |
| 529 | + return $s; |
| 530 | + } |
| 531 | +} |
| 532 | + |
| 533 | +$definition = file_get_contents( dirname( __FILE__ ) . '/syntax.txt' ); |
| 534 | +$grammar = Grammar::parse( $definition ); |
| 535 | +$grammar->buildLRTable(); |
| 536 | +file_put_contents( 'LRTableBuildReport.html', $grammar->buildHTMLDump() ); |
| 537 | +file_put_contents( 'LRTable.php', $grammar->buildPHPFile() ); |
Property changes on: trunk/extensions/InlineScripts/interpreter/buildLRTables.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 538 | + native |
Index: trunk/extensions/InlineScripts/interpreterTests.txt |
— | — | @@ -3,13 +3,22 @@ |
4 | 4 | !! test |
5 | 5 | Basic mathematics |
6 | 6 | !! input |
7 | | -{{#inline:2 + 2 * 2 ** 2 - 3 * 7 % 5}} |
| 7 | +{{#inline:-2 + 2 * 2 ** 2 - 3 * 7 % 5;}} |
8 | 8 | !! result |
9 | | -<p>9 |
| 9 | +<p>5 |
10 | 10 | </p> |
11 | 11 | !! end |
12 | 12 | |
13 | 13 | !! test |
| 14 | +** associativity |
| 15 | +!! input |
| 16 | +{{#inline: 4 ** 3 ** 2;}}<!-- Not 4096! --> |
| 17 | +!! result |
| 18 | +<p>262144 |
| 19 | +</p> |
| 20 | +!! end |
| 21 | + |
| 22 | +!! test |
14 | 23 | String contecation and out() |
15 | 24 | !! input |
16 | 25 | <wikiscript> |
— | — | @@ -23,7 +32,7 @@ |
24 | 33 | !! test |
25 | 34 | Multiple variable assignment |
26 | 35 | !! input |
27 | | -{{#inline: a = b = 3; a + b }} |
| 36 | +{{#inline: a = b = 3; a + b; }} |
28 | 37 | !! result |
29 | 38 | <p>6 |
30 | 39 | </p> |
— | — | @@ -32,7 +41,7 @@ |
33 | 42 | !! test |
34 | 43 | Assigment with arithmetics (+=, -=, etc) |
35 | 44 | !! input |
36 | | -{{#inline: a = 2; a += 3; a -= 7; a }} |
| 45 | +{{#inline: a = 2; a += 3; a -= 7; a; }} |
37 | 46 | !! result |
38 | 47 | <p>-2 |
39 | 48 | </p> |
— | — | @@ -54,7 +63,7 @@ |
55 | 64 | Equality |
56 | 65 | !! input |
57 | 66 | {{#inline: "2" == 2 & "2" !== 2 & 4 === (2 + 2) & |
58 | | -null == "" & false == null & 0 == "" }} |
| 67 | +null == "" & false == null & 0 == ""; }} |
59 | 68 | !! result |
60 | 69 | <p>1 |
61 | 70 | </p> |
— | — | @@ -63,7 +72,7 @@ |
64 | 73 | !! test |
65 | 74 | Comments |
66 | 75 | !! input |
67 | | -{{#inline: 2 + /* 2 + */ 2}} |
| 76 | +{{#inline: 2 + /* 2 + */ 2; }} |
68 | 77 | !! result |
69 | 78 | <p>4 |
70 | 79 | </p> |
— | — | @@ -72,7 +81,7 @@ |
73 | 82 | !! test |
74 | 83 | Comparsions |
75 | 84 | !! input |
76 | | -{{#inline: 2 > 1 & 2 >= 2 & 2 <= 2 & 1 < 2}} |
| 85 | +{{#inline: 2 > 1 & 2 >= 2 & 2 <= 2 & 1 < 2; }} |
77 | 86 | !! result |
78 | 87 | <p>1 |
79 | 88 | </p> |
— | — | @@ -81,7 +90,7 @@ |
82 | 91 | !! test |
83 | 92 | Tag integration |
84 | 93 | !! input |
85 | | -{{lc:<wikiscript>out("AA")</wikiscript>}} |
| 94 | +{{lc:<wikiscript>out("AA");</wikiscript>}} |
86 | 95 | !! result |
87 | 96 | <p>aa |
88 | 97 | </p> |
— | — | @@ -90,7 +99,7 @@ |
91 | 100 | !! test |
92 | 101 | Conditions (?) |
93 | 102 | !! input |
94 | | -{{#inline: 2 + 2 == 4 ? "a" : "b"}} |
| 103 | +{{#inline: 2 + 2 == 4 ? "a" : "b";}} |
95 | 104 | !! result |
96 | 105 | <p>a |
97 | 106 | </p> |
— | — | @@ -100,13 +109,13 @@ |
101 | 110 | Conditions (if-then, if-then-else) |
102 | 111 | !! input |
103 | 112 | <wikiscript> |
104 | | -if 2 * 7 > 3 * 4 then |
105 | | - a = 7 |
106 | | -else { |
| 113 | +if( 2 * 7 > 3 * 4 ) { |
| 114 | + a = 7; |
| 115 | +} else { |
107 | 116 | a = 10; |
108 | | -}; |
| 117 | +} |
109 | 118 | |
110 | | -if a ** 2 < 50 then |
| 119 | +if( a ** 2 < 50 ) |
111 | 120 | out( "ok" ); |
112 | 121 | </wikiscript> |
113 | 122 | !! result |
— | — | @@ -118,7 +127,7 @@ |
119 | 128 | Template:Bullets |
120 | 129 | !! text |
121 | 130 | <wikiscript> |
122 | | -foreach a in args() do |
| 131 | +foreach( a in args() ) |
123 | 132 | out( "* " + a + "\n" ); |
124 | 133 | </wikiscript> |
125 | 134 | !! endarticle |
— | — | @@ -138,7 +147,7 @@ |
139 | 148 | !! article |
140 | 149 | Template:TranscludedSwitch |
141 | 150 | !! text |
142 | | -{{#inline: isTranscluded() ? arg(1) : "?!"}} |
| 151 | +{{#inline: isTranscluded() ? arg(1) : "?!";}} |
143 | 152 | !! endarticle |
144 | 153 | |
145 | 154 | !! test |
— | — | @@ -153,7 +162,7 @@ |
154 | 163 | !! test |
155 | 164 | Empty argument handling check |
156 | 165 | !! input |
157 | | -{{#inline: arg("test") === null}} |
| 166 | +{{#inline: arg("test") === null;}} |
158 | 167 | !! result |
159 | 168 | <p>1 |
160 | 169 | </p> |
— | — | @@ -162,7 +171,7 @@ |
163 | 172 | !! test |
164 | 173 | Casts |
165 | 174 | !! input |
166 | | -{{#inline: string(float(2)) === "2.0" & int(7.99) === 7}} |
| 175 | +{{#inline: string(float(2)) === "2.0" & int(7.99) === 7;}} |
167 | 176 | !! result |
168 | 177 | <p>1 |
169 | 178 | </p> |
— | — | @@ -171,7 +180,12 @@ |
172 | 181 | !! test |
173 | 182 | Exception handling |
174 | 183 | !! input |
175 | | -{{#inline: try 2 / 0 catch e e }} |
| 184 | +<wikiscript> |
| 185 | +try |
| 186 | + 2 / 0; |
| 187 | +catch( e ) |
| 188 | + out( e ); |
| 189 | +</wikiscript> |
176 | 190 | !! result |
177 | 191 | <p>dividebyzero |
178 | 192 | </p> |
— | — | @@ -199,7 +213,7 @@ |
200 | 214 | String functions 1 |
201 | 215 | !! input |
202 | 216 | {{#inline: lc( 'FOO' ) == 'foo' & uc( 'foo' ) == 'FOO' & |
203 | | -ucfirst( 'bar' ) == 'Bar' & urlencode( 'a="b"' ) == "a%3D%22b%22" }} |
| 217 | +ucfirst( 'bar' ) == 'Bar' & urlencode( 'a="b"' ) == "a%3D%22b%22"; }} |
204 | 218 | !! result |
205 | 219 | <p>1 |
206 | 220 | </p> |
— | — | @@ -209,7 +223,7 @@ |
210 | 224 | String functions 2 |
211 | 225 | !! input |
212 | 226 | {{#inline: strlen( "тест" ) == 4 & substr( "слово", 1, 2 ) == "ло" & |
213 | | - strreplace( "abcd", 'bc', 'ad' ) == 'aadd' |
| 227 | + strreplace( "abcd", 'bc', 'ad' ) == 'aadd'; |
214 | 228 | }} |
215 | 229 | !! result |
216 | 230 | <p>1 |
— | — | @@ -219,7 +233,7 @@ |
220 | 234 | !! test |
221 | 235 | split()/join() |
222 | 236 | !! input |
223 | | -{{#inline: join( '!', split( ':', 'a:b:c:d' ) ) + join( ' ', '', 'e', 'f' ) }} |
| 237 | +{{#inline: join( '!', split( ':', 'a:b:c:d' ) ) + join( ' ', '', 'e', 'f' ); }} |
224 | 238 | !! result |
225 | 239 | <p>a!b!c!d e f |
226 | 240 | </p> |
— | — | @@ -238,6 +252,17 @@ |
239 | 253 | </p> |
240 | 254 | !! end |
241 | 255 | |
| 256 | +!! test |
| 257 | +in/contains |
| 258 | +!! input |
| 259 | +<wikiscript> |
| 260 | +out( int( "a" in "b" + "c" in "cd" + "foobar" contains "oo" + "foobar" contains "baz" ) ); |
| 261 | +</wikiscript> |
| 262 | +!! result |
| 263 | +<p>2 |
| 264 | +</p> |
| 265 | +!! end |
| 266 | + |
242 | 267 | # |
243 | 268 | ## Lists |
244 | 269 | # |
— | — | @@ -246,7 +271,7 @@ |
247 | 272 | !! input |
248 | 273 | <wikiscript> |
249 | 274 | a = [ b = "a", b = "b", b = "c" ]; |
250 | | -out( a[1] + b ) |
| 275 | +out( a[1] + b ); |
251 | 276 | </wikiscript> |
252 | 277 | !! result |
253 | 278 | <p>bc |
— | — | @@ -258,7 +283,7 @@ |
259 | 284 | !! input |
260 | 285 | <wikiscript> |
261 | 286 | a = [ 1, 2, 3, 4, 5 ]; |
262 | | -foreach n in a do |
| 287 | +foreach( n in a ) |
263 | 288 | out( n * n + "\n\n"); |
264 | 289 | </wikiscript> |
265 | 290 | !! result |
— | — | @@ -274,7 +299,7 @@ |
275 | 300 | List merging |
276 | 301 | !! input |
277 | 302 | <wikiscript> |
278 | | -foreach element in [ 7, 4 ] + [ 2, 8 ] do |
| 303 | +foreach( element in [ 7, 4 ] + [ 2, 8 ] ) |
279 | 304 | out( element ); |
280 | 305 | </wikiscript> |
281 | 306 | !! result |
— | — | @@ -287,12 +312,14 @@ |
288 | 313 | !! input |
289 | 314 | <wikiscript> |
290 | 315 | a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; |
291 | | -foreach e in a do { |
292 | | - if e >= 6 & e < 9 then continue; |
| 316 | +foreach( e in a ) { |
| 317 | + if( e >= 6 & e < 9 ) |
| 318 | + continue; |
293 | 319 | out( e ); |
294 | | -}; |
295 | | -foreach e in a do { |
296 | | - if e == 3 then break; |
| 320 | +} |
| 321 | +foreach( e in a ) { |
| 322 | + if( e == 3 ) |
| 323 | + break; |
297 | 324 | out( e ); |
298 | 325 | } |
299 | 326 | </wikiscript> |
— | — | @@ -314,9 +341,9 @@ |
315 | 342 | <p>2 |
316 | 343 | 3 |
317 | 344 | 1 |
318 | | -</p><p>3 |
| 345 | +3 |
319 | 346 | 6 |
320 | | -</p><p>7 |
| 347 | +7 |
321 | 348 | </p> |
322 | 349 | !! end |
323 | 350 | |
— | — | @@ -331,66 +358,3 @@ |
332 | 359 | <p>2 |
333 | 360 | </p> |
334 | 361 | !! end |
335 | | - |
336 | | -# |
337 | | -## Error handling |
338 | | -# |
339 | | -!! test |
340 | | -Error handling: unexcepted token |
341 | | -!! input |
342 | | -{{#inline: 2 2}} |
343 | | -!! result |
344 | | -<p><strong class="error">Unexpected token at char 3</strong> |
345 | | -</p> |
346 | | -!! end |
347 | | - |
348 | | -!! test |
349 | | -Error handling: missing second argument |
350 | | -!! input |
351 | | -{{#inline: 2 + }} |
352 | | -!! result |
353 | | -<p><strong class="error">Not enough aruments for operator at char 3</strong> |
354 | | -</p> |
355 | | -!! end |
356 | | - |
357 | | -!! test |
358 | | -Error handling: token limit |
359 | | -!! config |
360 | | -wgInlineScriptsParserParams=array('parserClass'=>'ISCodeParserShuntingYard', 'limits' => array( 'tokens' => 10, 'evaluations' => 1000, 'depth' => 100 ) ) |
361 | | -!! input |
362 | | -{{#inline: 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2}} |
363 | | -!! result |
364 | | -<p><strong class="error">Exceeded tokens limit</strong> |
365 | | -</p> |
366 | | -!! end |
367 | | - |
368 | | -!! test |
369 | | -Error handling: evaluations limit |
370 | | -!! config |
371 | | -wgInlineScriptsParserParams=array('parserClass'=>'ISCodeParserShuntingYard', 'limits' => array( 'tokens' => 25000, 'evaluations' => 5, 'depth' => 100 ) ) |
372 | | -!! input |
373 | | -{{#inline: 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2}} |
374 | | -!! result |
375 | | -<p><strong class="error">Exceeded evaluations limit</strong> |
376 | | -</p> |
377 | | -!! end |
378 | | - |
379 | | -!! test |
380 | | -Error handling: AST depth limit |
381 | | -!! config |
382 | | -wgInlineScriptsParserParams=array('parserClass'=>'ISCodeParserShuntingYard', 'limits' => array( 'tokens' => 25000, 'evaluations' => 1000, 'depth' => 5 ) ) |
383 | | -!! input |
384 | | -{{#inline: 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 * 2 + 2 + 2}} |
385 | | -!! result |
386 | | -<p><strong class="error">Too deep abstract syntax tree</strong> |
387 | | -</p> |
388 | | -!! end |
389 | | - |
390 | | -!! test |
391 | | -Error handling: missing arguments |
392 | | -!! input |
393 | | -{{#inline: arg()}} |
394 | | -!! result |
395 | | -<p><strong class="error">Not enough arguments for function at char 3</strong> |
396 | | -</p> |
397 | | -!! end |
Index: trunk/extensions/InlineScripts/InlineScripts.i18n.php |
— | — | @@ -11,27 +11,22 @@ |
12 | 12 | * @author Victor Vasiliev |
13 | 13 | */ |
14 | 14 | $messages['en'] = array( |
15 | | - 'inlinescripts-desc' => 'Provides inline script interpreter', |
| 15 | + 'inlinescripts-desc' => 'Provides a build into wikitext scripting language', |
16 | 16 | |
| 17 | + 'inlinescripts-exception-unexceptedtoken' => 'Unexpected token $1 at line $2: expected $3', |
17 | 18 | 'inlinescripts-exception-unclosedstring' => 'Unclosed string at char $1', |
18 | 19 | 'inlinescripts-exception-unrecognisedtoken' => 'Unrecognized token at char $1', |
19 | 20 | 'inlinescripts-exception-toomanytokens' => 'Exceeded tokens limit', |
20 | | - 'inlinescripts-exception-toomanyevals' => 'Exceeded evaluations limit', |
| 21 | + 'inlinescripts-exception-toomanyevals' => 'Exceeded evaluations limit at line $1', |
21 | 22 | 'inlinescripts-exception-recoverflow' => 'Too deep abstract syntax tree', |
22 | | - 'inlinescripts-exception-expectingdata' => 'Unexpected token at char $1', |
23 | | - 'inlinescripts-exception-expectingoperator' => 'Unexpected token at char $1', |
24 | | - 'inlinescripts-exception-cantchangeconst' => 'Cannot assign value to a constant at char $1', |
25 | | - 'inlinescripts-exception-expectednotfound' => 'Expected $2, but not found at char $1', |
26 | | - 'inlinescripts-exception-unbalancedbraces' => 'Unbalanced parenthesis at char $1', |
27 | | - 'inlinescripts-exception-notanarray' => 'Tried to get an element of a non-array at char $1', |
28 | | - 'inlinescripts-exception-outofbounds' => 'Got out of array bounds at char $1', |
29 | | - 'inlinescripts-exception-invalidforeach' => 'Invalid argument supplied for foreach at char $1', |
30 | | - 'inlinescripts-exception-unexceptedop' => 'Unexpected operator $2', |
31 | | - 'inlinescripts-exception-notenoughargs' => 'Not enough arguments for function at char $1', |
32 | | - 'inlinescripts-exception-notenoughopargs' => 'Not enough aruments for operator at char $1', |
33 | | - 'inlinescripts-exception-dividebyzero' => 'Division by zero at char $1', |
34 | | - 'inlinescripts-exception-break' => '"break" called outside of foreach at char $1', |
35 | | - 'inlinescripts-exception-continue' => '"continue" called outside of foreach at char $1', |
| 23 | + 'inlinescripts-exception-notanarray' => 'Tried to get an element of a non-array at line $1', |
| 24 | + 'inlinescripts-exception-outofbounds' => 'Got out of array bounds at line $1', |
| 25 | + 'inlinescripts-exception-notenoughargs' => 'Not enough arguments for function at line $1', |
| 26 | + 'inlinescripts-exception-dividebyzero' => 'Division by zero at line $1', |
| 27 | + 'inlinescripts-exception-break' => '"break" called outside of foreach at line $1', |
| 28 | + 'inlinescripts-exception-continue' => '"continue" called outside of foreach at line $1', |
| 29 | + 'inlinescripts-exception-emptyidx' => 'Trying to get a value of an empty index at line $1', |
| 30 | + 'inlinescripts-exception-unknownvar' => 'Trying to use an undeclared variable at line $1', |
36 | 31 | ); |
37 | 32 | |
38 | 33 | // == Magic words == |
Index: trunk/extensions/InlineScripts/InlineScripts.php |
— | — | @@ -27,7 +27,7 @@ |
28 | 28 | 'path' => __FILE__, |
29 | 29 | 'name' => 'InlineScripts', |
30 | 30 | 'author' => 'Victor Vasiliev', |
31 | | - 'description' => 'Provides inline script interpreter', |
| 31 | + 'description' => 'Provides a build into wikitext scripting language', |
32 | 32 | 'descriptionmsg' => 'inlinescriprs-desc', |
33 | 33 | 'url' => 'http://www.mediawiki.org/wiki/Extension:InlineScripts', |
34 | 34 | ); |
— | — | @@ -35,37 +35,34 @@ |
36 | 36 | $dir = dirname(__FILE__) . '/'; |
37 | 37 | $wgExtensionMessagesFiles['InlineScripts'] = $dir . 'InlineScripts.i18n.php'; |
38 | 38 | $wgAutoloadClasses['InlineScriptInterpreter'] = $dir . 'interpreter/Interpreter.php'; |
39 | | -$wgAutoloadClasses['ISCodeParserShuntingYard'] = $dir . 'interpreter/ParserShuntingYard.php'; |
| 39 | +$wgAutoloadClasses['ISScanner'] = $dir . 'interpreter/Scanner.php'; |
| 40 | +$wgAutoloadClasses['ISLRParser'] = $dir . 'interpreter/LRParser.php'; |
40 | 41 | $wgParserTestFiles[] = $dir . 'interpreterTests.txt'; |
41 | 42 | $wgHooks['ParserFirstCallInit'][] = 'InlineScriptsHooks::setupParserHook'; |
42 | 43 | $wgHooks['ParserClearState'][] = 'InlineScriptsHooks::clearState'; |
43 | 44 | $wgHooks['ParserLimitReport'][] = 'InlineScriptsHooks::reportLimits'; |
44 | 45 | |
45 | | -$wgInlineScriptsParserParams = array( |
46 | | - /* Name of the code-to-AST parser class */ |
47 | | - 'parserClass' => 'ISCodeParserShuntingYard', |
48 | | - |
49 | | - /* Different sanity limits */ |
50 | | - 'limits' => array( |
51 | | - /** |
52 | | - * Maximal amount of tokens (strings, keywords, numbers, operators, |
53 | | - * but not whitespace) to be parsed. |
54 | | - */ |
55 | | - 'tokens' => 25000, |
56 | | - /** |
57 | | - * Maximal amount of operations (multiplications, comarsionss, function |
58 | | - * calls) to be done. |
59 | | - */ |
60 | | - 'evaluations' => 10000, |
61 | | - /** |
62 | | - * Maximal depth of recursion when evaluating the parser tree. For |
63 | | - * example 2 + 2 * 2 ** 2 is parsed to (2 + (2 * (2 ** 2))) and needs |
64 | | - * depth 3 to be parsed. |
65 | | - */ |
66 | | - 'depth' => 100, |
67 | | - ), |
| 46 | +$wgInlineScriptsLimits = array( |
| 47 | + /** |
| 48 | + * Maximal amount of tokens (strings, keywords, numbers, operators, |
| 49 | + * but not whitespace) to be parsed. |
| 50 | + */ |
| 51 | + 'tokens' => 25000, |
| 52 | + /** |
| 53 | + * Maximal amount of operations (multiplications, comarsionss, function |
| 54 | + * calls) to be done. |
| 55 | + */ |
| 56 | + 'evaluations' => 10000, |
| 57 | + /** |
| 58 | + * Maximal depth of recursion when evaluating the parser tree. For |
| 59 | + * example 2 + 2 * 2 ** 2 is parsed to (2 + (2 * (2 ** 2))) and needs |
| 60 | + * depth 3 to be parsed. |
| 61 | + */ |
| 62 | + 'depth' => 250, |
68 | 63 | ); |
69 | 64 | |
| 65 | +$wgInlineScriptsParserClass = 'ISLRParser'; |
| 66 | + |
70 | 67 | class InlineScriptsHooks { |
71 | 68 | static $scriptParser = null; |
72 | 69 | |
— | — | @@ -87,7 +84,7 @@ |
88 | 85 | |
89 | 86 | public static function inlineHook( &$parser, $frame, $args ) { |
90 | 87 | wfProfileIn( __METHOD__ ); |
91 | | - $scriptParser = self::getParser(); |
| 88 | + $scriptParser = self::getInterpreter(); |
92 | 89 | try { |
93 | 90 | $result = $scriptParser->evaluate( $parser->mStripState->unstripBoth( $args[0] ), |
94 | 91 | $parser, $frame ); |
— | — | @@ -102,9 +99,9 @@ |
103 | 100 | |
104 | 101 | public static function scriptHook( &$parser, $frame, $code, $attribs ) { |
105 | 102 | wfProfileIn( __METHOD__ ); |
106 | | - $scriptParser = self::getParser(); |
| 103 | + $scriptParser = self::getInterpreter(); |
107 | 104 | try { |
108 | | - $result = $scriptParser->evaluateForOutput( $code, $parser, $frame ); |
| 105 | + $result = $scriptParser->execute( $code, $parser, $frame ); |
109 | 106 | } catch( ISException $e ) { |
110 | 107 | $msg = nl2br( htmlspecialchars( $e->getMessage() ) ); |
111 | 108 | wfProfileOut( __METHOD__ ); |
— | — | @@ -127,7 +124,7 @@ |
128 | 125 | return true; |
129 | 126 | } |
130 | 127 | |
131 | | - public static function getParser() { |
| 128 | + public static function getInterpreter() { |
132 | 129 | if( !self::$scriptParser ) |
133 | 130 | self::$scriptParser = new InlineScriptInterpreter(); |
134 | 131 | return self::$scriptParser; |