W3TPL

From Woozle Writes Code
Revision as of 13:28, 1 September 2008 by Woozle (talk | contribs) (New page: ==Overview== category:software/incompleteW3TPL, which stands for Woozle's Wacky Wiki Text Processing Language, is a scripting language extension for [[Media...)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Overview

W3TPL, which stands for Woozle's Wacky Wiki Text Processing Language, is a scripting language extension for MediaWiki. It is currently in development, although it does perform some useful functions as-is.

Code

This is just a snapshot from my editor, which I haven't even saved; it might not run. I should have a runnable version later today.

All the tags except <w3tpl> work, but parser interactions make them problematic for more complex applications. <php><?php /*

HISTORY:

0.01 (Wzl) Mainly proof-of-concept; parser should later be optimized for execution time by using PHP intrinsic string fx instead of PHP-code loop 0.02 (Wzl) Kluge to let <xploop> pull #var value under MW <1.12

TO DO: If page is not protected, pre should be forced FALSE and post should be forced TRUE

"pre" can do weird things "post" set false allows raw HTML 0.03 (Wzl) <func> and related tags seem to be working 0.04 (Wzl) Some debugging; now works with v1.12 {{#tag}} function and template parameters (but not very well) names are always lowercased because sometimes the parser does it for you names are always trimmed, because sometimes the parser includes extra spaces 0.05 (Wzl) Added variable indirection ($); removed now-redundant "namer" attribute from <let> can we do something similar with pre-parsing? (i.e. a character to indicate the need for it -- @(stuff to parse)) <if> can now make more sense, i.e. using actual values instead of assuming variable names 0.06 (Wzl) xploop now using variable indirection; removed listvar parameter 0.07 (Wzl) Execution trace in <dump> 0.08 (Wzl) <trace> to set trace options; "input" option in <call> 0.09 (Wzl) <echo> tag; <call> does not output its contents

BUGS:

$wgW3_func needs to be an array, so you can do <call...><arg><arg></call> and then pass the return value as an argument to another function

ELEMENTS:

<hide>: Runs the parser on everything in between the tags, but doesn't display the result. Useful for doing a lot of "programmish" stuff, so you can format it nicely and comment it without messing up your display <let>, <get>: much like PageVars extension, but using XML tags instead of {{#parser}} functions <func>: defines a function which can be called with arguments later <arg>: optional method of passing arguments to a function <call>: call a previously defined function <dump>: show list of all variables (with values) and functions (with code) <if>, <else>: control structure <xploop list="\demarcated\list" repl=string-to-replace sep=separator></xploop>: Same as {{#xploop}}, but uses varname instead of $s$ TO-DO <w3tpl></w3tpl>: The language itself

  • /

$wgExtensionCredits['other'][] = array(

       'name' => 'W3TPL',
       'description' => 'Woozle\'s Wacky Wiki Text Processing Language',
       'author' => 'Woozle (Nick) Staddon',

'version' => '0.09 2008-08-24 (in progress)' );

define(ksFuncInit,'efW3TPLInit');

//Avoid unstubbing $wgParser on setHook() too early on modern (1.12+) MW versions, as per r35980 if ( defined( 'MW_SUPPORTS_PARSERFIRSTCALLINIT' ) ) {

       $wgHooks['ParserFirstCallInit'][] = ksFuncInit;

} else { // Otherwise do things the old fashioned way

       $wgExtensionFunctions[] = ksFuncInit;

} $wgHooks['LanguageGetMagic'][] = 'efW3_LanguageGetMagic';


function efW3TPLInit() {

       global $wgParser;

global $wgExtW3TPL;

// hook in <tag>-style functions:

       $wgParser->setHook( 'hide',	'efW3Hide' );
       $wgParser->setHook( 'let',	'efW3Let' );
       $wgParser->setHook( 'get',	'efW3Get' );
       $wgParser->setHook( 'echo',	'efW3Echo' );
       $wgParser->setHook( 'dump',	'efW3Dump' );
       $wgParser->setHook( 'trace',	'efW3Trace' );
       $wgParser->setHook( 'if',	'efW3If' );
       $wgParser->setHook( 'else',	'efW3Else' );
       $wgParser->setHook( 'func',	'efW3Func' );
       $wgParser->setHook( 'call',	'efW3Call' );
       $wgParser->setHook( 'arg',	'efW3Arg' );
       $wgParser->setHook( 'load',	'efW3Load' );
       $wgParser->setHook( 'xploop',	'efW3Xploop' );
       $wgParser->setHook( 'w3tpl',	'efW3TPLRender' );

// hook in {{#parser}}-style functions: $wgExtW3TPL = new W3TPL_fx ( ); $wgParser->setFunctionHook ( 'w3xploop', array ( &$wgExtW3TPL, 'runXploop' ) ); $wgParser->setFunctionHook ( 'w3xpcount', array ( &$wgExtW3TPL, 'runXpCount' ) ); $wgParser->setFunctionHook ( 'let', array ( &$wgExtW3TPL, 'runLet' ) ); $wgParser->setFunctionHook ( 'get', array ( &$wgExtW3TPL, 'runGet' ) );

       return true;

} function efW3_LanguageGetMagic( &$magicWords, $langCode = "en" ) {

   switch ( $langCode ) {
       default:
           $magicWords['w3xploop']	= array ( 0, 'w3xploop' );
           $magicWords['w3xpcount']	= array ( 0, 'w3xpcount' );
   }
   return true;

}

function TrueFalse($iVar) { return $iVar?'TRUE':'FALSE'; } function W3VarExists($iName) { global $wgW3Vars;

$strName = strtolower($iName); return isset($wgW3Vars[$strName]); } function W3KillVar($iName) { global $wgW3Vars;

$strName = strtolower($iName); unset($wgW3Vars[$strName]); } function W3SetVar($iName, $iValue) { global $wgW3Vars, $wgW3_traceVars;

$strName = strtolower($iName); $wgW3Vars[$strName] = $iValue; if ($wgW3_traceVars) { W3AddTrace(' ['.ShowHTML($strName).'] <= ['.$iValue.']'); } } function W3GetExpr($iName) { // very simple parser which recursively checks for "$", and returns value of var if found; // otherwise returns original string. global $wgW3Vars, $wgW3_traceVars; global $wgW3Trace_indent;

$wgW3Trace_indent++; $strName = $iName; if ($wgW3_traceVars) { W3AddTrace(' {GETEXPR ['.ShowHTML($iName).']'); } do { $chFirst = substr($strName,0,1); if ($chFirst == '$') { $isInd = true; $strRef = strtolower(substr($strName,1)); $strName = W3GetVal($strRef);

} else { $isInd = false; } } while ($isInd); if ($wgW3_traceVars) { W3AddTrace(' => ['.ShowHTML($strName).'] GETEXPR}'); } $wgW3Trace_indent--; return $strName; } function W3GetVal($iName) { // gets value of given variable // checks function arguments, if function is defined global $wgW3Vars,$wgW3_traceVars; global $wgW3Trace_indent;

$wgW3Trace_indent++; if ($wgW3_traceVars) { W3AddTrace('{GETVAL ['.ShowHTML($iName).']'); } $strName = strtolower(W3GetExpr($iName)); // follow any indirections and get ultimate value if (isset($wgW3Vars[$strName])) { $strVal = $wgW3Vars[$strName]; } else { $strVal = NULL; } if ($wgW3_traceVars) { W3AddTrace('['.ShowHTML($strName).'] => ['.ShowHTML($strVal).'] GETVAL}'); } $wgW3Trace_indent--; return $strVal; } function W3AddTrace($iLine,$iInd=0) { global $wgW3Trace, $wgW3Trace_indents, $wgW3Trace_indent;

$wgW3Trace[] = $iLine; $wgW3Trace_indents[] = $wgW3Trace_indent; $wgW3Trace_indent += $iInd; }

function efW3Hide( $input, $args, $parser ) { $parser->recursiveTagParse( $input ); return NULL; } function efW3Let( $input, $args, $parser ) { global $wgW3Vars;

$strCopy = NULL;

$strNameRaw = $args['name']; // trim whitespace and check for redirection: $strName = strtolower(W3GetExpr(trim($strNameRaw)));

if (isset($args['copy'])) { $strCopy = $args['copy']; $strVal = W3GetVal($strCopy); } else { $strVal = $input; } if (isset($args['pre'])) { $strVal = $parser->recursiveTagParse($strVal); // $strVal = $parser->replaceVariables($input); } $strTrace = 'LET “'.$strName; if ($strName != $strNameRaw) { $strTrace .= ' ('.$strNameRaw.')'; } $strTrace .= '” = “'.$strVal.'”'; if ($strCopy) { $strTrace .= ' copied from '.$strCopy.''; } W3AddTrace($strTrace); if (isset($args['append'])) { $wgW3Vars[$strName] .= $strVal; } else { $wgW3Vars[$strName] = $strVal; } if (isset($args['echo'])) { return $strVal; } else { return NULL; } } function efW3Get( $input, $args, $parser ) { $strName = strtolower($args['name']); if (isset($args['parsename'])) { $strName = $parser->recursiveTagParse($strName); } $strVal = W3GetVal($strName); if (isset($args['strip'])) { $strVal = ShowHTML($strVal); } else { $strVal = $parser->recursiveTagParse($strVal); } if (isset($args['len'])) { $strVal = substr($strVal,0,$args['len']); } return $strVal; } function efW3Echo( $input, $args, $parser ) { global $wgW3_ifDepth,$wgW3_funcOutput;

if (isset($args['strip'])) { $out = ShowHTML($input); } else { $out = $parser->recursiveTagParse($input); } W3AddTrace('ECHO ['.ShowHTML($input).']'); $wgW3_funcOutput[$wgW3_ifDepth] .= $out; } function efW3Dump( $input, $args, $parser ) { global $wgW3Vars, $wgW3_funcs, $wgW3Trace; global $wgW3Trace_indents;

$out = '

    '; if (is_array($wgW3Vars)) { $out .= '
  • Variables:
      '; foreach ($wgW3Vars as $name => $value) { $out .= '
    • ['.$name.'] = ['.$value.']'; } $out .= '
    ';

    } else {

    $out .= '
  • No variables set'; } if (is_array($wgW3_funcs)) { $out .= '
  • Functions:
      '; foreach ($wgW3_funcs as $name => $obj) { $out .= '
    • '.$obj->dump(); } $out .= '
    ';

    } else {

    $out .= '
  • No functions defined'; } if (isset($args['trace'])) { if (is_array($wgW3Trace)) { $out .= '
  • Trace:
      '; foreach ($wgW3Trace as $idx => $line) { $indLine = $wgW3Trace_indents[$idx]; if ($indLine > $indCur) { $out .= '
        '; } elseif ($indLine < $indCur) { $out .= '
      ';

      } $indCur = $indLine;

      $out .= '
    • '.$idx.' '.$line; } $out .= '
    ';

    } else {

    $out .= '
  • No trace events'; } } $out .= '

';

return $out; } function efW3Trace( $input, $args, $parser ) { global $wgW3_traceVars;

$wgW3_traceVars = isset($args['assigns']); } function efW3If( $input, $args, $parser ) { global $wgW3Vars,$wgW3_ifFlag,$wgW3_ifDepth,$wgW3_funcOutput;

$wgW3_ifDepth++; $ifFlag = false; if (isset($args['flag'])) { $strName = $args['flag']; $strVal = W3GetVal($strName); if (is_null($strVal) || ($strVal == )) { $ifFlag = FALSE; $dbgType = 'blank'; } else if (is_numeric($strVal)) { $ifFlag = ($strVal != 0); $dbgType = 'numeric'; } else { $ifFlag = TRUE; $dbgType = ; } W3AddTrace(' IF('.$wgW3_ifDepth.'): ['.$strVal.']  != 0: ['.TrueFalse($ifFlag).']:'.$dbgType); } elseif (isset($args['comp'])) { $strName = $args['comp']; $strVal1 = W3GetVal($strName); $strName = $args['with']; $strVal2 = W3GetVal($strName); if (isset($args['pre'])) { $strVal1 = $parser->recursiveTagParse($strVal1); $strVal2 = $parser->recursiveTagParse($strVal2); // $strVal1 = $parser->replaceVariables($strVal1); // $strVal2 = $parser->replaceVariables($strVal2);

} $ifFlag = ($strVal1 == $strVal2); } if ($ifFlag) { $out = $parser->recursiveTagParse($input); } else { $out = NULL; } $wgW3_ifFlag[$wgW3_ifDepth] = $ifFlag; // W3AddTrace('SETTING IFFLAG['.$wgW3_ifDepth.']=>['.TrueFalse($ifFlag).']'); // $out .= $wgW3_funcOutput[$wgW3_ifDepth]; // $wgW3_funcOutput[$wgW3_ifDepth] = NULL; // clear the accumulated echo output $wgW3_ifDepth--; return $out; } function efW3Else( $input, $args, $parser ) { global $wgW3_ifFlag, $wgW3_ifDepth, $wgW3_funcOutput;

$wgW3_ifDepth++; $ifFlag = $wgW3_ifFlag[$wgW3_ifDepth]; // W3AddTrace('GETTING IFFLAG['.$wgW3_ifDepth.']=>['.TrueFalse($ifFlag).']'); W3AddTrace(' ELSE('.$wgW3_ifDepth.'): ['.$ifFlag.']'); if ($ifFlag) { W3AddTrace('ELSE skipped'); $out = NULL; } else { W3AddTrace('ELSE executed'); $out = $parser->recursiveTagParse($input); // W3AddTrace('ELSE: OUT = ['.ShowHTML($out).']'); W3AddTrace('ELSE: OUT = ['.$out.']('.ShowHTML($out).')'); } $strSaved .= $wgW3_funcOutput[$wgW3_ifDepth]; $out .= $strSaved; W3AddTrace('ELSE: ADDING ['.$strSaved.']('.ShowHTML($strSaved).')'); $wgW3_funcOutput[$wgW3_ifDepth] = NULL; // clear the accumulated echo output

$wgW3_ifDepth--;

$wgW3_funcOutput[$wgW3_ifDepth] .= $out; return $out; } function efW3Func( $input, $args, $parser ) { global $wgW3_funcs;

foreach ($args as $name => $value) { if ($pcnt) { /* The parser apparently sets the argument's value to its name if no value is specified. This is a sort of bug for this purpose, but maybe it makes sense in other contexts. The real way to get around it is to use <w3tpl> block syntax instead of the <func> tag.

  • /

if ($value != $name) { $funcArgs[$name] = $value; } else { $funcArgs[$name] = null; } } else { $funcName = strtolower($name); } $pcnt++; } W3AddTrace('FUNC “'.$funcName.'”'); $objFunc = new clsW3Function($parser,$funcName,$funcArgs,$input); $wgW3_funcs[$funcName] = $objFunc; return NULL; } function efW3Call( $input, $args, $parser ) { global $wgW3_funcs,$wgW3_func,$wgW3_ifDepth,$wgW3_funcOutput;

$pcnt = 0; foreach ($args as $name => $value) { $strName = strtolower(trim($name)); if ($pcnt) { $strVal = W3GetExpr($value); $strTrace .= $objFunc->LoadArg($strVal,$strName); $strTrace .= ' '; } else { $funcName = $strName; $strTrace = 'CALL '.$funcName.'( '; $objFunc = $wgW3_funcs[$funcName]; $wgW3_func = $objFunc; if (!is_object($objFunc)) { $out = 'ERROR: Function "'.$funcName.'" is undefined.'; return $out; } $objFunc->ResetArgs(); } $pcnt++; } $strTrace .= ')'; if ($input) { // $strTrace .= ' INPUT: {'.ShowHTML($input).'}'; $res = $parser->recursiveTagParse($input); // $strTrace .= ' PARSED: {'.ShowHTML($res).'}'; } W3AddTrace($strTrace); $out = $objFunc->execute(); // LATER: implement "echo" option to include parsed contents of function $out = $wgW3_funcOutput[$wgW3_ifDepth]; $wgW3_funcOutput[$wgW3_ifDepth] = NULL; // clear the accumulated echo output $wgW3_func = NULL; // no active function (is this still used?) return $out; } function efW3Arg( $input, $args, $parser ) { global $wgW3_func;

if (isset($args['name'])) { $name = $args['name']; } else { $name = ; } if (isset($args['pre'])) { $value = $parser->recursiveTagParse($input); } else { $value = $input; } $strTrace = ' +ARG: '.$wgW3_func->LoadArg($value,$name); W3AddTrace($strTrace); } function efW3Load( $input, $args, $parser ) { global $wgParser,$wgTitle;

$strTitle = $args['page']; $strTitle = W3GetExpr($strTitle); $doEcho = isset($args['echo']);

W3AddTrace('LOAD {'.$strTitle.'}'); // Some pages apparently don't create the parser; if this code needs to run on one of those pages, // then this may need to create $wgParser if it doesn't exist. For now, we assume optimistically. $objTitle = Title::newFromText($strTitle); $objArticle = new Article($objTitle);

$txtContent = $objArticle->getContent(); $out = $parser->recursiveTagParse($txtContent); if ($doEcho) { return $out; } } function efW3Xploop( $input, $args, $parser ) { global $wgW3Vars,$wgW3_ifDepth,$wgW3_funcOutput;

$strListRaw = $args['list']; $strList = W3GetExpr($strListRaw); $strTok = $args['repl']; $doEcho = isset($args['echo']); if (isset($args['var'])) { $strVar = strtolower($args['var']); // THIS IS GETTING SET TO WEIRD UNIQ thing? } else { $strVar = NULL; } if (isset($args['sep'])) { $sepStr = $args['sep']; } else { $sepStr = NULL; } if ($strTok) { // doing a straight token replacement $strTrace = 'XPLOOP replace ('.ShowHTML($strTok).') <= “'.ShowHTML($strList).'”'; } else { // setting variable value $strTrace = 'XPLOOP set ('.ShowHTML($strVar).') <= “'.ShowHTML($strList).'”'; } if ($strList != $strListRaw) { $strTrace .= '<=“'.$strListRaw.'”'; } W3AddTrace($strTrace); if (isset($args['parselist'])) { $strList = $parser->recursiveTagParse( $strList ); }

$tok = substr ( $strList, 0, 1); // token for splitting if ($tok) { $tks = substr ( $strList, 1 ); // tokenized string $list = explode ( $tok, $tks ); // split the string $out = ; } else { return NULL; } if ($strTok) { // doing a straight token replacement foreach ($list as $value) { if ($out) { $out .= $sepStr; } $out .= str_replace( $strTok, $value, $input ); } $out = CharacterEscapes::charUnesc( $out, array(), $parser ); $out = $parser->recursiveTagParse( $out ); } else { foreach ($list as $value) { if ($out) { $out .= $sepStr; } $wgW3Vars[$strVar] = $value; W3AddTrace(' - XP: ['.$strVar.'] <- ['.$value.']'); $out .= $parser->recursiveTagParse( $input ); } } if ($doEcho) { return $out ; } else { $out = $wgW3_funcOutput[$wgW3_ifDepth]; $wgW3_funcOutput[$wgW3_ifDepth] = NULL; // clear accumulated echo output return $out; } }

function efW3TPLRender( $input, $args, $parser ) { global $w3step;

$out = $input; if (isset($args['step'])) { $w3step = true; // show each line of code before it is executed } if (isset($args['pre'])) { $out = $parser->recursiveTagParse( $out ); } if (!isset($args['notpl'])) { $out = ActualRender($out,$args); } if (isset($args['post'])) { $out = $parser->recursiveTagParse( $out ); } $out = ActualRender($out); return $out; }

function ActualRender($input) { // break the code up into separate commands $cmdObj = new clsW3Command(); $inQuo = FALSE; $brDepth = 0; $paDepth = 0; $line = ; $clause = ; for ($i = 0; $ch=substr($input,$i,1); $i++) { $isTok = FALSE; $doAdd = TRUE; $doSave = FALSE; switch ($ch) { // whitespace case ' ': // space case "\t": // tab if (!$inQuo) { $isTok = TRUE; } break; case '"': if (!$inEsc) { $inQuo != $inQuo; } break; case '\\': $inEsc = true; break; case '(': if (!$inQuo) { $paDepth++; } break; case ')': if (!$inQuo) { $paDepth--; if ($paDepth == 0) { $isArgs = TRUE; } } break; case '{': if (!$inQuo) { $brDepth++; $doAdd = false; // don't include braces in $clause } break; case '}': if (!$inQuo) { $brDepth--; if ($brDepth == 0) { // end of braces is equivalent to semicolon $doSave = true; $doAdd = false; // don't include braces in $clause } } break; case ';': if (!$inQuo) { if ($brDepth == 0) { $doSave = true; $doAdd = false; } } break; case "\n": $doAdd = false; // don't include linebreaks in $clause break; default: } if ($doAdd) { if ($brDepth) { $clause .= $ch; } else { $line .= $ch; } $inEsc = false; } if ($doSave) { W3AddTrace('TPL: LINE=['.$line.'] CLAUSE=['.$clause.']'); $out .= $cmdObj->execute($line,$clause); $line = ; $clause = ; $doSave = false; } } // final semicolon not required: if ($line) { $out .= $cmdObj->execute($line,$clause); } return $out; }

class clsW3Command { private $statemt; private $clause;

public function clsW3Command($iStatemt=, $iClause=) { $this->init($iStatemt, $iClause); } public function init($iStatemt, $iClause) { $this->statemt = trim($iStatemt); $this->clause = $iClause; } public function execute($iStatemt, $iClause) { // init + exec $this->init($iStatemt, $iClause); return $this->exec(); } public function exec() { global $w3step;

$strStmt = $this->statemt; if ($w3step) { $out = '
CMD: {'.$strStmt.'} CLAUSE: {'.$this->clause.'}'; } /* A command is in any of these forms: variable action-operator expression; expression; keyword (expression) {clause} [keyword (expression) {clause} ... ]; An expression is in any of these forms: function(expression[, expression[, ...]]) variable compare-operator expression variable constant A constant is either a quoted string or a number A variable is invoked by name


  • /

// parse the command: for ($i = 0; $ch=substr($strStmt,$i,1); $i++) { $isArgs = FALSE; if ($isTok) { if (!$inQuo) { if ($cmd == ) { $cmd = $phrase; $phrase = ; } } elseif ($isArgs) { $args = $phrase; $phrase = ; } else { $phrase .= $ch; } } W3AddTrace('TPL layer 2: CMD=['.$cmd.'] ARGS=['.$args.'] leftover=['.$phrase.']'); // return $out; } }

function ShowHTML($iText) { $out = str_replace ( '<','<',$iText ); $out = str_replace ( '>','>',$out); $out = str_replace ( chr(7),'^G',$out); return $out; }

/* class w3expr { private $text; abstract function Parse($iTask); } */

class clsW3Function { var $vParser; var $vName, $vParams, $vCode; var $vArgs, $vArgIdx;

public function clsW3Function($iParser, $iName, $iParams, $iCode) { $this->vParser = $iParser; $this->vName = $iName; $this->vParams = $iParams; $this->vCode = $iCode; } public function dump() { $out = ''.$this->vName.'('; if (is_array($this->vParams)) { foreach ($this->vParams as $name => $value) { if ($pcnt) { $out .= ', '; } $out .= ''.$name.''; if (isset($value)) { $out .= '='.$value; } $pcnt++; } } $out .= ') {'; $strCode = ShowHTML($this->vCode);

$out .= '

'.$strCode.'

}';

return $out; } public function ResetArgs() { $this->vArgIdx = 0; unset($this->vArgs); } public function LoadArg($iValue,$iName=) { if ($iName) { $strName = strtolower($iName); } else { $strName = $this->ParamName($this->vArgIdx); $this->vArgIdx++; } $this->vArgs[$strName] = $iValue; return '['.$strName.']=“'.$iValue.'”'; } public function HasArg($iName) { return isset($this->vArgs[$iName]); } public function ArgVal($iName) { return $this->vArgs[$iName]; } public function ParamName($iIndex) { $keys = array_keys($this->vParams); $key = $keys[$iIndex]; return $key; } public function execute() { // set variables from passed arguments if (is_array($this->vArgs)) { foreach ($this->vArgs as $name => $value) { if (W3VarExists($name)) { $oldVars[$name] = W3GetVar($name); } W3SetVar($name, $value); } } // parse (execute) the function code $out = $this->vParser->recursiveTagParse( $this->vCode ); // restore original variables (old value if any, or remove from list) if (is_array($this->vArgs)) { foreach ($this->vArgs as $name => $value) { if (isset($oldVars[$name])) { W3SetVar($name, $oldVars[$name]); } else { W3KillVar($name); } } } return $out; } }

class W3TPL_fx { function runXploop ( &$parser, $inStr = , $inTplt = '$s$', $inSep = ) { $tok = substr ( $inStr, 0, 1); // token for splitting if ($tok) { $tks = substr ( $inStr, 1 ); // tokenized string $list = explode ( $tok, $tks ); // split the string $sep = CharacterEscapes::charUnesc( $inSep, array(), $parser ); $tplt = CharacterEscapes::charUnesc( $inTplt, array(), $parser ); $out = ; foreach ($list as $value) { // $lcnt++; if ($out) { $out .= $sep; } $out .= str_replace( '$s$', $value, $tplt ); } return $parser->recursiveTagParse($out); // return array($out, 'noparse' => false, 'isHTML' => false); } else { return NULL; } } function runXpCount ( &$parser, $inStr = ) { $tok = substr ( $inStr, 0, 1); return substr_count($inStr, $tok); } function runLet ( &$parser, $iName = , $iVal = ) { global $wgW3Vars;

$strName = strtolower($iName); $strName = W3GetVal($strName); // check for indirection ($) $wgW3Vars[$strName] .= $iVal; } function runGet ( &$parser, $iName = ) { $strName = strtolower($iName); $strVal = W3GetVal('$'.$strName); return $parser->recursiveTagParse($strVal); } }</php>