Extension:WikiScripts

WikiScripts is an extension that allows users to write modules in a specially designed scripting language and then invoke those modules from wiki pages.

Installation
To install this extension, add the following to LocalSettings.php:

Please bear in mind that you need MediaWiki at least r94435 (1.19) or newer to run the scripts.

There are following configuration variables;
 * $wgScriptsUseGeSHi — enables syntax highlighting of module pages via GeSHi; needs SyntaxHighlight_GeSHi of version r94443 or newer.
 * $wgScriptsAllowRecursion — indicates whether users are allowed to use Recursion. Given the fantasy of our template writers, users may do very insane things with that and hence it is unsafe and disabled by default.
 * $wgScriptsMaxCallStackDepth — the maximum nesting limit of functions.
 * $wgScriptsLimits — variable that allows to impose different limits on the script execution:
 * tokens — amount of tokens (strings, operators, keywords, etc) in the script;
 * evaluations — maximum number of evaluated operations per page (outputted in the parser limit report);
 * depth — maximum depth of the syntax tree (outputted in the parser limit report).

If you have XDebug installed, you will have to increase the function nesting limit to approximately 1000 in default configuration (or cut the value of $wgScriptsMaxCallStackDepth/$wgScriptsLimits['tokens']).

Language
The language used in the scripts is loosely based on JavaScript; however, it includes several features of other languages (PHP and Python) and is case-sensitive.

Data typing
The language has weak typing. There are following types of data:
 * Null (null) is similar to the null in most languages. Unlike some languages, in most operations it is converted to other types, so  (while in SQL  ).
 * Integers (int) are integer numbers.
 * Floating point numbers (float) are numbers with floating point.
 * Booleans (bool) are either true or false.
 * Strings (string) are pieces of text. They can be defined using single quotes ('a') or double quotes ("b"). You can use the escape symbol to put different unusual characets (\" is a quote symbol, \n is a new line, \t is tab, \x7A is a character with hex value 7A).
 * Lists (list) are ordered sets of different elements. They can even be nested!. You can declare them like this:
 * You can later access the element of the list by its number (the first one would have ). In the example above,   would be "B".
 * You can later access the element of the list by its number (the first one would have ). In the example above,   would be "B".


 * Associated arrays (assoc) are arrays where each value is associated with certain string, called key. For example:
 * You can later access the element of the list by its key. In the example above,  would be "Sacramento".
 * You can later access the element of the list by its key. In the example above,  would be "Sacramento".

In most operations, one type would be automatically converted to another. The exception is the addition operator, which follows the following logic:
 * If one operand is an array and the other is null, the array is returned.
 * If both operands are lists or both are associative arrays, they are merged. For example,  is
 * If one operand is list and the other is not, the non-list is either prepended or appended to the list. For example,  is.
 * If one operand is string, both operands are converted to string and joined. For example,  is
 * If one operand is float, both operands are converted to float and added. For example,  is
 * Otherwise both operands (which may be bool, null or int) are converted to integers. For example,  is.

There are following built-in constructions useful for working with data and variables:
 * Type conversion functions:,  ,   and.
 * returns the length of a string or of an array.
 * returns whether a variable or an array item exists.
 * removes the variable or an array element. Please note that it throws error if the element does not exist.
 * (or ) differs for arrays and strings. For arrays, it returns whether the element a is present in array b (note that for checking the key you need to use isset( array[key] ). For string, it return whether the substring a is present in string b.

Control structures
The following control structures are supported: try { a = 2 / 0; } catch( e ) { return e; } will return "divisionbyzero".
 * — allows to catch an error. For example:
 * — allows to catch an error. For example:
 * — allows to catch an error. For example:
 * — executes the code for each element of the specified array. The value of the element is stored in var.
 * — executes the code for each element of the specified array. The value of the element is stored in value and the key is stored in key.
 * and  may be used to control the flow of loop execution.

Functions
All of the script code must be placed in a module. Modules are placed in a module namespace, so a module called "List example" would have title "Module:List example".

A module contains the functions. Each function may be defined using the following syntax: function name( argument1, argument2 ) { // Code here }

The name of the function, as well as name and number of arguments, is up to you.

After that you may invoke the code from the wikitext using the following construction:

You can also declare a function called main. Then you can call this function using a shorter syntax:

If you want to call another function, you will need to use the following syntax: "Module name"::functionName( arg1, arg2 ); or, if the function is in the same module: self::functionName( arg1, arg2 ); Instead of the string literal there may be a variable or any expression in brackets.

Returning values
There are three ways to return a value from a function. The first is using the return construction: function joiner( a, b ) { return a + b; }

Please note that all the values are passed as string. That means that if you call, the result will be "37", not 10. Yes, this is not PHP and you have to cast them using int function.

Another way to return value is to use  operator. It adds the argument to the current return value of the function (the initial value is null). For example, you may append the strings: function makeList( arr ) { for( element in arr ) { append "* " + element + "\n"; } } The function above accepts the array and returns the string. You can apply the  operator to the associated array as well: /** * The method below returns the following associated array: *  { "a" : 3, "b" : 7, "c" : 11 } */ function niceArray { append { "a" : 3 }; append { "b" : 7, "c" : 11 }; }

Another operator is. It is almost like Python, except that here it in fact return the list instead of the generator: /** * Returns [ 1, "five", [ 5, 7 ] ]. */

function niceList { yield 1; yield "five"; yield [ 5, 7 ]; }

You cannot use  and   in the same function. Also, if you use  with some value, the output of   and   will be forfeit; if you use   without any argument, the execution of function will be aborted and the append/yield value will be returned.

Operator precedence
The operators have following precedence (from the top):
 * Function calls
 * Unary operators
 * Keywords  and.
 * Boolean inverse
 * Power
 * Multiplitcation, division , division by module
 * Equality operators
 * Comparisons
 * Logical
 * Trinary
 * Variable setting (, , etc)
 * Return commands

Built-in functions
There is a limited set of built-in functions without prefixes:
 * Casting operators ( and others);
 * returns the length of a string or of an array;

The rest of the code is the part of the libraries. Each library has its own prefix, and after the prefix you put the underscore and the function name. Right now there are following libraries:
 * String library . Please note that in all string functions (when applicable) the first argument is the string with which the operation is done.
 * — joins the argument bits, putting delimiter between them;
 * — joins the list of strings. If specified, prefix is added to the beginning of each string, and postfix is appended.
 * Template library allows the script to access the parser and the parameters of the template that invokes the script.
 * returns the argument which name is specified as an argument, or the default value if it is not set (if no default is specified, it returns false).
 * returns all the named arguments to the template.
 * returns all the numbered arguments.
 * returns whether the code is invoked from a template.
 * — joins the argument bits, putting delimiter between them;
 * — joins the list of strings. If specified, prefix is added to the beginning of each string, and postfix is appended.
 * Template library allows the script to access the parser and the parameters of the template that invokes the script.
 * returns the argument which name is specified as an argument, or the default value if it is not set (if no default is specified, it returns false).
 * returns all the named arguments to the template.
 * returns all the numbered arguments.
 * returns whether the code is invoked from a template.
 * returns the argument which name is specified as an argument, or the default value if it is not set (if no default is specified, it returns false).
 * returns all the named arguments to the template.
 * returns all the numbered arguments.
 * returns whether the code is invoked from a template.

Finished example
Here is an example of a WikiScripts module: function outputTable( columns, table ) { append "{| class='wikitable'\n"; for( name : title in columns ) { append "! " + title + "\n"; }

for( row in table ) { append "|-\n"; for( name : title in columns ) { append "| " + row[name] + "\n"; } }

append "|}\n"; }

function getHeaders { return { "a" : "A's", "b" : "B's", "c" : "C's" }; }

function getTable { yield { "c" : 42, "b" : 22, "a" : 33 }; yield { "c" : 10, "b" : 21, "a" : 31 }; yield { "a" : "abc", "b" : "def", "c" : "ghi" }; yield { "b" : "Hello, world!", "a" : "Lol wut?", "c" : "Testing 12345" }; }

function main( a, b, c ) { return self::outputTable(   self::getHeaders,    self::getTable + { "a" : a, "b" : b, "c" : c }; }

The output, if we call it as  will be following:

Design

 * /Brainstorming