User:RV1971/StackFunctions Source

Extension Source
'''Note: Copy the source code by clicking on "edit" and copying from the edit window. Copying the text as displayed by the browser will not work because, for instance, &amp;lt; will be displayed as &lt;.

 'StackFunctions',  'version' => ExtStackFunctions::$mVersion,  'author' => 'RV1971',  'url' => 'http://www.mediawiki.org/wiki/Extension:StackFunctions',  'description' => 'implements a programming language which is basically PostScript without graphics' );

function wfStackFunctionsLanguageGetMagic( &$magicWords, $langCode ) { $magicWords['sf'] = array( 0, 'sf' ); return true; }

function wfStackFunctions { global $wgParser, $wgExtStackFunctions;

// parser function extension $wgExtStackFunctions = new ExtStackFunctions; $wgParser->setFunctionHook( 'sf', 			     array( &$wgExtStackFunctions, 'sf_func' ) );

// XML-style extension $wgParser->setHook( "sf", 		     array( &$wgExtStackFunctions, 'sf_tag' ) ); }

class ExtStackFunctions { public static $mVersion = "0.55";

private $mParser; private $mStack;

private $mStatusDict; private $mSystemDict; private $mUserDict = array; private $mDictStack; private $mCurrentDict; private $mRevDictKeys;

private $mExitFlag = false; private $mPrologPages = array; private $mStartTime; function __construct {     wfProfileIn( __METHOD__ );

$this->mStatusDict = array;

foreach( MagicWord::$mVariableIDs as $ID ) $this->mStatusDict[ $ID ] = array( "v" => op_magic, 					  "t" => "p",					   "a" => $ID );

$this->mSystemDict = array(        "abs" => array( "v" => "op_abs", "t" => "p", "a" => null ),	"add" => array( "v" => "op_add", "t" => "p", "a" => null ),	"aload" => array( "v" => "op_aload", "t" => "p", "a" => null ),	"anchorsearch" => array( "v" => "op_anchorsearch", "t" => "p", "a" => null ),	"and" => array( "v" => "op_and", "t" => "p", "a" => null ),	"array" => array( "v" => "op_array", "t" => "p", "a" => null ),	"astore" => array( "v" => "op_astore", "t" => "p", "a" => null ),	"atan" => array( "v" => "op_atan", "t" => "p", "a" => null ),	"begin" => array( "v" => "op_begin", "t" => "p", "a" => null ),	"bind" => array( "v" => "op_bind", "t" => "p", "a" => null ),	"bitshift" => array( "v" => "op_bitshift", "t" => "p", "a" => null ),	"ceiling" => array( "v" => "op_ceiling", "t" => "p", "a" => null ),	"clear" => array( "v" => "op_clear", "t" => "p", "a" => null ),	"cleardictstack" => array( "v" => "op_cleardictstack", "t" => "p", "a" => null ), "cleartomark" => array( "v" => "op_cleartomark", "t" => "p", "a" => null ), "copy" => array( "v" => "op_copy", "t" => "p", "a" => null ), "cos" => array( "v" => "op_cos", "t" => "p", "a" => null ), "count" => array( "v" => "op_count", "t" => "p", "a" => null ), "countdictstack" => array( "v" => "op_countdictstack", "t" => "p", "a" => null ), "counttomark" => array( "v" => "op_counttomark", "t" => "p", "a" => null ), "currentdict" => array( "v" => "op_currentdict", "t" => "p", "a" => null ), "cvi" => array( "v" => "op_cvi", "t" => "p", "a" => null ), "cvlit" => array( "v" => "op_cvlit", "t" => "p", "a" => null ), "cvn" => array( "v" => "op_cvn", "t" => "p", "a" => null ), "cvr" => array( "v" => "op_cvr", "t" => "p", "a" => null ), "cvs" => array( "v" => "op_cvs", "t" => "p", "a" => null ), "cvx" => array( "v" => "op_cvx", "t" => "p", "a" => null ), "def" => array( "v" => "op_def", "t" => "p", "a" => null ), "dict" => array( "v" => "op_dict", "t" => "p", "a" => null ), "dictstack" => array( "v" => "op_dictstack", "t" => "p", "a" => null ), "div" => array( "v" => "op_div", "t" => "p", "a" => null ), "dup" => array( "v" => "op_dup", "t" => "p", "a" => null ), "end" => array( "v" => "op_end", "t" => "p", "a" => null ), "eq" => array( "v" => "op_eq", "t" => "p", "a" => null ), "exch" => array( "v" => "op_exch", "t" => "p", "a" => null ), "exec" => array( "v" => "op_exec", "t" => "p", "a" => null ), "exit" => array( "v" => "op_exit", "t" => "p", "a" => null ), "exp" => array( "v" => "op_exp", "t" => "p", "a" => null ), "false" => false, "floor" => array( "v" => "op_floor", "t" => "p", "a" => null ), "for" => array( "v" => "op_for", "t" => "p", "a" => null ), "forall" => array( "v" => "op_forall", "t" => "p", "a" => null ), "ge" => array( "v" => "op_ge", "t" => "p", "a" => null ), "gt" => array( "v" => "op_gt", "t" => "p", "a" => null ), "get" => array( "v" => "op_get", "t" => "p", "a" => null ), "getinterval" => array( "v" => "op_getinterval", "t" => "p", "a" => null ), "idiv" => array( "v" => "op_idiv", "t" => "p", "a" => null ), "if" => array( "v" => "op_if", "t" => "p", "a" => null ), "ifelse" => array( "v" => "op_ifelse", "t" => "p", "a" => null ), "index" => array( "v" => "op_index", "t" => "p", "a" => null ), "known" => array( "v" => "op_known", "t" => "p", "a" => null ), "languagelevel" => 3, "le" => array( "v" => "op_le", "t" => "p", "a" => null ), "length" => array( "v" => "op_length", "t" => "p", "a" => null ), "log" => array( "v" => "op_log", "t" => "p", "a" => null ), "load" => array( "v" => "op_load", "t" => "p", "a" => null ), "loop" => array( "v" => "op_loop", "t" => "p", "a" => null ), "ln" => array( "v" => "op_ln", "t" => "p", "a" => null ), "lt" => array( "v" => "op_lt", "t" => "p", "a" => null ), "mark" => array( "v" => "op_mark", "t" => "p", "a" => null ), "mod" => array( "v" => "op_mod", "t" => "p", "a" => null ), "mul" => array( "v" => "op_mul", "t" => "p", "a" => null ), "ne" => array( "v" => "op_ne", "t" => "p", "a" => null ), "neg" => array( "v" => "op_neg", "t" => "p", "a" => null ), "not" => array( "v" => "op_not", "t" => "p", "a" => null ), "null" => null, "or" => array( "v" => "op_or", "t" => "p", "a" => null ), "parsetemplate" => array( "v" => "op_parsetemplate", "t" => "p", "a" => null ), "pop" => array( "v" => "op_pop", "t" => "p", "a" => null ), "product" => "MediaWiki", "prolog" => array( "v" => "op_prolog", "t" => "p", "a" => null ), "pstack" => array( "v" => "op_pstack", "t" => "p", "a" => null ), "put" => array( "v" => "op_put", "t" => "p", "a" => null ), "putinterval" => array( "v" => "op_putinterval", "t" => "p", "a" => null ), "query" => array( "v" => "op_query", "t" => "p", "a" => null ), "rand" => array( "v" => "op_rand", "t" => "p", "a" => null ), "realtime" => array( "v" => "op_realtime", "t" => "p", "a" => null ), "repeat" => array( "v" => "op_repeat", "t" => "p", "a" => null ), "revision" => $revision, "roll" => array( "v" => "op_roll", "t" => "p", "a" => null ), "round" => array( "v" => "op_round", "t" => "p", "a" => null ), "search" => array( "v" => "op_search", "t" => "p", "a" => null ), "serialnumber" => $_SERVER["SERVER_ADDR"], "show" => array( "v" => "op_show", "t" => "p", "a" => null ), "sin" => array( "v" => "op_sin", "t" => "p", "a" => null ), "sqrt" => array( "v" => "op_sqrt", "t" => "p", "a" => null ), "srand" => array( "v" => "op_srand", "t" => "p", "a" => null ), "store" => array( "v" => "op_store", "t" => "p", "a" => null ), "string" => array( "v" => "op_string", "t" => "p", "a" => null ), "sub" => array( "v" => "op_sub", "t" => "p", "a" => null ), "showtemplate" => array( "v" => "op_showtemplate", "t" => "p", "a" => null ), "statusdict" => array( "v" => &$this->mStatusDict, "t" => "d", "a" => null ), "systemdict" => array( "v" => &$this->mSystemDict, "t" => "d", "a" => null ), "true" => true, "truncate" => array( "v" => "op_truncate", "t" => "p", "a" => null ), "type" => array( "v" => "op_type", "t" => "p", "a" => null ), "undef" => array( "v" => "op_undef", "t" => "p", "a" => null ), "userdict" => array( "v" => &$this->mUserDict, "t" => "d", "a" => null ), "usertime" => array( "v" => "op_usertime", "t" => "p", "a" => null ), "version" => ExtStackFunctions::$mVersion, "where" => array( "v" => "op_where", "t" => "p", "a" => null ), "xcheck" => array( "v" => "op_xcheck", "t" => "p", "a" => null ), "xor" => array( "v" => "op_xor", "t" => "p" ) );     $this->op_cleardictstack;

wfProfileOut( __METHOD__ ); }

public function sf_func( &$parser, $value = '' ) {     wfProfileIn( __METHOD__ );

$this->mParser =& $parser;

try { $this->mStartTime = microtime( true ); $result = $this->execute( $this->parse( $value ) ); } catch( Exception $e ) { $result = "StackFunctions error '". $e->getMessage . "''' in <". "pre>". $e->getTraceAsString. "" . $this->op_pstack. "''";     }

wfProfileOut( __METHOD__ );

//     wfDebug( $result );

return $result; }

public function sf_tag( $input, $params, &$parser ) {     wfProfileIn( __METHOD__ );

$this->mParser =& $parser;

try { $this->mStartTime = microtime( true ); $result = $this->execute( $this->parseraw( $input ) ); } catch( Exception $e ) { $result = "StackFunctions error '". $e->getMessage . "''' in <". "pre>". $e->getTraceAsString. "" . $this->op_pstack. "''";     }

wfProfileOut( __METHOD__ );

//     wfDebug( $result );

return $parser->recursiveTagParse( $result ); }

private static function item_with_type( $arg ) {     if( is_numeric( $arg ) ) if( strpos( $arg, "." ) === false ) return (int)$arg; else return (float)$arg; elseif( $arg[0] == "/" ) return array( "t" => "n", "v" => substr( $arg, 1 ) ); else return array( "t" => "o", "v" => $arg ); } private function parse( $value = '' ) {     $args = array; $substring = "";

$length = strlen( $value ); for( $i = 0; $i < $length; $i++ ) {	 $c = $value[$i];

switch( $c ) {	   case ' ' : case "\n" : case "\r" : case "\t" : if( $substring > "" ) {		 $args[] = $this->item_with_type( $substring ); $substring = ""; }	     break;

case "%" : $i = strpos( $value, "\n", $i ); if( $i === false ) $i = $length; break;

case '(' :	     if( $substring > "" )		{		  $args[] = $this->item_with_type( $substring );		  $substring = "";		}

$spar = 1; for( $j = $i + 1; $j < $length; $j++ ) {		 $c = $value[$j]; if( $c == '(' )		   $spar++;		  elseif( $c == ')' ) {		     $spar--; if( !$spar ) break; }		}	     $args[] = stripcslashes( $this->mParser->preprocess2( substr( $value, $i + 1, $j - $i - 1 ) ) ); $i = $j; break;

case '{' : if( $substring > "" ) {		 $args[] = $this->item_with_type( $substring ); $substring = ""; }

$par = 1; $spar = 0; for( $j = $i + 1; $j < $length; $j++ ) {		 $c = $value[$j]; if( $c == '(' )		   $spar++;		  elseif( $c == ')' ) $spar--; elseif( !$spar ) if( $c == '{' ) $par++; elseif( $c == '}' ) {			$par--; if( !$par ) break; }		}	     $args[] = array( "t" => "x", 			       "v" => $this->parse( substr( $value, $i + 1, $j - $i - 1 ) ) ); $i = $j; break;

case '/' : if( $substring > "" ) $args[] = $this->item_with_type( $substring ); $substring = $c; break;

case '[' : if( $substring > "" ) {		 $args[] = $this->item_with_type( $substring ); $substring = ""; }	     $args[] = array( "t" => "m" ); break; case ']' : if( $substring > "" ) {		 $args[] = $this->item_with_type( $substring ); $substring = ""; }	     $args[] = array( "t" => "A" ); break;

case '&' : if( substr( $value, $i + 1, 7 ) == "lt;&lt;" ) {		 if( $substring > "" ) {		     $args[] = $this->item_with_type( $substring ); $substring = ""; }		 $args[] = array( "t" => "m" ); $i += 7; }	     else if( substr( $value, $i + 1, 7 ) == "gt;&gt;" ) {		 if( $substring > "" ) {		     $args[] = $this->item_with_type( $substring ); $substring = ""; }		 $args[] = array( "t" => "D" ); $i += 7; }	     break; default : $substring .= $c; }	}     if( $substring > "" ) $args[] = $this->item_with_type( $substring );

return $args; }

// identical to parse except for <<, >> which are assumed not to be preprocessed private function parseraw( $value = '' ) {     $args = array; $substring = "";

$length = strlen( $value ); for( $i = 0; $i < $length; $i++ ) {	 $c = $value[$i];

switch( $c ) {	   case ' ' : case "\n" : case "\r" : case "\t" : if( $substring > "" ) {		 $args[] = $this->item_with_type( $substring ); $substring = ""; }	     break;

case "%" : $i = strpos( $value, "\n", $i ); if( $i === false ) $i = $length; break;

case '(' :	     if( $substring > "" )		{		  $args[] = $this->item_with_type( $substring );		  $substring = "";		}

$spar = 1; for( $j = $i + 1; $j < $length; $j++ ) {		 $c = $value[$j]; if( $c == '(' )		   $spar++;		  elseif( $c == ')' ) {		     $spar--; if( !$spar ) break; }		}	     $args[] = stripcslashes( $this->mParser->preprocess2( substr( $value, $i + 1, $j - $i - 1 ) ) ); $i = $j; break;

case '{' : if( $substring > "" ) {		 $args[] = $this->item_with_type( $substring ); $substring = ""; }

$par = 1; $spar = 0; for( $j = $i + 1; $j < $length; $j++ ) {		 $c = $value[$j]; if( $c == '(' )		   $spar++;		  elseif( $c == ')' ) $spar--; elseif( !$spar ) if( $c == '{' ) $par++; elseif( $c == '}' ) {			$par--; if( !$par ) break; }		}	     $args[] = array( "t" => "x", 			       "v" => $this->parseraw( substr( $value, $i + 1, $j - $i - 1 ) ) ); $i = $j; break;

case '/' : if( $substring > "" ) $args[] = $this->item_with_type( $substring ); $substring = $c; break;

case '[' : if( $substring > "" ) {		 $args[] = $this->item_with_type( $substring ); $substring = ""; }	     $args[] = array( "t" => "m" ); break; case ']' : if( $substring > "" ) {		 $args[] = $this->item_with_type( $substring ); $substring = ""; }	     $args[] = array( "t" => "A" ); break;

case '<' : if( $value[$i + 1] == "<" ) {		 if( $substring > "" ) {		     $args[] = $this->item_with_type( $substring ); $substring = ""; }		 $args[] = array( "t" => "m" ); $i += 1; }	     break; case '>' : if( $value[$i + 1] == ">" ) {		 if( $substring > "" ) {		     $args[] = $this->item_with_type( $substring ); $substring = ""; }		 $args[] = array( "t" => "D" ); $i += 1; }	     break; default : $substring .= $c; }	}     if( $substring > "" ) $args[] = $this->item_with_type( $substring );

return $args; }

private function execute( &$args ) {     $result = ""; // can't use foreach( $args as $i => $arg ) // because on recursive execute, the inner loop // would modify the outer loop $indexes = array_keys( $args ); foreach( $indexes as $i ) {	 $arg =& $args[ $i ]; if( !is_array( $arg ) ) $this->mStack[] =& $args[ $i ]; else switch( $arg[ "t" ] ) {	     case "p" : // predefined operator $result .= call_user_func( array( $this, $arg[ "v" ] ), $arg[ "a" ] ); break;

// operator to resolve case "o" : $key = $arg[ "v" ];

$found = false; foreach( $this->mRevDictKeys as $dictkey ) if( array_key_exists( $key, $this->mDictStack[ $dictkey ] ) ) {		     $val =& $this->mDictStack[ $dictkey ][ $key ]; if( !is_array( $val ) ) $this->mStack[] =& $val; else switch( $val[ "t" ] ) {			 case "p" : $result .= call_user_func( array( $this, $val[ "v" ] ), $val[ "a" ] ); break;

case "x" : $result .= $this->execute( $val[ "v" ] ); break;

default : $this->mStack[] =& $val; }

$found = true; break; }		if( !$found ) throw new Exception( "undefined [$key]" ); break; // array, dictionary, mark, name, executable array case "a" : case "d" : case "m" : case "n" : case "x" : $this->mStack[] =& $args[ $i ]; break; // array construction case "A" : $this->create_array; break; // dictionary construction case "D" : $this->create_dict; break; }

if( $this->mExitFlag ) break; }     return $result; }

private function exec_any( &$any ) {     if( !is_array( $any ) ) $this->mStack[] =& $any; else switch( $any[ "t" ] ) {	 case "p" : return call_user_func( array( $this, $any[ "v" ] ), $any[ "a" ] ); break; case "x" : return $this->execute( $any[ "v" ] ); break; default : $this->mStack[] =& $any; }   }

private function create_array {      $offset = array_search( array( "t" => "m" ), 			      array_reverse( $this->mStack ), true ); if( is_null( $offset ) ) throw new Exception( "stackunderflow" );

if( $offset ) $new_array =& array_splice( $this->mStack, -$offset ); else $new_array = array;

$this->pop;

$this->push_array( $new_array ); }

private function create_dict {      $offset = array_search( array( "t" => "m" ), 			      array_reverse( $this->mStack ), true );

if( is_null( $offset ) ) throw new Exception( "stackunderflow" );

if( $offset ) $dict_data =& array_splice( $this->mStack, -$offset ); else $new_array = array;

$this->pop;

$count = count( $dict_data ); if( $count % 2 ) throw new Exception( "rangecheck" );

$new_dict = array; for( $i = 0; $i < $count; $i += 2 ) {	 $key =& $dict_data[ $i ]; if( is_array( $key ) ) if( $key[ "t" ] == "n" ) $key = $key[ "v" ]; else throw new Exception( "typecheck [$key]" );

$new_dict[ $key ] =& $dict_data[ $i + 1 ]; }     $this->push_dict( $new_dict ); }

private function &find_dict_in_dictstack( $key ) {     foreach( $this->mRevDictKeys as $dictkey ) if( array_key_exists( $key, $this->mDictStack[ $dictkey ] ) ) return $this->mDictStack[ $dictkey ]; }

private function pop {      if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" ); return array_pop( $this->mStack ); }

private function &pop_anyref {      if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" ); end( $this->mStack ); $any =& $this->mStack[ key( $this->mStack ) ]; unset( $this->mStack[ key( $this->mStack ) ] ); return $any; }

private function &pop_array {      if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" ); end( $this->mStack ); $array =& $this->mStack[ key( $this->mStack ) ]; unset( $this->mStack[ key( $this->mStack ) ] );

if( !is_array( $array ) || $array[ "t" ] != "a" ) throw new Exception( "typecheck [$array]" );

return $array; }

private function pop_bool {      if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" ); $bool = array_pop( $this->mStack );

if( !is_bool( $bool ) ) throw new Exception( "typecheck [$bool]" );

return $bool; }

private function &pop_dict {      if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" ); end( $this->mStack ); $dict =& $this->mStack[ key( $this->mStack ) ]; unset( $this->mStack[ key( $this->mStack ) ] );

if( !is_array( $dict ) || $dict[ "t" ] != "d" ) throw new Exception( "typecheck [$dict]" );

return $dict; }

private function pop_int {      if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" ); $int = array_pop( $this->mStack );

if( !is_int( $int ) ) throw new Exception( "typecheck [$int]" );

return $int; }

private function &pop_name {      if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" ); $name = array_pop( $this->mStack );

if( is_string( $name ) ) return $name; elseif( is_array( $name ) && $name[ "t" ] == "n" ) return $name[ "v" ]; else throw new Exception( "typecheck [$name]" ); }

private function pop_num {      if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" ); $num = array_pop( $this->mStack );

if( !(is_int( $num ) || is_float( $num )) ) throw new Exception( "typecheck [$num]" );

return $num; }

private function &pop_proc {      if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" ); end( $this->mStack ); $proc =& $this->mStack[ key( $this->mStack ) ]; unset( $this->mStack[ key( $this->mStack ) ] );

if( !is_array( $proc ) || $proc[ "t" ] != "x" ) throw new Exception( "typecheck [$proc]" );

return $proc; }

private function &pop_string {      if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" ); end( $this->mStack ); $string =& $this->mStack[ key( $this->mStack ) ]; unset( $this->mStack[ key( $this->mStack ) ] );

if( !is_string( $string ) ) throw new Exception( "typecheck [$string]" );

return $string; }

private function push_array( &$v ) {      $this->mStack[] = array( "v" => $v, "t" => "a" ); }

private function push_dict( &$v ) {      $this->mStack[] = array( "v" => &$v, "t" => "d" ); }

private function recursive_bind( &$proc ) {     foreach( $proc[ "v" ] as $i => $op ) {	 if( is_array( $op ) ) switch( $op[ "t" ] ) {		// replace operators case "o" : $key = $op[ "v" ]; foreach( $this->mRevDictKeys as $dictkey ) if( array_key_exists( $key, $this->mDictStack[ $dictkey ] ) ) {		     $any =& $this->mDictStack[ $dictkey ][ $key ]; if( $any[ "t" ] == "p" ) $proc[ "v" ][ $i ] =& $any; break; }		break; // replace executable arrays case "x" : $this->recursive_bind( $proc[ "v" ][ $i ] ); break; }	}   }

private function op_abs { $this->mStack[] = abs( $this->pop_num ); } private function op_add { $this->mStack[] = $this->pop_num + $this->pop_num; }

private function op_aload {     $array =& $this->pop_array; // can't use array_merge // because array_merge copies values instead of references foreach( $array[ "v" ] as $key => $val ) $this->mStack[] =& $array[ "v" ][ $key ]; $this->mStack[] =& $array; }

private function op_anchorsearch {     $seek =& $this->pop_string; $string =& $this->pop_string;

$pos = mb_strpos( $string, $seek );

if( $pos === 0 ) {	 $post = mb_substr( $string, mb_strlen( $seek ) ); if( $post === false ) $post = ""; $this->mStack[] = $post; $this->mStack[] = $seek; $this->mStack[] = true; }     else {	 $this->mStack[] =& $string; $this->mStack[] = false; }   }

private function op_and {      $any2 = $this->pop; $any1 = $this->pop; if( is_bool( $any1 ) && is_bool( $any2 ) ) $this->mStack[] = $any1 && $any2; else if( is_int( $any1 ) && is_int( $any2 ) ) $this->mStack[] = $any1 & $any2; else throw new Exception( "typecheck [$any1, $any2]" ); } private function op_array {     $int = $this->pop_int;

if( $int < 0 ) throw new Exception( "rangecheck" );

if( $int > 0 ) $this->push_array( array_fill( 0, $int, null ) ); else {	   $empty_array = array; $this->push_array( $empty_array ); }   }  private function op_astore {     $array =& $this->pop_array;

$length = count( $array[ "v" ] ); if( count( $this->mStack ) < $length ) throw new Exception( "stackunderflow" );

if( $length ) $array[ "v" ] =& array_splice( $this->mStack, -$length ); else $array[ "v" ] = array;

$this->mStack[] =& $array; } private function op_atan {     $den = $this->pop_num; $num = $this->pop_num; $this->mStack[] = rad2deg( atan2( $num, $den ) ); }

private function op_begin {      $dict =& $this->pop_dict; $this->mDictStack[] =& $dict[ "v" ]; $this->mCurrentDict =& $dict[ "v" ]; $this->mRevDictKeys = array_reverse( array_keys( $this->mDictStack ) ); }

private function op_bind {     end( $this->mStack ); $proc =& $this->mStack[ key( $this->mStack ) ];

if( !is_array( $proc ) || $proc[ "t" ] != "x" ) throw new Exception( "typecheck [$proc]" );

$this->recursive_bind( $proc ); }

private function op_bitshift {     $shift = $this->pop_int; $int1 = $this->pop_int; if( $shift > 0 ) $this->mStack[] = $int1 << $shift; else $this->mStack[] = $int1 >> -$shift; } private function op_ceiling { $this->mStack[] = ceil( $this->pop_num ); } private function op_clear { $this->mStack = array; } private function op_cleardictstack {      global $wgVersion;

$VersionArray = explode( ".", $wgVersion ); $revision = ($VersionArray[ 0 ] * 100 	 + $VersionArray[ 1 ]) * 100 + $VersionArray[ 2 ];

$this->mDictStack = array( 0 => &$this->mSystemDict, 				 1 => &$this->mUserDict ); $this->mCurrentDict =& $this->mUserDict;

$this->mRevDictKeys = array_reverse( array_keys( $this->mDictStack ) ); } private function op_cleartomark {     $offset = array_search( array( "t" => "m" ), 			      array_reverse( $this->mStack ), true );

if( is_null( $offset ) ) throw new Exception( "stackunderflow" );

if( $offset ) array_splice( $this->mStack, -$offset ); $this->pop; } private function op_copy {     $any =& $this->pop_anyref;

if( is_int( $any ) ) {	 if( count( $this->mStack ) < $any ) throw new Exception( "stackunderflow" ); // can't take a slice of the values // because slice copies values instead of references $slice =& array_slice( array_keys( $this->mStack ), -$any );

foreach( $slice as $key ) $this->mStack[] =& $this->mStack[ $key ]; }     elseif( is_string( $any ) ) {	 $string1 =& $this->pop_string; $any =& substr_replace( $any, $string1, 0, mb_strlen( $string1 ) ); $this->mStack[] =& $string1; }     elseif( is_array( $any ) ) switch( $any[ "t" ] ) {	 case "a" : case "x" : $array1 =& $this->pop_array; $length = count( $array1[ "v" ] ); for( $i = 0; $i < $length; $i++ ) $any[ "v" ][ $i ] =& $array1[ "v" ][ $i ]; $this->mStack[] =& $array1; break; case "d" : $dict1 =& $this->pop_dict; foreach( $dict1[ "v" ] as $key => $val ) $any[ "v" ][ $key ] =& $dict1[ "v" ][ $key ]; $this->mStack[] =& $dict1; break; }     else throw new Exception( "typecheck [$any]" ); } private function op_cos { $this->mStack[] = cos( deg2rad( $this->pop_num ) ); } private function op_count { $this->mStack[] = count( $this->mStack ); } private function op_countdictstack { $this->mStack[] = count( $this->mDictStack ); } private function op_counttomark {     $offset = array_search( array( "t" => "m" ), 			      array_reverse( $this->mStack ), true );

if( is_null( $offset ) ) throw new Exception( "stackunderflow" ); $this->mStack[] = $offset; }

private function op_currentdict { $this->push_dict( $this->mCurrentDict ); } private function op_cvi {     $any = $this->pop;

if( is_int( $any ) || is_float( $any ) || is_string( $any ) ) $this->mStack[] = (int)$any; else throw new Exception( "typecheck [$any]" ); }

private function op_cvlit {     $any =& $this->pop_anyref;

if( is_array( $any ) && $any[ "t" ] == "x" ) unset( $any[ "t" ] );

$this->mStack[] =& $any; } private function op_cvn {      $this->mStack[] = array( "v" => $this->pop_string, "t" => "n" ); } private function op_cvr {     $any = $this->pop;

if( is_int( $any ) || is_float( $any ) || is_string( $any ) ) $this->mStack[] = (float)$any; else throw new Exception( "typecheck [$any]" ); }

private function op_cvs {     $string =& $this->pop_string; $any = $this->pop;

if( is_bool( $any ) ) if( $any ) $literal = "true"; else $literal = "false"; elseif( is_array( $any ) ) if( $any[ "t" ] == "n" ) $literal = "/". $any[ "v" ]; else $literal = "--nostringval--"; else $literal = (string)$any; $string =& substr_replace( $string, $literal, 0, mb_strlen( $literal ) ); $this->mStack[] = $string; } private function op_cvx {     $any =& $this->pop_anyref;

if( is_array( $any ) && $any[ "t" ] == "a" ) $any[ "t" ] = "x";

$this->mStack[] =& $any; } private function op_def {     $value =& $this->pop_anyref;

$key = $this->pop_name;

$this->mCurrentDict[ $key ] =& $value; } private function op_dict {     $int = $this->pop_int;

if( $int < 0 ) throw new Exception( "rangecheck" );

$newdict = array;

$this->push_dict( $newdict ); } private function op_dictstack {     $array =& $this->pop_array;

if( count( $this->mDictStack ) > count( $array[ "v" ] ) ) throw new Exception( "rangecheck" );

$i = 0; foreach( $this->mDictStack as $key => $dict ) {	 $array[ $i ] = array( "v" => &$this->mDictStack[ $key ], "t" => "d" ); $subarray[] = array( "v" => &$this->mDictStack[ $key ], "t" => "d" ); $i++; }

$this->push_array( $subarray ); }

private function op_div {     $num2 = $this->pop_num; $num1 = $this->pop_num;

if( $num2 == 0 ) throw new Exception( "undefinedresult" );

$this->mStack[] = (float)($num1 / $num2); } private function op_dup {     if( !count( $this->mStack ) ) throw new Exception( "stackunderflow" );

end( $this->mStack ); $this->mStack[] =& $this->mStack[ key( $this->mStack ) ]; } private function op_end {      if( count( $this->mDictStack ) < 3 ) throw new Exception( "dictstackunderflow" );

array_pop( $this->mDictStack ); end( $this->mDictStack ); $this->mCurrentDict =& $this->mDictStack[ key( $this->mDictStack ) ];

$this->mRevDictKeys = array_reverse( array_keys( $this->mDictStack ) ); }

private function op_eq {     $any2 =& $this->pop_anyref; $any1 =& $this->pop_anyref;

if( is_array( $any1 ) && $any1[ "t" ] == "n" ) $any1 = $any1[ "v" ];

if( is_array( $any2 ) && $any2[ "t" ] == "n" ) $any1 = $any2[ "v" ];

if( (is_int( $any1 ) || is_float( $any1 ))	 && (is_int( $any2 ) || is_float( $any2 )) ) $this->mStack[] = $any1 == $any2; else # string, boolean, ... $this->mStack[] = $any1 === $any2; } private function op_exch {     $any2 =& $this->pop_anyref; $any1 =& $this->pop_anyref; $this->mStack[] =& $any2; $this->mStack[] =& $any1; } private function op_exec {      $any =& $this->pop_anyref; if( !is_array( $any ) ) $this->mStack[] =& $any; else switch( $any[ "t" ] ) {	 case "p" : return call_user_func( array( $this, $any[ "v" ] ), $any[ "a" ] ); break; case "x" : return $this->execute( $any[ "v" ] ); break; default : $this->mStack[] =& $any; }   }  private function op_exit { $this->mExitFlag = true; }

private function op_exp {     $exponent = $this->pop_num; $base = $this->pop_num; $this->mStack[] = pow( $base, $exponent ); }

private function op_floor { $this->mStack[] = floor( $this->pop_num ); } private function op_for {     $proc =& $this->pop_proc; $this->recursive_bind( $proc );

$limit = $this->pop_num; $increment = $this->pop_num; $i = $this->pop_num;

$this->mExitFlag = false; for($i <= $limit; $i += $increment ) {	 $this->mStack[] = $i; $result .= $this->execute( $proc[ "v" ] );

if( $this->mExitFlag ) {	     $this->mExitFlag = false; break; }	}

return $result; } private function op_forall {     $proc =& $this->pop_proc; $this->recursive_bind( $proc );

$any =& $this->pop_anyref;

$this->mExitFlag = false; if( is_string( $any ) ) {	 $len = mb_strlen( $any );

for( $i = 0; $i < $len; $i++ ) {	     $this->mStack[] = mb_substr( $any, $i, 1 ); $result .= $this->execute( $proc[ "v" ] ); if( $this->mExitFlag ) {		 $this->mExitFlag = false; break; }	   }	}      else if( is_array( $any ) ) switch( $any[ "t" ] ) {	 case "a" : case "x" : foreach( $any[ "v" ] as $key => $val ) {	       $this->mStack[] =& $any[ "v" ][ $key ]; $result .= $this->execute( $proc[ "v" ] ); if( $this->mExitFlag ) {		   $this->mExitFlag = false; break; }	     }	    break;

case "d" : foreach( $any[ "v" ] as $key => $val ) {		$this->mStack[] =& $key; $this->mStack[] =& $any[ "v" ][ $key ]; $result .= $this->execute( $proc[ "v" ] ); if( $this->mExitFlag ) {		   $this->mExitFlag = false; break; }	     }	    break; }     else throw new Exception( "typecheck [$any]" );

return $result; } private function op_ge {     $any2 =& $this->pop_anyref; $any1 =& $this->pop_anyref;

if( ((is_int( $any1 ) || is_float( $any1 )) && (is_int( $any2 ) || is_float( $any2 )))	 || (is_string( $any1 ) && is_string( $any2 )) ) $this->mStack[] = $any1 >= $any2; else throw new Exception( "typecheck [$any1, $any2]" ); } private function op_gt {     $any2 =& $this->pop_anyref; $any1 =& $this->pop_anyref;

if( ((is_int( $any1 ) || is_float( $any1 )) && (is_int( $any2 ) || is_float( $any2 )))	 || (is_string( $any1 ) && is_string( $any2 )) ) $this->mStack[] = $any1 > $any2; else throw new Exception( "typecheck [$any1, $any2]" ); } private function op_get {     $index = $this->pop; $any =& $this->pop_anyref;

if( is_string( $any ) ) {	 if( !is_int( $index ) ) throw new Exception( "typecheck [$index]" );

if( $index < 0 || $index > mb_strlen( $any ) - 1 ) throw new Exception( "rangecheck" );

$this->mStack[] = mb_substr( $any, $index, 1 ); }     else if( is_array( $any ) ) switch( $any[ "t" ] ) {	 case "a" : case "x" : if( !is_int( $index ) ) throw new Exception( "typecheck [$index]" ); if( $index < 0 || $index > count( $any[ "v" ] ) - 1 ) throw new Exception( "rangecheck" ); $this->mStack[] =& $any[ "v" ][ $index ]; break;

case "d" : if( $index[ "t" ] == "n" ) $index = $index[ "v" ]; if( !array_key_exists( $index, $any[ "v" ] ) ) throw new Exception( "undefined [" . $index . "]" ); else $this->mStack[] =& $any[ "v" ][ $index ]; }     else throw new Exception( "typecheck [$any]" ); } private function op_getinterval {     $count = $this->pop_int; $index = $this->pop_int; $any =& $this->pop_anyref;

if( is_string( $any ) ) {	 if( $index < 0 || $index + $count > mb_strlen( $any ) ) throw new Exception( "rangecheck" );

$substring = mb_substr( $any, $index, $count ); if( $substring === false ) $substring = "";

$this->mStack[] = $substring; }     else if( is_array( $any ) && ($any[ "t" ] == "a" || $any[ "t" ] == "x") ) {	 if( $index < 0 || $index + $count > count( $any[ "v" ] ) ) throw new Exception( "rangecheck" );

$max = $count + $index; for($index < $max; $index++ ) $subarray[] =& $any[ "v" ][ $index ];

$this->push_array( $subarray ); }     else throw new Exception( "typecheck [$any]" ); } private function op_idiv {     $int2 = $this->pop_int; $int1 = $this->pop_int;

$this->mStack[] = (int)($int1 / $int2); } private function op_if {     $proc =& $this->pop_proc; $bool = $this->pop_bool;

if( $bool ) return $this->execute( $proc[ "v" ] ); } private function op_ifelse {     $proc2 =& $this->pop_proc; $proc1 =& $this->pop_proc; $bool = $this->pop_bool;

if( $bool ) return $this->execute( $proc1[ "v" ] ); else return $this->execute( $proc2[ "v" ] ); } private function op_index {     $n = $this->pop_int;

if( count( $this->mStack ) < $n + 1 ) throw new Exception( "stackunderflow" );

$rkeys = array_reverse( array_keys( $this->mStack ) );

$this->mStack[] =& $this->mStack[ $rkeys[ $n ] ]; } private function op_known {     $key = $this->pop_name; $dict =& $this->pop_dict; $this->mStack[] = array_key_exists( $key, $dict[ "v" ] ); } private function op_le {     $any2 =& $this->pop_anyref; $any1 =& $this->pop_anyref;

if( ((is_int( $any1 ) || is_float( $any1 )) && (is_int( $any2 ) || is_float( $any2 )))	 || (is_string( $any1 ) && is_string( $any2 )) ) $this->mStack[] = $any1 <= $any2; else throw new Exception( "typecheck [$any1, $any2]" ); } private function op_length {     $any =& $this->pop_anyref;

if( is_array( $any ) && $any[ "t" ] == "n" ) $any = $any[ "v" ]; if( is_string( $any ) ) $this->mStack[] = mb_strlen( $any ); else if( is_array( $any ) ) $this->mStack[] = count( $any[ "v" ] ); else throw new Exception( "typecheck [$any]" ); } private function op_log { $this->mStack[] = log( $this->pop_num, 10 ); } private function op_load {     $key = $this->pop_name;

foreach( $this->mRevDictKeys as $dictkey ) if( array_key_exists( $key, $this->mDictStack[ $dictkey ] ) ) {	   $this->mStack[] =& $this->mDictStack[ $dictkey ][ $key ]; return; }     throw new Exception( "undefined [$key]" ); } private function op_loop {     $proc =& $this->pop_proc; $this->recursive_bind( $proc );

$this->mExitFlag = false; for {	 $result .= $this->execute( $proc[ "v" ] );

if( $this->mExitFlag ) {	     $this->mExitFlag = false; break; }	}

return $result; } private function op_ln { $this->mStack[] = log( $this->pop_num ); } private function op_lt {     $any2 =& $this->pop_anyref; $any1 =& $this->pop_anyref;

if( ((is_int( $any1 ) || is_float( $any1 )) && (is_int( $any2 ) || is_float( $any2 )))	 || (is_string( $any1 ) && is_string( $any2 )) ) $this->mStack[] = $any1 < $any2; else throw new Exception( "typecheck [$any1, $any2]" ); } /*   * - magic string *  * get contents of respective magic word */ private function op_magic( $name ) {     $this->mStack[] = $this->mParser->getVariableValue( $name ); }

private function op_mark { $this->mStack[] = array( "t" => "m" ); } private function op_mod {     $int2 = $this->pop_int; $int1 = $this->pop_int;

$this->mStack[] = $int1 % $int2; } private function op_mul { $this->mStack[] = $this->pop_num * $this->pop_num; } private function op_ne {     $any2 =& $this->pop_anyref; $any1 =& $this->pop_anyref;

if( is_array( $any1 ) && $any1[ "t" ] == "n" ) $any1 = $any1[ "v" ];

if( is_array( $any2 ) && $any2[ "t" ] == "n" ) $any1 = $any2[ "v" ];

if( (is_int( $any1 ) || is_float( $any1 ))	 && (is_int( $any2 ) || is_float( $any2 )) ) $this->mStack[] = $any1 != $any2; else # string, boolean, ... $this->mStack[] = $any1 !== $any2; } private function op_neg { $this->mStack[] = -$this->pop_num; } private function op_not {      $any1 = $this->pop; if( is_bool( $any1 ) ) $this->mStack[] = !$any1; else if( is_int( $any1 ) ) $this->mStack[] = ~$any1; else throw new Exception( "typecheck [$any1]" ); } private function op_or {      $any2 = $this->pop; $any1 = $this->pop; if( is_bool( $any1 ) && is_bool( $any2 ) ) $this->mStack[] = $any1 || $any2; else if( is_int( $any1 ) && is_int( $any2 ) ) $this->mStack[] = $any1 | $any2; else throw new Exception( "typecheck [$any1, $any2]" ); } /*   * simple string parsetemplate string * array string parsetemplate string * dict string parsetemplate string *   * builds a template string and executes the replaceVariables function * on the result *  * in the 1st case, the result is    * in the 2nd case, *   where any0|..|anyn are the contents of array * in the 3rd case, *   where key0 => val0, keyn => valn are the contents of array */ private function op_parsetemplate {     $template =& $this->pop_string; $arg = $this->pop;

if( is_array( $arg ) ) switch( $arg[ "t" ] ) {	 case "a" : case "x" : $result = ""; break;

case "d" : $result = ""; break; }     else $result = "";

$this->mStack[] = $this->mParser->replaceVariables( $result ); } private function op_pop { $this->pop; }

/*  * string prolog - *   * execute page indicated in as StackFunctions code (default namespace is project namespace) * stripping ..< /pre> at very begin/end of text * if prolog is executed several times with the same argument, only the first one is evaluated * note that for reasons of performance, "same" argument means literal identity; * two different arguments which refer to the same page are recognized as different *  */

private function op_prolog {     $prologpage = $this->pop_string; if( in_array( $prologpage, $this->mPrologPages ) ) return;

$this->mPrologPages[] = $prologpage;

$prologtext = $this->mParser->fetchTemplate( Title::newFromText( $prologpage, NS_PROJECT ) ); $prologtext = preg_replace( '/<' . 'pre>/', "", $prologtext ); $prologtext = preg_replace( '/<' . '\/pre>/', "", $prologtext );

$this->execute( $this->parseraw( $prologtext ) ); } private function op_pstack {     foreach( $this->mStack as $elem ) $stack .= " {". var_export( $elem, true ). "}";

$result = " <". "pre>[pstack:" . str_replace( "<", "&lt;", $stack) # avoid wiki tags . "] "; return $result; } private function op_put {     $any2 =& $this->pop_anyref; $index = $this->pop; $any1 =& $this->pop_anyref;

if( is_string( $any1 ) ) {	 if( !is_int( $index ) || $index < 0 || $index > mb_strlen( $any1 ) - 1 ) throw new Exception( "rangecheck" ); if( mb_strlen( $any2 ) != 1 ) throw new Exception( "rangecheck" );

$any1 = substr_replace( $any1, $any2, $index, 1 ); }     else if( is_array( $any1 ) ) switch( $any1[ "t" ] ) {	 case "a" : case "x" : if( !is_int( $index ) 		|| $index < 0 || $index > count( $any1[ "v" ] ) - 1 ) throw new Exception( "rangecheck" ); $any1[ "v" ][ $index ] =& $any2; break; case "d" : if( $index[ "t" ] == "n" ) $index = $index[ "v" ]; $any1[ "v" ][ $index ] =& $any2; }     else throw new Exception( "typecheck [$any1]" ); } private function op_putinterval {     $any2 =& $this->pop_anyref; $index = $this->pop_int; $any1 =& $this->pop_anyref;

if( is_string( $any1 ) ) {	 if( !is_string( $any2 ) ) throw new Exception( "typecheck [$any2]" );

$length = mb_strlen( $any2 );

if( $index < 0 || $index + $length > mb_strlen( $any1 ) ) throw new Exception( "rangecheck" ); $any1 =& substr_replace( $any1, $any2, $index, $length ); }     else if( is_array( $any1 ) && !$any1[ "t" ] ) {	 if( $any2[ "t" ] != "a" ) throw new Exception( "typecheck [$any2]" );

$length = count( $any2[ "v" ] );

if( $index < 0 || $index + $length > count( $any1[ "v" ] ) ) throw new Exception( "rangecheck" ); for( $i = 0; $i < $length; $i++ ) $any1[ "v" ][ $index + $i ] =& $any2[ "v" ][ $i ]; }     else throw new Exception( "typecheck [$any1]" ); }

/*  * dict query array true *        or false *   * query the database according to dict, return an array of rows if found *  * dict may contain the keys *  /select (optional, default "*") *  /from *  /where (optional) *  /groupby (optional) *  /having (optional) *  /orderby (optional) *  /return (optional, default dicttype) *   * /return can have the following values: *  /arraytype => return array of arrays *  /dicttype => return array of dictionaries *  /booleantype => return array of booleans *  /integertype => return array of integers *  /realtype => return array of reals *  /stringtype => return array of strings * if they type is not /arraytype or /dicttype, * only the first column is returned *  * you need to set $wfStackFunctionsEnableQuery = true * in your LocalSettings in order to enable queries *  */  private function op_query {     global $wfStackFunctionsEnableQuery;

if( !$wfStackFunctionsEnableQuery ) throw new Exception( "invalidaccess" );

$dict =& $this->pop_dict; $q = $dict[ "v" ];

$select = $q[ "select" ]; if( is_null( $select ) ) $qstring = "select *"; elseif( is_string( $select ) ) $qstring = "select $select"; else throw new Exception( "typecheck [$select]" );

$from = $q[ "from" ]; if( is_string( $from ) ) $qstring .= " from $from"; else throw new Exception( "typecheck [$from]" );

$where = $q[ "where" ]; if( !is_null( $where ) ) {	 if( is_string( $where ) ) $qstring .= " where $where"; else throw new Exception( "typecheck [$where]" ); }

$groupby = $q[ "groupby" ]; if( !is_null( $groupby ) ) {	 if( is_string( $groupby ) ) $qstring .= " group by $groupby"; else throw new Exception( "typecheck [$groupby]" ); }

$having = $q[ "having" ]; if( !is_null( $having ) ) {	 if( is_string( $having ) ) $qstring .= " having $having"; else throw new Exception( "typecheck [$having]" ); }

$orderby = $q[ "orderby" ]; if( !is_null( $orderby ) ) {	 if( is_string( $orderby ) ) $qstring .= " order by $orderby"; else throw new Exception( "typecheck [$orderby]" ); }

$return = $q[ "return" ]; if( $return[ "t" ] == "n" ) $return = $return[ "v" ];

$dbr =& wfGetDB( DB_SLAVE ); $res = $dbr->query( $qstring, __METHOD__ ); switch( $return ) {	case "" : case "dicttype" : while( $row = $dbr->fetchRow( $res ) ) {	     foreach( $row as $name => $value ) if( is_integer( $name ) ) unset( $row[ $name ] );

$result[] = array( "v" => $row, "t" => "d" ); }	 break;

case "arraytype" : while( $row = $dbr->fetchRow( $res ) ) {	     foreach( $row as $name => $value ) if( is_string( $name ) ) unset( $row[ $name ] );

$result[] = array( "v" => $row, "t" => "a" ); }	case "booleantype" : while( $row = $dbr->fetchRow( $res ) ) $result[] = (bool)$row[ 0 ]; break; case "integertype" : while( $row = $dbr->fetchRow( $res ) ) $result[] = (int)$row[ 0 ]; break;

case "realtype" : while( $row = $dbr->fetchRow( $res ) ) $result[] = (float)$row[ 0 ]; break; case "stringtype" : while( $row = $dbr->fetchRow( $res ) ) $result[] = (string)$row[ 0 ]; break; default: throw new Exception( "typecheck [$return]" ); }     $dbr->freeResult($res);

if( count( $result ) ) {	 $this->push_array( $result ); $this->mStack[] = true; }     else $this->mStack[] = false; } private function op_rand { $this->mStack[] = rand; } private function op_realtime {     $this->mStack[] = round( microtime( true ) * 1000 ); }

private function op_repeat {     $proc =& $this->pop_proc; $this->recursive_bind( $proc );

$int = $this->pop_int;

$this->mExitFlag = false; for($int > 0; $int-- ) {	 $result .= $this->execute( $proc[ "v" ] );

if( $this->mExitFlag ) {	     $this->mExitFlag = false; break; }	}

return $result; } private function op_roll {     $j = $this->pop_int; $n = $this->pop_int;

if( $n == 0 ) return; elseif( $n < 0 ) throw new Exception( "rangecheck" );

if( count( $this->mStack ) < $n ) throw new Exception( "stackunderflow" );

if( $j > 0 ) $j -= $n;

$values = array_splice( $this->mStack, -$n );

for( $i = 0; $i < $n; $i++ ) $this->mStack[] =& $values[ ($i - $j) % $n ]; } private function op_round { $this->mStack[] = round( $this->pop_num ); } private function op_search {     $seek =& $this->pop_string; $string =& $this->pop_string;

$pos = mb_strpos( $string, $seek );

if( $pos === false ) {	 $this->mStack[] =& $string; $this->mStack[] = false; }     else {	 $post = mb_substr( $string, $pos + mb_strlen( $seek ) ); if( $post === false ) $post = ""; $this->mStack[] = $post; $this->mStack[] = $seek; $this->mStack[] = mb_substr( $string, 0, $pos ); $this->mStack[] = true; }   }

private function op_show { return $this->pop; } private function op_sin { $this->mStack[] = sin( deg2rad( $this->pop_num ) ); } private function op_sqrt {      $num1 = $this->pop_num; if( $num1 < 0 ) throw new Exception( "rangecheck" );

$this->mStack[] = sqrt( $num1 ); } private function op_srand { srand( $this->pop ); } private function op_store {     $value =& $this->pop_anyref; $key = $this->pop_name;

foreach( $this->mRevDictKeys as $dictkey ) if( array_key_exists( $key, $this->mDictStack[ $dictkey ] ) ) {	   $this->mDictStack[ $dictkey ][ $key ] =& $value; return; }

$this->mCurrentDict[ $key ] =& $value; }

private function op_string {     $int = $this->pop_int;

if( $int < 0 ) throw new Exception( "rangecheck" );

$this->mStack[] = str_pad( "", $int, "\0" ); } private function op_sub {     $num2 = $this->pop_num; $num1 = $this->pop_num;

$this->mStack[] = $num1 - $num2; }

/*  * simple string showtemplate - * array string showtemplate - * dict string showtemplate - *   * builds a template string and outputs the result *  * in the 1st case, the output is    * in the 2nd case, *   where any0|..|anyn are the contents of array * in the 3rd case, *   where key0 => val0, keyn => valn are the contents of array */ private function op_showtemplate {     $template =&  $this->pop_string; $arg = $this->pop;

if( is_array( $arg ) ) switch( $arg[ "t" ] ) {	 case "a" : case "x" : return ""; break; case "d" : $result = ""; return $result; break; }     else return ""; } private function op_truncate {     $num1 = $this->pop_num; if( is_int( $num1 ) ) $this->mStack[] = (int)$num1; else $this->mStack[] = (float)(int)$num1; }

private function op_type {     $any = $this->pop;

if( is_bool( $any ) ) $type = "booleantype"; else if( is_int( $any ) ) $type = "integertype"; elseif( is_float( $any ) ) $type = "realtype"; elseif( is_string( $any ) ) $type = "stringtype"; elseif( is_array( $any ) ) switch( $any[ "t" ] ) {	 case "a" : case "x" : $type = "arraytype"; case "d" : $type = "dicttype"; break; case "m" : $type = "marktype"; break; case "n" : $type = "nametype"; break; case "p" : $type = "operatortype"; break; }     elseif( is_null( $any ) ) $type = "nulltype";

$this->mStack[] = array( "v" => $type, "t" => "n" ); } private function op_undef {     $key = $this->pop_name; $dict =& $this->pop_dict; unset( $dict[ "v" ][ $key ] ); } private function op_usertime {     $this->mStack[] = round( (microtime( true ) - $this->mStartTime) * 1000 ); }

private function op_where {     $key = $this->pop_name; foreach( $this->mRevDictKeys as $dictkey ) if( array_key_exists( $key, $this->mDictStack[ $dictkey ] ) ) {	   $this->push_dict( $this->mDictStack[ $dictkey ] ); $this->mStack[] = true; return; }     $this->mStack[] = false; }

private function op_xcheck {     $any = $this->pop;

if( is_array( $any ) && $any[ "t" ] == "x" ) $bool = true; else $bool = false;

$this->mStack[] = $bool; } private function op_xor {      $any2 = $this->pop; $any1 = $this->pop; if( is_bool( $any1 ) && is_bool( $any2 ) ) $this->mStack[] = $any1 xor $any2; else if( is_int( $any1 ) && is_int( $any2 ) ) $this->mStack[] = $any1 ^ $any2; else throw new Exception( "typecheck [$any1, $any2]" ); } } ?>

Patch to Parser.php
The following is for MediaWiki 1.9.2:

261a262,273 > 	 * execute 2nd part of preprocessing > 	 * needed for template parameters > 	 * which are not rendered but examined by string functions > 	 * > 	 * @public > 	 */ > 	function preprocess2( $text ) { > 	 $text = $this->mStripState->unstripBoth( $text ); > 	 return $text; > 	} > > 	/**