r94508 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r94507‎ | r94508 | r94509 >
Date:13:04, 15 August 2011
Author:vasilievvv
Status:deferred
Tags:
Comment:
Rename InlineScripts to WikiScripts, to reflect the fact they are no longer inline.
Modified paths:
  • /trunk/extensions/InlineScripts (deleted) (history)
  • /trunk/extensions/WikiScripts (added) (history)

Diff [purge]

Index: trunk/extensions/WikiScripts/geshi/wikiscript.php
@@ -0,0 +1,98 @@
 2+<?php
 3+/*************************************************************************************
 4+ * wikiscripts.php
 5+ * --------------
 6+ * Author: Victor Vasiliev (vasilvv@gmail.com)
 7+ * Copyright: (c) 2011 Victor Vasiliev (vasilvv@gmail.com)
 8+ * Release Version: 1.0
 9+ * Date Started: 2011/08/11
 10+ *
 11+ * WikiScripts language file for GeSHi.
 12+ *
 13+ *************************************************************************************
 14+ *
 15+ * This file is part of GeSHi and MediaWiki.
 16+ *
 17+ * GeSHi is free software; you can redistribute it and/or modify
 18+ * it under the terms of the GNU General Public License as published by
 19+ * the Free Software Foundation; either version 2 of the License, or
 20+ * (at your option) any later version.
 21+ *
 22+ * GeSHi is distributed in the hope that it will be useful,
 23+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 24+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 25+ * GNU General Public License for more details.
 26+ *
 27+ * You should have received a copy of the GNU General Public License
 28+ * along with GeSHi; if not, write to the Free Software
 29+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 30+ *
 31+ ************************************************************************************/
 32+
 33+$language_data = array (
 34+ 'LANG_NAME' => 'WikiScripts',
 35+ 'COMMENT_SINGLE' => array(1 => '//'),
 36+ 'COMMENT_MULTI' => array('/*' => '*/'),
 37+ 'CASE_KEYWORDS' => GESHI_CAPS_NO_CHANGE,
 38+ 'QUOTEMARKS' => array("'", '"'),
 39+ 'ESCAPE_CHAR' => '\\',
 40+ 'KEYWORDS' => array(
 41+ 1 => array(
 42+ 'append', 'break', 'catch', 'contains', 'continue', 'delete', 'else', 'false', 'for',
 43+ 'function', 'if', 'in', 'isset', 'null', 'return', 'self', 'then', 'true', 'try', 'yield',
 44+ ),
 45+ ),
 46+ 'SYMBOLS' => array(
 47+ '(', ')', '[', ']', '{', '}',
 48+ '+', '-', '*', '/',
 49+ '!', '&', '|', '^',
 50+ '<', '>', '=',
 51+ ',', ';', '?', ':', '::',
 52+ ),
 53+ 'CASE_SENSITIVE' => array(
 54+ GESHI_COMMENTS => false,
 55+ 1 => true,
 56+ ),
 57+ 'STYLES' => array(
 58+ 'KEYWORDS' => array(
 59+ 1 => 'color: #000066; font-weight: bold;',
 60+ ),
 61+ 'COMMENTS' => array(
 62+ 1 => 'color: #006600; font-style: italic;',
 63+ 2 => 'color: #009966; font-style: italic;',
 64+ 'MULTI' => 'color: #006600; font-style: italic;'
 65+ ),
 66+ 'ESCAPE_CHAR' => array(
 67+ 0 => 'color: #000099; font-weight: bold;'
 68+ ),
 69+ 'BRACKETS' => array(
 70+ 0 => 'color: #009900;'
 71+ ),
 72+ 'STRINGS' => array(
 73+ 0 => 'color: #3366CC;'
 74+ ),
 75+ 'NUMBERS' => array(
 76+ 0 => 'color: #CC0000;'
 77+ ),
 78+ 'METHODS' => array(
 79+ 1 => 'color: #660066;'
 80+ ),
 81+ 'SYMBOLS' => array(
 82+ 0 => 'color: #339933;'
 83+ ),
 84+ 'REGEXPS' => array(
 85+ ),
 86+ 'SCRIPT' => array(
 87+ 0 => '',
 88+ )
 89+ ),
 90+ 'URLS' => array(
 91+ 1 => '',
 92+ ),
 93+ 'OOLANG' => false,
 94+ 'REGEXPS' => array(
 95+ ),
 96+ 'STRICT_MODE_APPLIES' => GESHI_NEVER,
 97+ 'SCRIPT_DELIMITERS' => array(),
 98+ 'HIGHLIGHT_STRICT_BLOCK' => array(),
 99+);
Index: trunk/extensions/WikiScripts/i18n/Namespaces.php
@@ -0,0 +1,8 @@
 2+<?php
 3+
 4+$namespaceNames = array();
 5+
 6+$namespaceNames['en'] = array(
 7+ NS_MODULE => 'Module',
 8+ NS_MODULE_TALK => 'Module_talk',
 9+);
Index: trunk/extensions/WikiScripts/i18n/Magic.php
@@ -0,0 +1,7 @@
 2+<?php
 3+
 4+$magicWords = array();
 5+
 6+$magicWords['en'] = array(
 7+ 'i' => array( 0, 'i' ),
 8+);
Index: trunk/extensions/WikiScripts/i18n/Messages.php
@@ -0,0 +1,54 @@
 2+<?php
 3+/**
 4+ * Internationalisation file for extension InlineScripts.
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+$messages = array();
 11+
 12+/** English
 13+ * @author Victor Vasiliev
 14+ */
 15+$messages['en'] = array(
 16+ 'inlinescripts-desc' => 'Provides a build into wikitext scripting language',
 17+
 18+ 'inlinescripts-call-frommodule' => '$1::$2 called by $3::$4 at line $5',
 19+ 'inlinescripts-call-fromwikitext' => '$1::$2 called by wikitext',
 20+ 'inlinescripts-call-parse' => 'parse( "$1" )',
 21+
 22+ 'inlinescripts-error' => 'Following parsing {{plural:$1|error|errors}} detected:',
 23+ 'inlinescripts-codelocation' => 'in module $1 at line $2',
 24+
 25+ 'inlinescripts-exception-unexceptedtoken' => 'Unexpected token $2 $1: expected $3 (parser state $4)',
 26+ 'inlinescripts-exception-unclosedstring' => 'Unclosed string $1',
 27+ 'inlinescripts-exception-unrecognisedtoken' => 'Unrecognized token $1',
 28+ 'inlinescripts-exception-toomanytokens' => 'Exceeded tokens limit',
 29+ 'inlinescripts-exception-toomanyevals' => 'Exceeded evaluations limit $1',
 30+ 'inlinescripts-exception-recoverflow' => 'Too deep abstract syntax tree',
 31+ 'inlinescripts-exception-notanarray' => 'Tried to get or set an element of a non-array $1',
 32+ 'inlinescripts-exception-outofbounds' => 'Got out of array bounds $1',
 33+ 'inlinescripts-exception-notenoughargs' => 'Not enough arguments for function $1',
 34+ 'inlinescripts-exception-dividebyzero' => 'Division by zero $1',
 35+ 'inlinescripts-exception-break' => '"break" called outside of foreach $1',
 36+ 'inlinescripts-exception-continue' => '"continue" called outside of foreach $1',
 37+ 'inlinescripts-exception-emptyidx' => 'Trying to get a value of an empty index $1',
 38+ 'inlinescripts-exception-unknownvar' => 'Trying to use an undeclared variable $1',
 39+ 'inlinescripts-exception-unknownfunction' => 'Trying to use an unnknown function $2 $1',
 40+ 'inlinescripts-exception-notlist' => 'Trying to append an element to the end of \'\'associated\'\' array $1',
 41+ 'inlinescripts-exception-appendyield' => 'Trying to use append and yield in the same function $1',
 42+
 43+ 'inlinescripts-exception-notenoughargs-user' => 'Not enough arguments for function $2::$3 $1',
 44+ 'inlinescripts-exception-nonexistent-module' => 'Call to non-existent module $2 $1',
 45+ 'inlinescripts-exception-unknownfunction-user' => 'Trying to use an unnknown user function $2::$3 $1',
 46+ 'inlinescripts-exception-recursion' => 'Function loop detected when calling function $2::$3 $1',
 47+ 'inlinescripts-exception-toodeeprecursion' => 'The maximum function nesting limit of $2 exceeded $1',
 48+
 49+ 'inlinescripts-transerror-notenoughargs-user' => 'Not enough arguments for function $1::$2',
 50+ 'inlinescripts-transerror-nonexistent-module' => 'Call to non-existent module $1',
 51+ 'inlinescripts-transerror-unknownfunction-user' => 'Trying to use an unnknown user function $1::$2',
 52+ 'inlinescripts-transerror-recursion' => 'Function loop detected when calling function $1::$2',
 53+ 'inlinescripts-transerror-nofunction' => 'Missing function name when invoking the script',
 54+ 'inlinescripts-transerror-toodeeprecursion' => 'The maximum function nesting limit of $1 exceeded',
 55+);
Property changes on: trunk/extensions/WikiScripts/i18n/Messages.php
___________________________________________________________________
Added: svn:executable
156 + *
Index: trunk/extensions/WikiScripts/interpreter/Data.php
@@ -0,0 +1,268 @@
 2+<?php
 3+/**
 4+ * Built-in scripting language for MediaWiki: data.
 5+ * Based on the AbuseFilter AFData.
 6+ * Copyright (C) 2008-2011 Victor Vasiliev <vasilvv@gmail.com>, Andrew Garrett <andrew@epstone.net>
 7+ * http://www.mediawiki.org/
 8+ *
 9+ * This program is free software; you can redistribute it and/or modify
 10+ * it under the terms of the GNU General Public License as published by
 11+ * the Free Software Foundation; either version 2 of the License, or
 12+ * (at your option) any later version.
 13+ *
 14+ * This program is distributed in the hope that it will be useful,
 15+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 17+ * GNU General Public License for more details.
 18+ *
 19+ * You should have received a copy of the GNU General Public License along
 20+ * with this program; if not, write to the Free Software Foundation, Inc.,
 21+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 22+ * http://www.gnu.org/copyleft/gpl.html
 23+ */
 24+
 25+if( !defined( 'MEDIAWIKI' ) )
 26+ die();
 27+
 28+/**
 29+ * Class implementing data in the scripts.
 30+ */
 31+class ISData {
 32+ // Data types
 33+ const DInt = 'int';
 34+ const DString = 'string';
 35+ const DNull = 'null';
 36+ const DBool = 'bool';
 37+ const DFloat = 'float';
 38+ const DList = 'list'; // int -> value
 39+ const DAssoc = 'assoc'; // associative array
 40+
 41+ public $type;
 42+ public $data;
 43+
 44+ public function __construct( $type = self::DNull, $val = null ) {
 45+ $this->type = $type;
 46+ $this->data = $val;
 47+ }
 48+
 49+ public static function newFromPHPVar( $var ) {
 50+ if( is_string( $var ) )
 51+ return new ISData( self::DString, $var );
 52+ elseif( is_int( $var ) )
 53+ return new ISData( self::DInt, $var );
 54+ elseif( is_float( $var ) )
 55+ return new ISData( self::DFloat, $var );
 56+ elseif( is_bool( $var ) )
 57+ return new ISData( self::DBool, $var );
 58+ elseif( is_array( $var ) ) {
 59+ if( !$var )
 60+ return new ISData( self::DList, array() );
 61+ $result = array();
 62+ foreach( $var as $item )
 63+ $result[] = self::newFromPHPVar( $item );
 64+ return new ISData( self::DList, $result );
 65+ }
 66+ elseif( is_null( $var ) )
 67+ return new ISData();
 68+ else
 69+ throw new ISException(
 70+ "Data type " . gettype( $var ) . " is not supported by InlineScrtips" );
 71+ }
 72+
 73+ public function dup() {
 74+ return new ISData( $this->type, $this->data );
 75+ }
 76+
 77+ public static function castTypes( $orig, $target ) {
 78+ if( $orig->type == $target )
 79+ return $orig->dup();
 80+ if( $target == self::DNull ) {
 81+ return new ISData();
 82+ }
 83+
 84+ if( $orig->isArray() ) {
 85+ if( $target == self::DBool )
 86+ return new ISData( self::DBool, (bool)count( $orig->data ) );
 87+ if( $target == self::DFloat ) {
 88+ return new ISData( self::DFloat, doubleval( count( $orig->data ) ) );
 89+ }
 90+ if( $target == self::DInt ) {
 91+ return new ISData( self::DInt, intval( count( $orig->data ) ) );
 92+ }
 93+ if( $target == self::DString ) {
 94+ $s = array();
 95+ foreach( $orig->data as $item )
 96+ $s[] = $item->toString();
 97+ return new ISData( self::DString, implode( "\n", $s ) );
 98+ }
 99+ }
 100+
 101+ if( $target == self::DBool ) {
 102+ return new ISData( self::DBool, (bool)$orig->data );
 103+ }
 104+ if( $target == self::DFloat ) {
 105+ return new ISData( self::DFloat, doubleval( $orig->data ) );
 106+ }
 107+ if( $target == self::DInt ) {
 108+ return new ISData( self::DInt, intval( $orig->data ) );
 109+ }
 110+ if( $target == self::DString ) {
 111+ return new ISData( self::DString, strval( $orig->data ) );
 112+ }
 113+ if( $target == self::DList ) {
 114+ return new ISData( self::DList, array( $orig ) );
 115+ }
 116+ }
 117+
 118+ public static function boolInvert( $value ) {
 119+ return new ISData( self::DBool, !$value->toBool() );
 120+ }
 121+
 122+ public static function pow( $base, $exponent ) {
 123+ if( $base->type == self::DInt && $exponent->type == self::DInt )
 124+ return new ISData( self::DInt, pow( $base->toInt(), $exponent->toInt() ) );
 125+ else
 126+ return new ISData( self::DFloat, pow( $base->toFloat(), $exponent->toFloat() ) );
 127+ }
 128+
 129+ // Checks whether a is in b
 130+ public static function keywordIn( $a, $b ) {
 131+ if( $b->isArray() ) {
 132+ foreach( $b->data as $elem ) {
 133+ if( self::equals( $elem, $a ) )
 134+ return new ISData( self::DBool, true );
 135+ }
 136+ return new ISData( self::DBool, false );
 137+ } else {
 138+ $a = $a->toString();
 139+ $b = $b->toString();
 140+
 141+ if( $a == '' || $b == '' ) {
 142+ return new ISData( self::DBool, false );
 143+ }
 144+
 145+ return new ISData( self::DBool, in_string( $a, $b ) );
 146+ }
 147+ }
 148+
 149+ public static function equals( $d1, $d2 ) {
 150+ return $d1->data == $d2->data;
 151+ }
 152+
 153+ public static function unaryMinus( $data ) {
 154+ if( $data->type == self::DInt ) {
 155+ return new ISData( $data->type, -$data->toInt() );
 156+ } else {
 157+ return new ISData( $data->type, -$data->toFloat() );
 158+ }
 159+ }
 160+
 161+ public static function compareOp( $a, $b, $op ) {
 162+ if( $op == '==' )
 163+ return new ISData( self::DBool, self::equals( $a, $b ) );
 164+ if( $op == '!=' )
 165+ return new ISData( self::DBool, !self::equals( $a, $b ) );
 166+ if( $op == '===' )
 167+ return new ISData( self::DBool, $a->type == $b->type && self::equals( $a, $b ) );
 168+ if( $op == '!==' )
 169+ return new ISData( self::DBool, $a->type != $b->type || !self::equals( $a, $b ) );
 170+ $a = $a->toString();
 171+ $b = $b->toString();
 172+ if( $op == '>' )
 173+ return new ISData( self::DBool, $a > $b );
 174+ if( $op == '<' )
 175+ return new ISData( self::DBool, $a < $b );
 176+ if( $op == '>=' )
 177+ return new ISData( self::DBool, $a >= $b );
 178+ if( $op == '<=' )
 179+ return new ISData( self::DBool, $a <= $b );
 180+ throw new ISException( "Invalid comparison operation: {$op}" ); // Should never happen
 181+ }
 182+
 183+ public static function mulRel( $a, $b, $op, $module, $pos ) {
 184+ // Figure out the type.
 185+ if( ( $a->type == self::DFloat || $b->type == self::DFloat ) &&
 186+ $op != '/' ) {
 187+ $type = self::DInt;
 188+ $a = $a->toInt();
 189+ $b = $b->toInt();
 190+ } else {
 191+ $type = self::DFloat;
 192+ $a = $a->toFloat();
 193+ $b = $b->toFloat();
 194+ }
 195+
 196+ if( $op != '*' && $b == 0 ) {
 197+ throw new ISUserVisibleException( 'dividebyzero', $module, $pos, array($a) );
 198+ }
 199+
 200+ $data = null;
 201+ if( $op == '*' )
 202+ $data = $a * $b;
 203+ elseif( $op == '/' )
 204+ $data = $a / $b;
 205+ elseif( $op == '%' )
 206+ $data = $a % $b;
 207+ else
 208+ throw new ISException( "Invalid multiplication-related operation: {$op}" ); // Should never happen
 209+
 210+ if( $type == self::DInt )
 211+ $data = intval( $data );
 212+ else
 213+ $data = doubleval( $data );
 214+
 215+ return new ISData( $type, $data );
 216+ }
 217+
 218+ public static function sum( $a, $b ) {
 219+ if( $a->type == self::DString || $b->type == self::DString )
 220+ return new ISData( self::DString, $a->toString() . $b->toString() );
 221+ elseif( $a->type == self::DList && $b->type == self::DList )
 222+ return new ISData( self::DList, array_merge( $a->toList(), $b->toList() ) );
 223+ elseif( $a->type == self::DList )
 224+ return new ISData( self::DList, array_merge( $a->toList(), array( $b ) ) );
 225+ elseif( $a->type == self::DAssoc && $b->type == self::DAssoc )
 226+ return new ISData( self::DAssoc, array_merge( $a->toAssoc(), $b->toAssoc() ) );
 227+ elseif( $a->type == self::DInt && $b->type == self::DInt )
 228+ return new ISData( self::DInt, $a->toInt() + $b->toInt() );
 229+ else
 230+ return new ISData( self::DFloat, $a->toFloat() + $b->toFloat() );
 231+ }
 232+
 233+ public static function sub( $a, $b ) {
 234+ if( $a->type == self::DInt && $b->type == self::DInt )
 235+ return new ISData( self::DInt, $a->toInt() - $b->toInt() );
 236+ else
 237+ return new ISData( self::DFloat, $a->toFloat() - $b->toFloat() );
 238+ }
 239+
 240+ public function isArray() {
 241+ return $this->type == self::DList || $this->type == self::DAssoc;
 242+ }
 243+
 244+
 245+ /** Convert shorteners */
 246+ public function toBool() {
 247+ return self::castTypes( $this, self::DBool )->data;
 248+ }
 249+
 250+ public function toString() {
 251+ return self::castTypes( $this, self::DString )->data;
 252+ }
 253+
 254+ public function toFloat() {
 255+ return self::castTypes( $this, self::DFloat )->data;
 256+ }
 257+
 258+ public function toInt() {
 259+ return self::castTypes( $this, self::DInt )->data;
 260+ }
 261+
 262+ public function toList() {
 263+ return self::castTypes( $this, self::DList )->data;
 264+ }
 265+
 266+ public function toAssoc() {
 267+ return self::castTypes( $this, self::DAssoc )->data;
 268+ }
 269+}
Property changes on: trunk/extensions/WikiScripts/interpreter/Data.php
___________________________________________________________________
Added: svn:eol-style
1270 + native
Index: trunk/extensions/WikiScripts/interpreter/Scanner.php
@@ -0,0 +1,340 @@
 2+<?php
 3+/**
 4+ * Built-in scripting language for MediaWiki: scanner.
 5+ * Based on the AbuseFilter scanner.
 6+ * Copyright (C) 2008-2011 Victor Vasiliev <vasilvv@gmail.com>, Andrew Garrett <andrew@epstone.net>
 7+ * http://www.mediawiki.org/
 8+ *
 9+ * This program is free software; you can redistribute it and/or modify
 10+ * it under the terms of the GNU General Public License as published by
 11+ * the Free Software Foundation; either version 2 of the License, or
 12+ * (at your option) any later version.
 13+ *
 14+ * This program is distributed in the hope that it will be useful,
 15+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 17+ * GNU General Public License for more details.
 18+ *
 19+ * You should have received a copy of the GNU General Public License along
 20+ * with this program; if not, write to the Free Software Foundation, Inc.,
 21+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 22+ * http://www.gnu.org/copyleft/gpl.html
 23+ */
 24+
 25+if( !defined( 'MEDIAWIKI' ) )
 26+ die();
 27+
 28+/**
 29+ * Lexical analizator for inline scripts. Splits strings to tokens.
 30+ */
 31+
 32+require_once( 'Shared.php' );
 33+
 34+class ISScanner implements Iterator {
 35+ var $mModule, $mCode, $mPos, $mCur, $mEof;
 36+
 37+ // Order is important. The punctuation-matching regex requires that
 38+ // ** comes before *, etc. They are sorted to make it easy to spot
 39+ // such errors.
 40+ static $mOps = array(
 41+ '!==', '!=', '!', // Inequality
 42+ '+=', '-=', // Setting 1
 43+ '*=', '/=', // Setting 2
 44+ '**', '*', // Multiplication/exponentiation
 45+ '/', '+', '-', '%', // Other arithmetic
 46+ '&', '|', '^', // Logic
 47+ '?', '::', ':', // Ternery
 48+ '<=','<', // Less than
 49+ '>=', '>', // Greater than
 50+ '===', '==', '=', // Equality
 51+ ',', ';', // Comma, semicolon
 52+ '(', '[', '{', // Braces
 53+ ')', ']', '}', // Braces
 54+ );
 55+
 56+ static $mKeywords = array(
 57+ 'append', 'break', 'catch', 'contains', 'continue', 'delete', 'else', 'false', 'for',
 58+ 'function', 'if', 'in', 'isset', 'null', 'return', 'self', 'then', 'true', 'try', 'yield',
 59+ );
 60+
 61+ public function __construct( $module, $code ) {
 62+ $this->mModule = $module;
 63+ $this->mCode = $code;
 64+ $this->rewind();
 65+ }
 66+
 67+ public function rewind() {
 68+ $this->mPos = 0;
 69+ $this->mCur = null;
 70+ $this->mEof = false;
 71+ $this->move();
 72+ }
 73+
 74+ public function current() {
 75+ return $this->mCur;
 76+ }
 77+
 78+ public function key() {
 79+ return $this->mPos;
 80+ }
 81+
 82+ public function next() {
 83+ return $this->move();
 84+ }
 85+
 86+ public function valid() {
 87+ return !$this->mEof;
 88+ }
 89+
 90+ private function move() {
 91+ if( $this->mEof || ( $this->mCur && $this->mCur->type == ISToken::TEnd ) ) {
 92+ $this->mEof = true;
 93+ return $this->mCur = null;
 94+ }
 95+ list( $val, $type ) = $this->nextToken();
 96+
 97+ $lineno = count( explode( "\n", substr( $this->mCode, 0, $this->mPos ) ) );
 98+ return $this->mCur = new ISToken( $type, $val, $lineno );
 99+ }
 100+
 101+ private function nextToken() {
 102+ $tok = '';
 103+
 104+ // Spaces
 105+ $matches = array();
 106+ if ( preg_match( '/\s+/uA', $this->mCode, $matches, 0, $this->mPos ) )
 107+ $this->mPos += strlen($matches[0]);
 108+
 109+ if( $this->mPos >= strlen($this->mCode) )
 110+ return array( null, ISToken::TEnd );
 111+
 112+ // Comments
 113+ if ( substr($this->mCode, $this->mPos, 2) == '/*' ) {
 114+ $this->mPos = strpos( $this->mCode, '*/', $this->mPos ) + 2;
 115+ return self::nextToken();
 116+ }
 117+
 118+ if( substr( $this->mCode, $this->mPos, 2 ) == '//' ) {
 119+ $newlinePos = strpos( $this->mCode, "\n", $this->mPos );
 120+ if( $newlinePos === false ) {
 121+ return array( null, ISToken::TEnd );
 122+ } else {
 123+ $this->mPos = $newlinePos + 1;
 124+ return self::nextToken();
 125+ }
 126+ }
 127+
 128+ // Strings
 129+ if( $this->mCode[$this->mPos] == '"' || $this->mCode[$this->mPos] == "'" ) {
 130+ $type = $this->mCode[$this->mPos];
 131+ $this->mPos++;
 132+ $strLen = strlen($this->mCode);
 133+ while( $this->mPos < $strLen ) {
 134+ if( $this->mCode[$this->mPos] == $type ) {
 135+ $this->mPos++;
 136+ return array( $tok, ISToken::TString );
 137+ }
 138+
 139+ // Performance: Use a PHP function (implemented in C)
 140+ // to scan ahead.
 141+ $addLength = strcspn( $this->mCode, $type."\\", $this->mPos );
 142+ if ($addLength) {
 143+ $tok .= substr( $this->mCode, $this->mPos, $addLength );
 144+ $this->mPos += $addLength;
 145+ } elseif( $this->mCode[$this->mPos] == '\\' ) {
 146+ switch( $this->mCode[$this->mPos + 1] ) {
 147+ case '\\':
 148+ $tok .= '\\';
 149+ break;
 150+ case $type:
 151+ $tok .= $type;
 152+ break;
 153+ case 'n';
 154+ $tok .= "\n";
 155+ break;
 156+ case 'r':
 157+ $tok .= "\r";
 158+ break;
 159+ case 't':
 160+ $tok .= "\t";
 161+ break;
 162+ case 'x':
 163+ $chr = substr( $this->mCode, $this->mPos + 2, 2 );
 164+
 165+ if ( preg_match( '/^[0-9A-Fa-f]{2}$/', $chr ) ) {
 166+ $chr = base_convert( $chr, 16, 10 );
 167+ $tok .= chr($chr);
 168+ $this->mPos += 2; # \xXX -- 2 done later
 169+ } else {
 170+ $tok .= 'x';
 171+ }
 172+ break;
 173+ default:
 174+ $tok .= "\\" . $this->mCode[$this->mPos + 1];
 175+ }
 176+ $this->mPos+=2;
 177+ } else {
 178+ $tok .= $this->mCode[$this->mPos];
 179+ $this->mPos++;
 180+ }
 181+ }
 182+ throw new ISUserVisibleException( 'unclosedstring', $this->mModule, $this->mPos, array() );
 183+ }
 184+
 185+ // Find operators
 186+ static $operator_regex = null;
 187+ // Match using a regex. Regexes are faster than PHP
 188+ if (!$operator_regex) {
 189+ $quoted_operators = array();
 190+
 191+ foreach( self::$mOps as $op )
 192+ $quoted_operators[] = preg_quote( $op, '/' );
 193+ $operator_regex = '/('.implode('|', $quoted_operators).')/A';
 194+ }
 195+
 196+ $matches = array();
 197+
 198+ preg_match( $operator_regex, $this->mCode, $matches, 0, $this->mPos );
 199+
 200+ if( count( $matches ) ) {
 201+ $tok = $matches[0];
 202+ $this->mPos += strlen( $tok );
 203+ return array( $tok, $this->getOperatorType( $tok ) );
 204+ }
 205+
 206+ // Find bare numbers
 207+ $bases = array( 'b' => 2,
 208+ 'x' => 16,
 209+ 'o' => 8 );
 210+ $baseChars = array(
 211+ 2 => '[01]',
 212+ 16 => '[0-9A-Fa-f]',
 213+ 8 => '[0-8]',
 214+ 10 => '[0-9.]',
 215+ );
 216+ $baseClass = '['.implode('', array_keys($bases)).']';
 217+ $radixRegex = "/([0-9A-Fa-f]+(?:\.\d*)?|\.\d+)($baseClass)?/Au";
 218+ $matches = array();
 219+
 220+ if ( preg_match( $radixRegex, $this->mCode, $matches, 0, $this->mPos ) ) {
 221+ $input = $matches[1];
 222+ $baseChar = @$matches[2];
 223+ $num = null;
 224+ // Sometimes the base char gets mixed in with the rest of it because
 225+ // the regex targets hex, too.
 226+ // This mostly happens with binary
 227+ if (!$baseChar && !empty( $bases[ substr( $input, -1 ) ] ) ) {
 228+ $baseChar = substr( $input, -1, 1 );
 229+ $input = substr( $input, 0, -1 );
 230+ }
 231+
 232+ if( $baseChar )
 233+ $base = $bases[$baseChar];
 234+ else
 235+ $base = 10;
 236+
 237+ // Check against the appropriate character class for input validation
 238+ $baseRegex = "/^".$baseChars[$base]."+$/";
 239+
 240+ if ( preg_match( $baseRegex, $input ) ) {
 241+ if ($base != 10) {
 242+ $num = base_convert( $input, $base, 10 );
 243+ } else {
 244+ $num = $input;
 245+ }
 246+
 247+ $this->mPos += strlen( $matches[0] );
 248+
 249+ $float = in_string( '.', $input );
 250+
 251+ return array(
 252+ $float
 253+ ? doubleval( $num )
 254+ : intval( $num ),
 255+ $float
 256+ ? ISToken::TFloat
 257+ : ISToken::TInt,
 258+ );
 259+ }
 260+ }
 261+
 262+ // The rest are considered IDs
 263+
 264+ // Regex match > PHP
 265+ $idSymbolRegex = '/[0-9A-Za-z_]+/A';
 266+ $matches = array();
 267+
 268+ if ( preg_match( $idSymbolRegex, $this->mCode, $matches, 0, $this->mPos ) ) {
 269+ $tok = $matches[0];
 270+
 271+ $type = in_array( $tok, self::$mKeywords )
 272+ ? $tok : ISToken::TID;
 273+
 274+ $this->mPos += strlen( $tok );
 275+ return array( $tok, $type );
 276+ }
 277+
 278+ throw new ISUserVisibleException(
 279+ 'unrecognisedtoken', $this->mModule, $this->mPos, array( substr( $this->mCode, $this->mPos ) ) );
 280+ }
 281+
 282+ private static function getOperatorType( $op ) {
 283+ switch( $op ) {
 284+ case '::':
 285+ return ISToken::TDoubleColon;
 286+ case ':':
 287+ return ISToken::TColon;
 288+ case ',':
 289+ return ISToken::TComma;
 290+ case '>':
 291+ case '<':
 292+ case '>=':
 293+ case '<=':
 294+ return ISToken::TCompareOperator;
 295+ case '==':
 296+ case '!=':
 297+ case '===':
 298+ case '!==':
 299+ return ISToken::TEqualsToOperator;
 300+ case '!':
 301+ return ISToken::TBoolInvert;
 302+ case '(':
 303+ return ISToken::TLeftBracket;
 304+ case '{':
 305+ return ISToken::TLeftCurly;
 306+ case '[':
 307+ return ISToken::TLeftSquare;
 308+ case '&':
 309+ case '|':
 310+ case '^':
 311+ return ISToken::TLogicalOperator;
 312+ case '*':
 313+ case '/':
 314+ case '%':
 315+ return ISToken::TMulOperator;
 316+ case '**':
 317+ return ISToken::TPow;
 318+ case ')':
 319+ return ISToken::TRightBracket;
 320+ case '}':
 321+ return ISToken::TRightCurly;
 322+ case ']':
 323+ return ISToken::TRightSquare;
 324+ case ';':
 325+ return ISToken::TSemicolon;
 326+ case '=':
 327+ case '+=':
 328+ case '-=':
 329+ case '*=':
 330+ case '/=':
 331+ return ISToken::TSet;
 332+ case '+':
 333+ case '-':
 334+ return ISToken::TSumOperator;
 335+ case '?':
 336+ return ISToken::TTrinary;
 337+ default:
 338+ throw new ISException( "Invalid operator: {$op}" );
 339+ }
 340+ }
 341+}
Property changes on: trunk/extensions/WikiScripts/interpreter/Scanner.php
___________________________________________________________________
Added: svn:eol-style
1342 + native
Index: trunk/extensions/WikiScripts/interpreter/LRParser.php
@@ -0,0 +1,114 @@
 2+<?php
 3+
 4+/**
 5+ * LR parser for inline scripts.
 6+ * Inputs tokens and LR table (ACTION/GOTO).
 7+ * Outputs parser tree.
 8+ *
 9+ * See http://en.wikipedia.org/wiki/LR_parser for details of how does that works.
 10+ */
 11+
 12+require_once( 'LRTableVersion.php' );
 13+
 14+class ISLRParser implements ISParser {
 15+ const Shift = 0;
 16+ const Reduce = 1;
 17+ const Accept = 2;
 18+
 19+ static $mLoaded, $mNonterminals, $mProductions, $mAction, $mGoto;
 20+
 21+ public static function getVersion() {
 22+ return IS_LR_VERSION;
 23+ }
 24+
 25+ private function loadGrammar() {
 26+ wfProfileIn( __METHOD__ );
 27+
 28+ if( self::$mLoaded )
 29+ return;
 30+
 31+ require_once( 'LRTable.php' );
 32+
 33+ self::$mNonterminals = ISLRTable::$nonterminals;
 34+ self::$mProductions = ISLRTable::$productions;
 35+ self::$mAction = ISLRTable::$action;
 36+ self::$mGoto = ISLRTable::$goto;
 37+ self::$mLoaded = true;
 38+
 39+ wfProfileOut( __METHOD__ );
 40+ }
 41+
 42+ public function needsScanner() {
 43+ return true;
 44+ }
 45+
 46+ public function parse( $scanner, $module, $maxTokens ) {
 47+ self::loadGrammar();
 48+
 49+ $states = array( array( null, 0 ) );
 50+ $scanner->rewind();
 51+ $tokenCount = 0;
 52+
 53+ wfProfileIn( __METHOD__ );
 54+
 55+ for( ; ; ) {
 56+ $token = $scanner->current();
 57+ $cur = $token->type;
 58+ if( !$token ) {
 59+ wfProfileOut( __METHOD__ );
 60+ throw new ISException( 'Non-token input in LRParser::parse' );
 61+ }
 62+
 63+ $tokenCount++;
 64+ if( $tokenCount > $maxTokens ) {
 65+ wfProfileOut( __METHOD__ );
 66+ throw new ISUserVisibleException( 'toomanytokens', $module, $token->line );
 67+ }
 68+
 69+ list( $stateval, $state ) = end( $states );
 70+ $act = @self::$mAction[$state][$cur];
 71+ if( !$act ) {
 72+ wfProfileOut( __METHOD__ );
 73+ throw new ISUserVisibleException( 'unexceptedtoken', $module, $token->line,
 74+ array( $token, implode( ', ', array_keys( @self::$mAction[$state] ) ), $state ) );
 75+ }
 76+ if( $act[0] == self::Shift ) {
 77+ $states[] = array( $token, $act[1] );
 78+ $scanner->next();
 79+ } elseif( $act[0] == self::Reduce ) {
 80+ list( $nonterm, $prod ) = self::$mProductions[$act[1]];
 81+ $len = count( $prod );
 82+
 83+ // Change state
 84+ $str = array();
 85+ for( $i = 0; $i < $len; $i++ )
 86+ $str[] = array_pop( $states );
 87+ $str = array_reverse( $str );
 88+ list( $stateval, $state ) = end( $states );
 89+
 90+ $node = new ISParserTreeNode( $this, $nonterm );
 91+ foreach( $str as $symbol ) {
 92+ list( $val ) = $symbol;
 93+ $node->addChild( $val );
 94+ }
 95+ $states[] = array( $node, self::$mGoto[$state][$nonterm] );
 96+ } elseif( $act[0] == self::Accept ) {
 97+ break;
 98+ }
 99+ }
 100+
 101+ wfProfileOut( __METHOD__ );
 102+
 103+ return new ISParserOutput( $states[1][0], $tokenCount );
 104+ }
 105+
 106+ public function getSyntaxErrors( $input, $module, $maxTokens ) {
 107+ try {
 108+ $this->parse( $input, $module, $maxTokens );
 109+ } catch( ISUserVisibleException $e ) {
 110+ return array( $e->getMessage() );
 111+ }
 112+
 113+ return array();
 114+ }
 115+}
Property changes on: trunk/extensions/WikiScripts/interpreter/LRParser.php
___________________________________________________________________
Added: svn:eol-style
1116 + native
Index: trunk/extensions/WikiScripts/interpreter/LRTableVersion.php
@@ -0,0 +1,9 @@
 2+<?php
 3+
 4+/**
 5+ * This file includes timestamp which indicates the version of LRTable.php file.
 6+ * Since the file is too large, loading it every time is expensive and we store the
 7+ * version in separate file.
 8+ */
 9+
 10+define( 'IS_LR_VERSION', "2011-08-13 21:53:42" );
Index: trunk/extensions/WikiScripts/interpreter/EvaluationContext.php
@@ -0,0 +1,764 @@
 2+<?php
 3+/**
 4+ * Built-in scripting language for MediaWiki.
 5+ * Copyright (C) 2009-2011 Victor Vasiliev <vasilvv@gmail.com>
 6+ * http://www.mediawiki.org/
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License along
 19+ * with this program; if not, write to the Free Software Foundation, Inc.,
 20+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 21+ * http://www.gnu.org/copyleft/gpl.html
 22+ */
 23+
 24+if( !defined( 'MEDIAWIKI' ) )
 25+ die();
 26+
 27+/**
 28+ * An internal class used by InlineScript. Used to evaluate a parsed code
 29+ * in a sepereate context with its own output, variables and parser frame.
 30+ *
 31+ * Handles evaluation of an individual functions.
 32+ */
 33+class ISEvaluationContext {
 34+ var $mVars, $mFrame, $mName, $mInterpreter, $mModule;
 35+
 36+ var $mOutput, $mListOutput;
 37+
 38+ static $mFunctions = array(
 39+ /* String functions */
 40+ 'lc' => 'funcLc',
 41+ 'uc' => 'funcUc',
 42+ 'ucfirst' => 'funcUcFirst',
 43+ 'urlencode' => 'funcUrlencode',
 44+ 'grammar' => 'funcGrammar',
 45+ 'plural' => 'funcPlural',
 46+ 'anchorencode' => 'funcAnchorEncode',
 47+ 'strlen' => 'funcStrlen',
 48+ 'substr' => 'funcSubstr',
 49+ 'strreplace' => 'funcStrreplace',
 50+ 'split' => 'funcSplit',
 51+
 52+ /* Array functions */
 53+ 'join' => 'funcJoin',
 54+ 'count' => 'funcCount',
 55+
 56+ /* Parser interaction functions */
 57+ 'arg' => 'funcArg',
 58+ 'args' => 'funcArgs',
 59+ 'isTranscluded' => 'funcIsTranscluded',
 60+ 'parse' => 'funcParse',
 61+
 62+ /* Cast functions */
 63+ 'string' => 'castString',
 64+ 'int' => 'castInt',
 65+ 'float' => 'castFloat',
 66+ 'bool' => 'castBool',
 67+ );
 68+
 69+ public function __construct( $interpreter, $module, $name, $frame ) {
 70+ $this->mVars = array();
 71+ $this->mOut = '';
 72+ $this->mModule = $module;
 73+ $this->mModuleName = $module->getName();
 74+ $this->mName = $name;
 75+ $this->mInterpreter = $interpreter;
 76+ $this->mParser = $interpreter->getParser();
 77+ $this->mFrame = $frame;
 78+
 79+ $this->mOutput = new ISData();
 80+ $this->mListOutput = array();
 81+ }
 82+
 83+ public function getModule() {
 84+ return $this->mModule;
 85+ }
 86+
 87+ public function getFrame() {
 88+ return $this->mFrame;
 89+ }
 90+
 91+ public function getOutput() {
 92+ if( $this->mOutput->type != ISData::DNull ) {
 93+ return $this->mOutput;
 94+ } elseif( $this->mListOutput ) {
 95+ return new ISData( ISData::DList, $this->mListOutput );
 96+ } else {
 97+ return new ISData();
 98+ }
 99+ }
 100+
 101+ public function setArgument( $name, $value ) {
 102+ $this->mVars[$name] = $value->dup();
 103+ }
 104+
 105+ /**
 106+ * The core interpreter method. Evaluates a single AST node.
 107+ * The $rec parameter must be increated by 1 each time the function is called
 108+ * recursively.
 109+ */
 110+ public function evaluateNode( $node, $rec ) {
 111+ if( !$node instanceof ISParserTreeNode ) {
 112+ throw new ISException( 'evaluateNode() accepts only nonterminals' );
 113+ }
 114+
 115+ if( !$this->mInterpreter->checkRecursionLimit( $rec ) ) {
 116+ throw new ISUserVisibleException( 'recoverflow', $this->mModuleName, $this->getLine( $node ) );
 117+ }
 118+
 119+ $c = $node->getChildren();
 120+ switch( $node->getType() ) {
 121+ case 'stmts':
 122+ $stmts = array();
 123+ while( isset( $c[1] ) ) {
 124+ array_unshift( $stmts, $c[1] );
 125+ $c = $c[0]->getChildren();
 126+ }
 127+ array_unshift( $stmts, $c[0] );
 128+ foreach( $stmts as $stmt )
 129+ $res = $this->evaluateNode( $stmt, $rec + 1 );
 130+ return $res;
 131+ case 'stmt':
 132+ if( $c[0] instanceof ISToken ) {
 133+ switch( $c[0]->type ) {
 134+ case 'leftcurly':
 135+ return $this->evaluateNode( $c[1], $rec + 1 );
 136+ case 'if':
 137+ $cond = $this->evaluateNode( $c[2], $rec + 1 );
 138+ if( $cond->toBool() ) {
 139+ return $this->evaluateNode( $c[4], $rec + 1 );
 140+ } else {
 141+ if( isset( $c[6] ) ) {
 142+ return $this->evaluateNode( $c[6], $rec + 1 );
 143+ } else {
 144+ return new ISData();
 145+ }
 146+ }
 147+ case 'for':
 148+ $array = $this->evaluateNode( $c[4], $rec + 1 );
 149+ if( !$array->isArray() )
 150+ throw new ISUserVisibleException( 'invalidforeach', $this->mModuleName, $c[0]->line );
 151+ $last = new ISData();
 152+ $lvalues = $c[2]->getChildren();
 153+
 154+ foreach( $array->data as $key => $item ) {
 155+ // <forlvalue> ::= <lvalue> | <lvalue> colon <lvalue>
 156+ if( count( $lvalues ) > 1 ) {
 157+ $this->setVar( $lvalues[0], ISData::newFromPHPVar( $key ), $rec );
 158+ $this->setVar( $lvalues[2], $item, $rec );
 159+ } else {
 160+ $this->setVar( $lvalues[0], $item, $rec );
 161+ }
 162+ try {
 163+ $last = $this->evaluateNode( $c[6], $rec + 1 );
 164+ } catch( ISUserVisibleException $e ) {
 165+ if( $e->getExceptionID() == 'break' )
 166+ break;
 167+ elseif( $e->getExceptionID() == 'continue' )
 168+ continue;
 169+ else
 170+ throw $e;
 171+ }
 172+ }
 173+ return $last;
 174+ case 'try':
 175+ try {
 176+ return $this->evaluateNode( $c[1], $rec + 1 );
 177+ } catch( ISUserVisibleException $e ) {
 178+ if( $e instanceof ISControlException ) {
 179+ throw $e;
 180+ } else {
 181+ $this->setVar( $c[4], new ISData( ISData::DString, $e->getExceptionID() ), $rec );
 182+ return $this->evaluateNode( $c[6], $rec + 1 );
 183+ }
 184+ }
 185+ default:
 186+ throw new ISException( "Unknown keyword: {$c[0]->type}" );
 187+ }
 188+ } else {
 189+ return $this->evaluateNode( $c[0], $rec + 1 );
 190+ }
 191+ case 'exprreturn':
 192+ switch( $c[0]->value ) {
 193+ case 'return':
 194+ if( isset( $c[1] ) ) {
 195+ $retval = $this->evaluateNode( $c[1], $rec + 1 );
 196+ $empty = false;
 197+ } else {
 198+ $retval = new ISData();
 199+ $empty = true;
 200+ }
 201+ throw new ISReturnException( $retval, $empty );
 202+ case 'append':
 203+ if( $this->mListOutput ) {
 204+ throw new ISUserVisibleException( 'appendyield', $this->mModuleName, $c[0]->line );
 205+ }
 206+
 207+ $this->mOutput = ISData::sum( $this->mOutput, $this->evaluateNode( $c[1], $rec + 1 ) );
 208+ break 2;
 209+ case 'yield':
 210+ if( $this->mOutput->type != ISData::DNull ) {
 211+ throw new ISUserVisibleException( 'appendyield', $this->mModuleName, $c[0]->line );
 212+ }
 213+
 214+ $this->mListOutput[] = $this->evaluateNode( $c[1], $rec + 1 );
 215+ break 2;
 216+ default:
 217+ throw new ISException( "Unknown return keyword: {$c[0]->value}" );
 218+ }
 219+ case 'exprset':
 220+ $this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
 221+ if( $c[1]->value == '=' ) {
 222+ $new = $this->evaluateNode( $c[2], $rec + 1 );
 223+ $this->setVar( $c[0], $new, $rec );
 224+ return $new;
 225+ } else {
 226+ $old = $this->getVar( $c[0], $rec, false );
 227+ $new = $this->evaluateNode( $c[2], $rec + 1 );
 228+ $new = $this->getValueForSetting( $old, $new,
 229+ $c[1]->value, $c[1]->line );
 230+ $this->setVar( $c[0], $new, $rec );
 231+ return $new;
 232+ }
 233+ case 'exprtrinary':
 234+ $cond = $this->evaluateNode( $c[0], $rec + 1 );
 235+ if( $cond->toBool() ) {
 236+ return $this->evaluateNode( $c[2], $rec + 1 );
 237+ } else {
 238+ return $this->evaluateNode( $c[4], $rec + 1 );
 239+ }
 240+ case 'exprlogical':
 241+ $this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
 242+ $arg1 = $this->evaluateNode( $c[0], $rec + 1 );
 243+ switch( $c[1]->value ) {
 244+ case '&':
 245+ if( !$arg1->toBool() )
 246+ return new ISData( ISData::DBool, false );
 247+ else
 248+ return $this->evaluateNode( $c[2], $rec + 1 );
 249+ case '|':
 250+ if( $arg1->toBool() )
 251+ return new ISData( ISData::DBool, true );
 252+ else
 253+ return $this->evaluateNode( $c[2], $rec + 1 );
 254+ case '^':
 255+ $arg2 = $this->evaluateNode( $c[2], $rec + 1 );
 256+ return new ISData( ISData::DBool, $arg1->toBool() xor $arg2->toBool() );
 257+ default:
 258+ throw new ISException( "Invalid logical operation: {$c[1]->value}" );
 259+ }
 260+ case 'exprequals':
 261+ case 'exprcompare':
 262+ $this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
 263+ $arg1 = $this->evaluateNode( $c[0], $rec + 1 );
 264+ $arg2 = $this->evaluateNode( $c[2], $rec + 1 );
 265+ return ISData::compareOp( $arg1, $arg2, $c[1]->value );
 266+ case 'exprsum':
 267+ $this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
 268+ $arg1 = $this->evaluateNode( $c[0], $rec + 1 );
 269+ $arg2 = $this->evaluateNode( $c[2], $rec + 1 );
 270+ switch( $c[1]->value ) {
 271+ case '+':
 272+ return ISData::sum( $arg1, $arg2 );
 273+ case '-':
 274+ return ISData::sub( $arg1, $arg2 );
 275+ }
 276+ case 'exprmul':
 277+ $this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
 278+ $arg1 = $this->evaluateNode( $c[0], $rec + 1 );
 279+ $arg2 = $this->evaluateNode( $c[2], $rec + 1 );
 280+ return ISData::mulRel( $arg1, $arg2, $c[1]->value, $this->mModuleName, $c[1]->line );
 281+ case 'exprpow':
 282+ $this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
 283+ $arg1 = $this->evaluateNode( $c[0], $rec + 1 );
 284+ $arg2 = $this->evaluateNode( $c[2], $rec + 1 );
 285+ return ISData::pow( $arg1, $arg2 );
 286+ case 'exprkeyword':
 287+ $this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
 288+ $arg1 = $this->evaluateNode( $c[0], $rec + 1 );
 289+ $arg2 = $this->evaluateNode( $c[2], $rec + 1 );
 290+ switch( $c[1]->value ) {
 291+ case 'in':
 292+ return ISData::keywordIn( $arg1, $arg2 );
 293+ case 'contains':
 294+ return ISData::keywordIn( $arg2, $arg1 );
 295+ default:
 296+ throw new ISException( "Invalid keyword: {$c[1]->value}" );
 297+ }
 298+ case 'exprinvert':
 299+ $this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[0]->line );
 300+ $arg = $this->evaluateNode( $c[1], $rec + 1 );
 301+ return ISData::boolInvert( $arg );
 302+ case 'exprunary':
 303+ $this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[0]->line );
 304+ $arg = $this->evaluateNode( $c[1], $rec + 1 );
 305+ if( $c[0]->value == '-' )
 306+ return ISData::unaryMinus( $arg );
 307+ else
 308+ return $arg;
 309+ case 'exprfunction':
 310+ // <exprFunction> ::= <funcid> leftbracket <commaListPlain> rightbracket | <funcid> leftbracket rightbracket
 311+ // <exprFunction> ::= <varfunc> leftbracket <lvalue> rightbracket | <exprAtom>
 312+ // <varfunc> ::= isset | delete
 313+ // <funcid> ::= id | <exprAtom> doublecolon id | self doublecolon id
 314+
 315+ $this->mInterpreter->increaseEvaluationsCount( $this->mModuleName, $c[1]->line );
 316+ if( $c[0]->getType() == 'funcid' ) {
 317+ if( $c[2] instanceof ISParserTreeNode ) {
 318+ $args = $this->parseArray( $c[2], $rec, $dummy );
 319+ } else {
 320+ $args = array();
 321+ }
 322+
 323+ $idch = $c[0]->getChildren();
 324+ if( count( $idch ) == 1 ) {
 325+ $funcname = $idch[0]->value;
 326+ if( !isset( self::$mFunctions[$funcname] ) )
 327+ throw new ISUserVisibleException( 'unknownfunction', $this->mModuleName, $idch[0]->line, array( $funcname ) );
 328+ $func = self::$mFunctions[$funcname];
 329+ return $this->$func( $args, $idch[0]->line );
 330+ } else {
 331+ $funcname = $idch[2]->value;
 332+ if( $idch[0] instanceof ISToken ) {
 333+ // self::function()
 334+ $module = $this->mModule;
 335+ } else {
 336+ // "ModuleName"::function()
 337+ $module = $this->evaluateNode( $idch[0], $rec + 1 )->toString();
 338+ }
 339+ return $this->mInterpreter->invokeUserFunctionFromModule(
 340+ $module, $funcname, $args, $this, $idch[1]->line );
 341+ }
 342+ } else {
 343+ $type = $c[0]->mChildren[0]->value;
 344+ switch( $type ) {
 345+ case 'isset':
 346+ $val = $this->getVar( $c[2], $rec, true );
 347+ return new ISData( ISData::DBool, $val !== null );
 348+ case 'delete':
 349+ $this->deleteVar( $c[2], $rec );
 350+ return new ISData();
 351+ default:
 352+ throw new ISException( "Unknown keyword: {$type}" );
 353+ }
 354+ }
 355+ case 'expratom':
 356+ if( $c[0] instanceof ISParserTreeNode ) {
 357+ if( $c[0]->getType() == 'atom' ) {
 358+ list( $val ) = $c[0]->getChildren();
 359+ switch( $val->type ) {
 360+ case 'string':
 361+ return new ISData( ISData::DString, $val->value );
 362+ case 'int':
 363+ return new ISData( ISData::DInt, $val->value );
 364+ case 'float':
 365+ return new ISData( ISData::DFloat, $val->value );
 366+ case 'true':
 367+ return new ISData( ISData::DBool, true );
 368+ case 'false':
 369+ return new ISData( ISData::DBool, false );
 370+ case 'null':
 371+ return new ISData();
 372+ }
 373+ } else {
 374+ return $this->getVar( $c[0], $rec );
 375+ }
 376+ } else {
 377+ switch( $c[0]->type ) {
 378+ case 'leftbracket':
 379+ return $this->evaluateNode( $c[1], $rec + 1 );
 380+ case 'leftsquare':
 381+ case 'leftcurly':
 382+ $arraytype = null;
 383+ $array = $this->parseArray( $c[1], $rec + 1, $arraytype );
 384+ return new ISData( $arraytype, $array );
 385+ case 'break':
 386+ throw new ISControlException( 'break', $this->mModuleName, $c[0]->line );
 387+ case 'continue':
 388+ throw new ISControlException( 'continue', $this->mModuleName, $c[0]->line );
 389+ }
 390+ }
 391+ default:
 392+ $type = $node->getType();
 393+ throw new ISException( "Invalid node type passed to evaluateNode(): {$type}" );
 394+ }
 395+ }
 396+
 397+ /*
 398+ * Converts commaList* to a PHP array.
 399+ */
 400+ protected function parseArray( $node, $rec, &$arraytype ) {
 401+ $c = $node->getChildren();
 402+ $type = $node->getType();
 403+ if( $type == 'commalist' ) {
 404+ return $this->parseArray( $c[0], $rec, $arraytype );
 405+ }
 406+
 407+ wfProfileIn( __METHOD__ );
 408+
 409+ // <commaListWhatever> ::= <commaListWhatever> comma <expr> | <expr>
 410+ $elements = $result = array();
 411+ while( isset( $c[2] ) ) {
 412+ array_unshift( $elements, $c[2] );
 413+ $c = $c[0]->getChildren();
 414+ }
 415+ array_unshift( $elements, $c[0] );
 416+
 417+ switch( $type ) {
 418+ case 'commalistplain':
 419+ foreach( $elements as $elem ) {
 420+ $result[] = $this->evaluateNode( $elem, $rec + 1 );
 421+ }
 422+
 423+ $arraytype = ISData::DList;
 424+ wfProfileOut( __METHOD__ );
 425+ return $result;
 426+
 427+ case 'commalistassoc':
 428+ foreach( $elements as $elem ) {
 429+ //<keyValue> ::= <expr> colon <expr>
 430+ list( $key, $crap, $value ) = $elem->getChildren();
 431+ $key = $this->evaluateNode( $key, $rec + 1 );
 432+ $value = $this->evaluateNode( $value, $rec + 1 );
 433+ $result[ $key->toString() ] = $value;
 434+ }
 435+
 436+ $arraytype = ISData::DAssoc;
 437+ wfProfileOut( __METHOD__ );
 438+ return $result;
 439+ }
 440+ }
 441+
 442+ /**
 443+ * Returns a value of the variable in $lval. If $nullIfNotSet is set to true,
 444+ * returns null if variable does not exist, otherwise throws an exception.
 445+ */
 446+ protected function getVar( $lval, $rec, $nullIfNotSet = false ) {
 447+ // <lvalue> ::= id | <lvalue> <arrayIdx>
 448+ // <arrayIdx> ::= leftsquare <expr> rightsquare | leftsquare rightsquare
 449+
 450+ if( !$this->mInterpreter->checkRecursionLimit( $rec ) ) {
 451+ throw new ISUserVisibleException( 'recoverflow', $this->mModuleName, $this->getLine( $node ) );
 452+ }
 453+
 454+ $c = $lval->getChildren();
 455+ if( $c[0] instanceof ISToken ) {
 456+ // Variable ID
 457+ $varname = $c[0]->value;
 458+ if( !isset( $this->mVars[$varname] ) ) {
 459+ if( $nullIfNotSet )
 460+ return null;
 461+ else
 462+ throw new ISUserVisibleException( 'unknownvar', $this->mModuleName, $c[0]->line, array( $varname ) );
 463+ }
 464+ return $this->mVars[$varname];
 465+ } else {
 466+ // Array element
 467+ $idxchildren = $c[1]->getChildren();
 468+ $var = $this->getVar( $c[0], $rec + 1, $nullIfNotSet );
 469+ if( $nullIfNotSet && $var === null )
 470+ return null;
 471+
 472+ if( count( $idxchildren ) == 2 ) {
 473+ // x = a[]. a[] is still legitimage in a[] = x
 474+ throw new ISUserVisibleException( 'emptyidx', $this->mModuleName, $idxchildren[0]->line );
 475+ }
 476+
 477+ switch( $var->type ) {
 478+ case ISData::DList:
 479+ $idx = $this->evaluateNode( $idxchildren[1], $rec + 1 )->toInt();
 480+ if( $idx >= count( $var->data ) ) {
 481+ if( $nullIfNotSet )
 482+ return null;
 483+ else
 484+ throw new ISUserVisibleException( 'outofbounds', $this->mModuleName, $idxchildren[0]->line );
 485+ }
 486+ return $var->data[$idx];
 487+ case ISData::DAssoc:
 488+ $idx = $this->evaluateNode( $idxchildren[1], $rec + 1 )->toString();
 489+ if( !isset( $var->data[$idx] ) ) {
 490+ if( $nullIfNotSet )
 491+ return null;
 492+ else
 493+ throw new ISUserVisibleException( 'outofbounds', $this->mModuleName, $idxchildren[0]->line );
 494+ }
 495+ return $var->data[$idx];
 496+ default:
 497+ throw new ISUserVisibleException( 'notanarray', $this->mModuleName, $idxchildren[0]->line );
 498+ }
 499+ }
 500+ }
 501+
 502+ /**
 503+ * Gets the line of the first terminal in the node.
 504+ */
 505+ protected function getLine( $node ) {
 506+ while( $node instanceof ISParserTreeNode ) {
 507+ $children = $node->getChildren();
 508+ $node = $children[0];
 509+ }
 510+ return $node->line;
 511+ }
 512+
 513+ /**
 514+ * Changes the value of variable or array element specified in $lval to $newval.
 515+ */
 516+ protected function setVar( $lval, $newval, $rec ) {
 517+ $var = &$this->setVarGetRef( $lval, $rec );
 518+ $var = $newval;
 519+ unset( $var );
 520+ }
 521+
 522+ /**
 523+ * Recursive function that return the link to the place
 524+ * where the new value of the variable must be set.
 525+ */
 526+ protected function &setVarGetRef( $lval, $rec ) {
 527+ if( !$this->mInterpreter->checkRecursionLimit( $rec ) ) {
 528+ throw new ISUserVisibleException( 'recoverflow', $this->mModuleName, $this->getLine( $node ) );
 529+ }
 530+
 531+ $c = $lval->getChildren();
 532+ if( count( $c ) == 1 ) {
 533+ if( !isset( $this->mVars[ $c[0]->value ] ) )
 534+ $this->mVars[ $c[0]->value ] = new ISPlaceholder();
 535+ return $this->mVars[ $c[0]->value ];
 536+ } else {
 537+ $ref = &$this->setVarGetRef( $c[0], $rec + 1 );
 538+
 539+ // <arrayIdx> ::= leftsquare <expr> rightsquare | leftsquare rightsquare
 540+ $idxc = $c[1]->getChildren();
 541+ if( $ref instanceof ISPlaceholder ) {
 542+ if( count( $idxc ) > 2 ) {
 543+ $index = $this->evaluateNode( $idxc[1], $rec + 1 );
 544+ $ref = new ISData( ISData::DAssoc, array() );
 545+ } else {
 546+ $ref = new ISData( ISData::DList, array() );
 547+ }
 548+ }
 549+
 550+ switch( $ref->type ) {
 551+ case ISData::DList:
 552+ if( count( $idxc ) > 2 ) {
 553+ $index = $this->evaluateNode( $idxc[1], $rec + 1 );
 554+ $key = $index->toInt();
 555+
 556+ if( $key < 0 || $key > count( $ref->data ) )
 557+ throw new ISUserVisibleException( 'outofbounds', $this->mModuleName, $idxc[0]->line );
 558+ } else {
 559+ $key = count( $ref->data );
 560+ }
 561+
 562+ if( !isset( $ref->data[$key] ) )
 563+ $ref->data[$key] = new ISPlaceholder();
 564+
 565+ return $ref->data[$key];
 566+ case ISData::DAssoc:
 567+ if( count( $idxc ) > 2 ) {
 568+ if( !isset( $index ) )
 569+ $index = $this->evaluateNode( $idxc[1], $rec + 1 );
 570+ $key = $index->toString();
 571+
 572+ if( !isset( $ref->data[$key] ) )
 573+ $ref->data[$key] = new ISPlaceholder();
 574+ return $ref->data[$key];
 575+ } else {
 576+ throw new ISUserVisibleException( 'notlist', $this->mModuleName, $idxc[0]->line );
 577+ }
 578+ break;
 579+ default:
 580+ throw new ISUserVisibleException( 'notanarray', $this->mModuleName, $idxc[0]->line );
 581+ }
 582+ }
 583+ }
 584+
 585+ protected function getValueForSetting( $old, $new, $set, $line ) {
 586+ switch( $set ) {
 587+ case '+=':
 588+ return ISData::sum( $old, $new );
 589+ case '-=':
 590+ return ISData::sub( $old, $new );
 591+ case '*=':
 592+ return ISData::mulRel( $old, $new, '*', $line );
 593+ case '/=':
 594+ return ISData::mulRel( $old, $new, '/', $line );
 595+ default:
 596+ return $new;
 597+ }
 598+ }
 599+
 600+ protected function checkParamsCount( $args, $pos, $count ) {
 601+ if( count( $args ) < $count )
 602+ throw new ISUserVisibleException( 'notenoughargs', $this->mModuleName, $pos );
 603+ }
 604+
 605+ protected function deleteVar( $lval, $rec ) {
 606+ $c = $lval->getChildren();
 607+ $line = $c[0]->line;
 608+ $varname = $c[0]->value;
 609+ if( isset( $c[1] ) ) {
 610+ throw new ISException( 'delete() is not usable for array elements' );
 611+ }
 612+ unset( $this->mVars[$varname] );
 613+ }
 614+
 615+ /** Functions */
 616+ protected function funcArg( $args, $pos ) {
 617+ $this->checkParamsCount( $args, $pos, 1 );
 618+
 619+ $argName = $args[0]->toString();
 620+ $default = isset( $args[1] ) ? $args[1] : new ISData();
 621+ if( $this->mFrame->getArgument( $argName ) === false )
 622+ return $default;
 623+ else
 624+ return new ISData( ISData::DString, $this->mFrame->getArgument( $argName ) );
 625+ }
 626+
 627+ protected function funcArgs( $args, $pos ) {
 628+ return ISData::newFromPHPVar( $this->mFrame->getNumberedArguments() );
 629+ }
 630+
 631+ protected function funcIsTranscluded( $args, $pos ) {
 632+ return new ISData( ISData::DBool, $this->mFrame->isTemplate() );
 633+ }
 634+
 635+ protected function funcParse( $args, $pos ) {
 636+ $this->checkParamsCount( $args, $pos, 1 );
 637+
 638+ $text = $args[0]->toString();
 639+ $this->mInterpreter->mCallStack->addParse( $text );
 640+
 641+ $oldOT = $this->mParser->mOutputType;
 642+ $this->mParser->setOutputType( Parser::OT_PREPROCESS );
 643+ $parsed = $this->mParser->replaceVariables( $text, $this->mFrame );
 644+ $parsed = $this->mParser->mStripState->unstripBoth( $parsed );
 645+ $this->mParser->setOutputType( $oldOT );
 646+
 647+ $this->mInterpreter->mCallStack->pop();
 648+ return new ISData( ISData::DString, $parsed );
 649+ }
 650+
 651+ protected function funcLc( $args, $pos ) {
 652+ global $wgContLang;
 653+ $this->checkParamsCount( $args, $pos, 1 );
 654+ return new ISData( ISData::DString, $wgContLang->lc( $args[0]->toString() ) );
 655+ }
 656+
 657+ protected function funcUc( $args, $pos ) {
 658+ global $wgContLang;
 659+ $this->checkParamsCount( $args, $pos, 1 );
 660+ return new ISData( ISData::DString, $wgContLang->uc( $args[0]->toString() ) );
 661+ }
 662+
 663+ protected function funcUcFirst( $args, $pos ) {
 664+ global $wgContLang;
 665+ $this->checkParamsCount( $args, $pos, 1 );
 666+ return new ISData( ISData::DString, $wgContLang->ucfirst( $args[0]->toString() ) );
 667+ }
 668+
 669+ protected function funcUrlencode( $args, $pos ) {
 670+ $this->checkParamsCount( $args, $pos, 1 );
 671+ return new ISData( ISData::DString, urlencode( $args[0]->toString() ) );
 672+ }
 673+
 674+ protected function funcAnchorEncode( $args, $pos ) {
 675+ $this->checkParamsCount( $args, $pos, 1 );
 676+
 677+ $s = urlencode( $args[0]->toString() );
 678+ $s = strtr( $s, array( '%' => '.', '+' => '_' ) );
 679+ $s = str_replace( '.3A', ':', $s );
 680+
 681+ return new ISData( ISData::DString, $s );
 682+ }
 683+
 684+ protected function funcGrammar( $args, $pos ) {
 685+ $this->checkParamsCount( $args, $pos, 2 );
 686+ list( $case, $word ) = $args;
 687+ $res = $this->mParser->getFunctionLang()->convertGrammar(
 688+ $word->toString(), $case->toString() );
 689+ return new ISData( ISData::DString, $res );
 690+ }
 691+
 692+ protected function funcPlural( $args, $pos ) {
 693+ $this->checkParamsCount( $args, $pos, 2 );
 694+ $num = $args[0]->toInt();
 695+ for( $i = 1; $i < count( $args ); $i++ )
 696+ $forms[] = $args[$i]->toString();
 697+ $res = $this->mParser->getFunctionLang()->convertPlural( $num, $forms );
 698+ return new ISData( ISData::DString, $res );
 699+ }
 700+
 701+ protected function funcStrlen( $args, $pos ) {
 702+ $this->checkParamsCount( $args, $pos, 1 );
 703+ return new ISData( ISData::DInt, mb_strlen( $args[0]->toString() ) );
 704+ }
 705+
 706+ protected function funcSubstr( $args, $pos ) {
 707+ $this->checkParamsCount( $args, $pos, 3 );
 708+ $s = $args[0]->toString();
 709+ $start = $args[1]->toInt();
 710+ $end = $args[2]->toInt();
 711+ return new ISData( ISData::DString, mb_substr( $s, $start, $end ) );
 712+ }
 713+
 714+ protected function funcStrreplace( $args, $pos ) {
 715+ $this->checkParamsCount( $args, $pos, 3 );
 716+ $s = $args[0]->toString();
 717+ $old = $args[1]->toString();
 718+ $new = $args[2]->toString();
 719+ return new ISData( ISData::DString, str_replace( $old, $new, $s ) );
 720+ }
 721+
 722+ protected function funcSplit( $args, $pos ) {
 723+ $this->checkParamsCount( $args, $pos, 2 );
 724+ $list = explode( $args[0]->toString(), $args[1]->toString() );
 725+ return ISData::newFromPHPVar( $list );
 726+ }
 727+
 728+ protected function funcJoin( $args, $pos ) {
 729+ $this->checkParamsCount( $args, $pos, 2 );
 730+ $seperator = $args[0]->toString();
 731+ if( $args[1]->type == ISData::DList ) {
 732+ $bits = $args[1]->data;
 733+ } else {
 734+ $bits = array_slice( $args, 1 );
 735+ }
 736+ foreach( $bits as &$bit )
 737+ $bit = $bit->toString();
 738+ return new ISData( ISData::DString, implode( $seperator, $bits ) );
 739+ }
 740+
 741+ protected function funcCount( $args, $pos ) {
 742+ $this->checkParamsCount( $args, $pos, 1 );
 743+ return new ISData( ISData::DInt, count( $args[0]->toList()->data ) );
 744+ }
 745+
 746+ protected function castString( $args, $pos ) {
 747+ $this->checkParamsCount( $args, $pos, 1 );
 748+ return ISData::castTypes( $args[0], ISData::DString );
 749+ }
 750+
 751+ protected function castInt( $args, $pos ) {
 752+ $this->checkParamsCount( $args, $pos, 1 );
 753+ return ISData::castTypes( $args[0], ISData::DInt );
 754+ }
 755+
 756+ protected function castFloat( $args, $pos ) {
 757+ $this->checkParamsCount( $args, $pos, 1 );
 758+ return ISData::castTypes( $args[0], ISData::DFloat );
 759+ }
 760+
 761+ protected function castBool( $args, $pos ) {
 762+ $this->checkParamsCount( $args, $pos, 1 );
 763+ return ISData::castTypes( $args[0], ISData::DBool );
 764+ }
 765+}
Index: trunk/extensions/WikiScripts/interpreter/syntax.txt
@@ -0,0 +1,45 @@
 2+<module> ::= <functions>
 3+<functions> ::= <function> <functions> | <function>
 4+
 5+<function> ::= function id leftbracket <args> rightbracket leftcurly <stmts> rightcurly
 6+<function> ::= function id leftbracket rightbracket leftcurly <stmts> rightcurly
 7+<args> ::= id comma <args> | id
 8+
 9+<stmts> ::= <stmts> <stmt> | <stmt>
 10+<stmt> ::= <expr> semicolon
 11+<stmt> ::= if leftbracket <expr> rightbracket <stmt>
 12+<stmt> ::= if leftbracket <expr> rightbracket <stmt> else <stmt>
 13+<stmt> ::= for leftbracket <forlvalue> in <expr> rightbracket <stmt>
 14+<stmt> ::= try <stmt> catch leftbracket <lvalue> rightbracket <stmt>
 15+<stmt> ::= leftcurly <stmts> rightcurly
 16+
 17+<expr> ::= <exprReturn>
 18+<exprReturn> ::= return | return <exprSet> | <exprSet>
 19+<exprReturn> ::= append <exprSet> | yield <exprSet>
 20+<exprSet> ::= <lvalue> setto <exprSet> | <exprTrinary>
 21+<exprTrinary> ::= <exprLogical> trinary <exprTrinary> colon <exprTrinary> | <exprLogical>
 22+<exprLogical> ::= <exprLogical> logicop <exprCompare> | <exprCompare>
 23+<exprCompare> ::= <exprCompare> compareop <exprEquals> | <exprEquals>
 24+<exprEquals> ::= <exprSum> equalsto <exprSum> | <exprSum>
 25+<exprSum> ::= <exprSum> sum <exprMul> | <exprMul>
 26+<exprMul> ::= <exprMul> mul <exprPow> | <exprPow>
 27+<exprPow> ::= <exprInvert> pow <exprPow> | <exprInvert>
 28+<exprInvert> ::= invert <exprKeyword> | <exprKeyword>
 29+<exprKeyword> ::= <exprUnary> in <exprUnary> | <exprUnary> contains <exprUnary> | <exprUnary>
 30+<exprUnary> ::= sum <exprFunction> | <exprFunction>
 31+<exprFunction> ::= <funcid> leftbracket <commaListPlain> rightbracket | <funcid> leftbracket rightbracket
 32+<exprFunction> ::= <varfunc> leftbracket <lvalue> rightbracket | <exprAtom>
 33+<exprAtom> ::= <lvalue> | <atom> | break | continue
 34+<exprAtom> ::= leftbracket <expr> rightbracket
 35+<exprAtom> ::= leftsquare <commaListPlain> rightsquare | leftcurly <commaListAssoc> rightcurly
 36+
 37+<varfunc> ::= isset | delete
 38+<funcid> ::= id | <exprAtom> doublecolon id | self doublecolon id
 39+
 40+<commaListPlain> ::= <commaListPlain> comma <expr> | <expr>
 41+<commaListAssoc> ::= <commaListAssoc> comma <keyValue> | <keyValue>
 42+<keyValue> ::= <expr> colon <expr>
 43+<atom> ::= string | int | float | true | false | null
 44+<lvalue> ::= id | <lvalue> <arrayIdx>
 45+<arrayIdx> ::= leftsquare <expr> rightsquare | leftsquare rightsquare
 46+<forlvalue> ::= <lvalue> | <lvalue> colon <lvalue>
Property changes on: trunk/extensions/WikiScripts/interpreter/syntax.txt
___________________________________________________________________
Added: svn:eol-style
147 + native
Index: trunk/extensions/WikiScripts/interpreter/CallStack.php
@@ -0,0 +1,90 @@
 2+<?php
 3+
 4+abstract class ISCallStackEntry {
 5+ abstract function toString();
 6+}
 7+
 8+abstract class ISCallStackFunctionEntry extends ISCallStackEntry {
 9+ public $module;
 10+ public $function;
 11+}
 12+
 13+class ISCallStackFunctionFromModuleEntry extends ISCallStackFunctionEntry {
 14+ public $invokingModule;
 15+ public $line;
 16+
 17+ public function toString() {
 18+ return wfMsg( 'inlinescripts-call-frommodule', $this->module, $this->function,
 19+ $this->invokingModule, $this->line );
 20+ }
 21+}
 22+
 23+class ISCallStackFunctionFromWikitextEntry extends ISCallStackFunctionEntry {
 24+ public function toString() {
 25+ return wfMsg( 'inlinescripts-call-frommodule', $this->module, $this->function );
 26+ }
 27+}
 28+
 29+class ISCallStackParseEntry extends ISCallStackEntry {
 30+ public $text;
 31+
 32+ public function toString() {
 33+ return wfMsg( 'inlinescripts-call-parse', $this->text );
 34+ }
 35+}
 36+
 37+class ISCallStack {
 38+ var $mInterpreter, $mStack;
 39+
 40+ public function __construct( $interpreter ) {
 41+ $this->mInterpreter = $interpreter;
 42+ $this->mStack = array();
 43+ }
 44+
 45+ public function pop() {
 46+ array_pop( $this->mStack );
 47+ }
 48+
 49+ public function isFull() {
 50+ global $wgInlineScriptsMaxCallStackDepth;
 51+
 52+ return count( $this->mStack ) >= $wgInlineScriptsMaxCallStackDepth;
 53+ }
 54+
 55+ public function contains( $module, $name ) {
 56+ foreach( $this->mStack as $entry ) {
 57+ if( $entry instanceof ISCallStackFunctionEntry ) {
 58+ if( $entry->module == $module && $entry->function == $name ) {
 59+ return true;
 60+ }
 61+ }
 62+ }
 63+ return false;
 64+ }
 65+
 66+ public function addFunctionFromModule( $module, $name, $invokingModule, $line ) {
 67+ $entry = new ISCallStackFunctionFromModuleEntry();
 68+ $entry->module = $module;
 69+ $entry->function = $name;
 70+ $entry->invokingModule = $invokingModule;
 71+ $entry->line = $line;
 72+ $this->mStack[] = $entry;
 73+ }
 74+
 75+ public function addFunctionFromWikitext( $module, $name ) {
 76+ $entry = new ISCallStackFunctionFromWikitextEntry();
 77+ $entry->module = $module;
 78+ $entry->function = $name;
 79+ $this->mStack[] = $entry;
 80+ }
 81+
 82+ public function addParse( $wikitext ) {
 83+ if( strlen( $wikitext ) > 64 ) {
 84+ $wikitext = substr( $wikitext, 0, 64 ) . "...";
 85+ }
 86+
 87+ $entry = new ISCallStackParseEntry();
 88+ $entry->text = $wikitext;
 89+ $this->mStack[] = $entry;
 90+ }
 91+}
Index: trunk/extensions/WikiScripts/interpreter/Interpreter.php
@@ -0,0 +1,484 @@
 2+<?php
 3+/**
 4+ * Built-in scripting language for MediaWiki: interpreter.
 5+ * Copyright (C) 2009-2011 Victor Vasiliev <vasilvv@gmail.com>
 6+ * http://www.mediawiki.org/
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License along
 19+ * with this program; if not, write to the Free Software Foundation, Inc.,
 20+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 21+ * http://www.gnu.org/copyleft/gpl.html
 22+ */
 23+
 24+if( !defined( 'MEDIAWIKI' ) )
 25+ die();
 26+
 27+require_once( 'Shared.php' );
 28+require_once( 'Data.php' );
 29+require_once( 'EvaluationContext.php' );
 30+require_once( 'CallStack.php' );
 31+
 32+/**
 33+ * The global interpreter object. Each parser has one.
 34+ */
 35+class ISInterpreter {
 36+ const ParserVersion = 1;
 37+
 38+ var $mParser, $mUseCache, $mUsedModules, $mCallStack;
 39+ var $mMaxRecursion, $mEvaluations;
 40+ var $mParserCache; // Unserializing can be expensive as well
 41+
 42+ static $mCodeParser;
 43+
 44+ public function __construct( $parser ) {
 45+ global $wgInlineScriptsUseCache;
 46+
 47+ $this->mParser = $parser;
 48+ $this->mUseCache = $wgInlineScriptsUseCache;
 49+
 50+ $this->mCallStack = new ISCallStack( $this );
 51+ $this->mUsedModules = array();
 52+ $this->mMaxRecursion =
 53+ $this->mEvaluations =
 54+ 0;
 55+ }
 56+
 57+ public static function invokeCodeParser( $code, $module, $method = 'parse' ) {
 58+ global $wgInlineScriptsParserClass, $wgInlineScriptsLimits;
 59+
 60+ if( !self::$mCodeParser ) {
 61+ self::$mCodeParser = new $wgInlineScriptsParserClass();
 62+ }
 63+
 64+ if( self::$mCodeParser->needsScanner() ) {
 65+ $input = new ISScanner( $module, $code );
 66+ } else {
 67+ $input = $code;
 68+ }
 69+
 70+ return self::$mCodeParser->$method( $input, $module, $wgInlineScriptsLimits['tokens'] );
 71+ }
 72+
 73+ /**
 74+ * Invalidate the module by title.
 75+ */
 76+ public static function invalidateModule( $title ) {
 77+ global $parserMemc;
 78+
 79+ $key = ISModule::getCacheKey( $title );
 80+ $parserMemc->delete( $key );
 81+ }
 82+
 83+ /**
 84+ * Checks the syntax of the script and returns an array of syntax errors.
 85+ */
 86+ public static function getSyntaxErrors( $module, $text ) {
 87+ return self::invokeCodeParser( $text, $module, 'getSyntaxErrors' );
 88+ }
 89+
 90+ /**
 91+ * Disable cache for benchmarking or debugging purposes.
 92+ */
 93+ public function disableCache() {
 94+ $this->mUseCache = false;
 95+ }
 96+
 97+ /**
 98+ * Get the wikitext parser linked to the interpreter.
 99+ */
 100+ public function getParser() {
 101+ return $this->mParser;
 102+ }
 103+
 104+ /** Limit handling code */
 105+
 106+ /**
 107+ * Check whether the argument is more than recursion limit.
 108+ *
 109+ * @return Boolean
 110+ */
 111+ public function checkRecursionLimit( $rec ) {
 112+ global $wgInlineScriptsLimits;
 113+ if( $rec > $this->mMaxRecursion )
 114+ $this->mMaxRecursion = $rec;
 115+ return $rec <= $wgInlineScriptsLimits['depth'];
 116+ }
 117+
 118+ /**
 119+ * Increases the number of evaluations.
 120+ *
 121+ * @param $module ISModule Module where the evaluation happens
 122+ * @param $line int Line number of the evaluation
 123+ * @return Boolean
 124+ */
 125+ public function increaseEvaluationsCount( $module, $line ) {
 126+ global $wgInlineScriptsLimits;
 127+ $this->mEvaluations++;
 128+ if( $this->mEvaluations > $wgInlineScriptsLimits['evaluations'] )
 129+ throw new ISUserVisibleException( 'toomanyevals', $module, $line );
 130+ }
 131+
 132+ public function getMaxTokensLeft() {
 133+ global $wgInlineScriptsLimits;
 134+ return $wgInlineScriptsLimits['tokens'];
 135+ }
 136+
 137+ /**
 138+ * Adds the title of the module used by the parser for tracking purposes.
 139+ *
 140+ * @param $title Title Title of the module.
 141+ */
 142+ public function addModuleTitle( $title ) {
 143+ if( !in_array( $title->getText(), $this->mUsedModules ) )
 144+ $this->mUsedModules[] = $title->getDBkey();
 145+ }
 146+
 147+ /** Module loading code */
 148+
 149+ /**
 150+ * Loads module from cache or from page and parses it. Cached as much as possible.
 151+ *
 152+ * @param $title Title Title of the module to load.
 153+ */
 154+ public function getModule( $title ) {
 155+ global $parserMemc;
 156+
 157+ wfProfileIn( __METHOD__ );
 158+
 159+ // Add nominal title. Real title may differ due to redirects
 160+ $this->addModuleTitle( $title );
 161+
 162+ // Try local cache
 163+ $key = ISModule::getCacheKey( $title );
 164+ if( $this->mUseCache && isset( $this->mParserCache[$key] ) ) {
 165+ $module = $this->mParserCache[$key];
 166+ $this->addModuleTitle( $module->getTitle() );
 167+ wfProfileOut( __METHOD__ );
 168+ return $module;
 169+ }
 170+
 171+ // Try the object cache
 172+ wfProfileIn( __METHOD__ . '-unserialize' );
 173+ $cached = $parserMemc->get( $key );
 174+ wfProfileOut( __METHOD__ . '-unserialize' );
 175+ if( $this->mUseCache && @$cached instanceof ISModule && !$cached->isOutOfDate() ) {
 176+ $this->mParserCache[$key] = $cached;
 177+ $this->addModuleTitle( $cached->getTitle() );
 178+ wfProfileOut( __METHOD__ );
 179+ return $cached;
 180+ }
 181+
 182+ // Load from database
 183+ $rev = self::getModuleRev( $title );
 184+ if( !$rev || $rev->getTitle()->getNamespace() != NS_MODULE ) {
 185+ return false;
 186+ }
 187+
 188+ // Parse
 189+ $moduleName = $rev->getTitle()->getText();
 190+ $out = self::invokeCodeParser( $rev->getText(), $moduleName );
 191+ $module = ISModule::newFromParserOutput( $this, $rev->getTitle(), $rev->getId(), $out );
 192+
 193+ // Save to cache
 194+ $this->mParserCache[$key] = $module;
 195+ $parserMemc->set( $key, $module );
 196+ $this->addModuleTitle( $module->getTitle() );
 197+
 198+ wfProfileOut( __METHOD__ );
 199+ return $module;
 200+ }
 201+
 202+ /**
 203+ * Fetches the revision for given module title.
 204+ */
 205+ private function getModuleRev( $title ) {
 206+ $rev = Revision::newFromTitle( $title );
 207+ if( $rev && $real = Title::newFromRedirect( $rev->getText() ) ) {
 208+ $rev = Revision::newFromTitle( $real );
 209+ }
 210+ return $rev;
 211+ }
 212+
 213+ /** Function evaluation code */
 214+
 215+ /**
 216+ * Invokes the user function from script code.
 217+ *
 218+ * @param $module ISModule/string Module or its name
 219+ * @param $name string Name of the function
 220+ * @param $args array(ISData) Arguments of the function
 221+ * @param $parentContext ISEvaluationContext The context from which the function was invoked
 222+ * @param $line The line from which the function was invoked.
 223+ * @return ISData
 224+ */
 225+ public function invokeUserFunctionFromModule( $module, $name, $args, $parentContext, $line ) {
 226+ global $wgInlineScriptsAllowRecursion, $wgInlineScriptsMaxCallStackDepth;
 227+
 228+ // Load module
 229+ if( $module instanceof ISModule ) {
 230+ $moduleName = $module->getName();
 231+ } else {
 232+ $moduleName = $module;
 233+
 234+ $moduleTitle = Title::makeTitleSafe( NS_MODULE, $moduleName );
 235+ if( !$moduleTitle instanceof Title || $moduleTitle->getNamespace() != NS_MODULE ) {
 236+ throw new ISUserVisibleException( 'nonexistent-module', $parentContext->mModuleName, $line, array( $moduleName ) );
 237+ }
 238+
 239+ $module = $this->getModule( $moduleTitle );
 240+ if( !$module ) {
 241+ throw new ISUserVisibleException( 'nonexistent-module', $parentContext->mModuleName, $line, array( $moduleName ) );
 242+ }
 243+ }
 244+
 245+ // Load the function and handle possible errors
 246+ $function = $module->getFunction( $name );
 247+ if( !$function ) {
 248+ throw new ISUserVisibleException( 'unknownfunction-user', $parentContext->mModuleName, $line, array( $moduleName, $name ) );
 249+ }
 250+ if( count( $args ) < $function->getMinArgCount() ) {
 251+ throw new ISUserVisibleException( 'notenoughargs-user', $parentContext->mModuleName, $line, array( $moduleName, $name ) );
 252+ }
 253+ if( !$wgInlineScriptsAllowRecursion && $this->mCallStack->contains( $moduleName, $name ) ) {
 254+ throw new ISUserVisibleException( 'recursion', $parentContext->mModuleName, $line, array( $moduleName, $name ) );
 255+ }
 256+ if( $this->mCallStack->isFull() ) {
 257+ throw new ISUserVisibleException( 'toodeeprecursion', $parentContext->mModuleName, $line, array( $wgInlineScriptsMaxCallStackDepth ) );
 258+ }
 259+
 260+ // Prepare the context and the arguments
 261+ $context = new ISEvaluationContext( $this, $module, $name, $parentContext->getFrame() );
 262+ foreach( $args as $n => $arg ) {
 263+ if( isset( $function->args[$n] ) ) {
 264+ $argname = $function->args[$n];
 265+ $context->setArgument( $argname, $arg );
 266+ }
 267+ }
 268+
 269+ // Push function into call stack
 270+ $this->mCallStack->addFunctionFromModule( $moduleName, $name, $parentContext->mModuleName, $line );
 271+
 272+ // Finally execute it!
 273+ $result = $this->doInvokeFunction( $function, $context );
 274+
 275+ // Pop out of the call stack and return
 276+ $this->mCallStack->pop();
 277+ return $result;
 278+ }
 279+
 280+ /**
 281+ * Invokes the user function from wikitext.
 282+ *
 283+ * @param $module string The name of the module
 284+ * @param $name string Name of the function
 285+ * @param $args array(string) Arguments of the function
 286+ * @param $frame PPFrame The parser frame from which the function was invoked
 287+ * @return string
 288+ */
 289+ public function invokeUserFunctionFromWikitext( $moduleName, $name, $args, $frame ) {
 290+ global $wgInlineScriptsAllowRecursion;
 291+
 292+ // Load module
 293+ $moduleTitle = Title::makeTitleSafe( NS_MODULE, $moduleName );
 294+ if( !$moduleTitle instanceof Title || $moduleTitle->getNamespace() != NS_MODULE ) {
 295+ throw new ISTransclusionException( 'nonexistent-module', array( $moduleName ) );
 296+ }
 297+
 298+ $module = $this->getModule( $moduleTitle );
 299+ if( !$module ) {
 300+ throw new ISTransclusionException( 'nonexistent-module', array( $moduleName ) );
 301+ }
 302+
 303+ // Load the function and handle possible errors
 304+ $function = $module->getFunction( $name );
 305+ if( !$function ) {
 306+ throw new ISTransclusionException( 'unknownfunction-user', array( $moduleName, $name ) );
 307+ }
 308+ if( count( $args ) < $function->getMinArgCount() ) {
 309+ throw new ISTransclusionException( 'notenoughargs-user', array( $moduleName, $name ) );
 310+ }
 311+ if( !$wgInlineScriptsAllowRecursion && $this->mCallStack->contains( $moduleName, $name ) ) {
 312+ throw new ISTransclusionException( 'recursion', array( $moduleName, $name ) );
 313+ }
 314+ if( $this->mCallStack->isFull() ) {
 315+ // Depsite seeming an unlikely place, this may actually happen if the user will try to bypass the
 316+ // stack depth limit by using parse( '{{i:module|func}}' )
 317+ throw new ISTransclusionException( 'toodeeprecursion', array( $wgInlineScriptsMaxCallStackDepth ) );
 318+ }
 319+
 320+ // Prepare the context and the arguments
 321+ $context = new ISEvaluationContext( $this, $module, $name, $frame );
 322+ foreach( $args as $n => $arg ) {
 323+ if( isset( $function->args[$n] ) ) {
 324+ $argname = $function->args[$n];
 325+ $context->setArgument( $argname, new ISData( ISData::DString, strval( $arg ) ) );
 326+ }
 327+ }
 328+
 329+ // Push function into call stack
 330+ $this->mCallStack->addFunctionFromWikitext( $moduleName, $name );
 331+
 332+ // Finally execute it!
 333+ $result = $this->doInvokeFunction( $function, $context );
 334+
 335+ // Pop out of the call stack and return
 336+ $this->mCallStack->pop();
 337+ return $result->toString();
 338+ }
 339+
 340+ protected function doInvokeFunction( $function, $context ) {
 341+ // Indicates whether the data from append/yield should be used
 342+ $useOutput = true;
 343+ $result = new ISData();
 344+
 345+ try {
 346+ $context->evaluateNode( $function->body, 0 );
 347+ } catch( ISException $e ) {
 348+ if( $e instanceof ISReturnException ) {
 349+ $result = $e->getResult();
 350+ $useOutput = $e->isEmpty();
 351+ } else {
 352+ $this->mCallStack->pop();
 353+ throw $e;
 354+ }
 355+ }
 356+
 357+ if( $useOutput ) {
 358+ $result = $context->getOutput();
 359+ }
 360+
 361+ return $result;
 362+ }
 363+}
 364+
 365+/**
 366+ * Represents an individual module.
 367+ */
 368+class ISModule {
 369+ var $mTitle, $mFunctions, $mParserVersion;
 370+
 371+ // Revision ID
 372+ // Not used now, will be used if we ever introduce the function output cache
 373+ var $mRevID;
 374+
 375+ protected function __construct() {}
 376+
 377+ /**
 378+ * Initializes module from the code parser output.
 379+ */
 380+ public static function newFromParserOutput( $interpreter, $title, $revid, $output ) {
 381+ $m = new ISModule();
 382+ $m->mTitle = $title;
 383+ $m->mRevID = $revid;
 384+ $m->mParserVersion = $output->getVersion();
 385+ $m->mFunctions = array();
 386+
 387+ $cur = $output->getParserTree();
 388+ for( ; ; ) {
 389+ $curch = $cur->getChildren();
 390+ $funcnode = $curch[0];
 391+
 392+ // <function> ::= function id leftbracket <arglist> rightbracket leftcurly <stmts> rightcurly (total 8)
 393+ // <function> ::= function id leftbracket rightbracket leftcurly <stmts> rightcurly (total 7)
 394+ $c = $funcnode->getChildren();
 395+ $func = new ISFunction();
 396+ $func->name = $c[1]->value;
 397+
 398+ if( $funcnode->getChildrenCount() == 8 ) {
 399+ $func->body = $c[6];
 400+ $func->args = self::parseArgs( $c[3] );
 401+ } else {
 402+ $func->body = $c[5];
 403+ $func->args = array();
 404+ }
 405+ $m->mFunctions[$func->name] = $func;
 406+
 407+ if( $cur->hasSingleChild() )
 408+ break;
 409+ else
 410+ $cur = $curch[1];
 411+ }
 412+
 413+ return $m;
 414+ }
 415+
 416+ /**
 417+ * Converts the argument list node into an array.
 418+ */
 419+ private static function parseArgs( $argsnode ) {
 420+ $args = array();
 421+ $cur = $argsnode;
 422+ for( ; ; ) {
 423+ $c = $cur->getChildren();
 424+ $args[] = $c[0]->value;
 425+
 426+ if( $cur->hasSingleChild() )
 427+ break;
 428+ else
 429+ $cur = $c[2];
 430+ }
 431+ return $args;
 432+ }
 433+
 434+ /**
 435+ * Gets the cache key for a module.
 436+ */
 437+ public static function getCacheKey( $title ) {
 438+ return 'scripts:module:' . sha1( $title->getText() );
 439+ }
 440+
 441+ /**
 442+ * Returns the name of a module.
 443+ */
 444+ public function getName() {
 445+ return $this->mTitle->getText();
 446+ }
 447+
 448+ /**
 449+ * Returns the title of a module.
 450+ */
 451+ public function getTitle() {
 452+ return $this->mTitle;
 453+ }
 454+
 455+ /**
 456+ * Returns a specific function or null if it does not exist.
 457+ */
 458+ public function getFunction( $name ) {
 459+ if( isset( $this->mFunctions[$name] ) )
 460+ return $this->mFunctions[$name];
 461+ else
 462+ return null;
 463+ }
 464+
 465+ /**
 466+ * Returns whether the module should be reparsed or not.
 467+ */
 468+ public function isOutOfDate() {
 469+ global $wgInlineScriptsParserClass;
 470+ return $wgInlineScriptsParserClass::getVersion() != $this->mParserVersion;
 471+ }
 472+}
 473+
 474+/**
 475+ * Represents a function.
 476+ */
 477+class ISFunction {
 478+ public $name;
 479+ public $args;
 480+ public $body;
 481+
 482+ public function getMinArgCount() {
 483+ return count( $this->args );
 484+ }
 485+}
Property changes on: trunk/extensions/WikiScripts/interpreter/Interpreter.php
___________________________________________________________________
Added: svn:eol-style
1486 + native
Index: trunk/extensions/WikiScripts/interpreter/benchmark/benchmark.php
@@ -0,0 +1,54 @@
 2+<?php
 3+
 4+/**
 5+ * Initial very basic framework for doing some benchmark tests, comparing
 6+ * scripts in the inline script to equivalent raw PHP execution (eval'd)
 7+ *
 8+ * <brion@pobox.com> 2010-04-14
 9+ */
 10+
 11+if (php_sapi_name() != 'cli') {
 12+ die("cli only");
 13+}
 14+
 15+require_once "../../../../maintenance/commandLine.inc";
 16+
 17+require_once "../../InlineScripts.php"; // if ext not already enabled
 18+
 19+$testcases = array(array(
 20+'script' => <<<END_SCRIPT
 21+"Hello, world!";
 22+END_SCRIPT
 23+,
 24+'php' => <<<END_PHP
 25+return "Hello, world!";
 26+END_PHP
 27+));
 28+
 29+function runtest( $lang, $source ) {
 30+ if ($lang == 'php') {
 31+ return eval($source);
 32+ } else {
 33+ $scriptParser = new ISInterpreter();
 34+ $parser = new Parser();
 35+ $frame = null; //new Frame();
 36+ return $scriptParser->evaluate( $source, $parser, $frame );
 37+ }
 38+}
 39+
 40+$runs = 10;
 41+foreach( $testcases as $testcase ) {
 42+ foreach( $testcase as $lang => $code ) {
 43+ $start = microtime( true );
 44+ $result = runtest( $lang, $code );
 45+ $delta1 = microtime( true ) - $start;
 46+ for ($i = 1; $i < $runs; $i++) {
 47+ runtest( $lang, $code );
 48+ }
 49+ $delta = (microtime( true ) - $start) / $runs;
 50+ printf( "%s: %0.3f ms first, %0.3f ms avg of %d runs\n",
 51+ $lang, $delta1 * 1000.0, $delta * 1000.0, $runs );
 52+ var_dump($result);
 53+ print "\n";
 54+ }
 55+}
Property changes on: trunk/extensions/WikiScripts/interpreter/benchmark/benchmark.php
___________________________________________________________________
Added: svn:eol-style
156 + native
Index: trunk/extensions/WikiScripts/interpreter/LRTable.php
@@ -0,0 +1,2288 @@
 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 2011-08-13 21:53:42.
 24+ */
 25+
 26+class ISLRTable {
 27+
 28+const Timestamp = '2011-08-13 21:53:42';
 29+
 30+static $nonterminals = array(
 31+ 0 => 'module',
 32+ 1 => 'functions',
 33+ 2 => 'function',
 34+ 3 => 'args',
 35+ 4 => 'stmts',
 36+ 5 => 'stmt',
 37+ 6 => 'expr',
 38+ 7 => 'forlvalue',
 39+ 8 => 'lvalue',
 40+ 9 => 'exprreturn',
 41+ 10 => 'exprset',
 42+ 11 => 'exprtrinary',
 43+ 12 => 'exprlogical',
 44+ 13 => 'exprcompare',
 45+ 14 => 'exprequals',
 46+ 15 => 'exprsum',
 47+ 16 => 'exprmul',
 48+ 17 => 'exprpow',
 49+ 18 => 'exprinvert',
 50+ 19 => 'exprkeyword',
 51+ 20 => 'exprunary',
 52+ 21 => 'exprfunction',
 53+ 22 => 'funcid',
 54+ 23 => 'commalistplain',
 55+ 24 => 'varfunc',
 56+ 25 => 'expratom',
 57+ 26 => 'atom',
 58+ 27 => 'commalistassoc',
 59+ 28 => 'keyvalue',
 60+ 29 => 'arrayidx',
 61+);
 62+
 63+static $productions = array(
 64+ 0 => array( 0, array( 1 ) ),
 65+ 1 => array( 1, array( 2, 1 ) ),
 66+ 2 => array( 1, array( 2 ) ),
 67+ 3 => array( 2, array( 'function', 'id', 'leftbracket', 3, 'rightbracket', 'leftcurly', 4, 'rightcurly' ) ),
 68+ 4 => array( 2, array( 'function', 'id', 'leftbracket', 'rightbracket', 'leftcurly', 4, 'rightcurly' ) ),
 69+ 5 => array( 3, array( 'id', 'comma', 3 ) ),
 70+ 6 => array( 3, array( 'id' ) ),
 71+ 7 => array( 4, array( 4, 5 ) ),
 72+ 8 => array( 4, array( 5 ) ),
 73+ 9 => array( 5, array( 6, 'semicolon' ) ),
 74+ 10 => array( 5, array( 'if', 'leftbracket', 6, 'rightbracket', 5 ) ),
 75+ 11 => array( 5, array( 'if', 'leftbracket', 6, 'rightbracket', 5, 'else', 5 ) ),
 76+ 12 => array( 5, array( 'for', 'leftbracket', 7, 'in', 6, 'rightbracket', 5 ) ),
 77+ 13 => array( 5, array( 'try', 5, 'catch', 'leftbracket', 8, 'rightbracket', 5 ) ),
 78+ 14 => array( 5, array( 'leftcurly', 4, 'rightcurly' ) ),
 79+ 15 => array( 6, array( 9 ) ),
 80+ 16 => array( 9, array( 'return' ) ),
 81+ 17 => array( 9, array( 'return', 10 ) ),
 82+ 18 => array( 9, array( 10 ) ),
 83+ 19 => array( 9, array( 'append', 10 ) ),
 84+ 20 => array( 9, array( 'yield', 10 ) ),
 85+ 21 => array( 10, array( 8, 'setto', 10 ) ),
 86+ 22 => array( 10, array( 11 ) ),
 87+ 23 => array( 11, array( 12, 'trinary', 11, 'colon', 11 ) ),
 88+ 24 => array( 11, array( 12 ) ),
 89+ 25 => array( 12, array( 12, 'logicop', 13 ) ),
 90+ 26 => array( 12, array( 13 ) ),
 91+ 27 => array( 13, array( 13, 'compareop', 14 ) ),
 92+ 28 => array( 13, array( 14 ) ),
 93+ 29 => array( 14, array( 15, 'equalsto', 15 ) ),
 94+ 30 => array( 14, array( 15 ) ),
 95+ 31 => array( 15, array( 15, 'sum', 16 ) ),
 96+ 32 => array( 15, array( 16 ) ),
 97+ 33 => array( 16, array( 16, 'mul', 17 ) ),
 98+ 34 => array( 16, array( 17 ) ),
 99+ 35 => array( 17, array( 18, 'pow', 17 ) ),
 100+ 36 => array( 17, array( 18 ) ),
 101+ 37 => array( 18, array( 'invert', 19 ) ),
 102+ 38 => array( 18, array( 19 ) ),
 103+ 39 => array( 19, array( 20, 'in', 20 ) ),
 104+ 40 => array( 19, array( 20, 'contains', 20 ) ),
 105+ 41 => array( 19, array( 20 ) ),
 106+ 42 => array( 20, array( 'sum', 21 ) ),
 107+ 43 => array( 20, array( 21 ) ),
 108+ 44 => array( 21, array( 22, 'leftbracket', 23, 'rightbracket' ) ),
 109+ 45 => array( 21, array( 22, 'leftbracket', 'rightbracket' ) ),
 110+ 46 => array( 21, array( 24, 'leftbracket', 8, 'rightbracket' ) ),
 111+ 47 => array( 21, array( 25 ) ),
 112+ 48 => array( 25, array( 8 ) ),
 113+ 49 => array( 25, array( 26 ) ),
 114+ 50 => array( 25, array( 'break' ) ),
 115+ 51 => array( 25, array( 'continue' ) ),
 116+ 52 => array( 25, array( 'leftbracket', 6, 'rightbracket' ) ),
 117+ 53 => array( 25, array( 'leftsquare', 23, 'rightsquare' ) ),
 118+ 54 => array( 25, array( 'leftcurly', 27, 'rightcurly' ) ),
 119+ 55 => array( 24, array( 'isset' ) ),
 120+ 56 => array( 24, array( 'delete' ) ),
 121+ 57 => array( 22, array( 'id' ) ),
 122+ 58 => array( 22, array( 25, 'doublecolon', 'id' ) ),
 123+ 59 => array( 22, array( 'self', 'doublecolon', 'id' ) ),
 124+ 60 => array( 23, array( 23, 'comma', 6 ) ),
 125+ 61 => array( 23, array( 6 ) ),
 126+ 62 => array( 27, array( 27, 'comma', 28 ) ),
 127+ 63 => array( 27, array( 28 ) ),
 128+ 64 => array( 28, array( 6, 'colon', 6 ) ),
 129+ 65 => array( 26, array( 'string' ) ),
 130+ 66 => array( 26, array( 'int' ) ),
 131+ 67 => array( 26, array( 'float' ) ),
 132+ 68 => array( 26, array( 'true' ) ),
 133+ 69 => array( 26, array( 'false' ) ),
 134+ 70 => array( 26, array( 'null' ) ),
 135+ 71 => array( 8, array( 'id' ) ),
 136+ 72 => array( 8, array( 8, 29 ) ),
 137+ 73 => array( 29, array( 'leftsquare', 6, 'rightsquare' ) ),
 138+ 74 => array( 29, array( 'leftsquare', 'rightsquare' ) ),
 139+ 75 => array( 7, array( 8 ) ),
 140+ 76 => array( 7, array( 8, 'colon', 8 ) ),
 141+);
 142+
 143+static $action = array(
 144+ 0 => array(
 145+ 'function' => array( 0, 1 ),
 146+ ),
 147+ 1 => array(
 148+ 'id' => array( 0, 4 ),
 149+ ),
 150+ 2 => array(
 151+ '$' => array( 2, null ),
 152+ ),
 153+ 3 => array(
 154+ '$' => array( 1, 2 ),
 155+ 'function' => array( 0, 1 ),
 156+ ),
 157+ 4 => array(
 158+ 'leftbracket' => array( 0, 6 ),
 159+ ),
 160+ 5 => array(
 161+ '$' => array( 1, 1 ),
 162+ ),
 163+ 6 => array(
 164+ 'rightbracket' => array( 0, 8 ),
 165+ 'id' => array( 0, 7 ),
 166+ ),
 167+ 7 => array(
 168+ 'comma' => array( 0, 10 ),
 169+ 'rightbracket' => array( 1, 6 ),
 170+ ),
 171+ 8 => array(
 172+ 'leftcurly' => array( 0, 11 ),
 173+ ),
 174+ 9 => array(
 175+ 'rightbracket' => array( 0, 12 ),
 176+ ),
 177+ 10 => array(
 178+ 'id' => array( 0, 7 ),
 179+ ),
 180+ 11 => array(
 181+ 'if' => array( 0, 17 ),
 182+ 'for' => array( 0, 18 ),
 183+ 'try' => array( 0, 19 ),
 184+ 'leftcurly' => array( 0, 16 ),
 185+ 'return' => array( 0, 20 ),
 186+ 'append' => array( 0, 21 ),
 187+ 'yield' => array( 0, 22 ),
 188+ 'id' => array( 0, 14 ),
 189+ 'invert' => array( 0, 24 ),
 190+ 'sum' => array( 0, 23 ),
 191+ 'self' => array( 0, 30 ),
 192+ 'isset' => array( 0, 28 ),
 193+ 'delete' => array( 0, 29 ),
 194+ 'break' => array( 0, 25 ),
 195+ 'continue' => array( 0, 26 ),
 196+ 'leftbracket' => array( 0, 15 ),
 197+ 'leftsquare' => array( 0, 27 ),
 198+ 'string' => array( 0, 31 ),
 199+ 'int' => array( 0, 32 ),
 200+ 'float' => array( 0, 33 ),
 201+ 'true' => array( 0, 34 ),
 202+ 'false' => array( 0, 35 ),
 203+ 'null' => array( 0, 36 ),
 204+ ),
 205+ 12 => array(
 206+ 'leftcurly' => array( 0, 58 ),
 207+ ),
 208+ 13 => array(
 209+ 'rightbracket' => array( 1, 5 ),
 210+ ),
 211+ 14 => array(
 212+ 'rightbracket' => array( 1, 71 ),
 213+ 'setto' => array( 1, 71 ),
 214+ 'in' => array( 1, 71 ),
 215+ 'pow' => array( 1, 71 ),
 216+ 'equalsto' => array( 1, 71 ),
 217+ 'trinary' => array( 1, 71 ),
 218+ 'semicolon' => array( 1, 71 ),
 219+ 'colon' => array( 1, 71 ),
 220+ 'logicop' => array( 1, 71 ),
 221+ 'compareop' => array( 1, 71 ),
 222+ 'sum' => array( 1, 71 ),
 223+ 'mul' => array( 1, 71 ),
 224+ 'contains' => array( 1, 71 ),
 225+ 'leftsquare' => array( 1, 71 ),
 226+ 'doublecolon' => array( 1, 71 ),
 227+ 'rightsquare' => array( 1, 71 ),
 228+ 'comma' => array( 1, 71 ),
 229+ 'rightcurly' => array( 1, 71 ),
 230+ 'leftbracket' => array( 1, 57 ),
 231+ ),
 232+ 15 => array(
 233+ 'return' => array( 0, 20 ),
 234+ 'append' => array( 0, 21 ),
 235+ 'yield' => array( 0, 22 ),
 236+ 'id' => array( 0, 14 ),
 237+ 'invert' => array( 0, 24 ),
 238+ 'sum' => array( 0, 23 ),
 239+ 'self' => array( 0, 30 ),
 240+ 'isset' => array( 0, 28 ),
 241+ 'delete' => array( 0, 29 ),
 242+ 'break' => array( 0, 25 ),
 243+ 'continue' => array( 0, 26 ),
 244+ 'leftbracket' => array( 0, 15 ),
 245+ 'leftsquare' => array( 0, 27 ),
 246+ 'leftcurly' => array( 0, 59 ),
 247+ 'string' => array( 0, 31 ),
 248+ 'int' => array( 0, 32 ),
 249+ 'float' => array( 0, 33 ),
 250+ 'true' => array( 0, 34 ),
 251+ 'false' => array( 0, 35 ),
 252+ 'null' => array( 0, 36 ),
 253+ ),
 254+ 16 => array(
 255+ 'if' => array( 0, 17 ),
 256+ 'for' => array( 0, 18 ),
 257+ 'try' => array( 0, 19 ),
 258+ 'leftcurly' => array( 0, 16 ),
 259+ 'return' => array( 0, 20 ),
 260+ 'append' => array( 0, 21 ),
 261+ 'yield' => array( 0, 22 ),
 262+ 'id' => array( 0, 14 ),
 263+ 'invert' => array( 0, 24 ),
 264+ 'sum' => array( 0, 23 ),
 265+ 'self' => array( 0, 30 ),
 266+ 'isset' => array( 0, 28 ),
 267+ 'delete' => array( 0, 29 ),
 268+ 'break' => array( 0, 25 ),
 269+ 'continue' => array( 0, 26 ),
 270+ 'leftbracket' => array( 0, 15 ),
 271+ 'leftsquare' => array( 0, 27 ),
 272+ 'string' => array( 0, 31 ),
 273+ 'int' => array( 0, 32 ),
 274+ 'float' => array( 0, 33 ),
 275+ 'true' => array( 0, 34 ),
 276+ 'false' => array( 0, 35 ),
 277+ 'null' => array( 0, 36 ),
 278+ ),
 279+ 17 => array(
 280+ 'leftbracket' => array( 0, 65 ),
 281+ ),
 282+ 18 => array(
 283+ 'leftbracket' => array( 0, 66 ),
 284+ ),
 285+ 19 => array(
 286+ 'if' => array( 0, 17 ),
 287+ 'for' => array( 0, 18 ),
 288+ 'try' => array( 0, 19 ),
 289+ 'leftcurly' => array( 0, 16 ),
 290+ 'return' => array( 0, 20 ),
 291+ 'append' => array( 0, 21 ),
 292+ 'yield' => array( 0, 22 ),
 293+ 'id' => array( 0, 14 ),
 294+ 'invert' => array( 0, 24 ),
 295+ 'sum' => array( 0, 23 ),
 296+ 'self' => array( 0, 30 ),
 297+ 'isset' => array( 0, 28 ),
 298+ 'delete' => array( 0, 29 ),
 299+ 'break' => array( 0, 25 ),
 300+ 'continue' => array( 0, 26 ),
 301+ 'leftbracket' => array( 0, 15 ),
 302+ 'leftsquare' => array( 0, 27 ),
 303+ 'string' => array( 0, 31 ),
 304+ 'int' => array( 0, 32 ),
 305+ 'float' => array( 0, 33 ),
 306+ 'true' => array( 0, 34 ),
 307+ 'false' => array( 0, 35 ),
 308+ 'null' => array( 0, 36 ),
 309+ ),
 310+ 20 => array(
 311+ 'semicolon' => array( 1, 16 ),
 312+ 'rightbracket' => array( 1, 16 ),
 313+ 'rightsquare' => array( 1, 16 ),
 314+ 'comma' => array( 1, 16 ),
 315+ 'colon' => array( 1, 16 ),
 316+ 'rightcurly' => array( 1, 16 ),
 317+ 'id' => array( 0, 14 ),
 318+ 'invert' => array( 0, 24 ),
 319+ 'sum' => array( 0, 23 ),
 320+ 'self' => array( 0, 30 ),
 321+ 'isset' => array( 0, 28 ),
 322+ 'delete' => array( 0, 29 ),
 323+ 'break' => array( 0, 25 ),
 324+ 'continue' => array( 0, 26 ),
 325+ 'leftbracket' => array( 0, 15 ),
 326+ 'leftsquare' => array( 0, 27 ),
 327+ 'leftcurly' => array( 0, 59 ),
 328+ 'string' => array( 0, 31 ),
 329+ 'int' => array( 0, 32 ),
 330+ 'float' => array( 0, 33 ),
 331+ 'true' => array( 0, 34 ),
 332+ 'false' => array( 0, 35 ),
 333+ 'null' => array( 0, 36 ),
 334+ ),
 335+ 21 => array(
 336+ 'id' => array( 0, 14 ),
 337+ 'invert' => array( 0, 24 ),
 338+ 'sum' => array( 0, 23 ),
 339+ 'self' => array( 0, 30 ),
 340+ 'isset' => array( 0, 28 ),
 341+ 'delete' => array( 0, 29 ),
 342+ 'break' => array( 0, 25 ),
 343+ 'continue' => array( 0, 26 ),
 344+ 'leftbracket' => array( 0, 15 ),
 345+ 'leftsquare' => array( 0, 27 ),
 346+ 'leftcurly' => array( 0, 59 ),
 347+ 'string' => array( 0, 31 ),
 348+ 'int' => array( 0, 32 ),
 349+ 'float' => array( 0, 33 ),
 350+ 'true' => array( 0, 34 ),
 351+ 'false' => array( 0, 35 ),
 352+ 'null' => array( 0, 36 ),
 353+ ),
 354+ 22 => array(
 355+ 'id' => array( 0, 14 ),
 356+ 'invert' => array( 0, 24 ),
 357+ 'sum' => array( 0, 23 ),
 358+ 'self' => array( 0, 30 ),
 359+ 'isset' => array( 0, 28 ),
 360+ 'delete' => array( 0, 29 ),
 361+ 'break' => array( 0, 25 ),
 362+ 'continue' => array( 0, 26 ),
 363+ 'leftbracket' => array( 0, 15 ),
 364+ 'leftsquare' => array( 0, 27 ),
 365+ 'leftcurly' => array( 0, 59 ),
 366+ 'string' => array( 0, 31 ),
 367+ 'int' => array( 0, 32 ),
 368+ 'float' => array( 0, 33 ),
 369+ 'true' => array( 0, 34 ),
 370+ 'false' => array( 0, 35 ),
 371+ 'null' => array( 0, 36 ),
 372+ ),
 373+ 23 => array(
 374+ 'id' => array( 0, 71 ),
 375+ 'self' => array( 0, 30 ),
 376+ 'isset' => array( 0, 28 ),
 377+ 'delete' => array( 0, 29 ),
 378+ 'break' => array( 0, 25 ),
 379+ 'continue' => array( 0, 26 ),
 380+ 'leftbracket' => array( 0, 15 ),
 381+ 'leftsquare' => array( 0, 27 ),
 382+ 'leftcurly' => array( 0, 59 ),
 383+ 'string' => array( 0, 31 ),
 384+ 'int' => array( 0, 32 ),
 385+ 'float' => array( 0, 33 ),
 386+ 'true' => array( 0, 34 ),
 387+ 'false' => array( 0, 35 ),
 388+ 'null' => array( 0, 36 ),
 389+ ),
 390+ 24 => array(
 391+ 'sum' => array( 0, 23 ),
 392+ 'id' => array( 0, 71 ),
 393+ 'self' => array( 0, 30 ),
 394+ 'isset' => array( 0, 28 ),
 395+ 'delete' => array( 0, 29 ),
 396+ 'break' => array( 0, 25 ),
 397+ 'continue' => array( 0, 26 ),
 398+ 'leftbracket' => array( 0, 15 ),
 399+ 'leftsquare' => array( 0, 27 ),
 400+ 'leftcurly' => array( 0, 59 ),
 401+ 'string' => array( 0, 31 ),
 402+ 'int' => array( 0, 32 ),
 403+ 'float' => array( 0, 33 ),
 404+ 'true' => array( 0, 34 ),
 405+ 'false' => array( 0, 35 ),
 406+ 'null' => array( 0, 36 ),
 407+ ),
 408+ 25 => array(
 409+ 'in' => array( 1, 50 ),
 410+ 'pow' => array( 1, 50 ),
 411+ 'equalsto' => array( 1, 50 ),
 412+ 'trinary' => array( 1, 50 ),
 413+ 'semicolon' => array( 1, 50 ),
 414+ 'rightbracket' => array( 1, 50 ),
 415+ 'colon' => array( 1, 50 ),
 416+ 'logicop' => array( 1, 50 ),
 417+ 'compareop' => array( 1, 50 ),
 418+ 'sum' => array( 1, 50 ),
 419+ 'mul' => array( 1, 50 ),
 420+ 'contains' => array( 1, 50 ),
 421+ 'doublecolon' => array( 1, 50 ),
 422+ 'rightsquare' => array( 1, 50 ),
 423+ 'comma' => array( 1, 50 ),
 424+ 'rightcurly' => array( 1, 50 ),
 425+ ),
 426+ 26 => array(
 427+ 'in' => array( 1, 51 ),
 428+ 'pow' => array( 1, 51 ),
 429+ 'equalsto' => array( 1, 51 ),
 430+ 'trinary' => array( 1, 51 ),
 431+ 'semicolon' => array( 1, 51 ),
 432+ 'rightbracket' => array( 1, 51 ),
 433+ 'colon' => array( 1, 51 ),
 434+ 'logicop' => array( 1, 51 ),
 435+ 'compareop' => array( 1, 51 ),
 436+ 'sum' => array( 1, 51 ),
 437+ 'mul' => array( 1, 51 ),
 438+ 'contains' => array( 1, 51 ),
 439+ 'doublecolon' => array( 1, 51 ),
 440+ 'rightsquare' => array( 1, 51 ),
 441+ 'comma' => array( 1, 51 ),
 442+ 'rightcurly' => array( 1, 51 ),
 443+ ),
 444+ 27 => array(
 445+ 'return' => array( 0, 20 ),
 446+ 'append' => array( 0, 21 ),
 447+ 'yield' => array( 0, 22 ),
 448+ 'id' => array( 0, 14 ),
 449+ 'invert' => array( 0, 24 ),
 450+ 'sum' => array( 0, 23 ),
 451+ 'self' => array( 0, 30 ),
 452+ 'isset' => array( 0, 28 ),
 453+ 'delete' => array( 0, 29 ),
 454+ 'break' => array( 0, 25 ),
 455+ 'continue' => array( 0, 26 ),
 456+ 'leftbracket' => array( 0, 15 ),
 457+ 'leftsquare' => array( 0, 27 ),
 458+ 'leftcurly' => array( 0, 59 ),
 459+ 'string' => array( 0, 31 ),
 460+ 'int' => array( 0, 32 ),
 461+ 'float' => array( 0, 33 ),
 462+ 'true' => array( 0, 34 ),
 463+ 'false' => array( 0, 35 ),
 464+ 'null' => array( 0, 36 ),
 465+ ),
 466+ 28 => array(
 467+ 'leftbracket' => array( 1, 55 ),
 468+ ),
 469+ 29 => array(
 470+ 'leftbracket' => array( 1, 56 ),
 471+ ),
 472+ 30 => array(
 473+ 'doublecolon' => array( 0, 77 ),
 474+ ),
 475+ 31 => array(
 476+ 'in' => array( 1, 65 ),
 477+ 'pow' => array( 1, 65 ),
 478+ 'equalsto' => array( 1, 65 ),
 479+ 'trinary' => array( 1, 65 ),
 480+ 'semicolon' => array( 1, 65 ),
 481+ 'rightbracket' => array( 1, 65 ),
 482+ 'colon' => array( 1, 65 ),
 483+ 'logicop' => array( 1, 65 ),
 484+ 'compareop' => array( 1, 65 ),
 485+ 'sum' => array( 1, 65 ),
 486+ 'mul' => array( 1, 65 ),
 487+ 'contains' => array( 1, 65 ),
 488+ 'doublecolon' => array( 1, 65 ),
 489+ 'rightsquare' => array( 1, 65 ),
 490+ 'comma' => array( 1, 65 ),
 491+ 'rightcurly' => array( 1, 65 ),
 492+ ),
 493+ 32 => array(
 494+ 'in' => array( 1, 66 ),
 495+ 'pow' => array( 1, 66 ),
 496+ 'equalsto' => array( 1, 66 ),
 497+ 'trinary' => array( 1, 66 ),
 498+ 'semicolon' => array( 1, 66 ),
 499+ 'rightbracket' => array( 1, 66 ),
 500+ 'colon' => array( 1, 66 ),
 501+ 'logicop' => array( 1, 66 ),
 502+ 'compareop' => array( 1, 66 ),
 503+ 'sum' => array( 1, 66 ),
 504+ 'mul' => array( 1, 66 ),
 505+ 'contains' => array( 1, 66 ),
 506+ 'doublecolon' => array( 1, 66 ),
 507+ 'rightsquare' => array( 1, 66 ),
 508+ 'comma' => array( 1, 66 ),
 509+ 'rightcurly' => array( 1, 66 ),
 510+ ),
 511+ 33 => array(
 512+ 'in' => array( 1, 67 ),
 513+ 'pow' => array( 1, 67 ),
 514+ 'equalsto' => array( 1, 67 ),
 515+ 'trinary' => array( 1, 67 ),
 516+ 'semicolon' => array( 1, 67 ),
 517+ 'rightbracket' => array( 1, 67 ),
 518+ 'colon' => array( 1, 67 ),
 519+ 'logicop' => array( 1, 67 ),
 520+ 'compareop' => array( 1, 67 ),
 521+ 'sum' => array( 1, 67 ),
 522+ 'mul' => array( 1, 67 ),
 523+ 'contains' => array( 1, 67 ),
 524+ 'doublecolon' => array( 1, 67 ),
 525+ 'rightsquare' => array( 1, 67 ),
 526+ 'comma' => array( 1, 67 ),
 527+ 'rightcurly' => array( 1, 67 ),
 528+ ),
 529+ 34 => array(
 530+ 'in' => array( 1, 68 ),
 531+ 'pow' => array( 1, 68 ),
 532+ 'equalsto' => array( 1, 68 ),
 533+ 'trinary' => array( 1, 68 ),
 534+ 'semicolon' => array( 1, 68 ),
 535+ 'rightbracket' => array( 1, 68 ),
 536+ 'colon' => array( 1, 68 ),
 537+ 'logicop' => array( 1, 68 ),
 538+ 'compareop' => array( 1, 68 ),
 539+ 'sum' => array( 1, 68 ),
 540+ 'mul' => array( 1, 68 ),
 541+ 'contains' => array( 1, 68 ),
 542+ 'doublecolon' => array( 1, 68 ),
 543+ 'rightsquare' => array( 1, 68 ),
 544+ 'comma' => array( 1, 68 ),
 545+ 'rightcurly' => array( 1, 68 ),
 546+ ),
 547+ 35 => array(
 548+ 'in' => array( 1, 69 ),
 549+ 'pow' => array( 1, 69 ),
 550+ 'equalsto' => array( 1, 69 ),
 551+ 'trinary' => array( 1, 69 ),
 552+ 'semicolon' => array( 1, 69 ),
 553+ 'rightbracket' => array( 1, 69 ),
 554+ 'colon' => array( 1, 69 ),
 555+ 'logicop' => array( 1, 69 ),
 556+ 'compareop' => array( 1, 69 ),
 557+ 'sum' => array( 1, 69 ),
 558+ 'mul' => array( 1, 69 ),
 559+ 'contains' => array( 1, 69 ),
 560+ 'doublecolon' => array( 1, 69 ),
 561+ 'rightsquare' => array( 1, 69 ),
 562+ 'comma' => array( 1, 69 ),
 563+ 'rightcurly' => array( 1, 69 ),
 564+ ),
 565+ 36 => array(
 566+ 'in' => array( 1, 70 ),
 567+ 'pow' => array( 1, 70 ),
 568+ 'equalsto' => array( 1, 70 ),
 569+ 'trinary' => array( 1, 70 ),
 570+ 'semicolon' => array( 1, 70 ),
 571+ 'rightbracket' => array( 1, 70 ),
 572+ 'colon' => array( 1, 70 ),
 573+ 'logicop' => array( 1, 70 ),
 574+ 'compareop' => array( 1, 70 ),
 575+ 'sum' => array( 1, 70 ),
 576+ 'mul' => array( 1, 70 ),
 577+ 'contains' => array( 1, 70 ),
 578+ 'doublecolon' => array( 1, 70 ),
 579+ 'rightsquare' => array( 1, 70 ),
 580+ 'comma' => array( 1, 70 ),
 581+ 'rightcurly' => array( 1, 70 ),
 582+ ),
 583+ 37 => array(
 584+ 'rightcurly' => array( 0, 78 ),
 585+ 'if' => array( 0, 17 ),
 586+ 'for' => array( 0, 18 ),
 587+ 'try' => array( 0, 19 ),
 588+ 'leftcurly' => array( 0, 16 ),
 589+ 'return' => array( 0, 20 ),
 590+ 'append' => array( 0, 21 ),
 591+ 'yield' => array( 0, 22 ),
 592+ 'id' => array( 0, 14 ),
 593+ 'invert' => array( 0, 24 ),
 594+ 'sum' => array( 0, 23 ),
 595+ 'self' => array( 0, 30 ),
 596+ 'isset' => array( 0, 28 ),
 597+ 'delete' => array( 0, 29 ),
 598+ 'break' => array( 0, 25 ),
 599+ 'continue' => array( 0, 26 ),
 600+ 'leftbracket' => array( 0, 15 ),
 601+ 'leftsquare' => array( 0, 27 ),
 602+ 'string' => array( 0, 31 ),
 603+ 'int' => array( 0, 32 ),
 604+ 'float' => array( 0, 33 ),
 605+ 'true' => array( 0, 34 ),
 606+ 'false' => array( 0, 35 ),
 607+ 'null' => array( 0, 36 ),
 608+ ),
 609+ 38 => array(
 610+ 'rightcurly' => array( 1, 8 ),
 611+ 'if' => array( 1, 8 ),
 612+ 'for' => array( 1, 8 ),
 613+ 'try' => array( 1, 8 ),
 614+ 'leftcurly' => array( 1, 8 ),
 615+ 'return' => array( 1, 8 ),
 616+ 'append' => array( 1, 8 ),
 617+ 'yield' => array( 1, 8 ),
 618+ 'id' => array( 1, 8 ),
 619+ 'invert' => array( 1, 8 ),
 620+ 'sum' => array( 1, 8 ),
 621+ 'break' => array( 1, 8 ),
 622+ 'continue' => array( 1, 8 ),
 623+ 'leftbracket' => array( 1, 8 ),
 624+ 'leftsquare' => array( 1, 8 ),
 625+ 'self' => array( 1, 8 ),
 626+ 'isset' => array( 1, 8 ),
 627+ 'delete' => array( 1, 8 ),
 628+ 'string' => array( 1, 8 ),
 629+ 'int' => array( 1, 8 ),
 630+ 'float' => array( 1, 8 ),
 631+ 'true' => array( 1, 8 ),
 632+ 'false' => array( 1, 8 ),
 633+ 'null' => array( 1, 8 ),
 634+ ),
 635+ 39 => array(
 636+ 'semicolon' => array( 0, 80 ),
 637+ ),
 638+ 40 => array(
 639+ 'setto' => array( 0, 81 ),
 640+ 'in' => array( 1, 48 ),
 641+ 'pow' => array( 1, 48 ),
 642+ 'equalsto' => array( 1, 48 ),
 643+ 'trinary' => array( 1, 48 ),
 644+ 'semicolon' => array( 1, 48 ),
 645+ 'rightbracket' => array( 1, 48 ),
 646+ 'colon' => array( 1, 48 ),
 647+ 'logicop' => array( 1, 48 ),
 648+ 'compareop' => array( 1, 48 ),
 649+ 'sum' => array( 1, 48 ),
 650+ 'mul' => array( 1, 48 ),
 651+ 'contains' => array( 1, 48 ),
 652+ 'doublecolon' => array( 1, 48 ),
 653+ 'rightsquare' => array( 1, 48 ),
 654+ 'comma' => array( 1, 48 ),
 655+ 'rightcurly' => array( 1, 48 ),
 656+ 'leftsquare' => array( 0, 82 ),
 657+ ),
 658+ 41 => array(
 659+ 'semicolon' => array( 1, 15 ),
 660+ 'rightbracket' => array( 1, 15 ),
 661+ 'rightsquare' => array( 1, 15 ),
 662+ 'comma' => array( 1, 15 ),
 663+ 'colon' => array( 1, 15 ),
 664+ 'rightcurly' => array( 1, 15 ),
 665+ ),
 666+ 42 => array(
 667+ 'semicolon' => array( 1, 18 ),
 668+ 'rightbracket' => array( 1, 18 ),
 669+ 'rightsquare' => array( 1, 18 ),
 670+ 'comma' => array( 1, 18 ),
 671+ 'colon' => array( 1, 18 ),
 672+ 'rightcurly' => array( 1, 18 ),
 673+ ),
 674+ 43 => array(
 675+ 'semicolon' => array( 1, 22 ),
 676+ 'rightbracket' => array( 1, 22 ),
 677+ 'rightsquare' => array( 1, 22 ),
 678+ 'comma' => array( 1, 22 ),
 679+ 'colon' => array( 1, 22 ),
 680+ 'rightcurly' => array( 1, 22 ),
 681+ ),
 682+ 44 => array(
 683+ 'trinary' => array( 0, 84 ),
 684+ 'semicolon' => array( 1, 24 ),
 685+ 'rightbracket' => array( 1, 24 ),
 686+ 'colon' => array( 1, 24 ),
 687+ 'rightsquare' => array( 1, 24 ),
 688+ 'comma' => array( 1, 24 ),
 689+ 'rightcurly' => array( 1, 24 ),
 690+ 'logicop' => array( 0, 85 ),
 691+ ),
 692+ 45 => array(
 693+ 'trinary' => array( 1, 26 ),
 694+ 'semicolon' => array( 1, 26 ),
 695+ 'rightbracket' => array( 1, 26 ),
 696+ 'colon' => array( 1, 26 ),
 697+ 'logicop' => array( 1, 26 ),
 698+ 'rightsquare' => array( 1, 26 ),
 699+ 'comma' => array( 1, 26 ),
 700+ 'rightcurly' => array( 1, 26 ),
 701+ 'compareop' => array( 0, 86 ),
 702+ ),
 703+ 46 => array(
 704+ 'trinary' => array( 1, 28 ),
 705+ 'semicolon' => array( 1, 28 ),
 706+ 'rightbracket' => array( 1, 28 ),
 707+ 'colon' => array( 1, 28 ),
 708+ 'logicop' => array( 1, 28 ),
 709+ 'compareop' => array( 1, 28 ),
 710+ 'rightsquare' => array( 1, 28 ),
 711+ 'comma' => array( 1, 28 ),
 712+ 'rightcurly' => array( 1, 28 ),
 713+ ),
 714+ 47 => array(
 715+ 'equalsto' => array( 0, 87 ),
 716+ 'trinary' => array( 1, 30 ),
 717+ 'semicolon' => array( 1, 30 ),
 718+ 'rightbracket' => array( 1, 30 ),
 719+ 'colon' => array( 1, 30 ),
 720+ 'logicop' => array( 1, 30 ),
 721+ 'compareop' => array( 1, 30 ),
 722+ 'rightsquare' => array( 1, 30 ),
 723+ 'comma' => array( 1, 30 ),
 724+ 'rightcurly' => array( 1, 30 ),
 725+ 'sum' => array( 0, 88 ),
 726+ ),
 727+ 48 => array(
 728+ 'equalsto' => array( 1, 32 ),
 729+ 'trinary' => array( 1, 32 ),
 730+ 'semicolon' => array( 1, 32 ),
 731+ 'rightbracket' => array( 1, 32 ),
 732+ 'colon' => array( 1, 32 ),
 733+ 'logicop' => array( 1, 32 ),
 734+ 'compareop' => array( 1, 32 ),
 735+ 'sum' => array( 1, 32 ),
 736+ 'rightsquare' => array( 1, 32 ),
 737+ 'comma' => array( 1, 32 ),
 738+ 'rightcurly' => array( 1, 32 ),
 739+ 'mul' => array( 0, 89 ),
 740+ ),
 741+ 49 => array(
 742+ 'equalsto' => array( 1, 34 ),
 743+ 'trinary' => array( 1, 34 ),
 744+ 'semicolon' => array( 1, 34 ),
 745+ 'rightbracket' => array( 1, 34 ),
 746+ 'colon' => array( 1, 34 ),
 747+ 'logicop' => array( 1, 34 ),
 748+ 'compareop' => array( 1, 34 ),
 749+ 'sum' => array( 1, 34 ),
 750+ 'mul' => array( 1, 34 ),
 751+ 'rightsquare' => array( 1, 34 ),
 752+ 'comma' => array( 1, 34 ),
 753+ 'rightcurly' => array( 1, 34 ),
 754+ ),
 755+ 50 => array(
 756+ 'pow' => array( 0, 90 ),
 757+ 'equalsto' => array( 1, 36 ),
 758+ 'trinary' => array( 1, 36 ),
 759+ 'semicolon' => array( 1, 36 ),
 760+ 'rightbracket' => array( 1, 36 ),
 761+ 'colon' => array( 1, 36 ),
 762+ 'logicop' => array( 1, 36 ),
 763+ 'compareop' => array( 1, 36 ),
 764+ 'sum' => array( 1, 36 ),
 765+ 'mul' => array( 1, 36 ),
 766+ 'rightsquare' => array( 1, 36 ),
 767+ 'comma' => array( 1, 36 ),
 768+ 'rightcurly' => array( 1, 36 ),
 769+ ),
 770+ 51 => array(
 771+ 'pow' => array( 1, 38 ),
 772+ 'equalsto' => array( 1, 38 ),
 773+ 'trinary' => array( 1, 38 ),
 774+ 'semicolon' => array( 1, 38 ),
 775+ 'rightbracket' => array( 1, 38 ),
 776+ 'colon' => array( 1, 38 ),
 777+ 'logicop' => array( 1, 38 ),
 778+ 'compareop' => array( 1, 38 ),
 779+ 'sum' => array( 1, 38 ),
 780+ 'mul' => array( 1, 38 ),
 781+ 'rightsquare' => array( 1, 38 ),
 782+ 'comma' => array( 1, 38 ),
 783+ 'rightcurly' => array( 1, 38 ),
 784+ ),
 785+ 52 => array(
 786+ 'in' => array( 0, 91 ),
 787+ 'contains' => array( 0, 92 ),
 788+ 'pow' => array( 1, 41 ),
 789+ 'equalsto' => array( 1, 41 ),
 790+ 'trinary' => array( 1, 41 ),
 791+ 'semicolon' => array( 1, 41 ),
 792+ 'rightbracket' => array( 1, 41 ),
 793+ 'colon' => array( 1, 41 ),
 794+ 'logicop' => array( 1, 41 ),
 795+ 'compareop' => array( 1, 41 ),
 796+ 'sum' => array( 1, 41 ),
 797+ 'mul' => array( 1, 41 ),
 798+ 'rightsquare' => array( 1, 41 ),
 799+ 'comma' => array( 1, 41 ),
 800+ 'rightcurly' => array( 1, 41 ),
 801+ ),
 802+ 53 => array(
 803+ 'in' => array( 1, 43 ),
 804+ 'pow' => array( 1, 43 ),
 805+ 'equalsto' => array( 1, 43 ),
 806+ 'trinary' => array( 1, 43 ),
 807+ 'semicolon' => array( 1, 43 ),
 808+ 'rightbracket' => array( 1, 43 ),
 809+ 'colon' => array( 1, 43 ),
 810+ 'logicop' => array( 1, 43 ),
 811+ 'compareop' => array( 1, 43 ),
 812+ 'sum' => array( 1, 43 ),
 813+ 'mul' => array( 1, 43 ),
 814+ 'contains' => array( 1, 43 ),
 815+ 'rightsquare' => array( 1, 43 ),
 816+ 'comma' => array( 1, 43 ),
 817+ 'rightcurly' => array( 1, 43 ),
 818+ ),
 819+ 54 => array(
 820+ 'leftbracket' => array( 0, 93 ),
 821+ ),
 822+ 55 => array(
 823+ 'leftbracket' => array( 0, 94 ),
 824+ ),
 825+ 56 => array(
 826+ 'in' => array( 1, 47 ),
 827+ 'pow' => array( 1, 47 ),
 828+ 'equalsto' => array( 1, 47 ),
 829+ 'trinary' => array( 1, 47 ),
 830+ 'semicolon' => array( 1, 47 ),
 831+ 'rightbracket' => array( 1, 47 ),
 832+ 'colon' => array( 1, 47 ),
 833+ 'logicop' => array( 1, 47 ),
 834+ 'compareop' => array( 1, 47 ),
 835+ 'sum' => array( 1, 47 ),
 836+ 'mul' => array( 1, 47 ),
 837+ 'contains' => array( 1, 47 ),
 838+ 'rightsquare' => array( 1, 47 ),
 839+ 'comma' => array( 1, 47 ),
 840+ 'rightcurly' => array( 1, 47 ),
 841+ 'doublecolon' => array( 0, 95 ),
 842+ ),
 843+ 57 => array(
 844+ 'in' => array( 1, 49 ),
 845+ 'pow' => array( 1, 49 ),
 846+ 'equalsto' => array( 1, 49 ),
 847+ 'trinary' => array( 1, 49 ),
 848+ 'semicolon' => array( 1, 49 ),
 849+ 'rightbracket' => array( 1, 49 ),
 850+ 'colon' => array( 1, 49 ),
 851+ 'logicop' => array( 1, 49 ),
 852+ 'compareop' => array( 1, 49 ),
 853+ 'sum' => array( 1, 49 ),
 854+ 'mul' => array( 1, 49 ),
 855+ 'contains' => array( 1, 49 ),
 856+ 'doublecolon' => array( 1, 49 ),
 857+ 'rightsquare' => array( 1, 49 ),
 858+ 'comma' => array( 1, 49 ),
 859+ 'rightcurly' => array( 1, 49 ),
 860+ ),
 861+ 58 => array(
 862+ 'if' => array( 0, 17 ),
 863+ 'for' => array( 0, 18 ),
 864+ 'try' => array( 0, 19 ),
 865+ 'leftcurly' => array( 0, 16 ),
 866+ 'return' => array( 0, 20 ),
 867+ 'append' => array( 0, 21 ),
 868+ 'yield' => array( 0, 22 ),
 869+ 'id' => array( 0, 14 ),
 870+ 'invert' => array( 0, 24 ),
 871+ 'sum' => array( 0, 23 ),
 872+ 'self' => array( 0, 30 ),
 873+ 'isset' => array( 0, 28 ),
 874+ 'delete' => array( 0, 29 ),
 875+ 'break' => array( 0, 25 ),
 876+ 'continue' => array( 0, 26 ),
 877+ 'leftbracket' => array( 0, 15 ),
 878+ 'leftsquare' => array( 0, 27 ),
 879+ 'string' => array( 0, 31 ),
 880+ 'int' => array( 0, 32 ),
 881+ 'float' => array( 0, 33 ),
 882+ 'true' => array( 0, 34 ),
 883+ 'false' => array( 0, 35 ),
 884+ 'null' => array( 0, 36 ),
 885+ ),
 886+ 59 => array(
 887+ 'return' => array( 0, 20 ),
 888+ 'append' => array( 0, 21 ),
 889+ 'yield' => array( 0, 22 ),
 890+ 'id' => array( 0, 14 ),
 891+ 'invert' => array( 0, 24 ),
 892+ 'sum' => array( 0, 23 ),
 893+ 'self' => array( 0, 30 ),
 894+ 'isset' => array( 0, 28 ),
 895+ 'delete' => array( 0, 29 ),
 896+ 'break' => array( 0, 25 ),
 897+ 'continue' => array( 0, 26 ),
 898+ 'leftbracket' => array( 0, 15 ),
 899+ 'leftsquare' => array( 0, 27 ),
 900+ 'leftcurly' => array( 0, 59 ),
 901+ 'string' => array( 0, 31 ),
 902+ 'int' => array( 0, 32 ),
 903+ 'float' => array( 0, 33 ),
 904+ 'true' => array( 0, 34 ),
 905+ 'false' => array( 0, 35 ),
 906+ 'null' => array( 0, 36 ),
 907+ ),
 908+ 60 => array(
 909+ 'rightbracket' => array( 0, 98 ),
 910+ ),
 911+ 61 => array(
 912+ 'rightcurly' => array( 0, 99 ),
 913+ 'if' => array( 0, 17 ),
 914+ 'for' => array( 0, 18 ),
 915+ 'try' => array( 0, 19 ),
 916+ 'leftcurly' => array( 0, 16 ),
 917+ 'return' => array( 0, 20 ),
 918+ 'append' => array( 0, 21 ),
 919+ 'yield' => array( 0, 22 ),
 920+ 'id' => array( 0, 14 ),
 921+ 'invert' => array( 0, 24 ),
 922+ 'sum' => array( 0, 23 ),
 923+ 'self' => array( 0, 30 ),
 924+ 'isset' => array( 0, 28 ),
 925+ 'delete' => array( 0, 29 ),
 926+ 'break' => array( 0, 25 ),
 927+ 'continue' => array( 0, 26 ),
 928+ 'leftbracket' => array( 0, 15 ),
 929+ 'leftsquare' => array( 0, 27 ),
 930+ 'string' => array( 0, 31 ),
 931+ 'int' => array( 0, 32 ),
 932+ 'float' => array( 0, 33 ),
 933+ 'true' => array( 0, 34 ),
 934+ 'false' => array( 0, 35 ),
 935+ 'null' => array( 0, 36 ),
 936+ ),
 937+ 62 => array(
 938+ 'semicolon' => array( 0, 80 ),
 939+ 'colon' => array( 0, 100 ),
 940+ ),
 941+ 63 => array(
 942+ 'rightcurly' => array( 0, 101 ),
 943+ 'comma' => array( 0, 102 ),
 944+ ),
 945+ 64 => array(
 946+ 'rightcurly' => array( 1, 63 ),
 947+ 'comma' => array( 1, 63 ),
 948+ ),
 949+ 65 => array(
 950+ 'return' => array( 0, 20 ),
 951+ 'append' => array( 0, 21 ),
 952+ 'yield' => array( 0, 22 ),
 953+ 'id' => array( 0, 14 ),
 954+ 'invert' => array( 0, 24 ),
 955+ 'sum' => array( 0, 23 ),
 956+ 'self' => array( 0, 30 ),
 957+ 'isset' => array( 0, 28 ),
 958+ 'delete' => array( 0, 29 ),
 959+ 'break' => array( 0, 25 ),
 960+ 'continue' => array( 0, 26 ),
 961+ 'leftbracket' => array( 0, 15 ),
 962+ 'leftsquare' => array( 0, 27 ),
 963+ 'leftcurly' => array( 0, 59 ),
 964+ 'string' => array( 0, 31 ),
 965+ 'int' => array( 0, 32 ),
 966+ 'float' => array( 0, 33 ),
 967+ 'true' => array( 0, 34 ),
 968+ 'false' => array( 0, 35 ),
 969+ 'null' => array( 0, 36 ),
 970+ ),
 971+ 66 => array(
 972+ 'id' => array( 0, 104 ),
 973+ ),
 974+ 67 => array(
 975+ 'catch' => array( 0, 107 ),
 976+ ),
 977+ 68 => array(
 978+ 'semicolon' => array( 1, 17 ),
 979+ 'rightbracket' => array( 1, 17 ),
 980+ 'rightsquare' => array( 1, 17 ),
 981+ 'comma' => array( 1, 17 ),
 982+ 'colon' => array( 1, 17 ),
 983+ 'rightcurly' => array( 1, 17 ),
 984+ ),
 985+ 69 => array(
 986+ 'semicolon' => array( 1, 19 ),
 987+ 'rightbracket' => array( 1, 19 ),
 988+ 'rightsquare' => array( 1, 19 ),
 989+ 'comma' => array( 1, 19 ),
 990+ 'colon' => array( 1, 19 ),
 991+ 'rightcurly' => array( 1, 19 ),
 992+ ),
 993+ 70 => array(
 994+ 'semicolon' => array( 1, 20 ),
 995+ 'rightbracket' => array( 1, 20 ),
 996+ 'rightsquare' => array( 1, 20 ),
 997+ 'comma' => array( 1, 20 ),
 998+ 'colon' => array( 1, 20 ),
 999+ 'rightcurly' => array( 1, 20 ),
 1000+ ),
 1001+ 71 => array(
 1002+ 'leftbracket' => array( 1, 57 ),
 1003+ 'rightbracket' => array( 1, 71 ),
 1004+ 'setto' => array( 1, 71 ),
 1005+ 'in' => array( 1, 71 ),
 1006+ 'pow' => array( 1, 71 ),
 1007+ 'equalsto' => array( 1, 71 ),
 1008+ 'trinary' => array( 1, 71 ),
 1009+ 'semicolon' => array( 1, 71 ),
 1010+ 'colon' => array( 1, 71 ),
 1011+ 'logicop' => array( 1, 71 ),
 1012+ 'compareop' => array( 1, 71 ),
 1013+ 'sum' => array( 1, 71 ),
 1014+ 'mul' => array( 1, 71 ),
 1015+ 'contains' => array( 1, 71 ),
 1016+ 'leftsquare' => array( 1, 71 ),
 1017+ 'doublecolon' => array( 1, 71 ),
 1018+ 'rightsquare' => array( 1, 71 ),
 1019+ 'comma' => array( 1, 71 ),
 1020+ 'rightcurly' => array( 1, 71 ),
 1021+ ),
 1022+ 72 => array(
 1023+ 'in' => array( 1, 48 ),
 1024+ 'pow' => array( 1, 48 ),
 1025+ 'equalsto' => array( 1, 48 ),
 1026+ 'trinary' => array( 1, 48 ),
 1027+ 'semicolon' => array( 1, 48 ),
 1028+ 'rightbracket' => array( 1, 48 ),
 1029+ 'colon' => array( 1, 48 ),
 1030+ 'logicop' => array( 1, 48 ),
 1031+ 'compareop' => array( 1, 48 ),
 1032+ 'sum' => array( 1, 48 ),
 1033+ 'mul' => array( 1, 48 ),
 1034+ 'contains' => array( 1, 48 ),
 1035+ 'doublecolon' => array( 1, 48 ),
 1036+ 'rightsquare' => array( 1, 48 ),
 1037+ 'comma' => array( 1, 48 ),
 1038+ 'rightcurly' => array( 1, 48 ),
 1039+ 'leftsquare' => array( 0, 82 ),
 1040+ ),
 1041+ 73 => array(
 1042+ 'in' => array( 1, 42 ),
 1043+ 'pow' => array( 1, 42 ),
 1044+ 'equalsto' => array( 1, 42 ),
 1045+ 'trinary' => array( 1, 42 ),
 1046+ 'semicolon' => array( 1, 42 ),
 1047+ 'rightbracket' => array( 1, 42 ),
 1048+ 'colon' => array( 1, 42 ),
 1049+ 'logicop' => array( 1, 42 ),
 1050+ 'compareop' => array( 1, 42 ),
 1051+ 'sum' => array( 1, 42 ),
 1052+ 'mul' => array( 1, 42 ),
 1053+ 'contains' => array( 1, 42 ),
 1054+ 'rightsquare' => array( 1, 42 ),
 1055+ 'comma' => array( 1, 42 ),
 1056+ 'rightcurly' => array( 1, 42 ),
 1057+ ),
 1058+ 74 => array(
 1059+ 'pow' => array( 1, 37 ),
 1060+ 'equalsto' => array( 1, 37 ),
 1061+ 'trinary' => array( 1, 37 ),
 1062+ 'semicolon' => array( 1, 37 ),
 1063+ 'rightbracket' => array( 1, 37 ),
 1064+ 'colon' => array( 1, 37 ),
 1065+ 'logicop' => array( 1, 37 ),
 1066+ 'compareop' => array( 1, 37 ),
 1067+ 'sum' => array( 1, 37 ),
 1068+ 'mul' => array( 1, 37 ),
 1069+ 'rightsquare' => array( 1, 37 ),
 1070+ 'comma' => array( 1, 37 ),
 1071+ 'rightcurly' => array( 1, 37 ),
 1072+ ),
 1073+ 75 => array(
 1074+ 'rightbracket' => array( 1, 61 ),
 1075+ 'rightsquare' => array( 1, 61 ),
 1076+ 'comma' => array( 1, 61 ),
 1077+ ),
 1078+ 76 => array(
 1079+ 'rightsquare' => array( 0, 109 ),
 1080+ 'comma' => array( 0, 108 ),
 1081+ ),
 1082+ 77 => array(
 1083+ 'id' => array( 0, 110 ),
 1084+ ),
 1085+ 78 => array(
 1086+ 'function' => array( 1, 4 ),
 1087+ '$' => array( 1, 4 ),
 1088+ ),
 1089+ 79 => array(
 1090+ 'rightcurly' => array( 1, 7 ),
 1091+ 'if' => array( 1, 7 ),
 1092+ 'for' => array( 1, 7 ),
 1093+ 'try' => array( 1, 7 ),
 1094+ 'leftcurly' => array( 1, 7 ),
 1095+ 'return' => array( 1, 7 ),
 1096+ 'append' => array( 1, 7 ),
 1097+ 'yield' => array( 1, 7 ),
 1098+ 'id' => array( 1, 7 ),
 1099+ 'invert' => array( 1, 7 ),
 1100+ 'sum' => array( 1, 7 ),
 1101+ 'break' => array( 1, 7 ),
 1102+ 'continue' => array( 1, 7 ),
 1103+ 'leftbracket' => array( 1, 7 ),
 1104+ 'leftsquare' => array( 1, 7 ),
 1105+ 'self' => array( 1, 7 ),
 1106+ 'isset' => array( 1, 7 ),
 1107+ 'delete' => array( 1, 7 ),
 1108+ 'string' => array( 1, 7 ),
 1109+ 'int' => array( 1, 7 ),
 1110+ 'float' => array( 1, 7 ),
 1111+ 'true' => array( 1, 7 ),
 1112+ 'false' => array( 1, 7 ),
 1113+ 'null' => array( 1, 7 ),
 1114+ ),
 1115+ 80 => array(
 1116+ 'rightcurly' => array( 1, 9 ),
 1117+ 'if' => array( 1, 9 ),
 1118+ 'for' => array( 1, 9 ),
 1119+ 'try' => array( 1, 9 ),
 1120+ 'leftcurly' => array( 1, 9 ),
 1121+ 'return' => array( 1, 9 ),
 1122+ 'append' => array( 1, 9 ),
 1123+ 'yield' => array( 1, 9 ),
 1124+ 'id' => array( 1, 9 ),
 1125+ 'invert' => array( 1, 9 ),
 1126+ 'sum' => array( 1, 9 ),
 1127+ 'break' => array( 1, 9 ),
 1128+ 'continue' => array( 1, 9 ),
 1129+ 'leftbracket' => array( 1, 9 ),
 1130+ 'leftsquare' => array( 1, 9 ),
 1131+ 'self' => array( 1, 9 ),
 1132+ 'isset' => array( 1, 9 ),
 1133+ 'delete' => array( 1, 9 ),
 1134+ 'string' => array( 1, 9 ),
 1135+ 'int' => array( 1, 9 ),
 1136+ 'float' => array( 1, 9 ),
 1137+ 'true' => array( 1, 9 ),
 1138+ 'false' => array( 1, 9 ),
 1139+ 'null' => array( 1, 9 ),
 1140+ 'else' => array( 1, 9 ),
 1141+ 'catch' => array( 1, 9 ),
 1142+ ),
 1143+ 81 => array(
 1144+ 'id' => array( 0, 14 ),
 1145+ 'invert' => array( 0, 24 ),
 1146+ 'sum' => array( 0, 23 ),
 1147+ 'self' => array( 0, 30 ),
 1148+ 'isset' => array( 0, 28 ),
 1149+ 'delete' => array( 0, 29 ),
 1150+ 'break' => array( 0, 25 ),
 1151+ 'continue' => array( 0, 26 ),
 1152+ 'leftbracket' => array( 0, 15 ),
 1153+ 'leftsquare' => array( 0, 27 ),
 1154+ 'leftcurly' => array( 0, 59 ),
 1155+ 'string' => array( 0, 31 ),
 1156+ 'int' => array( 0, 32 ),
 1157+ 'float' => array( 0, 33 ),
 1158+ 'true' => array( 0, 34 ),
 1159+ 'false' => array( 0, 35 ),
 1160+ 'null' => array( 0, 36 ),
 1161+ ),
 1162+ 82 => array(
 1163+ 'rightsquare' => array( 0, 112 ),
 1164+ 'return' => array( 0, 20 ),
 1165+ 'append' => array( 0, 21 ),
 1166+ 'yield' => array( 0, 22 ),
 1167+ 'id' => array( 0, 14 ),
 1168+ 'invert' => array( 0, 24 ),
 1169+ 'sum' => array( 0, 23 ),
 1170+ 'self' => array( 0, 30 ),
 1171+ 'isset' => array( 0, 28 ),
 1172+ 'delete' => array( 0, 29 ),
 1173+ 'break' => array( 0, 25 ),
 1174+ 'continue' => array( 0, 26 ),
 1175+ 'leftbracket' => array( 0, 15 ),
 1176+ 'leftsquare' => array( 0, 27 ),
 1177+ 'leftcurly' => array( 0, 59 ),
 1178+ 'string' => array( 0, 31 ),
 1179+ 'int' => array( 0, 32 ),
 1180+ 'float' => array( 0, 33 ),
 1181+ 'true' => array( 0, 34 ),
 1182+ 'false' => array( 0, 35 ),
 1183+ 'null' => array( 0, 36 ),
 1184+ ),
 1185+ 83 => array(
 1186+ 'rightbracket' => array( 1, 72 ),
 1187+ 'setto' => array( 1, 72 ),
 1188+ 'in' => array( 1, 72 ),
 1189+ 'pow' => array( 1, 72 ),
 1190+ 'equalsto' => array( 1, 72 ),
 1191+ 'trinary' => array( 1, 72 ),
 1192+ 'semicolon' => array( 1, 72 ),
 1193+ 'colon' => array( 1, 72 ),
 1194+ 'logicop' => array( 1, 72 ),
 1195+ 'compareop' => array( 1, 72 ),
 1196+ 'sum' => array( 1, 72 ),
 1197+ 'mul' => array( 1, 72 ),
 1198+ 'contains' => array( 1, 72 ),
 1199+ 'leftsquare' => array( 1, 72 ),
 1200+ 'doublecolon' => array( 1, 72 ),
 1201+ 'rightsquare' => array( 1, 72 ),
 1202+ 'comma' => array( 1, 72 ),
 1203+ 'rightcurly' => array( 1, 72 ),
 1204+ ),
 1205+ 84 => array(
 1206+ 'invert' => array( 0, 24 ),
 1207+ 'sum' => array( 0, 23 ),
 1208+ 'id' => array( 0, 71 ),
 1209+ 'self' => array( 0, 30 ),
 1210+ 'isset' => array( 0, 28 ),
 1211+ 'delete' => array( 0, 29 ),
 1212+ 'break' => array( 0, 25 ),
 1213+ 'continue' => array( 0, 26 ),
 1214+ 'leftbracket' => array( 0, 15 ),
 1215+ 'leftsquare' => array( 0, 27 ),
 1216+ 'leftcurly' => array( 0, 59 ),
 1217+ 'string' => array( 0, 31 ),
 1218+ 'int' => array( 0, 32 ),
 1219+ 'float' => array( 0, 33 ),
 1220+ 'true' => array( 0, 34 ),
 1221+ 'false' => array( 0, 35 ),
 1222+ 'null' => array( 0, 36 ),
 1223+ ),
 1224+ 85 => array(
 1225+ 'invert' => array( 0, 24 ),
 1226+ 'sum' => array( 0, 23 ),
 1227+ 'id' => array( 0, 71 ),
 1228+ 'self' => array( 0, 30 ),
 1229+ 'isset' => array( 0, 28 ),
 1230+ 'delete' => array( 0, 29 ),
 1231+ 'break' => array( 0, 25 ),
 1232+ 'continue' => array( 0, 26 ),
 1233+ 'leftbracket' => array( 0, 15 ),
 1234+ 'leftsquare' => array( 0, 27 ),
 1235+ 'leftcurly' => array( 0, 59 ),
 1236+ 'string' => array( 0, 31 ),
 1237+ 'int' => array( 0, 32 ),
 1238+ 'float' => array( 0, 33 ),
 1239+ 'true' => array( 0, 34 ),
 1240+ 'false' => array( 0, 35 ),
 1241+ 'null' => array( 0, 36 ),
 1242+ ),
 1243+ 86 => array(
 1244+ 'invert' => array( 0, 24 ),
 1245+ 'sum' => array( 0, 23 ),
 1246+ 'id' => array( 0, 71 ),
 1247+ 'self' => array( 0, 30 ),
 1248+ 'isset' => array( 0, 28 ),
 1249+ 'delete' => array( 0, 29 ),
 1250+ 'break' => array( 0, 25 ),
 1251+ 'continue' => array( 0, 26 ),
 1252+ 'leftbracket' => array( 0, 15 ),
 1253+ 'leftsquare' => array( 0, 27 ),
 1254+ 'leftcurly' => array( 0, 59 ),
 1255+ 'string' => array( 0, 31 ),
 1256+ 'int' => array( 0, 32 ),
 1257+ 'float' => array( 0, 33 ),
 1258+ 'true' => array( 0, 34 ),
 1259+ 'false' => array( 0, 35 ),
 1260+ 'null' => array( 0, 36 ),
 1261+ ),
 1262+ 87 => array(
 1263+ 'invert' => array( 0, 24 ),
 1264+ 'sum' => array( 0, 23 ),
 1265+ 'id' => array( 0, 71 ),
 1266+ 'self' => array( 0, 30 ),
 1267+ 'isset' => array( 0, 28 ),
 1268+ 'delete' => array( 0, 29 ),
 1269+ 'break' => array( 0, 25 ),
 1270+ 'continue' => array( 0, 26 ),
 1271+ 'leftbracket' => array( 0, 15 ),
 1272+ 'leftsquare' => array( 0, 27 ),
 1273+ 'leftcurly' => array( 0, 59 ),
 1274+ 'string' => array( 0, 31 ),
 1275+ 'int' => array( 0, 32 ),
 1276+ 'float' => array( 0, 33 ),
 1277+ 'true' => array( 0, 34 ),
 1278+ 'false' => array( 0, 35 ),
 1279+ 'null' => array( 0, 36 ),
 1280+ ),
 1281+ 88 => array(
 1282+ 'invert' => array( 0, 24 ),
 1283+ 'sum' => array( 0, 23 ),
 1284+ 'id' => array( 0, 71 ),
 1285+ 'self' => array( 0, 30 ),
 1286+ 'isset' => array( 0, 28 ),
 1287+ 'delete' => array( 0, 29 ),
 1288+ 'break' => array( 0, 25 ),
 1289+ 'continue' => array( 0, 26 ),
 1290+ 'leftbracket' => array( 0, 15 ),
 1291+ 'leftsquare' => array( 0, 27 ),
 1292+ 'leftcurly' => array( 0, 59 ),
 1293+ 'string' => array( 0, 31 ),
 1294+ 'int' => array( 0, 32 ),
 1295+ 'float' => array( 0, 33 ),
 1296+ 'true' => array( 0, 34 ),
 1297+ 'false' => array( 0, 35 ),
 1298+ 'null' => array( 0, 36 ),
 1299+ ),
 1300+ 89 => array(
 1301+ 'invert' => array( 0, 24 ),
 1302+ 'sum' => array( 0, 23 ),
 1303+ 'id' => array( 0, 71 ),
 1304+ 'self' => array( 0, 30 ),
 1305+ 'isset' => array( 0, 28 ),
 1306+ 'delete' => array( 0, 29 ),
 1307+ 'break' => array( 0, 25 ),
 1308+ 'continue' => array( 0, 26 ),
 1309+ 'leftbracket' => array( 0, 15 ),
 1310+ 'leftsquare' => array( 0, 27 ),
 1311+ 'leftcurly' => array( 0, 59 ),
 1312+ 'string' => array( 0, 31 ),
 1313+ 'int' => array( 0, 32 ),
 1314+ 'float' => array( 0, 33 ),
 1315+ 'true' => array( 0, 34 ),
 1316+ 'false' => array( 0, 35 ),
 1317+ 'null' => array( 0, 36 ),
 1318+ ),
 1319+ 90 => array(
 1320+ 'invert' => array( 0, 24 ),
 1321+ 'sum' => array( 0, 23 ),
 1322+ 'id' => array( 0, 71 ),
 1323+ 'self' => array( 0, 30 ),
 1324+ 'isset' => array( 0, 28 ),
 1325+ 'delete' => array( 0, 29 ),
 1326+ 'break' => array( 0, 25 ),
 1327+ 'continue' => array( 0, 26 ),
 1328+ 'leftbracket' => array( 0, 15 ),
 1329+ 'leftsquare' => array( 0, 27 ),
 1330+ 'leftcurly' => array( 0, 59 ),
 1331+ 'string' => array( 0, 31 ),
 1332+ 'int' => array( 0, 32 ),
 1333+ 'float' => array( 0, 33 ),
 1334+ 'true' => array( 0, 34 ),
 1335+ 'false' => array( 0, 35 ),
 1336+ 'null' => array( 0, 36 ),
 1337+ ),
 1338+ 91 => array(
 1339+ 'sum' => array( 0, 23 ),
 1340+ 'id' => array( 0, 71 ),
 1341+ 'self' => array( 0, 30 ),
 1342+ 'isset' => array( 0, 28 ),
 1343+ 'delete' => array( 0, 29 ),
 1344+ 'break' => array( 0, 25 ),
 1345+ 'continue' => array( 0, 26 ),
 1346+ 'leftbracket' => array( 0, 15 ),
 1347+ 'leftsquare' => array( 0, 27 ),
 1348+ 'leftcurly' => array( 0, 59 ),
 1349+ 'string' => array( 0, 31 ),
 1350+ 'int' => array( 0, 32 ),
 1351+ 'float' => array( 0, 33 ),
 1352+ 'true' => array( 0, 34 ),
 1353+ 'false' => array( 0, 35 ),
 1354+ 'null' => array( 0, 36 ),
 1355+ ),
 1356+ 92 => array(
 1357+ 'sum' => array( 0, 23 ),
 1358+ 'id' => array( 0, 71 ),
 1359+ 'self' => array( 0, 30 ),
 1360+ 'isset' => array( 0, 28 ),
 1361+ 'delete' => array( 0, 29 ),
 1362+ 'break' => array( 0, 25 ),
 1363+ 'continue' => array( 0, 26 ),
 1364+ 'leftbracket' => array( 0, 15 ),
 1365+ 'leftsquare' => array( 0, 27 ),
 1366+ 'leftcurly' => array( 0, 59 ),
 1367+ 'string' => array( 0, 31 ),
 1368+ 'int' => array( 0, 32 ),
 1369+ 'float' => array( 0, 33 ),
 1370+ 'true' => array( 0, 34 ),
 1371+ 'false' => array( 0, 35 ),
 1372+ 'null' => array( 0, 36 ),
 1373+ ),
 1374+ 93 => array(
 1375+ 'rightbracket' => array( 0, 123 ),
 1376+ 'return' => array( 0, 20 ),
 1377+ 'append' => array( 0, 21 ),
 1378+ 'yield' => array( 0, 22 ),
 1379+ 'id' => array( 0, 14 ),
 1380+ 'invert' => array( 0, 24 ),
 1381+ 'sum' => array( 0, 23 ),
 1382+ 'self' => array( 0, 30 ),
 1383+ 'isset' => array( 0, 28 ),
 1384+ 'delete' => array( 0, 29 ),
 1385+ 'break' => array( 0, 25 ),
 1386+ 'continue' => array( 0, 26 ),
 1387+ 'leftbracket' => array( 0, 15 ),
 1388+ 'leftsquare' => array( 0, 27 ),
 1389+ 'leftcurly' => array( 0, 59 ),
 1390+ 'string' => array( 0, 31 ),
 1391+ 'int' => array( 0, 32 ),
 1392+ 'float' => array( 0, 33 ),
 1393+ 'true' => array( 0, 34 ),
 1394+ 'false' => array( 0, 35 ),
 1395+ 'null' => array( 0, 36 ),
 1396+ ),
 1397+ 94 => array(
 1398+ 'id' => array( 0, 104 ),
 1399+ ),
 1400+ 95 => array(
 1401+ 'id' => array( 0, 126 ),
 1402+ ),
 1403+ 96 => array(
 1404+ 'rightcurly' => array( 0, 127 ),
 1405+ 'if' => array( 0, 17 ),
 1406+ 'for' => array( 0, 18 ),
 1407+ 'try' => array( 0, 19 ),
 1408+ 'leftcurly' => array( 0, 16 ),
 1409+ 'return' => array( 0, 20 ),
 1410+ 'append' => array( 0, 21 ),
 1411+ 'yield' => array( 0, 22 ),
 1412+ 'id' => array( 0, 14 ),
 1413+ 'invert' => array( 0, 24 ),
 1414+ 'sum' => array( 0, 23 ),
 1415+ 'self' => array( 0, 30 ),
 1416+ 'isset' => array( 0, 28 ),
 1417+ 'delete' => array( 0, 29 ),
 1418+ 'break' => array( 0, 25 ),
 1419+ 'continue' => array( 0, 26 ),
 1420+ 'leftbracket' => array( 0, 15 ),
 1421+ 'leftsquare' => array( 0, 27 ),
 1422+ 'string' => array( 0, 31 ),
 1423+ 'int' => array( 0, 32 ),
 1424+ 'float' => array( 0, 33 ),
 1425+ 'true' => array( 0, 34 ),
 1426+ 'false' => array( 0, 35 ),
 1427+ 'null' => array( 0, 36 ),
 1428+ ),
 1429+ 97 => array(
 1430+ 'colon' => array( 0, 100 ),
 1431+ ),
 1432+ 98 => array(
 1433+ 'in' => array( 1, 52 ),
 1434+ 'pow' => array( 1, 52 ),
 1435+ 'equalsto' => array( 1, 52 ),
 1436+ 'trinary' => array( 1, 52 ),
 1437+ 'semicolon' => array( 1, 52 ),
 1438+ 'rightbracket' => array( 1, 52 ),
 1439+ 'colon' => array( 1, 52 ),
 1440+ 'logicop' => array( 1, 52 ),
 1441+ 'compareop' => array( 1, 52 ),
 1442+ 'sum' => array( 1, 52 ),
 1443+ 'mul' => array( 1, 52 ),
 1444+ 'contains' => array( 1, 52 ),
 1445+ 'doublecolon' => array( 1, 52 ),
 1446+ 'rightsquare' => array( 1, 52 ),
 1447+ 'comma' => array( 1, 52 ),
 1448+ 'rightcurly' => array( 1, 52 ),
 1449+ ),
 1450+ 99 => array(
 1451+ 'rightcurly' => array( 1, 14 ),
 1452+ 'if' => array( 1, 14 ),
 1453+ 'for' => array( 1, 14 ),
 1454+ 'try' => array( 1, 14 ),
 1455+ 'leftcurly' => array( 1, 14 ),
 1456+ 'return' => array( 1, 14 ),
 1457+ 'append' => array( 1, 14 ),
 1458+ 'yield' => array( 1, 14 ),
 1459+ 'id' => array( 1, 14 ),
 1460+ 'invert' => array( 1, 14 ),
 1461+ 'sum' => array( 1, 14 ),
 1462+ 'break' => array( 1, 14 ),
 1463+ 'continue' => array( 1, 14 ),
 1464+ 'leftbracket' => array( 1, 14 ),
 1465+ 'leftsquare' => array( 1, 14 ),
 1466+ 'self' => array( 1, 14 ),
 1467+ 'isset' => array( 1, 14 ),
 1468+ 'delete' => array( 1, 14 ),
 1469+ 'string' => array( 1, 14 ),
 1470+ 'int' => array( 1, 14 ),
 1471+ 'float' => array( 1, 14 ),
 1472+ 'true' => array( 1, 14 ),
 1473+ 'false' => array( 1, 14 ),
 1474+ 'null' => array( 1, 14 ),
 1475+ 'else' => array( 1, 14 ),
 1476+ 'catch' => array( 1, 14 ),
 1477+ ),
 1478+ 100 => array(
 1479+ 'return' => array( 0, 20 ),
 1480+ 'append' => array( 0, 21 ),
 1481+ 'yield' => array( 0, 22 ),
 1482+ 'id' => array( 0, 14 ),
 1483+ 'invert' => array( 0, 24 ),
 1484+ 'sum' => array( 0, 23 ),
 1485+ 'self' => array( 0, 30 ),
 1486+ 'isset' => array( 0, 28 ),
 1487+ 'delete' => array( 0, 29 ),
 1488+ 'break' => array( 0, 25 ),
 1489+ 'continue' => array( 0, 26 ),
 1490+ 'leftbracket' => array( 0, 15 ),
 1491+ 'leftsquare' => array( 0, 27 ),
 1492+ 'leftcurly' => array( 0, 59 ),
 1493+ 'string' => array( 0, 31 ),
 1494+ 'int' => array( 0, 32 ),
 1495+ 'float' => array( 0, 33 ),
 1496+ 'true' => array( 0, 34 ),
 1497+ 'false' => array( 0, 35 ),
 1498+ 'null' => array( 0, 36 ),
 1499+ ),
 1500+ 101 => array(
 1501+ 'in' => array( 1, 54 ),
 1502+ 'pow' => array( 1, 54 ),
 1503+ 'equalsto' => array( 1, 54 ),
 1504+ 'trinary' => array( 1, 54 ),
 1505+ 'semicolon' => array( 1, 54 ),
 1506+ 'rightbracket' => array( 1, 54 ),
 1507+ 'colon' => array( 1, 54 ),
 1508+ 'logicop' => array( 1, 54 ),
 1509+ 'compareop' => array( 1, 54 ),
 1510+ 'sum' => array( 1, 54 ),
 1511+ 'mul' => array( 1, 54 ),
 1512+ 'contains' => array( 1, 54 ),
 1513+ 'doublecolon' => array( 1, 54 ),
 1514+ 'rightsquare' => array( 1, 54 ),
 1515+ 'comma' => array( 1, 54 ),
 1516+ 'rightcurly' => array( 1, 54 ),
 1517+ ),
 1518+ 102 => array(
 1519+ 'return' => array( 0, 20 ),
 1520+ 'append' => array( 0, 21 ),
 1521+ 'yield' => array( 0, 22 ),
 1522+ 'id' => array( 0, 14 ),
 1523+ 'invert' => array( 0, 24 ),
 1524+ 'sum' => array( 0, 23 ),
 1525+ 'self' => array( 0, 30 ),
 1526+ 'isset' => array( 0, 28 ),
 1527+ 'delete' => array( 0, 29 ),
 1528+ 'break' => array( 0, 25 ),
 1529+ 'continue' => array( 0, 26 ),
 1530+ 'leftbracket' => array( 0, 15 ),
 1531+ 'leftsquare' => array( 0, 27 ),
 1532+ 'leftcurly' => array( 0, 59 ),
 1533+ 'string' => array( 0, 31 ),
 1534+ 'int' => array( 0, 32 ),
 1535+ 'float' => array( 0, 33 ),
 1536+ 'true' => array( 0, 34 ),
 1537+ 'false' => array( 0, 35 ),
 1538+ 'null' => array( 0, 36 ),
 1539+ ),
 1540+ 103 => array(
 1541+ 'rightbracket' => array( 0, 130 ),
 1542+ ),
 1543+ 104 => array(
 1544+ 'rightbracket' => array( 1, 71 ),
 1545+ 'setto' => array( 1, 71 ),
 1546+ 'in' => array( 1, 71 ),
 1547+ 'pow' => array( 1, 71 ),
 1548+ 'equalsto' => array( 1, 71 ),
 1549+ 'trinary' => array( 1, 71 ),
 1550+ 'semicolon' => array( 1, 71 ),
 1551+ 'colon' => array( 1, 71 ),
 1552+ 'logicop' => array( 1, 71 ),
 1553+ 'compareop' => array( 1, 71 ),
 1554+ 'sum' => array( 1, 71 ),
 1555+ 'mul' => array( 1, 71 ),
 1556+ 'contains' => array( 1, 71 ),
 1557+ 'leftsquare' => array( 1, 71 ),
 1558+ 'doublecolon' => array( 1, 71 ),
 1559+ 'rightsquare' => array( 1, 71 ),
 1560+ 'comma' => array( 1, 71 ),
 1561+ 'rightcurly' => array( 1, 71 ),
 1562+ ),
 1563+ 105 => array(
 1564+ 'in' => array( 0, 131 ),
 1565+ ),
 1566+ 106 => array(
 1567+ 'in' => array( 1, 75 ),
 1568+ 'colon' => array( 0, 132 ),
 1569+ 'leftsquare' => array( 0, 82 ),
 1570+ ),
 1571+ 107 => array(
 1572+ 'leftbracket' => array( 0, 133 ),
 1573+ ),
 1574+ 108 => array(
 1575+ 'return' => array( 0, 20 ),
 1576+ 'append' => array( 0, 21 ),
 1577+ 'yield' => array( 0, 22 ),
 1578+ 'id' => array( 0, 14 ),
 1579+ 'invert' => array( 0, 24 ),
 1580+ 'sum' => array( 0, 23 ),
 1581+ 'self' => array( 0, 30 ),
 1582+ 'isset' => array( 0, 28 ),
 1583+ 'delete' => array( 0, 29 ),
 1584+ 'break' => array( 0, 25 ),
 1585+ 'continue' => array( 0, 26 ),
 1586+ 'leftbracket' => array( 0, 15 ),
 1587+ 'leftsquare' => array( 0, 27 ),
 1588+ 'leftcurly' => array( 0, 59 ),
 1589+ 'string' => array( 0, 31 ),
 1590+ 'int' => array( 0, 32 ),
 1591+ 'float' => array( 0, 33 ),
 1592+ 'true' => array( 0, 34 ),
 1593+ 'false' => array( 0, 35 ),
 1594+ 'null' => array( 0, 36 ),
 1595+ ),
 1596+ 109 => array(
 1597+ 'in' => array( 1, 53 ),
 1598+ 'pow' => array( 1, 53 ),
 1599+ 'equalsto' => array( 1, 53 ),
 1600+ 'trinary' => array( 1, 53 ),
 1601+ 'semicolon' => array( 1, 53 ),
 1602+ 'rightbracket' => array( 1, 53 ),
 1603+ 'colon' => array( 1, 53 ),
 1604+ 'logicop' => array( 1, 53 ),
 1605+ 'compareop' => array( 1, 53 ),
 1606+ 'sum' => array( 1, 53 ),
 1607+ 'mul' => array( 1, 53 ),
 1608+ 'contains' => array( 1, 53 ),
 1609+ 'doublecolon' => array( 1, 53 ),
 1610+ 'rightsquare' => array( 1, 53 ),
 1611+ 'comma' => array( 1, 53 ),
 1612+ 'rightcurly' => array( 1, 53 ),
 1613+ ),
 1614+ 110 => array(
 1615+ 'leftbracket' => array( 1, 59 ),
 1616+ ),
 1617+ 111 => array(
 1618+ 'semicolon' => array( 1, 21 ),
 1619+ 'rightbracket' => array( 1, 21 ),
 1620+ 'rightsquare' => array( 1, 21 ),
 1621+ 'comma' => array( 1, 21 ),
 1622+ 'colon' => array( 1, 21 ),
 1623+ 'rightcurly' => array( 1, 21 ),
 1624+ ),
 1625+ 112 => array(
 1626+ 'rightbracket' => array( 1, 74 ),
 1627+ 'setto' => array( 1, 74 ),
 1628+ 'in' => array( 1, 74 ),
 1629+ 'pow' => array( 1, 74 ),
 1630+ 'equalsto' => array( 1, 74 ),
 1631+ 'trinary' => array( 1, 74 ),
 1632+ 'semicolon' => array( 1, 74 ),
 1633+ 'colon' => array( 1, 74 ),
 1634+ 'logicop' => array( 1, 74 ),
 1635+ 'compareop' => array( 1, 74 ),
 1636+ 'sum' => array( 1, 74 ),
 1637+ 'mul' => array( 1, 74 ),
 1638+ 'contains' => array( 1, 74 ),
 1639+ 'leftsquare' => array( 1, 74 ),
 1640+ 'doublecolon' => array( 1, 74 ),
 1641+ 'rightsquare' => array( 1, 74 ),
 1642+ 'comma' => array( 1, 74 ),
 1643+ 'rightcurly' => array( 1, 74 ),
 1644+ ),
 1645+ 113 => array(
 1646+ 'rightsquare' => array( 0, 135 ),
 1647+ ),
 1648+ 114 => array(
 1649+ 'colon' => array( 0, 136 ),
 1650+ ),
 1651+ 115 => array(
 1652+ 'trinary' => array( 1, 25 ),
 1653+ 'semicolon' => array( 1, 25 ),
 1654+ 'rightbracket' => array( 1, 25 ),
 1655+ 'colon' => array( 1, 25 ),
 1656+ 'logicop' => array( 1, 25 ),
 1657+ 'rightsquare' => array( 1, 25 ),
 1658+ 'comma' => array( 1, 25 ),
 1659+ 'rightcurly' => array( 1, 25 ),
 1660+ 'compareop' => array( 0, 86 ),
 1661+ ),
 1662+ 116 => array(
 1663+ 'trinary' => array( 1, 27 ),
 1664+ 'semicolon' => array( 1, 27 ),
 1665+ 'rightbracket' => array( 1, 27 ),
 1666+ 'colon' => array( 1, 27 ),
 1667+ 'logicop' => array( 1, 27 ),
 1668+ 'compareop' => array( 1, 27 ),
 1669+ 'rightsquare' => array( 1, 27 ),
 1670+ 'comma' => array( 1, 27 ),
 1671+ 'rightcurly' => array( 1, 27 ),
 1672+ ),
 1673+ 117 => array(
 1674+ 'trinary' => array( 1, 29 ),
 1675+ 'semicolon' => array( 1, 29 ),
 1676+ 'rightbracket' => array( 1, 29 ),
 1677+ 'colon' => array( 1, 29 ),
 1678+ 'logicop' => array( 1, 29 ),
 1679+ 'compareop' => array( 1, 29 ),
 1680+ 'rightsquare' => array( 1, 29 ),
 1681+ 'comma' => array( 1, 29 ),
 1682+ 'rightcurly' => array( 1, 29 ),
 1683+ 'sum' => array( 0, 88 ),
 1684+ ),
 1685+ 118 => array(
 1686+ 'equalsto' => array( 1, 31 ),
 1687+ 'trinary' => array( 1, 31 ),
 1688+ 'semicolon' => array( 1, 31 ),
 1689+ 'rightbracket' => array( 1, 31 ),
 1690+ 'colon' => array( 1, 31 ),
 1691+ 'logicop' => array( 1, 31 ),
 1692+ 'compareop' => array( 1, 31 ),
 1693+ 'sum' => array( 1, 31 ),
 1694+ 'rightsquare' => array( 1, 31 ),
 1695+ 'comma' => array( 1, 31 ),
 1696+ 'rightcurly' => array( 1, 31 ),
 1697+ 'mul' => array( 0, 89 ),
 1698+ ),
 1699+ 119 => array(
 1700+ 'equalsto' => array( 1, 33 ),
 1701+ 'trinary' => array( 1, 33 ),
 1702+ 'semicolon' => array( 1, 33 ),
 1703+ 'rightbracket' => array( 1, 33 ),
 1704+ 'colon' => array( 1, 33 ),
 1705+ 'logicop' => array( 1, 33 ),
 1706+ 'compareop' => array( 1, 33 ),
 1707+ 'sum' => array( 1, 33 ),
 1708+ 'mul' => array( 1, 33 ),
 1709+ 'rightsquare' => array( 1, 33 ),
 1710+ 'comma' => array( 1, 33 ),
 1711+ 'rightcurly' => array( 1, 33 ),
 1712+ ),
 1713+ 120 => array(
 1714+ 'equalsto' => array( 1, 35 ),
 1715+ 'trinary' => array( 1, 35 ),
 1716+ 'semicolon' => array( 1, 35 ),
 1717+ 'rightbracket' => array( 1, 35 ),
 1718+ 'colon' => array( 1, 35 ),
 1719+ 'logicop' => array( 1, 35 ),
 1720+ 'compareop' => array( 1, 35 ),
 1721+ 'sum' => array( 1, 35 ),
 1722+ 'mul' => array( 1, 35 ),
 1723+ 'rightsquare' => array( 1, 35 ),
 1724+ 'comma' => array( 1, 35 ),
 1725+ 'rightcurly' => array( 1, 35 ),
 1726+ ),
 1727+ 121 => array(
 1728+ 'pow' => array( 1, 39 ),
 1729+ 'equalsto' => array( 1, 39 ),
 1730+ 'trinary' => array( 1, 39 ),
 1731+ 'semicolon' => array( 1, 39 ),
 1732+ 'rightbracket' => array( 1, 39 ),
 1733+ 'colon' => array( 1, 39 ),
 1734+ 'logicop' => array( 1, 39 ),
 1735+ 'compareop' => array( 1, 39 ),
 1736+ 'sum' => array( 1, 39 ),
 1737+ 'mul' => array( 1, 39 ),
 1738+ 'rightsquare' => array( 1, 39 ),
 1739+ 'comma' => array( 1, 39 ),
 1740+ 'rightcurly' => array( 1, 39 ),
 1741+ ),
 1742+ 122 => array(
 1743+ 'pow' => array( 1, 40 ),
 1744+ 'equalsto' => array( 1, 40 ),
 1745+ 'trinary' => array( 1, 40 ),
 1746+ 'semicolon' => array( 1, 40 ),
 1747+ 'rightbracket' => array( 1, 40 ),
 1748+ 'colon' => array( 1, 40 ),
 1749+ 'logicop' => array( 1, 40 ),
 1750+ 'compareop' => array( 1, 40 ),
 1751+ 'sum' => array( 1, 40 ),
 1752+ 'mul' => array( 1, 40 ),
 1753+ 'rightsquare' => array( 1, 40 ),
 1754+ 'comma' => array( 1, 40 ),
 1755+ 'rightcurly' => array( 1, 40 ),
 1756+ ),
 1757+ 123 => array(
 1758+ 'in' => array( 1, 45 ),
 1759+ 'pow' => array( 1, 45 ),
 1760+ 'equalsto' => array( 1, 45 ),
 1761+ 'trinary' => array( 1, 45 ),
 1762+ 'semicolon' => array( 1, 45 ),
 1763+ 'rightbracket' => array( 1, 45 ),
 1764+ 'colon' => array( 1, 45 ),
 1765+ 'logicop' => array( 1, 45 ),
 1766+ 'compareop' => array( 1, 45 ),
 1767+ 'sum' => array( 1, 45 ),
 1768+ 'mul' => array( 1, 45 ),
 1769+ 'contains' => array( 1, 45 ),
 1770+ 'rightsquare' => array( 1, 45 ),
 1771+ 'comma' => array( 1, 45 ),
 1772+ 'rightcurly' => array( 1, 45 ),
 1773+ ),
 1774+ 124 => array(
 1775+ 'rightbracket' => array( 0, 137 ),
 1776+ 'comma' => array( 0, 108 ),
 1777+ ),
 1778+ 125 => array(
 1779+ 'rightbracket' => array( 0, 138 ),
 1780+ 'leftsquare' => array( 0, 82 ),
 1781+ ),
 1782+ 126 => array(
 1783+ 'leftbracket' => array( 1, 58 ),
 1784+ ),
 1785+ 127 => array(
 1786+ 'function' => array( 1, 3 ),
 1787+ '$' => array( 1, 3 ),
 1788+ ),
 1789+ 128 => array(
 1790+ 'rightcurly' => array( 1, 64 ),
 1791+ 'comma' => array( 1, 64 ),
 1792+ ),
 1793+ 129 => array(
 1794+ 'rightcurly' => array( 1, 62 ),
 1795+ 'comma' => array( 1, 62 ),
 1796+ ),
 1797+ 130 => array(
 1798+ 'if' => array( 0, 17 ),
 1799+ 'for' => array( 0, 18 ),
 1800+ 'try' => array( 0, 19 ),
 1801+ 'leftcurly' => array( 0, 16 ),
 1802+ 'return' => array( 0, 20 ),
 1803+ 'append' => array( 0, 21 ),
 1804+ 'yield' => array( 0, 22 ),
 1805+ 'id' => array( 0, 14 ),
 1806+ 'invert' => array( 0, 24 ),
 1807+ 'sum' => array( 0, 23 ),
 1808+ 'self' => array( 0, 30 ),
 1809+ 'isset' => array( 0, 28 ),
 1810+ 'delete' => array( 0, 29 ),
 1811+ 'break' => array( 0, 25 ),
 1812+ 'continue' => array( 0, 26 ),
 1813+ 'leftbracket' => array( 0, 15 ),
 1814+ 'leftsquare' => array( 0, 27 ),
 1815+ 'string' => array( 0, 31 ),
 1816+ 'int' => array( 0, 32 ),
 1817+ 'float' => array( 0, 33 ),
 1818+ 'true' => array( 0, 34 ),
 1819+ 'false' => array( 0, 35 ),
 1820+ 'null' => array( 0, 36 ),
 1821+ ),
 1822+ 131 => array(
 1823+ 'return' => array( 0, 20 ),
 1824+ 'append' => array( 0, 21 ),
 1825+ 'yield' => array( 0, 22 ),
 1826+ 'id' => array( 0, 14 ),
 1827+ 'invert' => array( 0, 24 ),
 1828+ 'sum' => array( 0, 23 ),
 1829+ 'self' => array( 0, 30 ),
 1830+ 'isset' => array( 0, 28 ),
 1831+ 'delete' => array( 0, 29 ),
 1832+ 'break' => array( 0, 25 ),
 1833+ 'continue' => array( 0, 26 ),
 1834+ 'leftbracket' => array( 0, 15 ),
 1835+ 'leftsquare' => array( 0, 27 ),
 1836+ 'leftcurly' => array( 0, 59 ),
 1837+ 'string' => array( 0, 31 ),
 1838+ 'int' => array( 0, 32 ),
 1839+ 'float' => array( 0, 33 ),
 1840+ 'true' => array( 0, 34 ),
 1841+ 'false' => array( 0, 35 ),
 1842+ 'null' => array( 0, 36 ),
 1843+ ),
 1844+ 132 => array(
 1845+ 'id' => array( 0, 104 ),
 1846+ ),
 1847+ 133 => array(
 1848+ 'id' => array( 0, 104 ),
 1849+ ),
 1850+ 134 => array(
 1851+ 'rightbracket' => array( 1, 60 ),
 1852+ 'rightsquare' => array( 1, 60 ),
 1853+ 'comma' => array( 1, 60 ),
 1854+ ),
 1855+ 135 => array(
 1856+ 'rightbracket' => array( 1, 73 ),
 1857+ 'setto' => array( 1, 73 ),
 1858+ 'in' => array( 1, 73 ),
 1859+ 'pow' => array( 1, 73 ),
 1860+ 'equalsto' => array( 1, 73 ),
 1861+ 'trinary' => array( 1, 73 ),
 1862+ 'semicolon' => array( 1, 73 ),
 1863+ 'colon' => array( 1, 73 ),
 1864+ 'logicop' => array( 1, 73 ),
 1865+ 'compareop' => array( 1, 73 ),
 1866+ 'sum' => array( 1, 73 ),
 1867+ 'mul' => array( 1, 73 ),
 1868+ 'contains' => array( 1, 73 ),
 1869+ 'leftsquare' => array( 1, 73 ),
 1870+ 'doublecolon' => array( 1, 73 ),
 1871+ 'rightsquare' => array( 1, 73 ),
 1872+ 'comma' => array( 1, 73 ),
 1873+ 'rightcurly' => array( 1, 73 ),
 1874+ ),
 1875+ 136 => array(
 1876+ 'invert' => array( 0, 24 ),
 1877+ 'sum' => array( 0, 23 ),
 1878+ 'id' => array( 0, 71 ),
 1879+ 'self' => array( 0, 30 ),
 1880+ 'isset' => array( 0, 28 ),
 1881+ 'delete' => array( 0, 29 ),
 1882+ 'break' => array( 0, 25 ),
 1883+ 'continue' => array( 0, 26 ),
 1884+ 'leftbracket' => array( 0, 15 ),
 1885+ 'leftsquare' => array( 0, 27 ),
 1886+ 'leftcurly' => array( 0, 59 ),
 1887+ 'string' => array( 0, 31 ),
 1888+ 'int' => array( 0, 32 ),
 1889+ 'float' => array( 0, 33 ),
 1890+ 'true' => array( 0, 34 ),
 1891+ 'false' => array( 0, 35 ),
 1892+ 'null' => array( 0, 36 ),
 1893+ ),
 1894+ 137 => array(
 1895+ 'in' => array( 1, 44 ),
 1896+ 'pow' => array( 1, 44 ),
 1897+ 'equalsto' => array( 1, 44 ),
 1898+ 'trinary' => array( 1, 44 ),
 1899+ 'semicolon' => array( 1, 44 ),
 1900+ 'rightbracket' => array( 1, 44 ),
 1901+ 'colon' => array( 1, 44 ),
 1902+ 'logicop' => array( 1, 44 ),
 1903+ 'compareop' => array( 1, 44 ),
 1904+ 'sum' => array( 1, 44 ),
 1905+ 'mul' => array( 1, 44 ),
 1906+ 'contains' => array( 1, 44 ),
 1907+ 'rightsquare' => array( 1, 44 ),
 1908+ 'comma' => array( 1, 44 ),
 1909+ 'rightcurly' => array( 1, 44 ),
 1910+ ),
 1911+ 138 => array(
 1912+ 'in' => array( 1, 46 ),
 1913+ 'pow' => array( 1, 46 ),
 1914+ 'equalsto' => array( 1, 46 ),
 1915+ 'trinary' => array( 1, 46 ),
 1916+ 'semicolon' => array( 1, 46 ),
 1917+ 'rightbracket' => array( 1, 46 ),
 1918+ 'colon' => array( 1, 46 ),
 1919+ 'logicop' => array( 1, 46 ),
 1920+ 'compareop' => array( 1, 46 ),
 1921+ 'sum' => array( 1, 46 ),
 1922+ 'mul' => array( 1, 46 ),
 1923+ 'contains' => array( 1, 46 ),
 1924+ 'rightsquare' => array( 1, 46 ),
 1925+ 'comma' => array( 1, 46 ),
 1926+ 'rightcurly' => array( 1, 46 ),
 1927+ ),
 1928+ 139 => array(
 1929+ 'rightcurly' => array( 1, 10 ),
 1930+ 'if' => array( 1, 10 ),
 1931+ 'for' => array( 1, 10 ),
 1932+ 'try' => array( 1, 10 ),
 1933+ 'leftcurly' => array( 1, 10 ),
 1934+ 'return' => array( 1, 10 ),
 1935+ 'append' => array( 1, 10 ),
 1936+ 'yield' => array( 1, 10 ),
 1937+ 'id' => array( 1, 10 ),
 1938+ 'invert' => array( 1, 10 ),
 1939+ 'sum' => array( 1, 10 ),
 1940+ 'break' => array( 1, 10 ),
 1941+ 'continue' => array( 1, 10 ),
 1942+ 'leftbracket' => array( 1, 10 ),
 1943+ 'leftsquare' => array( 1, 10 ),
 1944+ 'self' => array( 1, 10 ),
 1945+ 'isset' => array( 1, 10 ),
 1946+ 'delete' => array( 1, 10 ),
 1947+ 'string' => array( 1, 10 ),
 1948+ 'int' => array( 1, 10 ),
 1949+ 'float' => array( 1, 10 ),
 1950+ 'true' => array( 1, 10 ),
 1951+ 'false' => array( 1, 10 ),
 1952+ 'null' => array( 1, 10 ),
 1953+ 'else' => array( 0, 144 ),
 1954+ 'catch' => array( 1, 10 ),
 1955+ ),
 1956+ 140 => array(
 1957+ 'rightbracket' => array( 0, 145 ),
 1958+ ),
 1959+ 141 => array(
 1960+ 'in' => array( 1, 76 ),
 1961+ 'leftsquare' => array( 0, 82 ),
 1962+ ),
 1963+ 142 => array(
 1964+ 'rightbracket' => array( 0, 146 ),
 1965+ 'leftsquare' => array( 0, 82 ),
 1966+ ),
 1967+ 143 => array(
 1968+ 'semicolon' => array( 1, 23 ),
 1969+ 'rightbracket' => array( 1, 23 ),
 1970+ 'colon' => array( 1, 23 ),
 1971+ 'rightsquare' => array( 1, 23 ),
 1972+ 'comma' => array( 1, 23 ),
 1973+ 'rightcurly' => array( 1, 23 ),
 1974+ ),
 1975+ 144 => array(
 1976+ 'if' => array( 0, 17 ),
 1977+ 'for' => array( 0, 18 ),
 1978+ 'try' => array( 0, 19 ),
 1979+ 'leftcurly' => array( 0, 16 ),
 1980+ 'return' => array( 0, 20 ),
 1981+ 'append' => array( 0, 21 ),
 1982+ 'yield' => array( 0, 22 ),
 1983+ 'id' => array( 0, 14 ),
 1984+ 'invert' => array( 0, 24 ),
 1985+ 'sum' => array( 0, 23 ),
 1986+ 'self' => array( 0, 30 ),
 1987+ 'isset' => array( 0, 28 ),
 1988+ 'delete' => array( 0, 29 ),
 1989+ 'break' => array( 0, 25 ),
 1990+ 'continue' => array( 0, 26 ),
 1991+ 'leftbracket' => array( 0, 15 ),
 1992+ 'leftsquare' => array( 0, 27 ),
 1993+ 'string' => array( 0, 31 ),
 1994+ 'int' => array( 0, 32 ),
 1995+ 'float' => array( 0, 33 ),
 1996+ 'true' => array( 0, 34 ),
 1997+ 'false' => array( 0, 35 ),
 1998+ 'null' => array( 0, 36 ),
 1999+ ),
 2000+ 145 => array(
 2001+ 'if' => array( 0, 17 ),
 2002+ 'for' => array( 0, 18 ),
 2003+ 'try' => array( 0, 19 ),
 2004+ 'leftcurly' => array( 0, 16 ),
 2005+ 'return' => array( 0, 20 ),
 2006+ 'append' => array( 0, 21 ),
 2007+ 'yield' => array( 0, 22 ),
 2008+ 'id' => array( 0, 14 ),
 2009+ 'invert' => array( 0, 24 ),
 2010+ 'sum' => array( 0, 23 ),
 2011+ 'self' => array( 0, 30 ),
 2012+ 'isset' => array( 0, 28 ),
 2013+ 'delete' => array( 0, 29 ),
 2014+ 'break' => array( 0, 25 ),
 2015+ 'continue' => array( 0, 26 ),
 2016+ 'leftbracket' => array( 0, 15 ),
 2017+ 'leftsquare' => array( 0, 27 ),
 2018+ 'string' => array( 0, 31 ),
 2019+ 'int' => array( 0, 32 ),
 2020+ 'float' => array( 0, 33 ),
 2021+ 'true' => array( 0, 34 ),
 2022+ 'false' => array( 0, 35 ),
 2023+ 'null' => array( 0, 36 ),
 2024+ ),
 2025+ 146 => array(
 2026+ 'if' => array( 0, 17 ),
 2027+ 'for' => array( 0, 18 ),
 2028+ 'try' => array( 0, 19 ),
 2029+ 'leftcurly' => array( 0, 16 ),
 2030+ 'return' => array( 0, 20 ),
 2031+ 'append' => array( 0, 21 ),
 2032+ 'yield' => array( 0, 22 ),
 2033+ 'id' => array( 0, 14 ),
 2034+ 'invert' => array( 0, 24 ),
 2035+ 'sum' => array( 0, 23 ),
 2036+ 'self' => array( 0, 30 ),
 2037+ 'isset' => array( 0, 28 ),
 2038+ 'delete' => array( 0, 29 ),
 2039+ 'break' => array( 0, 25 ),
 2040+ 'continue' => array( 0, 26 ),
 2041+ 'leftbracket' => array( 0, 15 ),
 2042+ 'leftsquare' => array( 0, 27 ),
 2043+ 'string' => array( 0, 31 ),
 2044+ 'int' => array( 0, 32 ),
 2045+ 'float' => array( 0, 33 ),
 2046+ 'true' => array( 0, 34 ),
 2047+ 'false' => array( 0, 35 ),
 2048+ 'null' => array( 0, 36 ),
 2049+ ),
 2050+ 147 => array(
 2051+ 'rightcurly' => array( 1, 11 ),
 2052+ 'if' => array( 1, 11 ),
 2053+ 'for' => array( 1, 11 ),
 2054+ 'try' => array( 1, 11 ),
 2055+ 'leftcurly' => array( 1, 11 ),
 2056+ 'return' => array( 1, 11 ),
 2057+ 'append' => array( 1, 11 ),
 2058+ 'yield' => array( 1, 11 ),
 2059+ 'id' => array( 1, 11 ),
 2060+ 'invert' => array( 1, 11 ),
 2061+ 'sum' => array( 1, 11 ),
 2062+ 'break' => array( 1, 11 ),
 2063+ 'continue' => array( 1, 11 ),
 2064+ 'leftbracket' => array( 1, 11 ),
 2065+ 'leftsquare' => array( 1, 11 ),
 2066+ 'self' => array( 1, 11 ),
 2067+ 'isset' => array( 1, 11 ),
 2068+ 'delete' => array( 1, 11 ),
 2069+ 'string' => array( 1, 11 ),
 2070+ 'int' => array( 1, 11 ),
 2071+ 'float' => array( 1, 11 ),
 2072+ 'true' => array( 1, 11 ),
 2073+ 'false' => array( 1, 11 ),
 2074+ 'null' => array( 1, 11 ),
 2075+ 'else' => array( 1, 11 ),
 2076+ 'catch' => array( 1, 11 ),
 2077+ ),
 2078+ 148 => array(
 2079+ 'rightcurly' => array( 1, 12 ),
 2080+ 'if' => array( 1, 12 ),
 2081+ 'for' => array( 1, 12 ),
 2082+ 'try' => array( 1, 12 ),
 2083+ 'leftcurly' => array( 1, 12 ),
 2084+ 'return' => array( 1, 12 ),
 2085+ 'append' => array( 1, 12 ),
 2086+ 'yield' => array( 1, 12 ),
 2087+ 'id' => array( 1, 12 ),
 2088+ 'invert' => array( 1, 12 ),
 2089+ 'sum' => array( 1, 12 ),
 2090+ 'break' => array( 1, 12 ),
 2091+ 'continue' => array( 1, 12 ),
 2092+ 'leftbracket' => array( 1, 12 ),
 2093+ 'leftsquare' => array( 1, 12 ),
 2094+ 'self' => array( 1, 12 ),
 2095+ 'isset' => array( 1, 12 ),
 2096+ 'delete' => array( 1, 12 ),
 2097+ 'string' => array( 1, 12 ),
 2098+ 'int' => array( 1, 12 ),
 2099+ 'float' => array( 1, 12 ),
 2100+ 'true' => array( 1, 12 ),
 2101+ 'false' => array( 1, 12 ),
 2102+ 'null' => array( 1, 12 ),
 2103+ 'else' => array( 1, 12 ),
 2104+ 'catch' => array( 1, 12 ),
 2105+ ),
 2106+ 149 => array(
 2107+ 'rightcurly' => array( 1, 13 ),
 2108+ 'if' => array( 1, 13 ),
 2109+ 'for' => array( 1, 13 ),
 2110+ 'try' => array( 1, 13 ),
 2111+ 'leftcurly' => array( 1, 13 ),
 2112+ 'return' => array( 1, 13 ),
 2113+ 'append' => array( 1, 13 ),
 2114+ 'yield' => array( 1, 13 ),
 2115+ 'id' => array( 1, 13 ),
 2116+ 'invert' => array( 1, 13 ),
 2117+ 'sum' => array( 1, 13 ),
 2118+ 'break' => array( 1, 13 ),
 2119+ 'continue' => array( 1, 13 ),
 2120+ 'leftbracket' => array( 1, 13 ),
 2121+ 'leftsquare' => array( 1, 13 ),
 2122+ 'self' => array( 1, 13 ),
 2123+ 'isset' => array( 1, 13 ),
 2124+ 'delete' => array( 1, 13 ),
 2125+ 'string' => array( 1, 13 ),
 2126+ 'int' => array( 1, 13 ),
 2127+ 'float' => array( 1, 13 ),
 2128+ 'true' => array( 1, 13 ),
 2129+ 'false' => array( 1, 13 ),
 2130+ 'null' => array( 1, 13 ),
 2131+ 'else' => array( 1, 13 ),
 2132+ 'catch' => array( 1, 13 ),
 2133+ ),
 2134+);
 2135+
 2136+static $goto = array(
 2137+ 0 => array( 1 => 2, 2 => 3 ),
 2138+ 1 => array(),
 2139+ 2 => array(),
 2140+ 3 => array( 1 => 5, 2 => 3 ),
 2141+ 4 => array(),
 2142+ 5 => array(),
 2143+ 6 => array( 3 => 9 ),
 2144+ 7 => array(),
 2145+ 8 => array(),
 2146+ 9 => array(),
 2147+ 10 => array( 3 => 13 ),
 2148+ 11 => array( 4 => 37, 5 => 38, 6 => 39, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2149+ 12 => array(),
 2150+ 13 => array(),
 2151+ 14 => array(),
 2152+ 15 => array( 6 => 60, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2153+ 16 => array( 4 => 61, 27 => 63, 5 => 38, 28 => 64, 6 => 62, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2154+ 17 => array(),
 2155+ 18 => array(),
 2156+ 19 => array( 5 => 67, 6 => 39, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2157+ 20 => array( 10 => 68, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2158+ 21 => array( 10 => 69, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2159+ 22 => array( 10 => 70, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2160+ 23 => array( 21 => 73, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2161+ 24 => array( 19 => 74, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2162+ 25 => array(),
 2163+ 26 => array(),
 2164+ 27 => array( 23 => 76, 6 => 75, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2165+ 28 => array(),
 2166+ 29 => array(),
 2167+ 30 => array(),
 2168+ 31 => array(),
 2169+ 32 => array(),
 2170+ 33 => array(),
 2171+ 34 => array(),
 2172+ 35 => array(),
 2173+ 36 => array(),
 2174+ 37 => array( 5 => 79, 6 => 39, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2175+ 38 => array(),
 2176+ 39 => array(),
 2177+ 40 => array( 29 => 83 ),
 2178+ 41 => array(),
 2179+ 42 => array(),
 2180+ 43 => array(),
 2181+ 44 => array(),
 2182+ 45 => array(),
 2183+ 46 => array(),
 2184+ 47 => array(),
 2185+ 48 => array(),
 2186+ 49 => array(),
 2187+ 50 => array(),
 2188+ 51 => array(),
 2189+ 52 => array(),
 2190+ 53 => array(),
 2191+ 54 => array(),
 2192+ 55 => array(),
 2193+ 56 => array(),
 2194+ 57 => array(),
 2195+ 58 => array( 4 => 96, 5 => 38, 6 => 39, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2196+ 59 => array( 27 => 63, 28 => 64, 6 => 97, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2197+ 60 => array(),
 2198+ 61 => array( 5 => 79, 6 => 39, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2199+ 62 => array(),
 2200+ 63 => array(),
 2201+ 64 => array(),
 2202+ 65 => array( 6 => 103, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2203+ 66 => array( 7 => 105, 8 => 106 ),
 2204+ 67 => array(),
 2205+ 68 => array(),
 2206+ 69 => array(),
 2207+ 70 => array(),
 2208+ 71 => array(),
 2209+ 72 => array( 29 => 83 ),
 2210+ 73 => array(),
 2211+ 74 => array(),
 2212+ 75 => array(),
 2213+ 76 => array(),
 2214+ 77 => array(),
 2215+ 78 => array(),
 2216+ 79 => array(),
 2217+ 80 => array(),
 2218+ 81 => array( 10 => 111, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2219+ 82 => array( 6 => 113, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2220+ 83 => array(),
 2221+ 84 => array( 11 => 114, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2222+ 85 => array( 13 => 115, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2223+ 86 => array( 14 => 116, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2224+ 87 => array( 15 => 117, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2225+ 88 => array( 16 => 118, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2226+ 89 => array( 17 => 119, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2227+ 90 => array( 17 => 120, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2228+ 91 => array( 20 => 121, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2229+ 92 => array( 20 => 122, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2230+ 93 => array( 23 => 124, 6 => 75, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2231+ 94 => array( 8 => 125 ),
 2232+ 95 => array(),
 2233+ 96 => array( 5 => 79, 6 => 39, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2234+ 97 => array(),
 2235+ 98 => array(),
 2236+ 99 => array(),
 2237+ 100 => array( 6 => 128, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2238+ 101 => array(),
 2239+ 102 => array( 28 => 129, 6 => 97, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2240+ 103 => array(),
 2241+ 104 => array(),
 2242+ 105 => array(),
 2243+ 106 => array( 29 => 83 ),
 2244+ 107 => array(),
 2245+ 108 => array( 6 => 134, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2246+ 109 => array(),
 2247+ 110 => array(),
 2248+ 111 => array(),
 2249+ 112 => array(),
 2250+ 113 => array(),
 2251+ 114 => array(),
 2252+ 115 => array(),
 2253+ 116 => array(),
 2254+ 117 => array(),
 2255+ 118 => array(),
 2256+ 119 => array(),
 2257+ 120 => array(),
 2258+ 121 => array(),
 2259+ 122 => array(),
 2260+ 123 => array(),
 2261+ 124 => array(),
 2262+ 125 => array( 29 => 83 ),
 2263+ 126 => array(),
 2264+ 127 => array(),
 2265+ 128 => array(),
 2266+ 129 => array(),
 2267+ 130 => array( 5 => 139, 6 => 39, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2268+ 131 => array( 6 => 140, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2269+ 132 => array( 8 => 141 ),
 2270+ 133 => array( 8 => 142 ),
 2271+ 134 => array(),
 2272+ 135 => array(),
 2273+ 136 => array( 11 => 143, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 8 => 72, 26 => 57 ),
 2274+ 137 => array(),
 2275+ 138 => array(),
 2276+ 139 => array(),
 2277+ 140 => array(),
 2278+ 141 => array( 29 => 83 ),
 2279+ 142 => array( 29 => 83 ),
 2280+ 143 => array(),
 2281+ 144 => array( 5 => 147, 6 => 39, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2282+ 145 => array( 5 => 148, 6 => 39, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2283+ 146 => array( 5 => 149, 6 => 39, 9 => 41, 10 => 42, 8 => 40, 11 => 43, 12 => 44, 13 => 45, 14 => 46, 15 => 47, 16 => 48, 17 => 49, 18 => 50, 19 => 51, 20 => 52, 21 => 53, 22 => 54, 24 => 55, 25 => 56, 26 => 57 ),
 2284+ 147 => array(),
 2285+ 148 => array(),
 2286+ 149 => array(),
 2287+);
 2288+
 2289+}
Property changes on: trunk/extensions/WikiScripts/interpreter/LRTable.php
___________________________________________________________________
Added: svn:eol-style
12290 + native
Index: trunk/extensions/WikiScripts/interpreter/Shared.php
@@ -0,0 +1,261 @@
 2+<?php
 3+/**
 4+ * Built-in scripting language for MediaWiki: implementation-independent interface
 5+ * for scripts parser.
 6+ * Copyright (C) 2009-2011 Victor Vasiliev <vasilvv@gmail.com>
 7+ * http://www.mediawiki.org/
 8+ *
 9+ * This program is free software; you can redistribute it and/or modify
 10+ * it under the terms of the GNU General Public License as published by
 11+ * the Free Software Foundation; either version 2 of the License, or
 12+ * (at your option) any later version.
 13+ *
 14+ * This program is distributed in the hope that it will be useful,
 15+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 17+ * GNU General Public License for more details.
 18+ *
 19+ * You should have received a copy of the GNU General Public License along
 20+ * with this program; if not, write to the Free Software Foundation, Inc.,
 21+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 22+ * http://www.gnu.org/copyleft/gpl.html
 23+ */
 24+
 25+if( !defined( 'MEDIAWIKI' ) )
 26+ die();
 27+
 28+/**
 29+ * This class represents a terminal of the script grammar.
 30+ */
 31+class ISToken {
 32+ // Constant values should match ones in syntax.txt
 33+ const TEnd = '$';
 34+ const TAppend = 'append';
 35+ const TBreak = 'break';
 36+ const TCatch = 'catch';
 37+ const TColon = 'colon'; // :
 38+ const TCompareOperator = 'compareop'; // <, >, <= or >=
 39+ const TComma = 'comma'; // ,
 40+ const TContains = 'contains';
 41+ const TContinue = 'continue';
 42+ const TDelete = 'delete';
 43+ const TDoubleColon = 'doublecolon';
 44+ const TElse = 'else';
 45+ const TEqualsToOperator = 'equalsto'; // ==, ===, != or !==
 46+ const TFalse = 'false';
 47+ const TFloat = 'float';
 48+ const TFor = 'for';
 49+ const TID = 'id';
 50+ const TIf = 'if';
 51+ const TIn = 'in';
 52+ const TInt = 'int';
 53+ const TBoolInvert = 'invert'; // !
 54+ const TIsset = 'isset';
 55+ const TLeftBracket = 'leftbracket'; // (
 56+ const TLeftCurly = 'leftcurly'; // {
 57+ const TLeftSquare = 'leftsquare'; // [
 58+ const TLogicalOperator = 'logicop'; // &, | or ^
 59+ const TMulOperator = 'mul'; // *, / or %
 60+ const TNull = 'null';
 61+ const TPow = 'pow'; // **
 62+ const TReturn = 'return';
 63+ const TRightBracket = 'rightbracket'; // )
 64+ const TRightCurly = 'rightcurly'; // }
 65+ const TRightSquare = 'rightsquare'; // ]
 66+ const TSemicolon = 'semicolon'; // ;
 67+ const TSet = 'setto'; // =
 68+ const TSelf = 'self';
 69+ const TString = 'string';
 70+ const TSumOperator = 'sum'; // + or -
 71+ const TTrinary = 'trinary'; // ?
 72+ const TTrue = 'true';
 73+ const TTry = 'try';
 74+ const TYield = 'yield';
 75+
 76+ var $type;
 77+ var $value;
 78+ var $line;
 79+
 80+ public function __construct( $type = self::TEnd, $value = null, $line = 0 ) {
 81+ $this->type = $type;
 82+ $this->value = $value;
 83+ $this->line = $line;
 84+ }
 85+
 86+ function __toString() {
 87+ return "{$this->value}";
 88+ }
 89+}
 90+
 91+/**
 92+ * This class represents a non-terminal of the script grammar.
 93+ */
 94+class ISParserTreeNode {
 95+ var $mType, $mChildren;
 96+
 97+ public function __construct( $parser, $id ) {
 98+ $parserClass = get_class( $parser );
 99+ $this->mType = $parserClass::$mNonterminals[$id];
 100+ }
 101+
 102+ public function addChild( $node ) {
 103+ // Since we do not want a long chain of "exprSomething -> exprWhatever" in the parser tree,
 104+ // we cut it out at the parsing stage
 105+ if( $node instanceof ISParserTreeNode ) {
 106+ $children = $node->getChildren();
 107+ if( count( $children ) == 1 && strpos( $node->mType, "expr" ) === 0
 108+ && strpos( @$children[0]->mType, "expr" ) === 0 ) {
 109+ $this->mChildren[] = $children[0];
 110+ return;
 111+ }
 112+ }
 113+
 114+ $this->mChildren[] = $node;
 115+ }
 116+
 117+ public function getChildren() {
 118+ return $this->mChildren;
 119+ }
 120+
 121+ public function getChildrenCount() {
 122+ return count( $this->mChildren );
 123+ }
 124+
 125+ public function hasSingleChild() {
 126+ return count( $this->mChildren ) == 1;
 127+ }
 128+
 129+ public function getType() {
 130+ return $this->mType;
 131+ }
 132+
 133+ public function __toString() {
 134+ $r = $this->formatStringArray();
 135+ return implode( "\n", $r );
 136+ }
 137+
 138+ public function formatStringArray() {
 139+ $s = array( "<nonterminal type=\"{$this->mType}\">" );
 140+ foreach( $this->mChildren as $child ) {
 141+ if( $child instanceof ISParserTreeNode ) {
 142+ $sub = $child->formatStringArray();
 143+ foreach( $sub as $str )
 144+ $s[] = "\t" . $str;
 145+ } else {
 146+ $s[] = "\t<terminal type=\"{$child->type}\" value=\"{$child->value}\" />";
 147+ }
 148+ }
 149+ $s[] = "</nonterminal>";
 150+ return $s;
 151+ }
 152+}
 153+
 154+/**
 155+ * Generalized script parser.
 156+ */
 157+interface ISParser {
 158+ /**
 159+ * If this function returns true, code scanner is passed to parse().
 160+ * Otherwise, code itself is passed.
 161+ */
 162+ public function needsScanner();
 163+
 164+ /**
 165+ * Parses code (in text or scanner) to parser tree.
 166+ * @param input ISScanner Input (scanner or string)
 167+ * @param maxTokens int Maximal amount of tokens
 168+ * @return ISParserTreeNode
 169+ */
 170+ public function parse( $input, $module, $maxTokens );
 171+
 172+ /**
 173+ * Returns an array of the syntax errors in the code
 174+ * @param input ISSCanner Input (scanner or string)
 175+ * @param maxTokens int Maximal amount of tokens
 176+ * @return array(string)
 177+ */
 178+ public function getSyntaxErrors( $input, $moudle, $maxTokens );
 179+}
 180+
 181+class ISException extends MWException {}
 182+
 183+// Exceptions that we might conceivably want to report to ordinary users
 184+// (i.e. exceptions that don't represent bugs in the extension itself)
 185+class ISUserVisibleException extends ISException {
 186+ function __construct( $exception_id, $module, $line, $params = array() ) {
 187+ $codelocation = wfMsg( 'inlinescripts-codelocation', $module, $line );
 188+ $msg = wfMsgExt( 'inlinescripts-exception-' . $exception_id, array(), array_merge( array( $codelocation ), $params ) );
 189+ parent::__construct( $msg );
 190+
 191+ $this->mExceptionID = $exception_id;
 192+ $this->mLine = $line;
 193+ $this->mModule = $module;
 194+ $this->mParams = $params;
 195+ }
 196+
 197+ public function getExceptionID() {
 198+ return $this->mExceptionID;
 199+ }
 200+}
 201+
 202+/**
 203+ * Exceptions caused by the error on script transclusion error, i.e. not in script.
 204+ */
 205+class ISTransclusionException extends ISException {
 206+ function __construct( $exception_id, $params = array() ) {
 207+ $msg = wfMsgExt( 'inlinescripts-transerror-' . $exception_id, array(), $params );
 208+ parent::__construct( $msg );
 209+
 210+ $this->mExceptionID = $exception_id;
 211+ $this->mParams = $params;
 212+ }
 213+}
 214+
 215+/**
 216+ * Exceptions used for control structures that need to break out of deep function
 217+ * nesting level (e.g. break or continue).
 218+ */
 219+class ISControlException extends ISUserVisibleException {}
 220+
 221+/**
 222+ * Exception that allows to return from a function.
 223+ */
 224+class ISReturnException extends ISControlException {
 225+ function __construct( $result, $empty ) {
 226+ $this->mResult = $result;
 227+ $this->mEmpty = $empty;
 228+ }
 229+
 230+ function getResult() {
 231+ return $this->mResult;
 232+ }
 233+
 234+ function isEmpty() {
 235+ return $this->mEmpty;
 236+ }
 237+}
 238+
 239+/**
 240+ * Code parser output.
 241+ */
 242+class ISParserOutput {
 243+ var $mTree, $mTokensCount, $mVersion;
 244+
 245+ public function __construct( $tree, $tokens ) {
 246+ global $wgInlineScriptsParserClass;
 247+ $this->mTree = $tree;
 248+ $this->mTokensCount = $tokens;
 249+ $this->mVersion = $wgInlineScriptsParserClass::getVersion();
 250+ }
 251+
 252+ public function getParserTree() {
 253+ return $this->mTree;
 254+ }
 255+
 256+ public function getVersion() {
 257+ return $this->mVersion;
 258+ }
 259+}
 260+
 261+// Used by ISEvaluationContext::setVar
 262+class ISPlaceholder {}
Property changes on: trunk/extensions/WikiScripts/interpreter/Shared.php
___________________________________________________________________
Added: svn:eol-style
1263 + native
Index: trunk/extensions/WikiScripts/interpreter/buildLRTables.php
@@ -0,0 +1,561 @@
 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+ $namevalpailr = self::parseLine( $g, $line, $i );
 66+ if( $namevalpailr ) {
 67+ list( $name, $vals ) = $namevalpailr;
 68+ foreach( $vals as $val )
 69+ $g->addProduction( $name, $val );
 70+ }
 71+ }
 72+ foreach( $g->mProductions as $prod ) {
 73+ list( $ntid, $prod ) = $prod;
 74+ foreach( $prod as $symbol )
 75+ if( is_string( $symbol ) && !in_array( $symbol, $g->mTerminals ) )
 76+ $g->mTerminals[] = $symbol;
 77+ }
 78+ $g->mTerminals[] = '$';
 79+ $g->mSymbols = array_merge( $g->mTerminals, array_keys( $g->mNonterminals ) );
 80+ return $g;
 81+ }
 82+
 83+ private static function parseLine( $g, $line, $lnum ) {
 84+ $i = 0;
 85+ wfSuppressWarnings(); // @ doesn't help to supress "uninitialized string offset" warning
 86+
 87+ self::skipWhitespace( $line, $i );
 88+ if( $line[$i] == '#' )
 89+ return null;
 90+ if( $line[$i] != '<' )
 91+ die( "Invalid BNF at line $lnum" );
 92+ $i++;
 93+
 94+ $end = strpos( $line, '>', $i );
 95+ if( $end === false )
 96+ die( "Invalid BNF at line $lnum" );
 97+ $name = $g->getNonterminalID( substr( $line, $i, $end - $i ) );
 98+ $i = $end + 1;
 99+
 100+ self::skipWhitespace( $line, $i );
 101+ if( substr( $line, $i, 3 ) != '::=' )
 102+ die( "Invalid BNF at line $lnum" );
 103+ $i += 3;
 104+
 105+ $prods = array();
 106+ $curProd = array();
 107+ while( $i + 1 < strlen( $line ) ) {
 108+ self::skipWhitespace( $line, $i );
 109+ if( $line[$i] == '|' ) {
 110+ $prods[] = $curProd;
 111+ $curProd = array();
 112+ $i++;
 113+ } elseif( $line[$i] == '<' ) {
 114+ $i++;
 115+ $end = strpos( $line, '>', $i );
 116+ if( $end === false )
 117+ die( "Invalid BNF at line $lnum" );
 118+ $curProd[] = $g->getNonterminalID( substr( $line, $i, $end - $i ) );
 119+ $i = $end + 1;
 120+ } else {
 121+ for( $termName = ''; ctype_alnum( $line[$i] ); $i++ )
 122+ $termName .= $line[$i];
 123+ if( !$termName )
 124+ die( "Invalid BNF at line $lnum" );
 125+ $curProd[] = $termName;
 126+ }
 127+ }
 128+ $prods[] = $curProd;
 129+ wfRestoreWarnings();
 130+ return array( $name, $prods );
 131+ }
 132+
 133+ private static function skipWhitespace( $line, &$pos ) {
 134+ while( ctype_space( $line[$pos] ) && $pos + 1 < strlen( $line ) )
 135+ $pos++;
 136+ }
 137+
 138+ private function buildFirstTable() {
 139+ foreach( $this->mSymbols as $symbol )
 140+ $this->mFirst[$symbol] = array();
 141+
 142+ foreach( $this->mTerminals as $t )
 143+ $this->mFirst[$t][] = $t;
 144+
 145+ for( ; ; ) {
 146+ $added = 0;
 147+ foreach( $this->mProductions as $prodbundle ) {
 148+ list( $nt, $prod ) = $prodbundle;
 149+ foreach( $this->mFirst[$prod[0]] as $e ) {
 150+ if( !in_array( $e, $this->mFirst[$nt] ) ) {
 151+ $this->mFirst[$nt][] = $e;
 152+ $added++;
 153+ }
 154+ }
 155+ }
 156+ if( !$added )
 157+ break;
 158+ }
 159+ }
 160+
 161+ private function buildFollowTable() {
 162+ foreach( $this->mSymbols as $symbol )
 163+ $this->mFollow[$symbol] = array();
 164+ $this->mFollow[0][] = '$';
 165+ for( ; ; ) {
 166+ $added = 0;
 167+ foreach( $this->mProductions as $prodbundle ) {
 168+ list( $nt, $prod ) = $prodbundle;
 169+ for( $i = 0; $i < count( $prod ) - 1; $i++ ) {
 170+ $symbol = $prod[$i];
 171+ if( is_int( $symbol ) ) {
 172+ foreach( $this->mFirst[$prod[$i + 1]] as $fsymbol ) {
 173+ if( !in_array( $fsymbol, $this->mFollow[$symbol] ) ) {
 174+ $this->mFollow[$symbol][] = $fsymbol;
 175+ $added++;
 176+ }
 177+ }
 178+ }
 179+ }
 180+ $last = end( $prod );
 181+ if( is_int( $last ) ) {
 182+ foreach( $this->mFollow[$nt] as $symbol ) {
 183+ if( !in_array( $symbol, $this->mFollow[$last] ) ) {
 184+ $this->mFollow[$last][] = $symbol;
 185+ }
 186+ }
 187+ }
 188+ }
 189+ if( !$added )
 190+ break;
 191+ }
 192+ }
 193+
 194+ private function itemsClosure( $items ) {
 195+ for( ; ; ) {
 196+ $oldsize = count( $items );
 197+ foreach( $items as $item ) {
 198+ list( $prodid, $idx ) = $item;
 199+ list( $unused, $prod ) = $this->mProductions[$prodid];
 200+ if( is_int( @$prod[$idx] ) ) {
 201+ foreach( $this->getProdsForNt( $prod[$idx] ) as $id => $newProd ) {
 202+ $item = array( $id, 0 );
 203+ if( !in_array( $item, $items ) )
 204+ $items[] = $item;
 205+ }
 206+ }
 207+ }
 208+ if( count( $items ) == $oldsize )
 209+ return $items;
 210+ }
 211+ }
 212+
 213+ public function itemsGoto( $items, $symbol ) {
 214+ if( is_null( $symbol ) )
 215+ return array();
 216+ $result = array();
 217+ foreach( $items as $item ) {
 218+ list( $prodid, $idx ) = $item;
 219+ $prod = $this->mProductions[$prodid][1];
 220+ if( @$prod[$idx] === $symbol )
 221+ $result[] = array( $prodid, $idx + 1 );
 222+ }
 223+ return $this->itemsClosure( $result );
 224+ }
 225+
 226+ public function buildCanonicalSet() {
 227+ $r = array( $this->itemsClosure( array( array( 0, 0 ) ) ) );
 228+ $symbols = array_merge( $this->mTerminals, array_keys( $this->mNonterminals ) );
 229+ for( ; ; ) {
 230+ $oldsize = count( $r );
 231+ foreach( $r as $set ) {
 232+ foreach( $symbols as $symbol ) {
 233+ $goto = $this->itemsGoto( $set, $symbol );
 234+ if( $goto && !in_array( $goto, $r ) )
 235+ $r[] = $goto;
 236+ }
 237+ }
 238+ if( $oldsize == count( $r ) )
 239+ break;
 240+ }
 241+ return $r;
 242+ }
 243+
 244+ public function buildLRTable() {
 245+ $this->buildFirstTable();
 246+ $this->buildFollowTable();
 247+ $canonSet = $this->buildCanonicalSet();
 248+ $actionTable = array();
 249+ $gotoTable = array();
 250+ for( $i = 0; $i < count( $canonSet ); $i++ ) {
 251+ $set = $canonSet[$i];
 252+ $row = $rowGoto = array();
 253+ foreach( $set as $item ) {
 254+ list( $prodid, $idx ) = $item;
 255+ list( $nt, $prod ) = $this->mProductions[$prodid];
 256+ $goto = $this->itemsGoto( $set, @$prod[$idx] );
 257+ for( $j = 0; $j < count( $canonSet ); $j++ ) {
 258+ if( $goto == $canonSet[$j] ) {
 259+ if( is_string( $prod[$idx] ) ) {
 260+ $act = array( 'shift', $j );
 261+ if( isset( $row[$prod[$idx]] ) && $row[$prod[$idx]] != $act )
 262+ if( $prod[0] != 'if' ) // Grammar-specific manual hack for "hanging if" problem
 263+ $this->conflictError( $i, $set, $row[$prod[$idx]], $act, $prod[$idx] );
 264+ $row[$prod[$idx]] = $act;
 265+ } else {
 266+ $rowGoto[$prod[$idx]] = $j;
 267+ }
 268+ }
 269+ }
 270+ if( $idx == count( $prod ) ) {
 271+ if( $prodid ) {
 272+ foreach( $this->mFollow[$nt] as $symbol ) {
 273+ $act = array( 'reduce', $prodid );
 274+ if( isset( $row[$symbol] ) && $row[$symbol] != $act ) {
 275+ $this->conflictError( $i, $set, $row[$symbol], $act, $symbol );
 276+ }
 277+ $row[$symbol] = $act;
 278+ }
 279+ } else {
 280+ $row['$'] = array( 'accept' );
 281+ }
 282+ }
 283+ }
 284+ $actionTable[$i] = $row;
 285+ $gotoTable[$i] = $rowGoto;
 286+ }
 287+ $this->mAction = $actionTable;
 288+ $this->mGoto = $gotoTable;
 289+ }
 290+
 291+ /** Debug */
 292+ public function formatProduction( $prodid ) {
 293+ list( $subj, $val ) = $this->mProductions[$prodid];
 294+ $s = array( $this->getNtName( $subj ), "->" );
 295+ foreach( $val as $symbol ) {
 296+ if( is_string( $symbol ) )
 297+ $s[] = strtoupper( $symbol );
 298+ else
 299+ $s[] = $this->getNtName( $symbol );
 300+ }
 301+ return implode( ' ', $s );
 302+ }
 303+
 304+ public function formatItem( $item ) {
 305+ list( $prodid, $idx ) = $item;
 306+ list( $subj, $val ) = $this->mProductions[$prodid];
 307+ $s = array( $this->getNtName( $subj ), "->" );
 308+ for( $i = 0; $i <= count( $val ); $i++ ) {
 309+ if( $i == $idx )
 310+ $s[] = '(!)';
 311+ if( $symbol = @$val[$i] ) {
 312+ if( is_string( $symbol ) )
 313+ $s[] = strtoupper( $symbol );
 314+ else
 315+ $s[] = $this->getNtName( $symbol );
 316+ }
 317+ }
 318+ return implode( ' ', $s );
 319+ }
 320+
 321+ public function formatAction( $act ) {
 322+ @list( $name, $arg ) = $act;
 323+ if( $name == 'shift' ) {
 324+ return "Shift to state {$arg}";
 325+ }
 326+ if( $name == 'reduce' ) {
 327+ $prod = $this->formatProduction( $arg );
 328+ return "Reduce to production {$arg} ({$prod})";
 329+ }
 330+ if( $name == 'accept' ) {
 331+ return "Accept";
 332+ }
 333+ }
 334+
 335+ public function conflictError( $id, $state, $act1, $act2, $symbol ) {
 336+ echo "Found conflict in state {$id} for symbol {$symbol}.\n";
 337+ echo "Conflicting actions:\n";
 338+ foreach( array( $act1, $act2 ) as $act ) {
 339+ $str = $this->formatAction( $act );
 340+ echo "* {$str}\n";
 341+ }
 342+ echo "Items of the state:\n";
 343+ foreach( $state as $item ) {
 344+ $str = $this->formatItem( $item );
 345+ echo "* {$str}\n";
 346+ }
 347+ exit;
 348+ }
 349+
 350+ public function buildHTMLDump() {
 351+ $s = <<<END
 352+<html>
 353+<head>
 354+<title>Inline scripts LR table dump</title>
 355+<style type="text/css">
 356+table {
 357+ margin: 1em 1em 1em 0;
 358+ background: #f9f9f9;
 359+ border: 1px #aaa solid;
 360+ border-collapse: collapse;
 361+}
 362+th, td {
 363+ border: 1px #aaa solid;
 364+ padding: 0.2em;
 365+}
 366+th {
 367+ background: #f2f2f2;
 368+ text-align: center;
 369+}
 370+caption {
 371+ font-weight: bold;
 372+}
 373+</style>
 374+</head>
 375+<body>
 376+<p>Here is the dump of LR table itself, as well as data used to build it.</p>
 377+<p>Navigate: <a href="#first">FIRST()</a> | <a href="#follow">FOLLOW()</a> | <a href="#prods">Productions</a>
 378+ | <a href="#table">ACTION/GOTO</a></p>
 379+END;
 380+
 381+ $s .= "<h1><a name='first' id='first'>FIRST()</h1><table><tr><th>Symbol</th><th>FIRST(Symbol)</th></tr>\n";
 382+ foreach( $this->mFirst as $item => $val ) {
 383+ $itemname = is_int( $item ) ? '<i>' . $this->getNtName( $item ) . '</i>'
 384+ : "<b>{$item}</b>";
 385+ $s .= "<tr><td>{$itemname}</td><td>" . implode( ', ', $val ) . "</td></tr>\n";
 386+ }
 387+ $s .= "</table>\n";
 388+
 389+ $s .= "<h1><a name='follow' id='follow'>FOLLOW()</h1><table><tr><th>Symbol</th><th>FOLLOW(Symbol)</th></tr>\n";
 390+ foreach( $this->mFollow as $item => $val ) {
 391+ if( !$val ) continue;
 392+ $itemname = is_int( $item ) ? '<i>' . $this->getNtName( $item ) . '</i>'
 393+ : "<b>{$item}</b>";
 394+ $s .= "<tr><td>{$itemname}</td><td>" . implode( ', ', $val ) . "</td></tr>\n";
 395+ }
 396+ $s .= "</table>\n";
 397+
 398+ $s .= "<h1><a name='prods' id='prods'>Productions</h1><table><tr><th>ID</th><th>Production</th></tr>\n";
 399+ foreach( $this->mProductions as $id => $val ) {
 400+ $str = $this->formatProduction( $id );
 401+ $s .= "<tr><td><b>{$id}</b></td><td>{$str}</td></tr>\n";
 402+ }
 403+ $s .= "</table>\n";
 404+
 405+ $termLen = count( $this->mTerminals );
 406+ $nontermLen = count( $this->mNonterminals );
 407+ $s .= "<h1><a name='table' id='action'>LR-table (ACTION/GOTO)</h1><table><tr><th rowspan=2>State ID</th>" .
 408+ "<th colspan={$termLen}>ACTION</th><th colspan={$nontermLen}>GOTO</th></tr><tr><th>" .
 409+ implode( '</th><th>', $this->mTerminals ) . '</th><th>' . implode( '</th><th>', array_values( $this->mNonterminals ) ) . "</th></tr>\n";
 410+ for( $id = 0; $id < count( $this->mAction ); $id++ ) {
 411+ $row = $this->mAction[$id];
 412+ $goto = $this->mGoto[$id];
 413+ $s .= "\t<tr><td><b>{$id}</b></td>";
 414+ foreach( $this->mTerminals as $t ) {
 415+ $act = @$row[$t];
 416+ if( $act ) {
 417+ switch( $act[0] ) {
 418+ case 'shift':
 419+ $s .= "<td>s{$act[1]}</td>"; break;
 420+ case 'reduce':
 421+ $s .= "<td>r{$act[1]}</td>"; break;
 422+ case 'accept':
 423+ $s .= "<td>acc</td>"; break;
 424+ }
 425+ } else {
 426+ $s .= "<td></td>";
 427+ }
 428+ }
 429+ foreach( $this->mNonterminals as $ntid => $ntname ) {
 430+ if( isset( $goto[$ntid] ) ) {
 431+ $s .= "<td>{$goto[$ntid]}</td>";
 432+ } else {
 433+ $s .= "<td></td>";
 434+ }
 435+ }
 436+ $s .= "</tr>\n";
 437+ }
 438+ $s .= "</table>\n";
 439+
 440+ $s .= "<hr/><p>Autogenerated on " . gmdate( 'Y-m-d H:i:s' ) . "</p></body></html>";
 441+ return $s;
 442+ }
 443+
 444+ private function formatArray( $array ) {
 445+ if( !$array )
 446+ return 'array()';
 447+ foreach( $array as &$item ) {
 448+ if( is_string( $item ) )
 449+ $item = "'{$item}'";
 450+ }
 451+ return 'array( ' . implode( ', ', $array ) . ' )';
 452+ }
 453+
 454+ private function formatAssocArray( $array ) {
 455+ if( !$array )
 456+ return 'array()';
 457+ $result = array();
 458+ foreach( $array as $k => $v ) {
 459+ if( is_string( $v ) )
 460+ $v = "'{$v}'";
 461+ $result[] = "{$k} => {$v}";
 462+ }
 463+ return 'array( ' . implode( ', ', $result ) . ' )';
 464+ }
 465+
 466+ public function buildPHPFile( $ts ) {
 467+ $date = $ts;
 468+ $s = <<<ENDOFHEADER
 469+<?php
 470+
 471+/**
 472+ * Autogenerated SLR-table for inline scripts language.
 473+ *
 474+ * You should not try to modify it manually (it's very easy to break).
 475+ * Use syntax.txt and buildLRTables.php insteaed.
 476+ *
 477+ * Actions have following syntax
 478+ * array( 0, N ) means "shift and go to state N"
 479+ * array( 1, N ) means "reduce to production N"
 480+ * array( 2 ) means "accept"
 481+ * null means "error"
 482+ *
 483+ * Terminals are referred by names, nonterminals - by ids.
 484+ *
 485+ * Variables has following format:
 486+ * * \$nonterminals is a nonterminal ID -> name map.
 487+ * * \$productions is a ID -> array( nonterminal, body ) map.
 488+ * * Production body is an array of production symbols
 489+ *
 490+ * Generated on {$date}.
 491+ */
 492+
 493+class ISLRTable {
 494+
 495+const Timestamp = '{$date}';
 496+
 497+
 498+ENDOFHEADER;
 499+
 500+ $s .= "static \$nonterminals = array(\n";
 501+ foreach( $this->mNonterminals as $id => $val ) {
 502+ $s .= "\t{$id} => '{$val}',\n";
 503+ }
 504+ $s .= ");\n\n";
 505+
 506+ $s .= "static \$productions = array(\n";
 507+ foreach( $this->mProductions as $id => $val ) {
 508+ $body = $this->formatArray( $val[1] );
 509+ $s .= "\t{$id} => array( {$val[0]}, {$body} ),\n";
 510+ }
 511+ $s .= ");\n\n";
 512+
 513+ $s .= "static \$action = array(\n";
 514+ foreach( $this->mAction as $id => $row ) {
 515+ $s .= "\t{$id} => array(\n";
 516+ foreach( $row as $t => $action ) {
 517+ if( $action[0] == 'shift' )
 518+ $s .= "\t\t'{$t}' => array( 0, {$action[1]} ),\n";
 519+ if( $action[0] == 'reduce' )
 520+ $s .= "\t\t'{$t}' => array( 1, {$action[1]} ),\n";
 521+ if( $action[0] == 'accept' )
 522+ $s .= "\t\t'{$t}' => array( 2, null ),\n";
 523+ }
 524+ $s .= "\t),\n";
 525+ }
 526+ $s .= ");\n\n";
 527+
 528+ $s .= "static \$goto = array(\n";
 529+ foreach( $this->mGoto as $id => $row ) {
 530+ $body = $this->formatAssocArray( $row );
 531+ $s .= "\t{$id} => {$body},\n";
 532+ }
 533+ $s .= ");\n\n";
 534+
 535+ $s .= "}\n";
 536+ return $s;
 537+ }
 538+
 539+ public function buildPHPVersionFile( $ts ) {
 540+ return <<<EOF
 541+<?php
 542+
 543+/**
 544+ * This file includes timestamp which indicates the version of LRTable.php file.
 545+ * Since the file is too large, loading it every time is expensive and we store the
 546+ * version in separate file.
 547+ */
 548+
 549+define( 'IS_LR_VERSION', "{$ts}" );
 550+
 551+EOF;
 552+ }
 553+}
 554+
 555+$ts = gmdate( 'Y-m-d H:i:s' );
 556+
 557+$definition = file_get_contents( dirname( __FILE__ ) . '/syntax.txt' );
 558+$grammar = Grammar::parse( $definition );
 559+$grammar->buildLRTable();
 560+file_put_contents( 'LRTableBuildReport.html', $grammar->buildHTMLDump() );
 561+file_put_contents( 'LRTable.php', $grammar->buildPHPFile( $ts ) );
 562+file_put_contents( 'LRTableVersion.php', $grammar->buildPHPVersionFile( $ts ) );
Property changes on: trunk/extensions/WikiScripts/interpreter/buildLRTables.php
___________________________________________________________________
Added: svn:eol-style
1563 + native
Index: trunk/extensions/WikiScripts/inlinescripts.sql
@@ -0,0 +1,16 @@
 2+--
 3+-- Track script inclusions.
 4+--
 5+CREATE TABLE /*_*/scriptlinks (
 6+ -- Key to the page_id of the page containing the link.
 7+ sl_from int unsigned NOT NULL default 0,
 8+
 9+ -- Key to page_title of the target page.
 10+ -- The target page may or may not exist, and due to renames
 11+ -- and deletions may refer to different page records as time
 12+ -- goes by.
 13+ sl_to varchar(255) binary NOT NULL default ''
 14+) /*$wgDBTableOptions*/;
 15+
 16+CREATE UNIQUE INDEX /*i*/sl_from ON /*_*/scriptlinks (sl_from,sl_to);
 17+CREATE UNIQUE INDEX /*i*/sl_title ON /*_*/scriptlinks (sl_to,sl_from);
Index: trunk/extensions/WikiScripts/interpreterTests.txt
@@ -0,0 +1,630 @@
 2+# Test cases for MediaWiki inline scripts engine
 3+
 4+!! article
 5+Module:Basic mathematics
 6+!! text
 7+function run() {
 8+ return -2 + 2 * 2 ** 2 - 3 * 7 % 5;
 9+}
 10+!! endarticle
 11+
 12+!! test
 13+Basic mathematics
 14+!! input
 15+{{i:Basic mathematics|run}}
 16+!! result
 17+<p>5
 18+</p>
 19+!! end
 20+
 21+!! article
 22+Module:Pow associativity
 23+!! text
 24+function run() {
 25+ // Not 4096
 26+ return 4 ** 3 ** 2;
 27+}
 28+!! endarticle
 29+
 30+!! test
 31+** associativity
 32+!! input
 33+{{i:Pow associativity|run}}
 34+!! result
 35+<p>262144
 36+</p>
 37+!! end
 38+
 39+!! article
 40+Module:String contecation
 41+!! text
 42+function add( a, b ) {
 43+ // if you pass 3 and 7, it would be 37, because all arguments from wikitext
 44+ // are strings
 45+ return a + b;
 46+}
 47+!! endarticle
 48+
 49+!! test
 50+String contecation
 51+!! input
 52+{{i:string contecation|add|3|7}}
 53+!! result
 54+<p>37
 55+</p>
 56+!! end
 57+
 58+!! article
 59+Module:Multiple variable assignment
 60+!! text
 61+function run() {
 62+ // if you pass 3 and 7, it would be 37, because all arguments from wikitext
 63+ // are strings
 64+ a = b = 3;
 65+ return a + b;
 66+}
 67+!! endarticle
 68+
 69+!! test
 70+Multiple variable assignment
 71+!! input
 72+{{i:Multiple variable assignment|run}}
 73+!! result
 74+<p>6
 75+</p>
 76+!! end
 77+
 78+!! article
 79+Module:Assigment with arithmetics
 80+!! text
 81+function run() {
 82+ a = 2;
 83+ a += 3;
 84+ a -= 7;
 85+ return a;
 86+}
 87+!! endarticle
 88+
 89+!! test
 90+Assigment with arithmetics (+=, -=, etc)
 91+!! input
 92+{{i:Assigment with arithmetics|run}}
 93+!! result
 94+<p>-2
 95+</p>
 96+!! end
 97+
 98+!! article
 99+Module:Boolean shortcut
 100+!! text
 101+function run() {
 102+ /* Only first statement should be performed */
 103+ !(b = 2) | (b = 3) | (b = 4);
 104+ return b;
 105+}
 106+!! endarticle
 107+
 108+!! test
 109+Boolean shortcut
 110+!! input
 111+{{i:boolean shortcut|run}}
 112+!! result
 113+<p>3
 114+</p>
 115+!! end
 116+
 117+!! article
 118+Module:Equality
 119+!! text
 120+function run() {
 121+ return "2" == 2 & "2" !== 2 & 4 === (2 + 2) &
 122+ null == "" & false == null & 0 == "";
 123+}
 124+!! endarticle
 125+
 126+!! test
 127+Equality
 128+!! input
 129+{{i:equality|run}}
 130+!! result
 131+<p>1
 132+</p>
 133+!! end
 134+
 135+!! article
 136+Module:Comparisons
 137+!! text
 138+function run() {
 139+ return 2 > 1 & 2 >= 2 & 2 <= 2 & 1 < 2;
 140+}
 141+!! endarticle
 142+
 143+!! test
 144+Comparsions
 145+!! input
 146+{{i:comparisons|run}}
 147+!! result
 148+<p>1
 149+</p>
 150+!! end
 151+
 152+!! article
 153+Module:Trivial
 154+!! text
 155+function getText() {
 156+ return "AA";
 157+}
 158+!! endarticle
 159+
 160+!! test
 161+Integration with other functions
 162+!! input
 163+{{lc:{{i:trivial|getText}}}}
 164+!! result
 165+<p>aa
 166+</p>
 167+!! end
 168+
 169+!! article
 170+Module:Conditions
 171+!! text
 172+function run() {
 173+ return 2 + 2 == 4 ? "a" : "b";
 174+}
 175+!! endarticle
 176+
 177+!! test
 178+Conditions (?)
 179+!! input
 180+{{i:conditions|run}}
 181+!! result
 182+<p>a
 183+</p>
 184+!! end
 185+
 186+!! article
 187+Module:Conditions (if-then-else)
 188+!! text
 189+function run() {
 190+ if( 2 * 7 > 3 * 4 ) {
 191+ a = 7;
 192+ } else {
 193+ a = 10;
 194+ }
 195+
 196+ if( a ** 2 < 50 )
 197+ return "ok";
 198+}
 199+!! endarticle
 200+
 201+!! test
 202+Conditions (if-then, if-then-else)
 203+!! input
 204+{{i:Conditions (if-then-else)|run}}
 205+!! result
 206+<p>ok
 207+</p>
 208+!! end
 209+
 210+!! article
 211+Module:Bullets
 212+!! text
 213+function run() {
 214+ out = "";
 215+ for( a in args() )
 216+ out += "* " + a + "\n";
 217+ return out;
 218+}
 219+!! endarticle
 220+
 221+!! article
 222+Template:Bullets
 223+!! text
 224+{{i:bullets|run}}
 225+!! endarticle
 226+
 227+!! test
 228+args() function
 229+!! input
 230+{{bullets|a|b|c}}
 231+!! result
 232+<ul><li> a
 233+</li><li> b
 234+</li><li> c
 235+</li></ul>
 236+
 237+!! end
 238+
 239+!! article
 240+Module:TranscludedSwitch
 241+!! text
 242+function run() {
 243+ return isTranscluded() ? arg(1) : "?!";
 244+}
 245+!! endarticle
 246+
 247+!! article
 248+Template:TranscludedSwitch
 249+!! text
 250+{{i:TranscludedSwitch|run}}
 251+!! endarticle
 252+
 253+!! test
 254+isTranscluded()/arg() check
 255+!! input
 256+{{TranscludedSwitch|11}}
 257+!! result
 258+<p>11
 259+</p>
 260+!! end
 261+
 262+!! article
 263+Module:Empty argument handling check
 264+!! text
 265+function run() {
 266+ return arg("test") === null;
 267+}
 268+!! endarticle
 269+
 270+!! test
 271+Empty argument handling check
 272+!! input
 273+{{i:Empty argument handling check|run}}
 274+!! result
 275+<p>1
 276+</p>
 277+!! end
 278+
 279+!! article
 280+Module:Casts
 281+!! text
 282+function run() {
 283+ return string(float(2)) === "2.0" & int(7.99) === 7;
 284+}
 285+!! endarticle
 286+
 287+!! test
 288+Casts
 289+!! input
 290+{{i:Casts|run}}
 291+!! result
 292+<p>1
 293+</p>
 294+!! end
 295+
 296+!! article
 297+Module:Exception handling
 298+!! text
 299+function run() {
 300+ try
 301+ 2 / 0;
 302+ catch( e )
 303+ return e;
 304+}
 305+!! endarticle
 306+
 307+!! test
 308+Exception handling
 309+!! input
 310+{{i:Exception handling|run}}
 311+!! result
 312+<p>dividebyzero
 313+</p>
 314+!! end
 315+
 316+!! article
 317+Template:Numberofsomething
 318+!! text
 319+721
 320+!! endarticle
 321+
 322+!! article
 323+Module:Numberofsomething
 324+!! text
 325+function run() {
 326+ numofsmth = int( parse( '{{numberofsomething}}' ) ) + 279;
 327+ return '{{numberofsomething}}: ' + numofsmth;
 328+}
 329+!! endarticle
 330+
 331+!! test
 332+Template access via parse()
 333+!! input
 334+{{i:Numberofsomething|run}}
 335+!! result
 336+<p>{{numberofsomething}}: 1000
 337+</p>
 338+!! end
 339+
 340+!! article
 341+Module:123
 342+!! text
 343+function run1() {
 344+ return 123;
 345+}
 346+
 347+function run2() {
 348+ return parse( '{{123}}' );
 349+}
 350+!! endarticle
 351+
 352+!! article
 353+Template:123
 354+!! text
 355+{{i:123|run1}}
 356+!! endarticle
 357+
 358+!! test
 359+Nested wikiscripts via parse()
 360+!! input
 361+{{i:123|run2}}
 362+!! result
 363+<p>123
 364+</p>
 365+!! end
 366+
 367+!! article
 368+Module:String functions 1
 369+!! text
 370+function run() {
 371+ return lc( 'FOO' ) == 'foo' & uc( 'foo' ) == 'FOO' &
 372+ ucfirst( 'bar' ) == 'Bar' & urlencode( 'a="b"' ) == "a%3D%22b%22";
 373+}
 374+!! endarticle
 375+
 376+!! test
 377+String functions 1
 378+!! input
 379+{{i:String functions 1|run}}
 380+!! result
 381+<p>1
 382+</p>
 383+!! end
 384+
 385+!! article
 386+Module:String functions 2
 387+!! text
 388+function run() {
 389+ return strlen( "тест" ) == 4 & substr( "слово", 1, 2 ) == "ло" &
 390+ strreplace( "abcd", 'bc', 'ad' ) == 'aadd';
 391+}
 392+!! endarticle
 393+
 394+!! test
 395+String functions 2
 396+!! input
 397+{{i:String functions 2|run}}
 398+!! result
 399+<p>1
 400+</p>
 401+!! end
 402+
 403+!! article
 404+Module:split/join
 405+!! text
 406+function run() {
 407+ return join( '!', split( ':', 'a:b:c:d' ) ) + join( ' ', '', 'e', 'f' );
 408+}
 409+!! endarticle
 410+
 411+!! test
 412+split()/join()
 413+!! input
 414+{{i:split/join|run}}
 415+!! result
 416+<p>a!b!c!d e f
 417+</p>
 418+!! end
 419+
 420+!! article
 421+Module:isset/delete
 422+!! text
 423+function run() {
 424+ a = null;
 425+ b = 1;
 426+ delete( b );
 427+ return 'a: ' + isset( a ) + '; b: ' + int( isset( b ) );
 428+}
 429+!! endarticle
 430+
 431+!! test
 432+isset/delete
 433+!! input
 434+{{i:isset/delete|run}}
 435+!! result
 436+<p>a: 1; b: 0
 437+</p>
 438+!! end
 439+
 440+!! article
 441+Module:in/contains
 442+!! text
 443+function run() {
 444+ return int( "a" in "b" + "c" in "cd" + "foobar" contains "oo" + "foobar" contains "baz" );
 445+}
 446+!! endarticle
 447+
 448+!! test
 449+in/contains
 450+!! input
 451+{{i:in/contains|run}}
 452+!! result
 453+<p>2
 454+</p>
 455+!! end
 456+
 457+#
 458+## Lists
 459+#
 460+
 461+!! article
 462+Module:Lists: basics
 463+!! text
 464+function run() {
 465+ a = [ b = "a", b = "b", b = "c" ];
 466+ return a[1] + b;
 467+}
 468+!! endarticle
 469+
 470+!! test
 471+Lists: basics
 472+!! input
 473+{{i:Lists: basics|run}}
 474+!! result
 475+<p>bc
 476+</p>
 477+!! end
 478+
 479+!! article
 480+Module:Lists: foreach
 481+!! text
 482+function run() {
 483+ a = [ 1, 2, 3, 4, 5 ];
 484+ for( n in a )
 485+ append n * n + "\n\n";
 486+}
 487+!! endarticle
 488+
 489+!! test
 490+Lists: foreach
 491+!! input
 492+{{i:Lists: foreach|run}}
 493+!! result
 494+<p>1
 495+</p><p>4
 496+</p><p>9
 497+</p><p>16
 498+</p><p>25
 499+</p>
 500+!! end
 501+
 502+!! article
 503+Module:List merging
 504+!! text
 505+function run() {
 506+ for( element in [ 7, 4 ] + [ 2, 8 ] + 1 )
 507+ append string( element );
 508+}
 509+!! endarticle
 510+
 511+!! test
 512+List merging
 513+!! input
 514+{{i:List merging|run}}
 515+!! result
 516+<p>74281
 517+</p>
 518+!! end
 519+
 520+!! article
 521+Module:Lists: loop control (break/continue)
 522+!! text
 523+function run() {
 524+ a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
 525+ for( e in a ) {
 526+ if( e >= 6 & e < 9 )
 527+ continue;
 528+ append string( e );
 529+ }
 530+ for( e in a ) {
 531+ if( e == 3 )
 532+ break;
 533+ append e;
 534+ }
 535+}
 536+!! endarticle
 537+
 538+!! test
 539+Lists: loop control (break/continue)
 540+!! input
 541+{{i:Lists: loop control (break/continue)|run}}
 542+!! result
 543+<p>12345912
 544+</p>
 545+!! end
 546+
 547+!! article
 548+Module:Lists: changing value of an element
 549+!! text
 550+function run() {
 551+ a = [ [ 2, 3 ], [ 5, 6 ], 7 ];
 552+ a[1][0] = 3;
 553+ a[0][] = 1;
 554+ return a;
 555+}
 556+!! endarticle
 557+
 558+!! test
 559+Lists: changing value of an element
 560+!! input
 561+{{i:Lists: changing value of an element|run}}
 562+!! result
 563+<p>2
 564+3
 565+1
 566+3
 567+6
 568+7
 569+</p>
 570+!! end
 571+
 572+!! article
 573+Module:Lists: isset
 574+!! text
 575+function run() {
 576+ lst = [ 'a', 'b', 'c' ];
 577+ return isset( lst[1] ) + isset( lst[2] ) + isset( list[3] );
 578+}
 579+!! endarticle
 580+
 581+!! test
 582+Lists: isset
 583+!! input
 584+{{i:Lists: isset|run}}
 585+!! result
 586+<p>2
 587+</p>
 588+!! end
 589+
 590+!! article
 591+Module:Associated arrays: basics
 592+!! text
 593+function run() {
 594+ a = { "a" : 2, "b" : 13 };
 595+ a["c"] = 21;
 596+
 597+ for( k : v in a ) {
 598+ append k + " = " + v + "\n\n";
 599+ }
 600+}
 601+!! endarticle
 602+
 603+!! test
 604+Associated arrays: basics
 605+!! input
 606+{{i:Associated arrays: basics|run}}
 607+!! result
 608+<p>a = 2
 609+</p><p>b = 13
 610+</p><p>c = 21
 611+</p>
 612+!! end
 613+
 614+!! article
 615+Module:Mixed arrays
 616+!! text
 617+function run() {
 618+ a["b"][][]["c"] = 11;
 619+ return a["b"][0][0]["c"];
 620+}
 621+!! endarticle
 622+
 623+!! test
 624+Mixed arrays
 625+!! input
 626+{{i:Mixed arrays|run}}
 627+!! result
 628+<p>11
 629+</p>
 630+!! end
 631+
Property changes on: trunk/extensions/WikiScripts/interpreterTests.txt
___________________________________________________________________
Added: svn:eol-style
1632 + native
Index: trunk/extensions/WikiScripts/Hooks.php
@@ -0,0 +1,204 @@
 2+<?php
 3+/**
 4+ * Built-in scripting language for MediaWiki: hooks.
 5+ * Copyright (C) 2009-2011 Victor Vasiliev <vasilvv@gmail.com>
 6+ * http://www.mediawiki.org/
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License along
 19+ * with this program; if not, write to the Free Software Foundation, Inc.,
 20+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 21+ * http://www.gnu.org/copyleft/gpl.html
 22+ */
 23+
 24+/**
 25+ * Hooks for InlineScripts extension.
 26+ */
 27+class ISHooks {
 28+ /**
 29+ * Returns the interpreter for a given parser.
 30+ *
 31+ * @static
 32+ * @return InlineScriptInterpreter
 33+ */
 34+ public static function getInterpreter( $parser ) {
 35+ if( !isset( $parser->is_interpreter ) || !$parser->is_interpreter ) {
 36+ $parser->is_interpreter = new ISInterpreter( $parser );
 37+ }
 38+ return $parser->is_interpreter;
 39+ }
 40+
 41+ /**
 42+ * Register parser hooks.
 43+ * @param $parser Parser
 44+ */
 45+ public static function setupParserHook( &$parser ) {
 46+ $parser->setFunctionHook( 'i', 'ISHooks::callHook', SFH_NO_HASH | SFH_OBJECT_ARGS );
 47+ return true;
 48+ }
 49+
 50+ /**
 51+ * Called when interpreter is to be reset.
 52+ *
 53+ * @static
 54+ * @param $parser Parser
 55+ * @return bool
 56+ */
 57+ public static function clearState( &$parser ) {
 58+ $parser->is_interpreter = null;
 59+ return true;
 60+ }
 61+
 62+ /**
 63+ * Adds scriptlinks table to parser tests.
 64+ */
 65+ public static function addTestTables( &$tables ) {
 66+ $tables[] = 'scriptlinks';
 67+ return true;
 68+ }
 69+
 70+ /**
 71+ * Handles the {{i:module|func}} construction.
 72+ *
 73+ * @static
 74+ * @param $parser Parser
 75+ * @param $frame
 76+ * @param $args
 77+ * @return string
 78+ */
 79+ public static function callHook( &$parser, $frame, $args ) {
 80+ wfProfileIn( __METHOD__ );
 81+ $i = self::getInterpreter( $parser );
 82+
 83+ try {
 84+ if( count( $args ) < 2 ) {
 85+ throw new ISTransclusionException( 'nofunction' );
 86+ }
 87+
 88+ $moduleName = $parser->mStripState->unstripBoth( array_shift( $args ) );
 89+ $funcName = $frame->expand( array_shift( $args ) );
 90+ foreach( $args as &$arg ) {
 91+ $arg = $frame->expand( $arg );
 92+ }
 93+
 94+ $result = $i->invokeUserFunctionFromWikitext( $moduleName, $funcName, $args, $frame );
 95+ } catch( ISException $e ) {
 96+ $msg = $e->getMessage();
 97+ wfProfileOut( __METHOD__ );
 98+ return "<strong class=\"error\">{$msg}</strong>";
 99+ }
 100+
 101+ wfProfileOut( __METHOD__ );
 102+ return trim( $result );
 103+ }
 104+
 105+ /**
 106+ * Overrides the standard view for modules. Enables syntax highlighting when
 107+ * possible.
 108+ *
 109+ * @static
 110+ * @param $text
 111+ * @param $title Title
 112+ * @param $output OutputPage
 113+ * @return bool
 114+ */
 115+ public static function handleScriptView( $text, $title, $output ) {
 116+ global $wgInlineScriptsUseGeSHi;
 117+
 118+ if( $title->getNamespace() == NS_MODULE ) {
 119+ if( $wgInlineScriptsUseGeSHi ) {
 120+ $geshi = SyntaxHighlight_GeSHi::prepare( $text, 'wikiscript' );
 121+ $geshi->set_language_path( dirname( __FILE__ ) . '/geshi' );
 122+ $geshi->set_language( 'wikiscript' );
 123+ if( $geshi instanceof GeSHi && !$geshi->error() ) {
 124+ $code = $geshi->parse_code();
 125+ if( $code ) {
 126+ $output->addHeadItem( "source-wikiscript", SyntaxHighlight_GeSHi::buildHeadItem( $geshi ) );
 127+ $output->addHTML( "<div dir=\"ltr\">{$code}</div>" );
 128+ return false;
 129+ }
 130+ }
 131+ }
 132+
 133+ // No GeSHi, or GeSHi can't parse it, use plain <pre>
 134+ $output->addHTML( "<pre class=\"mw-code mw-script\" dir=\"ltr\">\n" );
 135+ $output->addHTML( htmlspecialchars( $text ) );
 136+ $output->addHTML( "\n</pre>\n" );
 137+ return false;
 138+ } else {
 139+ return true;
 140+ }
 141+ }
 142+
 143+ /**
 144+ * Indicates that modules are not wikitext.
 145+ */
 146+ public static function isWikitextPage( $title, &$result ) {
 147+ if( $title->getNamespace() == NS_MODULE ) {
 148+ $result = false;
 149+ return false;
 150+ }
 151+ return true;
 152+ }
 153+
 154+ /**
 155+ * Adds report of number of evaluations by the single wikitext page.
 156+ *
 157+ * @static
 158+ * @param $parser Parser
 159+ * @param $report
 160+ * @return bool
 161+ */
 162+ public static function reportLimits( $parser, &$report ) {
 163+ global $wgInlineScriptsLimits;
 164+ $i = self::getInterpreter( $parser );
 165+ $report .=
 166+ "Inline scripts parser evaluations: {$i->mEvaluations}/{$wgInlineScriptsLimits['evaluations']}\n" .
 167+ "Inline scripts AST maximal depth: {$i->mMaxRecursion}/{$wgInlineScriptsLimits['depth']}\n";
 168+ return true;
 169+ }
 170+
 171+ /**
 172+ * Adds the module namespaces.
 173+ */
 174+ public static function addCanonicalNamespaces( &$list ) {
 175+ $list[NS_MODULE] = 'Module';
 176+ $list[NS_MODULE_TALK] = 'Module_talk';
 177+ return true;
 178+ }
 179+
 180+ public static function validateScript( $editor, $text, $section, &$error ) {
 181+ global $wgUser;
 182+ $title = $editor->mTitle;
 183+
 184+ if( $title->getNamespace() == NS_MODULE ) {
 185+ $errors = ISInterpreter::getSyntaxErrors( $title->getText(), $text );
 186+ if( !$errors ) {
 187+ return true;
 188+ }
 189+
 190+ $errmsg = wfMsgExt( 'inlinescripts-error', array( 'parsemag' ), array( count( $errors ) ) );
 191+ $errlines = '* ' . implode( "\n* ", array_map( 'wfEscapeWikiText', $errors ) );
 192+ $error = <<<HTML
 193+<div class="errorbox">
 194+{$errmsg}
 195+{$errlines}
 196+</div>
 197+<br clear="all" />
 198+HTML;
 199+
 200+ return true;
 201+ }
 202+
 203+ return true;
 204+ }
 205+}
Index: trunk/extensions/WikiScripts/LinksUpdate.php
@@ -0,0 +1,199 @@
 2+<?php
 3+/**
 4+ * Built-in scripting language for MediaWiki: link updating code
 5+ * Copyright (C) 2011 Victor Vasiliev <vasilvv@gmail.com> et al
 6+ * Based on MediaWiki file LinksUpdate.php
 7+ * http://www.mediawiki.org/
 8+ *
 9+ * This program is free software; you can redistribute it and/or modify
 10+ * it under the terms of the GNU General Public License as published by
 11+ * the Free Software Foundation; either version 2 of the License, or
 12+ * (at your option) any later version.
 13+ *
 14+ * This program is distributed in the hope that it will be useful,
 15+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 17+ * GNU General Public License for more details.
 18+ *
 19+ * You should have received a copy of the GNU General Public License along
 20+ * with this program; if not, write to the Free Software Foundation, Inc.,
 21+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 22+ * http://www.gnu.org/copyleft/gpl.html
 23+ */
 24+
 25+if( !defined( 'MEDIAWIKI' ) )
 26+ die();
 27+
 28+
 29+/**
 30+ * Class that contains hooks related to tracking links to scripts and invalidating
 31+ * pages on script change.
 32+ */
 33+class ISLinksUpdateHooks {
 34+ /**
 35+ * Appends script links to the output.
 36+ */
 37+ public static function appendToOutput( &$parser, &$text ) {
 38+ if( isset( $parser->is_interpreter ) ) {
 39+ $parser->mOutput->is_links = $parser->is_interpreter->mUsedModules;
 40+ }
 41+ return true;
 42+ }
 43+
 44+ /**
 45+ * Runs the link updater.
 46+ */
 47+ public static function updateLinks( &$update ) {
 48+ $output = $update->mParserOutput;
 49+ if( isset( $output->is_links ) ) {
 50+ $new = $output->is_links;
 51+ } else {
 52+ $new = array();
 53+ }
 54+
 55+ $isupdate = new ISLinksUpdate( $update, $new );
 56+ $isupdate->run();
 57+ return true;
 58+ }
 59+
 60+ /**
 61+ * Purges cache for all the pages where the script is used.
 62+ */
 63+ public static function purgeCache( &$article, &$editInfo, $changed ) {
 64+ global $wgDeferredUpdateList;
 65+
 66+ if( $article->mTitle->getNamespace() == NS_MODULE ) {
 67+ // Invalidate the script cache
 68+ ISInterpreter::invalidateModule( $article->mTitle );
 69+
 70+ // Invalidate caches of articles which include the script
 71+ $wgDeferredUpdateList[] = new HTMLCacheUpdate( $article->mTitle, 'scriptlinks' );
 72+ }
 73+
 74+ return true;
 75+ }
 76+
 77+ /**
 78+ * Adds scriptlinks to the list of tables supported by BacklinkCache.
 79+ */
 80+ public static function getBacklinkCachePrefix( $table, &$prefix ) {
 81+ if( $table == 'scriptlinks' ) {
 82+ $prefix = 'sl';
 83+ return false;
 84+ } else {
 85+ return true;
 86+ }
 87+ }
 88+
 89+ /**
 90+ * Adds scriptlinks to the list of tables supported by BacklinkCache.
 91+ */
 92+ public static function getBacklinkCacheConditions( $table, $title, &$conds ) {
 93+ if( $table == 'scriptlinks' ) {
 94+ $conds = array(
 95+ 'sl_to' => $title->getDBkey(),
 96+ 'page_id=sl_from'
 97+ );
 98+ return false;
 99+ } else {
 100+ return true;
 101+ }
 102+ }
 103+}
 104+
 105+/**
 106+ * A class that updates links on scripts like phase3/includes/LinksUpdate.php does that
 107+ * with templates.
 108+ */
 109+class ISLinksUpdate {
 110+ var $mUpdate, $mId, $mNew;
 111+
 112+ public function __construct( $update, $new ) {
 113+ $this->mUpdate = $update;
 114+ $this->mId = $update->mId;
 115+ $this->mNew = $new;
 116+ }
 117+
 118+ public function run() {
 119+ global $wgUseDumbLinkUpdate;
 120+
 121+ wfProfileIn( __METHOD__ );
 122+
 123+ if( $wgUseDumbLinkUpdate ) {
 124+ $this->mUpdate->dumbTableUpdate( 'scriptlinks', $this->getScriptInsertions(), 'sl_from' );
 125+ } else {
 126+ $existing = $this->getExistingScripts();
 127+ $this->mUpdate->incrTableUpdate( 'scriptlinks', 'sl', $this->getScriptDeletions( $existing ),
 128+ $this->getScriptInsertions( $existing ) );
 129+ }
 130+
 131+ if( $this->mUpdate->mRecursive && $this->mUpdate->mTitle->getNamespace() == NS_MODULE ) {
 132+ $this->queueRecursiveJobs();
 133+ }
 134+
 135+ wfProfileOut( __METHOD__ );
 136+ }
 137+
 138+ protected function getExistingScripts() {
 139+ $result = array();
 140+
 141+ $res = $this->mUpdate->mDb->select( 'scriptlinks', array( 'sl_to' ),
 142+ array( 'sl_from' => $this->mId ), __METHOD__, $this->mUpdate->mOptions );
 143+ foreach ( $res as $row ) {
 144+ $result[] = $row->sl_to;
 145+ }
 146+
 147+ return $result;
 148+ }
 149+
 150+ protected function getScriptInsertions( $existing = array() ) {
 151+ $result = array();
 152+
 153+ foreach( array_diff( $this->mNew, $existing ) as $module ) {
 154+ $result[] = array(
 155+ 'sl_from' => $this->mId,
 156+ 'sl_to' => $module,
 157+ );
 158+ }
 159+
 160+ return $result;
 161+ }
 162+
 163+ protected function getScriptDeletions( $existing = array() ) {
 164+ $result = array();
 165+
 166+ foreach( array_diff( $existing, $this->mNew ) as $module ) {
 167+ $result[] = array(
 168+ 'sl_from' => $this->mId,
 169+ 'sl_to' => $module,
 170+ );
 171+ }
 172+
 173+ return $result;
 174+ }
 175+
 176+ protected function queueRecursiveJobs() {
 177+ global $wgUpdateRowsPerJob;
 178+ wfProfileIn( __METHOD__ );
 179+
 180+ $cache = $this->mUpdate->mTitle->getBacklinkCache();
 181+ $batches = $cache->partition( 'scriptlinks', $wgUpdateRowsPerJob );
 182+ if ( !$batches ) {
 183+ wfProfileOut( __METHOD__ );
 184+ return;
 185+ }
 186+ $jobs = array();
 187+ foreach ( $batches as $batch ) {
 188+ list( $start, $end ) = $batch;
 189+ $params = array(
 190+ 'table' => 'scriptlinks',
 191+ 'start' => $start,
 192+ 'end' => $end,
 193+ );
 194+ $jobs[] = new RefreshLinksJob2( $this->mUpdate->mTitle, $params );
 195+ }
 196+ Job::batchInsert( $jobs );
 197+
 198+ wfProfileOut( __METHOD__ );
 199+ }
 200+}
Index: trunk/extensions/WikiScripts/InlineScripts.php
@@ -0,0 +1,130 @@
 2+<?php
 3+/**
 4+ * Built-in scripting language for MediaWiki.
 5+ * Copyright (C) 2009-2011 Victor Vasiliev <vasilvv@gmail.com>
 6+ * http://www.mediawiki.org/
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License along
 19+ * with this program; if not, write to the Free Software Foundation, Inc.,
 20+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 21+ * http://www.gnu.org/copyleft/gpl.html
 22+ */
 23+
 24+if( !defined( 'MEDIAWIKI' ) )
 25+ die();
 26+
 27+$wgExtensionCredits['parserhook']['InlineScripts'] = array(
 28+ 'path' => __FILE__,
 29+ 'name' => 'InlineScripts',
 30+ 'author' => 'Victor Vasiliev',
 31+ 'descriptionmsg' => 'inlinescripts-desc',
 32+ 'url' => 'http://www.mediawiki.org/wiki/Extension:InlineScripts',
 33+);
 34+
 35+$dir = dirname(__FILE__) . '/';
 36+$wgExtensionMessagesFiles['InlineScripts'] = $dir . 'i18n/Messages.php';
 37+$wgExtensionMessagesFiles['InlineScriptsMagic'] = $dir . 'i18n/Magic.php';
 38+$wgExtensionMessagesFiles['InlineScriptsNamespaces'] = $dir . 'i18n/Namespaces.php';
 39+
 40+$wgAutoloadClasses['ISHooks'] = $dir . '/Hooks.php';
 41+$wgAutoloadClasses['ISLinksUpdateHooks'] = $dir . '/LinksUpdate.php';
 42+
 43+$wgAutoloadClasses['ISInterpreter'] = $dir . 'interpreter/Interpreter.php';
 44+$wgAutoloadClasses['ISScanner'] = $dir . 'interpreter/Scanner.php';
 45+$wgAutoloadClasses['ISLRParser'] = $dir . 'interpreter/LRParser.php';
 46+
 47+$wgParserTestFiles[] = $dir . 'interpreterTests.txt';
 48+$wgHooks['ParserFirstCallInit'][] = 'ISHooks::setupParserHook';
 49+$wgHooks['ParserLimitReport'][] = 'ISHooks::reportLimits';
 50+$wgHooks['ParserClearState'][] = 'ISHooks::clearState';
 51+$wgHooks['ParserTestTables'][] = 'ISHooks::addTestTables';
 52+
 53+$wgHooks['CanonicalNamespaces'][] = 'ISHooks::addCanonicalNamespaces';
 54+$wgHooks['ArticleViewCustom'][] = 'ISHooks::handleScriptView';
 55+$wgHooks['TitleIsWikitextPage'][] = 'ISHooks::isWikitextPage';
 56+$wgHooks['EditFilter'][] = 'ISHooks::validateScript';
 57+
 58+$wgHooks['LinksUpdate'][] = 'ISLinksUpdateHooks::updateLinks';
 59+$wgHooks['ArticleEditUpdates'][] = 'ISLinksUpdateHooks::purgeCache';
 60+$wgHooks['ParserAfterTidy'][] = 'ISLinksUpdateHooks::appendToOutput';
 61+$wgHooks['BacklinkCacheGetPrefix'][] = 'ISLinksUpdateHooks::getBacklinkCachePrefix';
 62+$wgHooks['BacklinkCacheGetConditions'][] = 'ISLinksUpdateHooks::getBacklinkCacheConditions';
 63+
 64+/** Configuration */
 65+
 66+/**
 67+ * Script namespace numbers. Should be redefined before
 68+ * the inlcusion of the extension.
 69+ */
 70+if( !isset( $wgScriptsNamespaceNumbers ) ) {
 71+ $wgScriptsNamespaceNumbers = array(
 72+ 'Module' => 20,
 73+ 'Module_talk' => 21,
 74+ );
 75+}
 76+
 77+/**
 78+ * Different limits of the scripts.
 79+ */
 80+$wgInlineScriptsLimits = array(
 81+ /**
 82+ * Maximal amount of tokens (strings, keywords, numbers, operators,
 83+ * but not whitespace) in a single module to be parsed.
 84+ */
 85+ 'tokens' => 1000000,
 86+
 87+ /**
 88+ * Maximal amount of operations (multiplications, comarsions, function
 89+ * calls) to be done.
 90+ */
 91+ 'evaluations' => 300000,
 92+
 93+ /**
 94+ * Maximal depth of recursion when evaluating the parser tree in a single function. For
 95+ * example 2 + 2 * 2 ** 2 is parsed to (2 + (2 * (2 ** 2))) and needs
 96+ * depth 3 to be parsed.
 97+ */
 98+ 'depth' => 100,
 99+);
 100+
 101+/**
 102+ * Turn on to true if you have linked or copied wikiscripts.php and
 103+ * SyntaxHighlight_GeSHi extension is enabled.
 104+ */
 105+$wgInlineScriptsUseGeSHi = false;
 106+
 107+/**
 108+ * Class of the actual parser. Must implement ISParser interface, as well as
 109+ * static getVersion() method.
 110+ */
 111+$wgInlineScriptsParserClass = 'ISLRParser';
 112+
 113+/**
 114+ * Should be enabled unless you are debugging or just have sado-masochistic
 115+ * attitude towards your server.
 116+ */
 117+$wgInlineScriptsUseCache = true;
 118+
 119+/**
 120+ * Indicates whether the function recursion is enabled. If it is, then users may
 121+ * build a Turing-complete machinge and do nice things like parsers, etc in wikitext!
 122+ */
 123+$wgInlineScriptsAllowRecursion = false;
 124+
 125+/**
 126+ * Maximun call stack depth. Includes functions and invokations of parse() function.
 127+ */
 128+$wgInlineScriptsMaxCallStackDepth = 25;
 129+
 130+define( 'NS_MODULE', $wgScriptsNamespaceNumbers['Module'] );
 131+define( 'NS_MODULE_TALK', $wgScriptsNamespaceNumbers['Module_talk'] );
Property changes on: trunk/extensions/WikiScripts/InlineScripts.php
___________________________________________________________________
Added: svn:eol-style
1132 + native

Status & tagging log