Extended template syntax (a proposal and prototype)

From Meta, a Wikimedia project coordination wiki

This suggestion is effectively the sum of the suggestions made by User:Gwalla and User:Benc at Extended template syntax. It contains a few additional features which seems very useful to make a relatively complete extention to the syntax of templates.

See Bugzilla for the prototype code.

Variable number of parameters[edit]

In the calling syntax a variable number of parameters can very easily be achieved by giving the same parameter name more than one value. This allows for cases where one can still have other named parameters, plus one or more vectors of parameters. It is required that not all template parameters be thrown into one group, but that those template parameters should be grouped that logically belong together. This is achieved via the name equality.

For example, the template newPage has four parameters, called {{{title}}}, {{{cat}}}, {{{shortcut}}} and {{{author}}}. Both {{{cat}}} and {{{author}} can have multiple values. This is achieved with:

  {{newPage|title=Test|cat=Book|cat=Doc|cat=Tut|shortcut=NP|author=Adam|author=Eve}}

For those parameters that has more than one value, a number of additional operations are possible.

  • {{{parm}}} is the last element specified (backwards compatible to existing behaviour)
  • {{{parm##}}} expands into the number of values in the array, if parm is not an array, it returns 1
  • {{{parm#n}}} returns the n'th element, from 1 to ##; for n<1 and n>## no expansion is done
  • {{{parm#*}}} is the array of elements, expanded to a space separated list
  • {{{parm#|}}} is the array of elements, expanded to a name=value separated by | list; required to use the parameters in a sub call
  • {{{parm#'''N'''}}} is the last element in the array (=={{{parm}}}, elegant alternative writing form)

One of the problems we face is that of knowing whether a parameter will be used in single or array mode. There is no declaration of template parameters. For example, given {{x|a=b}} does the template contain only code of the form "...{{{a}}}...", or does the template also contain code of the form "...{{{a#1}}}...(max: {{{a##}}})...". With this uncertainty, each parameter must considered and handled asif it is an array situation. Which effectively means we are using a brute force technique at implementation level to expand each parameter with each default form p, plus then at a minimum p#1=p, p##=1, p#*=p and p#|=p=v. To prevent any parser attachs with templates with large number of parameters, we are going to limit the number of template parameters to a sanity value, say 50.

The alternative would be to consider a writing style whereby the user in calling already expresses the array of parameters versus one single parameter. Then the parameter explosion is done only for those that look like arrays. One idea:

  {{newPage|title=Test|{{{cat=Book|Doc|Tut}}}|shortcut=NP|{{{author=Adam|Eve}}} }}

For the moment we are going to limit the number of parameters and use brute force to achieve our goal.

Finally, although there is nothing that prevents this from also working for unnamed parameters, example {{{1}}}, we will implement and limit this new functionality only to named parameters. No need to make to complicated.

Loops[edit]

Often is required to do a specific operation for all attributes. For this, a loop operator is defined. The syntax is:

  {{{loop|parm}}}
    .... {{{parm#}}} ... 
  {{{endLoop}}}

Notice that the loop operator uses three braces so as to associate this with variable processing within a template. It has the (small?) potential for name clashes with parameters used in the template. The constrain that must be accepted is that once one uses a loop operator within a template, one should not have any parameter named {{{loop}}} or {{{endLoop}}}.

Each loop element is addressed with {{{parm#}}}. (One idea could be to use {{{#}}}, but this fails for nested loops.)

For example:

   {{{loop|title}}}
     '''{{{title#}}}''' (will be only one; same as simply using {{{title}}})
   {{{endLoop}}}

   {{{loop|cat}}}
     {{{loop|author}}}
       [[Category:newPages {{{cat#}}} {{{author#}}}]]
     {{{endLoop}}}
   {{{endLoop}}}

The last example is slightly forced, but should just show the usage of the loop operator within a nested loop, and motivate the use of a named replacement variable versus the unnamed form.

If statements[edit]

If addition, a simple form of IF statement is defined:

    {{{if|expression}}}
       ...
    {{{else}}}
       ...
    {{{endif}}}

For the expression there are a number of use-cases that it should enable:

  • Test whether a parameter is set (or defined)
  • Test whether a parameter has a specific value
  • Enable the testing of multiple conditions using AND and OR. Theorectically this can be achieved by nesting new IF statements into the IF or ELSE blocks. Adding support for these two keywords is still within scope without making the solution space to complex.

A few examples:

  {{{if|{{{title}}}=""}}}
    Document has no title! Hmmm....
  {{{endif}}}

  {{{if|{{{title}}}<>""}}}
    '''Title:''' {{{title}}}
  {{{endif}}}

  {{{if|{{{colour}}}=red OR {{{colour}}}=green OR {{{colour}}}=blue}}}
    primary colour!
  {{{endif}}}

  {{{if|{{{author#}}}=Adam AND {{{author#}}}=Eve}}}
    must be a bible story.
  {{{endif}}}

  {{{if|{{{cat#}}}=Book}}}
    there must be more chapters around here
  {{{else}}}
    looks like this is it
  {{{endif}}}

The expression of the if-statement has the following format:

  expression = boolean_expression [AND|OR boolean_expression]
  boolean_expression = X operator X
  operator = = | <>
  X = parameter | value
  value = text|"" (symbolizes an empty parameter)
  parameter = {{{p}}} | {{{p#}}} 

Some remarks:

  • The test whether a parameter is set is done with ="" (not set) or <>"" (is set). Theorectically this is a logical writing condition only. Actually when the template is processed, and a parameter is not set, then on one side of the operation is will be the {{{parm}}} string. If this is detected, it is replaced with "" and the comparison completed.
  • We do not distinguish between the case where a parameter is not given and the case where the parameter is specified but without a value. In both cases the parameter has effectively no value.
  • It does not make a difference whether operation is written as {{{colour}}}=red or red={{{colour}}}.
  • In cases where the parameter is actually an array of values, we have to distinguish between the case where {{{parm}}} and {{{parm#}}}. The first case is used for the only value (non-array) or as the last value (current backwards compatible mode). The later (similar to indicating the loop variance) method implies that all values from the array must be tested one by one against either a fixed value or against another array (in which case the two arrays are compared). For example, the expression {{{cat}}}=Book will test the last set value, whereas {{{cat#}}} will test each value from {{{cat#1}} upto {{{cat#N}}} against Book, and will be true if any one of the values equal Book. Of course, one could also write {{{cat#3}}}=Book to test one specific value.
  • What is not supported is a technique to group parts of the expression so that the AND and OR sequences can be evaluated differently. This is considered to much complexity, making the solution space far exceed the problem space. What we do not want to do, is use the power of the programming language to evaluate the expression. This just opens the door for all problems are not required. Thus, we do a simple left to right evaluation of the expression. Also, no shortcut in the expression valuation is planned at the moment (although this can always be added later without affecting the current usage). It is recommended (but not enforced) that only AND or OR sequences be used in one expression.
  • The support of AND and OR sequences makes it easy to write code that reflects a contains semantic (p=a OR p=b OR p=c) and also the not-contains semantic (p<>a AND p<>b AND p<>c).

Summary[edit]

In the first instance one must look at the size of the problem space. Already templates within the MediaWiki is very powerful. Wikis inherently process and store text, and do not really require a complex scripting language (yet!). To introduce any operators that are more complex than described here requires a complete parser and state machine. Having already just a loop and if operator implies a solution that can handle loops in if-statements and if-statements in loops, all of this nested! The above suggestion has been sized to be still implementable but not require a major project.