Extension:StackFunctions

What the StackFunctions Extension Is
The StackFunctions extension implements a programming language which is basically PostScript without graphics.

What It Is Useful For
This extension can be considered as an alternative to the combination of ParserFunctions with StringFunctions (and maybe other extensions). Advantages are:
 * When using conditional expressions with ParserFunctions, wikitext in the false-branch is parsed before evaluating the condition. If that text contains complex templates, such superfluous parsing may take much time. With StackFunctions, wikitext is parsed only if needed for the output.
 * To execute loops, you would need either inefficient auxiliary templates with a limited number of runs or LoopFunctions which also have limits. StackFunctions is able to execute loops with any number of iterations and any depth of nesting in a relatively efficient way.
 * If you have text which on the one hand you'd like to display between ..  and on the other hand to use as an argument to ParserFunctions, you need to build more or less complex structures using ExpandAfter. StackFunctions can handle this in a much easier way.
 * StackFunctions offer a Turing-complete programming language which can be used to implement any algorithm (with more or less effort in a more or less efficient fashion) without need for additional extensions.
 * StackFunctions easily handle complex data structures like arrays and dictionaries, which may also be nested to any level.
 * My personal experience is that StackFunctions generally tend to execute faster than a combination of other existing extensions, especially when the latter solution would need a large number of auxiliary templates which are evaluated many times.

How Io Install It

 * Ensure your PHP has multibyte functions enabled. If you do not have the possibility to do that, you might decide to do without multibyte support. In that case, just delete all occurences of "mb_" from the extension source code.


 * Save the extension source code as a file extensions/StackFunctions/StackFunctions.php.


 * Apply the patch to Parser.php.

require_once( "extensions/StackFunctions/StackFunctions.php" );
 * Add the following line to your LocalSettings.php:

$wfStackFunctionsEnableQuery = true;
 * If you want to enable, also add the following line to your LocalSettings.php:

How To Use It
Preliminary Remark: programming with a stack processor like this is a matter of taste. You might alternatively find it extremely cool or totally unusable. You have been warned.

About PostScript
StackFunctions are basically an implementation of PostScript without graphical operators, with a few modifications and with some MediaWiki-specific extensions as explained in detail below. I'm not going to explain PostScript here; you might refer to the following:
 * The PostScript Wikipedia article for a very basic introduction.
 * The PostScript Language Tutorial and Cookbook for a good tutorial.
 * The PostScript Language Reference, third edition for a complete reference.

Implemented PostScript Operators
Chapter 8.1 of the PostScript Language Reference, third edition gives a summary of PostScript operators by category. The following are implemented in StackFunctions:


 * Operand Stack Manipulation Operators: all.
 * Arithmetic and Math Operators: all except rrand.
 * Array Operators: all.
 * Dictionary Operators: all except maxlength, errordict, $error, globaldict.
 * String Operators: all except token.
 * Relational, Boolean, and Bitwise Operators: all.
 * Control Operators: all except stop, stopped, countexecstack, execstack, quit, start.
 * Type, Attribute, and Conversion Operators: all except executeonly, noaccess, readonly, rcheck, wcheck, cvrs</tt>.
 * Miscellaneous Operators: all except executive, echo, prompt</tt>.

In addition, the show</tt> operator has been implemented: it simply writes its argument to the output (which is then further interpreted by the MediaWiki parser).

Differences to PostScript
There are a few things I implemented differently from PostScript because I believe this way they fit better to the needs of the MediaWiki developer:


 * The whole implementation supports multibyte character sets.
 * The show operator accepts any kind of argumnt (even though only strings and number provide useful results).
 * The string versions of get and put accept one-character strings instead of ASCII numbers. Otherwise it would be difficult to cope with multibyte characters.

Other things are not (yet) implemented because it would require some effort to implement them while I consider them less useful for the MediaWiki developer:


 * Radix numbers, such as 8#1777 16#FFFE 2#1000, are currently not supported.
 * Literal string objects can be specified as (..) only. Hexadecimal data, enclosed in, and ASCII base-85 data, enclosed in <~ and ~>, are currently not supported.
 * The executable attribute has been implemented for arrays only, and the readable/writable attributes have not been implemented at all. I think there is little point in such attributes for MediaWiki programming, and the only way to implement them would have been to represent any kind of data (including numbers and strings) as arrays in PHP. This would have made the StackFunctions code larger and slower.

Finally, some differences are due to the nature of PHP which is different from what a PostScript engine needs:
 * Test for equality of composite opjects checks whether the elements contain the same values, not whether they refer to the same object. I wouldn't know how to implement this in PHP.
 * Substrings are not part of string objects, but independent objects. This implies, for instance, that the copy operator for strings leaves on the stack a new string rather than a substring of the original string.
 * The operator serialnumber</tt> returns the IP adress of the webserver as a string. If you can think of any more useful usage for this operator, please let me know.
 * The loop operators for, forall, loop, repeat</tt> perform a bind</tt> on their procedure argument. If other copies of the procedure exist on the stack or in some dictionary, they will reflect this. I cannot imagine a reasonable application where this behaviour would cause a problem.

For all these differences, comments and suggestions are welcome.

Almost all typechecks have been implemented as in PostScript. Furthermore, the concept that composite objects on the stack are references has been implemented just as in PostScript. Thus, operators like dup create a new reference to the same composite object rather than copying a value.

Prologs

 * prolog : string prolog –
 * Execute StackFunctions code stored in the page indicated in string. If string does not contain an explicit namespace, takes the project namespace as default. The page should contain nothing but code, optionally included in  ..  </tt> which are ignored. If prolog is executed several times with the same argument, only the first one is evaluated. This saves parsing time when a template containing StackFunctions code is used many times on the same page: then you can store definitions (for instance, macros and dictionaries) on a separate page which is parsed only once. Note that for reasons of performance, "same" argument means literal identity; two different arguments which refer to the same page are recognized as different.


 * Prolog pages are searched in the project namespace by default if no explicit namespace is specified.


 * As the mechanism to find a prolog page is the same as that to find a template, prolog pages are listed in "Templates used on this page:" when you edit a page.

Template Evaluation

 * parsetemplate : simple parsetemplate string array parsetemplate string dict parsetemplate string
 * In the first form, simple denotes an argument of any type which is neither an array nor a dictionary. As a result, the string  </tt> is passed to the parser for template substitution and the result pushed on the stack.
 * In the second form, the same is done with  </tt> where any0 .. anyn are the elements of the array.
 * In the third form, the same is done with  </tt> where key0 .. keyn are the keys and val0 .. valn the corresponding values in the dictionary.
 * In all three forms, the result is eveluated by the parser before execeution of StackFunctions code continues. This means that you can examine the result to see what is substituted. Note that parsetemplate also works with parser functions.


 * showtemplate : simple showtemplate – array showtemplate – dict showtemplate –
 * This works the same way as parsetemplate; the difference is that the result, instead of being pushed onto the stack, is written to the output.

Database Querying
Database querying allows you to access directly the database MediaWiki runs on. This might be not particularly useful on the basic MediaWiki installation. It becomes interesting when you want to display other data stored in the same database and accessible to the MediaWiki database user, or in connection with MediaWiki extensions which store data in additional tables.


 * query : dict query array true
 * or false
 * Query the MediaWiki Database with a SQL statement. The dictionary may contain the following keys:


 * Hence, the only required key is /from. The result is returned as an array of rows, where each row is either an array, a dictionary (where keys are column names) or a simple type, depending on the value of /return. Note that in the latter case only the first column is considered, and it is converted to the requested type.

Parser Issues
The basic rule is that any  </tt> structures are parsed by the MediaWiki parser before any StackFunctions code is executed. There is a number of consequences:


 * Take care not to have any  </tt> in your code. If you have consecutive braces, put a space in between.


 * You cannot use a literal | character inside  </tt> because the parser would interpret it as a separator for an additional parameter passed to #sf:</tt>. Within a string constant, you can write its octal representation \174</tt> instead.


 * If you put a template, magic constant or parser function in a string literal, it will first be evaluated and then the result passed to the StackFunctions code. Use parsetemplate or showtemplate (see above) if you want to evaluate it only while executing the StackFunctions code. In particular, you should do so within conditional branches so that the parser spends time only on those templates which are actually needed.

Internals

 * Design considerations
 * Drawbacks
 * Open problems