r59345 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r59344‎ | r59345 | r59346 >
Date:20:55, 22 November 2009
Author:vasilievvv
Status:deferred
Tags:
Comment:
Commit a rewritten version of an inline scripts interpreter.
Modified paths:
  • /trunk/extensions/InlineScripts/InlineScripts.i18n.php (modified) (history)
  • /trunk/extensions/InlineScripts/InlineScripts.php (modified) (history)
  • /trunk/extensions/InlineScripts/interpreter/Data.php (added) (history)
  • /trunk/extensions/InlineScripts/interpreter/Interpreter.php (modified) (history)
  • /trunk/extensions/InlineScripts/interpreter/LRParser.php (added) (history)
  • /trunk/extensions/InlineScripts/interpreter/LRTable.php (added) (history)
  • /trunk/extensions/InlineScripts/interpreter/ParserShuntingYard.php (deleted) (history)
  • /trunk/extensions/InlineScripts/interpreter/Scanner.php (added) (history)
  • /trunk/extensions/InlineScripts/interpreter/Shared.php (added) (history)
  • /trunk/extensions/InlineScripts/interpreter/Utils.php (deleted) (history)
  • /trunk/extensions/InlineScripts/interpreter/buildLRTables.php (added) (history)
  • /trunk/extensions/InlineScripts/interpreter/syntax.txt (added) (history)
  • /trunk/extensions/InlineScripts/interpreterTests.txt (modified) (history)

Diff [purge]

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
1260 + 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
1306 + 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
185 + 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
138 + native
Index: trunk/extensions/InlineScripts/interpreter/Interpreter.php
@@ -1,22 +1,18 @@
22 <?php
33 /**
4 - * Interpreter for MediaWiki inline scripts
 4+ * Interpreter for MediaWiki inline scripts.
55 * Copyright (C) Victor Vasiliev, Andrew Garrett, 2008-2009.
66 * Distributed under GNU GPL v2 or later terms.
77 */
88
9 -require_once( 'Utils.php' );
 9+require_once( 'Shared.php' );
 10+require_once( 'Data.php' );
1011
1112 class InlineScriptInterpreter {
12 - /**
13 - * Used to invalidate AST cache. Increment whenever you change
14 - * code parser or $mFunctions/$mOps
15 - */
1613 const ParserVersion = 1;
1714
1815 var $mVars, $mOut, $mParser, $mFrame, $mCodeParser;
1916
20 - // length,lcase,ccnorm,rmdoubles,specialratio,rmspecials,norm,count
2117 static $mFunctions = array(
2218 'out' => 'funcOut',
2319
@@ -50,33 +46,10 @@
5147 'bool' => 'castBool',
5248 );
5349
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 -
7750 public function __construct() {
78 - global $wgInlineScriptsParserParams;
 51+ global $wgInlineScriptsParserClass;
7952 $this->resetState();
80 - $this->mCodeParser = new $wgInlineScriptsParserParams['parserClass']( $this );
 53+ $this->mCodeParser = new $wgInlineScriptsParserClass( $this );
8154 }
8255
8356 public function resetState() {
@@ -85,33 +58,32 @@
8659 }
8760
8861 protected function checkRecursionLimit( $rec ) {
89 - global $wgInlineScriptsParserParams;
 62+ global $wgInlineScriptsLimits;
9063 if( $rec > $this->mParser->is_maxDepth )
9164 $this->mParser->is_maxDepth = $rec;
92 - return $rec <= $wgInlineScriptsParserParams['limits']['depth'];
 65+ return $rec <= $wgInlineScriptsLimits['depth'];
9366 }
9467
9568 protected function increaseEvaluationsCount() {
96 - global $wgInlineScriptsParserParams;
 69+ global $wgInlineScriptsLimits;
9770 $this->mParser->is_evalsCount++;
98 - return $this->mParser->is_evalsCount <= $wgInlineScriptsParserParams['limits']['evaluations'];
 71+ return $this->mParser->is_evalsCount <= $wgInlineScriptsLimits['evaluations'];
9972 }
10073
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;
10577 }
10678
107 - public function evaluateForOutput( $code, $parser, $frame, $resetState = true ) {
 79+ public function execute( $code, $parser, $frame, $resetState = true ) {
10880 wfProfileIn( __METHOD__ );
10981 if( $resetState )
11082 $this->resetState();
11183 $this->mParser = $parser;
11284 $this->mFrame = $frame;
11385
114 - $ast = $this->getCodeAST( $code );
115 - $this->evaluateASTNode( $ast );
 86+ $ast = $this->parseCode( $code );
 87+ $this->evaluateNode( $ast, 0 );
11688 wfProfileOut( __METHOD__ );
11789 return $this->mOut;
11890 }
@@ -123,330 +95,356 @@
12496 $this->mParser = $parser;
12597 $this->mFrame = $frame;
12698
127 - $ast = $this->getCodeAST( $code );
 99+ $ast = $this->parseCode( $code );
128100 wfProfileOut( __METHOD__ );
129 - return $this->evaluateASTNode( $ast )->toString();
 101+ return $this->evaluateNode( $ast, 0 )->toString();
130102 }
131103
132 - public function getCodeAST( $code ) {
 104+ public function parseCode( $code ) {
133105 global $parserMemc;
134 - static $ASTCache;
 106+ static $parserCache; // Unserializing can be expensive as well
135107
136108 wfProfileIn( __METHOD__ );
137109 $code = trim( $code );
138110
139111 $memcKey = 'isparser:ast:' . md5( $code );
140 - if( isset( $ASTCache[$memcKey] ) ) {
 112+
 113+ if( isset( $parserCache[$memcKey] ) ) {
141114 wfProfileOut( __METHOD__ );
142 - return $ASTCache[$memcKey];
 115+ return $parserCache[$memcKey];
143116 }
144117
145118 $cached = $parserMemc->get( $memcKey );
146119 if( @$cached instanceof ISParserOutput && !$cached->isOutOfDate() ) {
147120 $cached->appendTokenCount( $this );
148 - $ASTCache[$memcKey] = $cached->getAST();
 121+ $parserCache[$memcKey] = $cached->getParserTree();
149122 wfProfileOut( __METHOD__ );
150 - return $cached->getAST();
 123+ return $cached->getParserTree();
151124 }
152125
153 - $out = $this->mCodeParser->parse( $code );
 126+ $scanner = new ISScanner( $code );
 127+ $out = $this->mCodeParser->parse( $scanner, $this->getMaxTokensLeft() );
 128+
 129+ $out->appendTokenCount( $this );
154130 $parserMemc->set( $memcKey, $out );
155 - $ASTCache[$memcKey] = $out->getAST();
 131+ $parserCache[$memcKey] = $out->getParserTree();
 132+
156133 wfProfileOut( __METHOD__ );
157 - return $out->getAST();
 134+ return $out->getParserTree();
158135 }
159136
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' );
163140 }
164141
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();
207149 }
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+ }
222203 } else {
223 - return new ISData( ISData::DBool, false );
 204+ return $this->evaluateNode( $c[0], $rec + 1 );
224205 }
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;
229211 } 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;
231218 }
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 );
273223 } 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 );
279225 }
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}" );
287244 }
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 );
310285 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 );
322299 } 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}" );
345309 }
346310 }
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 );
361331 }
362 - return $val;
363332 } 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 );
368342 }
369343 }
 344+ default:
 345+ $type = $node->getType();
 346+ throw new ISException( "Invalid node type passed to evaluateNode(): {$type}" );
 347+ }
 348+ }
370349
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();
385363 }
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+ }
400372
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];
404402 } else {
405 - throw new ISUserVisibleException( 'cantchangeconst', $ast->getPos() );
 403+ throw new ISUserVisibleException( 'notanarray', $line );
406404 }
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];
425409 }
426410 }
427411
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 );
436430 else
437 - return new ISData();
 431+ $idx = null;
 432+ }
 433+ $this->mVars[$varname]->setValueByIndices( $newval, $idxs, $lineno );
 434+ } else {
 435+ $this->mVars[$varname] = $newval;
438436 }
439437 }
440438
441 - protected function getValueForSetting( $old, $new, $set ) {
 439+ protected function getValueForSetting( $old, $new, $set, $line ) {
442440 switch( $set ) {
443 - case ISOperatorNode::OSetAdd:
 441+ case '+=':
444442 return ISData::sum( $old, $new );
445 - case ISOperatorNode::OSetSub:
 443+ case '-=':
446444 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 );
451449 default:
452450 return $new;
453451 }
@@ -457,6 +455,55 @@
458456 throw new ISUserVisibleException( 'notenoughargs', $pos );
459457 }
460458
 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+
461508 /** Functions */
462509 protected function funcOut( $args, $pos ) {
463510 $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
11810 + 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
1173 + 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
1538 + native
Index: trunk/extensions/InlineScripts/interpreterTests.txt
@@ -3,13 +3,22 @@
44 !! test
55 Basic mathematics
66 !! input
7 -{{#inline:2 + 2 * 2 ** 2 - 3 * 7 % 5}}
 7+{{#inline:-2 + 2 * 2 ** 2 - 3 * 7 % 5;}}
88 !! result
9 -<p>9
 9+<p>5
1010 </p>
1111 !! end
1212
1313 !! test
 14+** associativity
 15+!! input
 16+{{#inline: 4 ** 3 ** 2;}}<!-- Not 4096! -->
 17+!! result
 18+<p>262144
 19+</p>
 20+!! end
 21+
 22+!! test
1423 String contecation and out()
1524 !! input
1625 <wikiscript>
@@ -23,7 +32,7 @@
2433 !! test
2534 Multiple variable assignment
2635 !! input
27 -{{#inline: a = b = 3; a + b }}
 36+{{#inline: a = b = 3; a + b; }}
2837 !! result
2938 <p>6
3039 </p>
@@ -32,7 +41,7 @@
3342 !! test
3443 Assigment with arithmetics (+=, -=, etc)
3544 !! input
36 -{{#inline: a = 2; a += 3; a -= 7; a }}
 45+{{#inline: a = 2; a += 3; a -= 7; a; }}
3746 !! result
3847 <p>-2
3948 </p>
@@ -54,7 +63,7 @@
5564 Equality
5665 !! input
5766 {{#inline: "2" == 2 & "2" !== 2 & 4 === (2 + 2) &
58 -null == "" & false == null & 0 == "" }}
 67+null == "" & false == null & 0 == ""; }}
5968 !! result
6069 <p>1
6170 </p>
@@ -63,7 +72,7 @@
6473 !! test
6574 Comments
6675 !! input
67 -{{#inline: 2 + /* 2 + */ 2}}
 76+{{#inline: 2 + /* 2 + */ 2; }}
6877 !! result
6978 <p>4
7079 </p>
@@ -72,7 +81,7 @@
7382 !! test
7483 Comparsions
7584 !! input
76 -{{#inline: 2 > 1 & 2 >= 2 & 2 <= 2 & 1 < 2}}
 85+{{#inline: 2 > 1 & 2 >= 2 & 2 <= 2 & 1 < 2; }}
7786 !! result
7887 <p>1
7988 </p>
@@ -81,7 +90,7 @@
8291 !! test
8392 Tag integration
8493 !! input
85 -{{lc:<wikiscript>out("AA")</wikiscript>}}
 94+{{lc:<wikiscript>out("AA");</wikiscript>}}
8695 !! result
8796 <p>aa
8897 </p>
@@ -90,7 +99,7 @@
91100 !! test
92101 Conditions (?)
93102 !! input
94 -{{#inline: 2 + 2 == 4 ? "a" : "b"}}
 103+{{#inline: 2 + 2 == 4 ? "a" : "b";}}
95104 !! result
96105 <p>a
97106 </p>
@@ -100,13 +109,13 @@
101110 Conditions (if-then, if-then-else)
102111 !! input
103112 <wikiscript>
104 -if 2 * 7 > 3 * 4 then
105 - a = 7
106 -else {
 113+if( 2 * 7 > 3 * 4 ) {
 114+ a = 7;
 115+} else {
107116 a = 10;
108 -};
 117+}
109118
110 -if a ** 2 < 50 then
 119+if( a ** 2 < 50 )
111120 out( "ok" );
112121 </wikiscript>
113122 !! result
@@ -118,7 +127,7 @@
119128 Template:Bullets
120129 !! text
121130 <wikiscript>
122 -foreach a in args() do
 131+foreach( a in args() )
123132 out( "* " + a + "\n" );
124133 </wikiscript>
125134 !! endarticle
@@ -138,7 +147,7 @@
139148 !! article
140149 Template:TranscludedSwitch
141150 !! text
142 -{{#inline: isTranscluded() ? arg(1) : "?!"}}
 151+{{#inline: isTranscluded() ? arg(1) : "?!";}}
143152 !! endarticle
144153
145154 !! test
@@ -153,7 +162,7 @@
154163 !! test
155164 Empty argument handling check
156165 !! input
157 -{{#inline: arg("test") === null}}
 166+{{#inline: arg("test") === null;}}
158167 !! result
159168 <p>1
160169 </p>
@@ -162,7 +171,7 @@
163172 !! test
164173 Casts
165174 !! input
166 -{{#inline: string(float(2)) === "2.0" & int(7.99) === 7}}
 175+{{#inline: string(float(2)) === "2.0" & int(7.99) === 7;}}
167176 !! result
168177 <p>1
169178 </p>
@@ -171,7 +180,12 @@
172181 !! test
173182 Exception handling
174183 !! input
175 -{{#inline: try 2 / 0 catch e e }}
 184+<wikiscript>
 185+try
 186+ 2 / 0;
 187+catch( e )
 188+ out( e );
 189+</wikiscript>
176190 !! result
177191 <p>dividebyzero
178192 </p>
@@ -199,7 +213,7 @@
200214 String functions 1
201215 !! input
202216 {{#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"; }}
204218 !! result
205219 <p>1
206220 </p>
@@ -209,7 +223,7 @@
210224 String functions 2
211225 !! input
212226 {{#inline: strlen( "тест" ) == 4 & substr( "слово", 1, 2 ) == "ло" &
213 - strreplace( "abcd", 'bc', 'ad' ) == 'aadd'
 227+ strreplace( "abcd", 'bc', 'ad' ) == 'aadd';
214228 }}
215229 !! result
216230 <p>1
@@ -219,7 +233,7 @@
220234 !! test
221235 split()/join()
222236 !! input
223 -{{#inline: join( '!', split( ':', 'a:b:c:d' ) ) + join( ' ', '', 'e', 'f' ) }}
 237+{{#inline: join( '!', split( ':', 'a:b:c:d' ) ) + join( ' ', '', 'e', 'f' ); }}
224238 !! result
225239 <p>a!b!c!d e f
226240 </p>
@@ -238,6 +252,17 @@
239253 </p>
240254 !! end
241255
 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+
242267 #
243268 ## Lists
244269 #
@@ -246,7 +271,7 @@
247272 !! input
248273 <wikiscript>
249274 a = [ b = "a", b = "b", b = "c" ];
250 -out( a[1] + b )
 275+out( a[1] + b );
251276 </wikiscript>
252277 !! result
253278 <p>bc
@@ -258,7 +283,7 @@
259284 !! input
260285 <wikiscript>
261286 a = [ 1, 2, 3, 4, 5 ];
262 -foreach n in a do
 287+foreach( n in a )
263288 out( n * n + "\n\n");
264289 </wikiscript>
265290 !! result
@@ -274,7 +299,7 @@
275300 List merging
276301 !! input
277302 <wikiscript>
278 -foreach element in [ 7, 4 ] + [ 2, 8 ] do
 303+foreach( element in [ 7, 4 ] + [ 2, 8 ] )
279304 out( element );
280305 </wikiscript>
281306 !! result
@@ -287,12 +312,14 @@
288313 !! input
289314 <wikiscript>
290315 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;
293319 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;
297324 out( e );
298325 }
299326 </wikiscript>
@@ -314,9 +341,9 @@
315342 <p>2
316343 3
317344 1
318 -</p><p>3
 345+3
319346 6
320 -</p><p>7
 347+7
321348 </p>
322349 !! end
323350
@@ -331,66 +358,3 @@
332359 <p>2
333360 </p>
334361 !! 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 @@
1212 * @author Victor Vasiliev
1313 */
1414 $messages['en'] = array(
15 - 'inlinescripts-desc' => 'Provides inline script interpreter',
 15+ 'inlinescripts-desc' => 'Provides a build into wikitext scripting language',
1616
 17+ 'inlinescripts-exception-unexceptedtoken' => 'Unexpected token $1 at line $2: expected $3',
1718 'inlinescripts-exception-unclosedstring' => 'Unclosed string at char $1',
1819 'inlinescripts-exception-unrecognisedtoken' => 'Unrecognized token at char $1',
1920 'inlinescripts-exception-toomanytokens' => 'Exceeded tokens limit',
20 - 'inlinescripts-exception-toomanyevals' => 'Exceeded evaluations limit',
 21+ 'inlinescripts-exception-toomanyevals' => 'Exceeded evaluations limit at line $1',
2122 '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',
3631 );
3732
3833 // == Magic words ==
Index: trunk/extensions/InlineScripts/InlineScripts.php
@@ -27,7 +27,7 @@
2828 'path' => __FILE__,
2929 'name' => 'InlineScripts',
3030 'author' => 'Victor Vasiliev',
31 - 'description' => 'Provides inline script interpreter',
 31+ 'description' => 'Provides a build into wikitext scripting language',
3232 'descriptionmsg' => 'inlinescriprs-desc',
3333 'url' => 'http://www.mediawiki.org/wiki/Extension:InlineScripts',
3434 );
@@ -35,37 +35,34 @@
3636 $dir = dirname(__FILE__) . '/';
3737 $wgExtensionMessagesFiles['InlineScripts'] = $dir . 'InlineScripts.i18n.php';
3838 $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';
4041 $wgParserTestFiles[] = $dir . 'interpreterTests.txt';
4142 $wgHooks['ParserFirstCallInit'][] = 'InlineScriptsHooks::setupParserHook';
4243 $wgHooks['ParserClearState'][] = 'InlineScriptsHooks::clearState';
4344 $wgHooks['ParserLimitReport'][] = 'InlineScriptsHooks::reportLimits';
4445
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,
6863 );
6964
 65+$wgInlineScriptsParserClass = 'ISLRParser';
 66+
7067 class InlineScriptsHooks {
7168 static $scriptParser = null;
7269
@@ -87,7 +84,7 @@
8885
8986 public static function inlineHook( &$parser, $frame, $args ) {
9087 wfProfileIn( __METHOD__ );
91 - $scriptParser = self::getParser();
 88+ $scriptParser = self::getInterpreter();
9289 try {
9390 $result = $scriptParser->evaluate( $parser->mStripState->unstripBoth( $args[0] ),
9491 $parser, $frame );
@@ -102,9 +99,9 @@
103100
104101 public static function scriptHook( &$parser, $frame, $code, $attribs ) {
105102 wfProfileIn( __METHOD__ );
106 - $scriptParser = self::getParser();
 103+ $scriptParser = self::getInterpreter();
107104 try {
108 - $result = $scriptParser->evaluateForOutput( $code, $parser, $frame );
 105+ $result = $scriptParser->execute( $code, $parser, $frame );
109106 } catch( ISException $e ) {
110107 $msg = nl2br( htmlspecialchars( $e->getMessage() ) );
111108 wfProfileOut( __METHOD__ );
@@ -127,7 +124,7 @@
128125 return true;
129126 }
130127
131 - public static function getParser() {
 128+ public static function getInterpreter() {
132129 if( !self::$scriptParser )
133130 self::$scriptParser = new InlineScriptInterpreter();
134131 return self::$scriptParser;

Status & tagging log