OpenWetWare:Software/Extensions/ParserFunctions

OpenWetWare:Software/Exensions/ParserFunctions

A collection of parser functions such as branching instructions and expression handler and time calculation unit.

Other languages: The extension ParserFunctions is a collection of parser functions (note the difference between the name of the collection, and the general term). These parser functions have a hash character in front of the function name, so they typically have the syntax:

Functions

 * Tip: With Special:ExpandTemplates one can evaluate expressions with parser functions directly.

This module defines eight functions at present: expr, if, ifeq, ifexist, ifexpr, switch, time, and rel2abs. <h3 style="background: beige">#expr:


 * The accuracy and format of numeric results varies with the operating system of the server.

The expr</tt> function computes mathematical expressions based on permutations of numbers (or variables/parameters that translate to numbers) and operators. It does not work with strings; use ifeq</tt> below instead. The syntax is:

A list of supported operators follows. For more details about the operator precedence see Help:Calculation, it's roughly (1) grouping (parentheses), (2) unary (+/- signs and NOT), (3) multiplicative (*, /, div, mod), (4) additive (+ and -), (5) round, (6) comparative (=, !=, &lt;, &gt;, etc.), (7) logical AND, (8) logical OR. Within the same precedence class operators are evaluated left to right. As always some redundant parentheses are better than erroneous terse code.

The boolean operators consider 0 to be "false" and any other number to be "true", on output "true" is shown as .

Numbers are given in decimal with "." for the decimal point. The  function can be used to change the decimal point to a comma for the appropriate locales. Scientific notation with E</tt> plus exponent is not yet supported on input for expressions, but used on output, for details see Help:Calculation.

<h3 style="background: beige">#if:

The function is an if-then-else construct. The syntax is:

The applied condition is "The condition string is non-empty". Thus, if the condition string is empty or consists only of whitespace, then the condition is false, and the else text is returned. Otherwise, the then text is returned. The else text may be omitted, in which case the result will be blank if the condition is false.

An example:

Note that the condition string is not itself a condition such as "1 = 2"; for example,  will return "yes", because the string " " is not blank. Thus the function does not support "=" signs or mathematical expressions.

<h3 style="background: beige">#ifeq:

compares two strings or numbers, and returns another string depending on the result of that comparison. The syntax is:

If both strings can be interpreted as numbers the comparison is numerical. To force a string comparison add tokens that can't be interpreted as numbers:


 * gives
 * gives

Comparison of strings is case-sensitive:
 * gives


 * For compatibility with older templates #if: cannot directly distinguish defined and undefined parameter values, it's a shorthand for a comparison with the empty string. With #ifeq: it's directly possible to identify undefined parameters&#58;


 * An undefined parameter without default counts in the comparison as a string consisting of the tag&#58;

Text between nowiki tags is during parsing temporarily replaced by a unique code. This affects comparisons: gives.

<h3 style="background: beige">#ifexist:

returns one of two results based on whether or not a particular page exists.

The usual case-sensitivity applies: if a page exists then also a non-canonical name for that page gives a positive result. E.g. on Meta:
 * gives, because Bugs exists
 * gives, because bugs is in canonical form the existing Bugs
 * gives because BUGS does not exist
 * gives although m:Help:Calculation exists, because of the interwiki prefix.

The first parameter is the title to check for, the second is the positive result, and the third, the negative result. If the parameter passed does not produce a valid title object, then the result is negative.

gives the same result, except that the result is positive for an interwiki link. You can also handle an interwiki link differently with.

doesn't handle relative paths (like '../foo'), for that, use it in combination of.

The page where appears is listed on Special:Whatlinkshere/foo (as a page which links to foo), see below.

<h3 style="background: beige">#ifexpr: evaluates a mathematical expression (see #expr) and returns one of two strings depending on the result.

If the expression evaluates to zero, then the else text is returned, otherwise the then text is returned. Expression syntax is the same as for expr</tt>.

Example:
 * -- Because 10 is greater than 9.


 * At the moment the else text is also returned for an empty expression&#58;
 * gives


 * Omitting both then text and else text gives no output except possibly an error message; this can be used to check the correctness of an expression, or to check the wording of the error message (emulated assertions, forced errors)&#58;
 * -- no result, hence " 1/" is a correct expression
 * -- "1=2" is a correct Boolean expression (not to be confused with one with the value 1, representing "true")
 * -- "1E2" not allowed in expressions
 * -- "1/0" not allowed
 * -- "1/0" not allowed


 * ("a=b" not allowed, to compare strings use #ifeq)

Example of an application:

For another application, see also.

<h3 style="background: beige">#switch:

compares a single value against multiple others, returning a string if a match is found. The syntax is basically:

&lt;comparison value&gt; | &lt;value1&gt; = &lt;result1&gt; | &lt;value2&gt; = &lt;result2&gt; | ... | &lt;valuen&gt; = &lt;resultn&gt; | &lt;default result&gt;

will search through each value passed until a match is found with the comparison value. When found, the result for that value is returned (the text string after the equal sign). If no match is found, but the last item has no equal sign in it, it will be returned as the default result. If your default result must have an equal sign, you may use :

&lt;comparison value&gt; | &lt;value&gt; = &lt;result&gt; | #default = &lt;default result&gt;

Note that it's also possible to have "fall through" for values (reducing the need to duplicate results). For example:

&lt;comparison value&gt; | &lt;value1&gt; | &lt;value2&gt; | &lt;value3&gt; = &lt;result1, 2, 3&gt; | ... | &lt;valuen&gt; = &lt;resultn&gt; | &lt;default result&gt;

Note how value1 and value2 contain no equal sign. If they're matched, they are given the result for value3 (that is, whatever is in result3).


 * As for #ifeq: the comparison is numerical where possible&#58;
 * gives
 * gives


 * The matched value can be empty, therefore the following constructs are equivalent&#58;
 * gives
 * gives

Comparison of strings is case-sensitive:
 * gives
 * gives
 * gives

This is not to be confused with the fact that parser function names are case-insensitive:
 * gives

To have the #switch statement be case-insensitive, use the construct or
 * gives
 * gives
 * gives

This is usually used in templates, when you want to have case-insensitivity on in param values: | a | b | c = abc or ABC | A | B | C = Memory corruption due to cosmic rays | #default = N/A gives

#switch may be used instead of #ifeq:
 * gives
 * gives

Note that with respect to the the function #switch is basically "wasteful" (only a small part of the transcluded content is "used"), especially if it contains a long list of alternatives. A different method without this problem is using.

<h3 style="background: beige">#time:

is a time and date formatting function (for dates from 1 Jan 1970 only!). The syntax is:

If the time is not specified, the time at which the article is converted to HTML is used. Note that due to caching, this may be up to a week different to the time at which the article is viewed. Manual updates may be required, this can be achieved by saving the page without making any changes (a "null edit") or viewed with  in search string of URL or viewed by a user whose option is "Disable page caching".

The format parameter is a format string similar to the one used by PHP's date.

The following format codes have the same meaning as in PHP. Significant differences from PHP's behaviour, apart from internationalisation (i.e. language and locale differences), should be considered an error of ParserFunctions and should be reported. All numeric format codes return numbers formatted according to the local language, use the xn code described below to override this.

The following format codes are extensions to the PHP syntax:

Any unrecognised character will be passed through to the output unmodified. There are also two quoting conventions which can be used to output literal characters.


 * Characters enclosed in double quotes will be considered literal (with the quotes themselves removed). Unmatched quotes will be considered literal quotes. Example:
 * &rarr;
 * &rarr;
 * Backslash escaping as in PHP's date is supported. \H produces a literal H, \" produces a literal ".

More format codes may be added in the future, as demanded by the users of this extension. This may come in the form of either a more complete implementation of PHP's format codes, or additional "x" codes.

The format of the time parameter is identical to the one used by PHP's function strtotime. It supports both absolute and relative dates, such as "December 11</tt>" and/or "+10 hours</tt>", which can be used for timezone translation. See also the GNU tar manual for more information.

Examples

 * gives ""
 * gives ""
 * gives ""
 * gives "" (32 days ago)
 * gives "" (6 hours later than UTC)
 * gives ""
 * gives "" (17 months ago)

In combination with user-specified date formatting:
 * gives ""
 * gives ""

Both give the user-specified format; the two are different if no preference has been specified.

Range
The range of proper functioning is 01 January 1970 00:00:01 through 19 January 2038 03:14:07, or 1 through $$2^{31}-1$$ seconds after the start of 1970 (the Year 2038 problem). For arbitrary dates that may exceed this range, use date computing templates such as instead.

Dates before 1901 give an error message, dates between 1901 and 1970 even give (perhaps depending on the server) a wrong result (!):



Incomplete dates

 * (not a year but a time: today, 20:07)
 * (19:97 is not a valid time, therefore interpreted as year, giving the current date and time in that year)
 * (19:67 is not a valid time, therefore interpreted as year; this gives a date that is out of range which is changed to the start of the valid range)
 * (start of the month)
 * (specified date of the current year)

February 29
Caution should be taken with February 29, as will vary with the year. For example


 * gives
 * gives

Timezones
Give UTC for a specified time, expressed in time zone UTC-3:

Give the UTC 3 hours ago:

Give the UTC 3 hours before a specified time expressed as UTC:

Give the UTC 3 hours before a specified time expressed in time zone UTC-3:

Thus "-0300" and "-3" add 3 hours, while "-3 hours" subtracts them.

<h3 style="background: beige">#rel2abs:

converts a relative path to an absolute path.

A relative path is a path starting with '/', './', '../'. or is containing '/../' or '/.' or is simply the strings '..' or '.'. if a base path</tt> is given, is should be defined in an absolute syntax.

Example:
 * If standing in Help:Foo/bar</tt> and is calling, the result will be </tt>
 * If standing in Help:Foo</tt> and is calling, the result will be </tt>
 * If standing in Help:Foo</tt> and is calling, the result will be </tt>
 * If calling, the result will be </tt>
 * If calling, the result will be </tt>

There is no checks if the path do exist, for that you might use in combination:
 * gives </tt>
 * gives <tt></tt>

<h3 style="background: beige">#titleparts: returns nr_of_segments slash-separated segments of pagename, starting from the beginning:
 * (full pagename).

returns nr_of_segments slash-separated segments of pagename, starting from the specified starting segment: This gives two parts of the title, starting at the second part. Likewise:

See also r22711 and bug 6067.

Caveats
Like other parser functions the parser functions in this extension are affected by 5678 in a predictable way. Summary: undefined parameters can be overwritten by corresponding parameters, for details see 5678 and Substitution. Substitution is the only case where this is critical with respect to parameter defaults. It doesn't affect defined parameters.

Substitution
Applying "subst:" to a parser function works, provided that there is no space between "subst:" and "#". For details see Help:Substitution. Note that unless a technique like optional substitution is used, substituting a template which uses a parser function does not replace that parser function with its result. This is often undesirable.

See also the previous section.

Tables
Currently wiki pipe table syntax doesn't work inside conditionals, but there are some workarounds. Note that "<tt>|</tt>" and "<tt>=</tt>" were always tricky within templates, this is no new issue.
 * Hide the pipe from parser functions by putting it in a template, e.g..
 * Use html style table syntax instead of wiki style table syntax. NB: $wgUserHtml must be true (this has since been removed, and thus should not need to be set to 'true' - check if it is relevant for your version); it also may be essential to set 
 * See also Help:Table, completely empty rows or columns are not displayed. Empty cells could be also transformed into dummy <tt>&amp;nbsp;</tt> cells on pages not affected by 5569.

If all else fails, try setting in your.

Expressions

 * div is not integer division and is redundant, use / (slash) for real divisions.
 * mod uses PHP's % operator, which is different from modulo-operators in all other programming languages, see also and 6068.
 * mod sometimes returns wrong results for the same input values, see 6356 and /MOD10000. Update: values less than 1E+12 are apparently not affected.
 * Valid #expr: results like are not yet supported as #expr: input:
 * yields .
 * Under certain conditions round 0 results in -0 instead of 0. For an expression x using 0+(x) fixes this oddity.

Conditional whitespace
Because conditionals trim the leading and trailing whitespaces around pipe characters (like named, but unlike unnamed template parameters), inserting a conditional whitespace character is not always simple. If you only want to insert spaces, you can use the HTML entity, which inserts "&#32;".

if you want to insert new lines or other whitespace, you can insert non-printing characters between the pipe and the whitespace:

first paragraph.

first paragraph.

See also Help:Newlines and spaces.

Code execution
In the case of conditional parser functions (<tt>if</tt>, <tt>ifeq</tt>, <tt>ifexist</tt>, <tt>ifexpr</tt>, <tt>switch</tt>), the wikitext for each case (then-part, else-part, etc.) is internally "executed"/"processed"/"evaluated" regardless of whether the condition is satisfied, although the resulting output depends on the condition. This concerns:
 * the pre-expand include size (see w:Wikipedia:Template limits)
 * in the case of conditional links and transclusions: the pagelinks table and the templatelinks table, and as a result:
 * lists of links from, and pages included in, another page (on the edit box of the source page and What links here of the target page
 * - a transclusion in the then-part or else-part etc. in a call of a conditional parser function in a cascade-protected page causes protection of the transcluded page regardless of the condition; see also Help:Cascading protection demo
 * other, see e.g. VariablesExtension
 * other, see e.g. VariablesExtension

One may want to reduce processing to the actually applicable wikitext, to reduce the pre-expand include size, to avoid inapplicable items in the lists of links and inclusions and in the list of wanted pages, and to avoid unnecessary cascading of protection. This can be done by using #ifexpr etc. to select a template or link target, and put the whole parser function call inside the braces or brackets, e.g. instead of. If there is no else-part a dummy template such as can be used:  instead of. If the parameters of a and b are not the same the parser function can be split up into one for the then-part, and one with inverse condition with the else-part becoming then-part (or the same condition and only an else-part):  instead of.

Similarly, in the case of links we can use   instead of. If there is no else-part we can use instead of. In this case we need to duplicate the condition: due to the inner copy of the condition either " a " or "" is produced; the former puts the link in the pagelinks table, the second does not give any entry in the pagelinks table; the outer copy of the condition avoids that "" is rendered if the condition is false.

In the case of nested conditional parser functions we have to copy for each link the whole nesting of conditional parser functions inside the link brackets. Similarly we can do that inside template braces. If " – " is produced this does not give any entry in the templatelinks table, and it is not rendered due to the conditional parser functions around it.

See also.

Installation
Download all 3 of these files and put them in a new directory called ParserFunctions in your extensions directory.


 * Expr.php
 * ParserFunctions.php
 * ParserFunctions.i18n.php

If you don't have php5 use these files instead (older revision):
 * Expr.php
 * ParserFunctions.php

Then put the following at the end of your LocalSettings.php:

require_once( "$IP/extensions/ParserFunctions/ParserFunctions.php" );

If you get errors such as "Warning: require_once(/extensions/ParserFunctions/ParserFunctions.php) [function.require-once]: failed to open stream:", you should substitute the line for:

require_once( 'extensions/ParserFunctions/ParserFunctions.php' );

You can also browse the code tree here:


 * ParserFunctions in MediaWiki SVN

Troubleshooting

 * ''The Current version of ParserFunctions requires php5 to function.

INSTALLATION WARNING: MediaWiki: 1.9.1, PHP: 5.1.2-Debian-0.1~sarge1 (apache2handler) function ctype_alpha not available, had to add the following to Expr.php if ( !function_exists('ctype_alpha') ) { function ctype_alpha($string) { //if ( !preg_match('|[^\pL]|', $string) ) /* Warning: preg_match [function.preg-match]: Compilation failed: PCRE does not support \L, \l, \N, \P, \p, \U, \u, or \X at offset 3 in        /.../extensions/ParserFunctions/Expr.php on line 6 */       /* alternative: */ // if ( !preg_match('|[^A-Za-z]|', $string) ) return true; else return false; } }

If you are using Gentoo Linux's portage system to install php, be sure to have the "ctype" USE flag set for dev-lang/php to avoid the above error.

1.8 and up
All the ParserFunctions work under 1.8 and up, also in the localised forms.

1.7
All the ParserFunctions work under 1.7, but only in English. However, the extension may cause problems when used together with the Cite extension; cf. . For the #time parser functions to work, you must also download SprintfDateCompat.php to the extensions/ParserFunctions directory.

1.6
When including the ParserFunctions in 1.6, some notices may be shown. Removing the following line (line 10) in ParserFunctions.php fixes the problem:

$wgHooks['LanguageGetMagic'][]      = 'wfParserFunctionsLanguageGetMagic';

For the #time parser functions to work, you must also download SprintfDateCompat.php to the extensions/ParserFunctions directory. But actually that won't fix the problem.

Most ParserFunctions (except #if, which does not work at all) work just as well on MediaWiki 1.6, but the syntax of ParserFunctions is without the '#' character. If you want to use the '#' character, find this section of ParserFunctions.php: $wgParser->setFunctionHook( 'expr', array( &$wgExtParserFunctions, 'expr' ) ); $wgParser->setFunctionHook( 'if', array( &$wgExtParserFunctions, 'ifHook' ) ); $wgParser->setFunctionHook( 'ifeq', array( &$wgExtParserFunctions, 'ifeq' ) ); $wgParser->setFunctionHook( 'ifexpr', array( &$wgExtParserFunctions, 'ifexpr' ) ); $wgParser->setFunctionHook( 'switch', array( &$wgExtParserFunctions, 'switchHook' ) ); $wgParser->setFunctionHook( 'ifexist', array( &$wgExtParserFunctions, 'ifexist' ) );

Then, replace it with this:

$wgParser->setFunctionHook( '#expr', array( &$wgExtParserFunctions, 'expr' ) ); $wgParser->setFunctionHook( '#if', array( &$wgExtParserFunctions, 'ifHook' ) ); $wgParser->setFunctionHook( '#ifeq', array( &$wgExtParserFunctions, 'ifeq' ) ); $wgParser->setFunctionHook( '#ifexpr', array( &$wgExtParserFunctions, 'ifexpr' ) ); $wgParser->setFunctionHook( '#switch', array( &$wgExtParserFunctions, 'switchHook' ) ); $wgParser->setFunctionHook( '#ifexist', array( &$wgExtParserFunctions, 'ifexist' ) );

In some cases it just helps to add the default language to the header function on line 169

function wfParserFunctionsLanguageGetMagic( &$magicWords, $langCode='en' ) {

A simple fix for #if in this version -

Replace:

function ifHook( &$parser, $test = , $then = , $else = '' ) { if ($test !== '') {

on line 57 with:

function ifHook( &$parser, $test = , $then = , $else = '' ) { if ( (string)$test !== '' ){

Although the above fix doesn't appear to work on PHP 4.3.9

Alteration and localization
The file ParserFunctions.i18n.php contains the localization for the parserfunctions. Adding more may be done by editing it according to the following guidelines.

In the latest MediaWiki versions, a new case for the language code should be added to function efParserFunctionsWords, for example: $words['fr'] = array(	   'expr'       => array( 0, 'calcul',        'expr' ),            'if'         => array( 0, 'si',            'if' ),            'ifeq'       => array( 0, 'si-similaire',  'ifeq' ),            'ifexpr'     => array( 0, 'si-calcul',     'ifexpr' ),            'switch'     => array( 0, 'achemine',      'switch' ),	    'default'    => array( 0, '#default' ); // same            'ifexist'    => array( 0, 'si-existe',     'ifexist' ),	    'time'       => array( 0, 'temps',         'time' ),	    'rel2abs'    => array( 0, 'rel-a-abso',    'rel2abs' ),	    'titleparts' => array( 0, 'part-titre',    'titleparts' ),        );
 * Please note that every hook established in function wfSetupParserFunctions must have an associated magic-words statement for it to work. Failure to associate a magic word may result in PHP warning messages (see bug #10166).

This method may not work on older version. Alternatively, hooks may be established in function wfSetupParserFunctions, as follows. // fr $wgParser->setFunctionHook( '#calcul', array( &$wgExtParserFunctions, 'expr' ) ); $wgParser->setFunctionHook( '#si', array( &$wgExtParserFunctions, 'ifHook' ) ); $wgParser->setFunctionHook( '#si-similaire', array( &$wgExtParserFunctions, 'ifeq' ) ); $wgParser->setFunctionHook( '#si-calcul', array( &$wgExtParserFunctions, 'ifexpr' ) ); $wgParser->setFunctionHook( '#achemine', array( &$wgExtParserFunctions, 'switchHook' ) ); $wgParser->setFunctionHook( '#si-existe', array( &$wgExtParserFunctions, 'ifexist' ) ); $wgParser->setFunctionHook( '#temps', array( &$wgExtParserFunctions, 'time' ) );
 * Tested on MediaWiki 1.6.10