WLang is a a reusable and extensible code generator, also known as a templating engine. Motivation for it can be found at www.revision-zero.org/wlang. The current file is the reference of the tool.
Probably the first section to read! Basic usage of wlang is explained here and pointers are given to continue your learning.
Standard rulesets are specified. As most of them are included in standard dialects, looking at standard rulesets is the quickest way to learn all of them at once.
Standard dialects are described. This page also provides useful cheatsheets of available tags in standard dialects.
Somewhat more tricky but powerful. The notion of hosting language is explained more deeply. Implementing you own hosting language abstraction (advanced topic) sometimes leads to cleaner and cross-implementation templates.
wlang comes with a terminology, knowing it will make your reading easier.
If you plan to create your own tags, it can be useful to know what is allowed and what is not. This pages covers this topic.
This document is a simple .html file without external dependencies (embedded CSS and javascript). As it contains several cheatsheets, you can simply save it on your harddisk without having to be online to browse the documentation. It has been generated using wlang itself using the following command:
wlang specification.wtpl
The file ‘specification.wtpl’ is almost empty and other files next to it are all kept simple and written in the most appropriate format for the task at hand (YAML for structured parts, RDoc for text sections, sometimes YAML embedding short sentences writted in RDoc style, etc.). One way to learn wlang quickly is to download the source distribution and to look how this is made possible ;-)
This reference document is under a Creative Commons Licence 2.0 contract. You can use it, redistribute it and modify it providing that you keep a reference to the original licensor (namely, the ‘University of Louvain’ or ‘Bernard and Louis Lambeau’).
Enjoy wlang !
The reference implementation of wlang, implemented in Ruby, is
freely available as a ‘wlang’ gem (under MIT licence).
Use 'gem install wlang' to install it. For repository and bug
tracker visit us on github
We don’t have another implementation up to now. If you plan to start one in another language, let us know!
wlang has been initially designed by Bernard and Louis Lambeau during the implementation of w@w, yet another web framework (proof of concept). They are also maintainers of the reference implementation.
This work is supported by the department of computer science of the University of Louvain (EPL/INGI, Universite Catholique de Louvain, UCL, Louvain-la-Neuve, Belgium).
This work was also partially supported by the Regional Government of Wallonia (ReQuest project, RW Conv. 315592 and GISELE project, RW Conv. 616425) and the MoVES project (PAI program of the Belgian government).
wlang helps you generating code, in a broad sense. It was originally the templating engine of w@w, a proof-of-concept web framework. While more powerful than the original version, the templating engine ability of wlang has been kept unchanged. For this reason, generating html code with wlang is probably a bit more mature than generating ruby, java or sql code, to take some examples of what wlang can do. It is the author opinion that wlang will also become mature quiclky for these tasks because of its foundations: its engine is generic (in a sense, wlang does not really care about what it generates) but is fully and easily configurable. Generation of html files is mature because wlang has been used a lot for such a job; thus its authors have acquired experience of what is useful when generating simple as well as complex html files. This experience led us to a mature configuration of the wlang engine for generating html files, as the following paragraph illustrates (for people interested in generating code in other languages than html, don’t stop your reading here: the paragraph immediately following contains information for you!)
Consider this file for example, which is completely self-contained. It consists of several parts, some of them being structured - the tables for example - while others are not. It also embeds a complete CSS stylesheet and some javascript functions. We have not written this file manually, nor do we maintain it this way. In fact, this reference document is entirely generated by wlang itself from separated parts written mainly in yaml and rdoc files. Also, the cheatsheets given later contains a lot of examples. To ensure that all of them are correct, we simply ask wlang to compute them during generation (technically, we say that wlang naturally allows metaprogramming). Lastly, if wlang can be used inside a web framework, it can also be used as a standalone (commandline) tool for generating single files like this one or multiple files, even if all of them are of different nature.
Maybe you are looking for a code generator for another language than html (which one does not really care, unless really specific; we call it the target language)? Don’t be affraid by our previous words about wlang’s maturity: even in such a case, wlang is your friend. Start with an existing dialect (see later about dialects), which will provide basic utilities for starting and try to identify common patterns when you use them. Then simply create special shortcuts that are more friendly to use than combining several existing utils … you are on the way of creating your own mature and reusable dialect for that target language. In this case, don’t forget to share it …
The wlang grammar used to write a template is generic and simple: every character stands for itself (meaning that it is reproduced exactly when the template is instantiated) except tags (and their associated blocks, enclosed between ’{’ and ’}’) that are replaced by what is called the replacement value. Consider the following example:
<html> <head> <title>${title}</title> </head> <body> <h1>Hello *{authors as who}{${who}}{, } !</h1> </body> </html>
Assume that we have some instantitation data through the following hash (or something similar, like a YAML file):
{"title" => "Short overview of wlang", "authors" => ["blambeau", "llambeau", "ancailliau"]}
When instantiated this template will produce exactly the same html file except for special tags ${title} and *{whos as who}{${who}}{, } that will be replaced by 'Short overview of wlang' and 'blambeau, llambeau, ancailliau', respectively. A lot of tags is available, each of them being designed for a specific task: inserting the value of a variable, iterating over collections, including another file, dynamically loading instantiation data, etc. All of these things are commonly proposed by templating engines and wlang is one of them … However, wlang is a bit different as will quickly appear.
Indeed (and maybe surprisingly) wlang can also behave really differently on the same template: replacing ${title} but not *{...} or the converse, or not replacing anything, or replacing both tags but not ${who}, etc. All of this is possible in wlang. The magic relies under the notion of dialect, which you need to understand.
The notion of dialect drives the recognition of tags as well as their replacement during instantiation. Dialects are what makes wlang really powerful: if instantiated as being written in the wlang/xhtml dialect, the template above will give the result mentionned previously. In contrast, if written in wlang/dummy the template will be reproduced whitout any change (no tag replacement at all). This behavior is not hardcoded; it results from the definition of wlang (standard) dialects: wlang/xhtml define special meanings for ${...} and *{...}{...}{...} while wlang/dummy does not.
The replacement of a given tag during instantiation is computed by what we call the rule attached to the tag (keeping rules and tags as different concepts leads to another feature of wlang: you can reuse rule implementations and attach them to other tags than those proposed). A dialect comes with a set of (tag, rule) pairs that determine its replacement behavior. Such a set is called a ruleset; for easier reuse, standard rulesets are already implemented. A dialect is a packaging of standard rulesets (and maybe implements specific tag/rule pairs) designed for generating code in a given target language.
A complete wlang implementation already provides standard dialects for common tasks: creating html pages, building SQL queries, generating code in Ruby or in another language, etc. Each dialect comes with special tags that are useful for the task at hand (a tag for back-quoting values is useful for creating SQL queries but does not really makes sense for generating an html page where, in contrast, a tag for encoding entities is probably welcome). Such an implementation also allows you to extend standard dialects and to create your own dialect by implementing specific tags and rules or by reusing existing ones. Lastlty, the dialect in used during instantiation can be changed dynamically (explicitly, by using the %{dialect/qualified/name}{...} standard tag and implicitly, when rules parse their blocks).
To learn more about standard dialects and reusable rules, read the ‘Dialects’ and ‘Rulesets’ pages of this documentation.
The (abstract) wlang grammar rules what forms a valid template. At first glance, this grammar does not depend on the dialect that is used for instantiation. It is simple, but comes with some constraints that are explained below:
block delimiters are ’{’ and ’}’ by default; wlang can be configured to use ’(’ and ’)’ or ’[’ and ’]’ instead. However, block delimiters are template-specific: only one kind of delimiters can be used inside the same template.
block delimiters must always be paired, even when not used for delimiting blocks. If an opening or closing delimiter is not paired, it must be escaped with a backslash, which will not be reproduced. If you want a backslash to appear before a block delimiter in the instantiation result, use a double backslash.
if a given tag has a special meaning in the current dialect and you don’t want it to be replaced by wlang you can escape it with a backslash as well (the backslash will not be reproduced).
some tags (precisely: some rules associated with tags) require multiple blocks (like *{...}{...}{...} in wlang/xhtml for example, with the third block bein optional). In such a case no character is allowed between the end of a block ’}’ and the start of the next one ’{’, not even spaces or a carriage return. In other words, multiple blocks (that must be interpreted as such) must touch each others using ’}{’ precisely, as ilustrated below. If a non-optional block is missing a parse error is raised by the wlang implementation.
*{authors as who}{${who}}{, } -> blambeau, llambeau, ancailliau *{authors as who}{${who}} {, } -> blambeaullambeauancailliau {, } *{authors as who} {${who}}{, } -> parse error 1:18, missing block 2 in *{...}{...}
In addition to these constraints, dialects and the hosting language may impose restrictions on what can be put inside specific blocks of tags/rules (for example, ‘authors as who’ is valid as first tag of *{...}{...} but not every string is, of course). These constraints are not specific to the wlang grammar per se and are explained in the ‘Rulesets’, ‘Dialects’ and ‘Hosting language’ pages of this document.
Standard ruleset are designed to be reusable: including them in your own dialect is made easy by a typical wlang implementation. Some of them are also included by standard dialects.
First of all, focus on the examples; they are written to let you learn wlang quickly and deeply. Some of them are a bit difficult to understand but they are representative of wlang powerfulness (don’t be affraid: in practice, some constructions are never used). Don’t forget that the wlang/dummy dialect does not recognize any tag. We also assume instantiation data to be the following hash:
{"name" => "O'Neil", "author" => "blambeau" "authors" => ["blambeau", "llambeau", "ancailliau"]}
Moreover, the dialect column in the examples is important; wlang behaves differently in different dialects. When the dialect does not care, we use wlang/* which means ‘in any dialect that includes this ruleset’.
Next, certain rule definitions are given as shortcuts for longer expressions, involving other tags. This is somewhat representative of wlang usage, even if these rules are not actually implemented this way (mainly for efficiency concerns). Once again, understanding shortcuts will help you mastering wlang! In definitions (textual as well as shortcuts), we use #1, #2, and #3 to refer to the content of the blocks. Those identifiers are not real wlang constructs, but are only used here for easier explanations (for those who know this kind of vocabulary: they are part of the meta-language, not the language per se).
Lastly, dialect names that appear in rule signatures are to be interpreted as an implicit dialect modulation: the corresponding block (often the first one) is not instantiated in the current dialect but in the one specified by the signature. In contrast, when we use ’…’ it means that the corresponding block is simply instantiated in the current dialect. Implicit dialect modulation is in fact natural: if a block expects an uri for example, the easiest way is to give it exactly: <<{a/file/to/include.txt}. But you can even compute it using wlang, as illustrated by the example below. In complex situations you will probably be happy to use a dialect that helps you doing so (think at all blocks that expect an expression in the hosting language, for example)!
# Concatenates all files of the 'files' array variable *{files as f}{<<{+{f}}}
The Basic ruleset is commonly installed on any dialect and provides access to wlang foundations inside your template: requesting the hosting language to execute some expression, changing the current dialect and encoding text.
signature | name | definition |
---|---|---|
!{wlang/hosted} | execution | Instantiates #1, looking for an expression of the hosting language. Evaluates it, looking for any object. Converts it to a string (using to_s for example if Ruby is the hosting language) and returns the result as replacement value. |
%{wlang/active-string}{...} | modulation | Instantiates #1, looking for a dialect qualified name. Instantiates #2 according to the rules defined by that dialect and returns the #2’s instantiation as replacement value. |
^{wlang/active-string}{...} | encoding | Instantiates #1, looking for an encoder qualified name. Instantiates #2 in the current dialect. Encode #2’s instantiation using encoder found in (#1) and returns encoding as replacement value. |
%!{wlang/active-string <using>? <with>?}{...} | recursive-application | Instantiates #1, looking for a dialect qualified name. Instantiates #2 in the current dialect. Instantiates #2’s instantiation in the dialect found in #1, using context installed by ‘using …’ and ‘with …’. Returns this instantiation as replacement value (this really advanced rule allows metaprogramming). |
${wlang/hosted} | injection | Same semantics as execution (intended to be overrided). |
+{wlang/hosted} | inclusion | Same semantics as execution (intended to be overrided). |
dialect | wlang expression | replacement value |
---|---|---|
wlang/active-string | Hello !{name} | Hello O'Neil |
wlang/active-string | Hello %{wlang/dummy}{!{name}} | Hello !{name} |
wlang/dummy | Hello %{wlang/dummy}{!{name}} | Hello %{wlang/dummy}{!{name}} |
wlang/active-string | Hello ^{plain-text/upcase}{${name}} | Hello O'NEIL |
wlang/ruby | puts +{name} | puts "O'Neil" |
wlang/ruby | puts +{authors} | puts ["blambeau", "llambeau", "ancailliau"] |
Almost all languages require escaping/encoding in specific situations: quoted string literals always come with an escaping mechanism (unfortunately different from one language to another), html requires entities-encoding, etc. The Encoding ruleset proposes shortcut tags for encoding. Note that these shortcuts are written in such a way that they don’t depend on the effective dialect. wlang hides language and vendors differences!
signature | name | definition |
---|---|---|
&{...} | main-encoding | ^{+{@parser.current_dialect}/main-encoding}{#1} |
&;{...} | entities-encoding | ^{+{@parser.current_dialect}/entities-encoding}{#1} |
&'{...} | single-quoting | ^{+{@parser.current_dialect}/single-quoting}{#1} |
&"{...} | double-quoting | ^{+{@parser.current_dialect}/double-quoting}{#1} |
${wlang/hosted} | injection | &{+{#1}} |
'{wlang/hosted} | single-quoted | '&'{+{#1}} (first single quote is kept in the result) |
"{wlang/hosted} | double-quoted | "&"{+{#1}} (first double quote is kept in the result) |
dialect | wlang expression | replacement value |
---|---|---|
wlang/xhtml | Hello &{name} | Hello name |
wlang/xhtml | Hello &{<script>} | Hello <script> |
wlang/xhtml | Hello &;{<script>} | Hello <script> |
wlang/ruby | puts 'Hello &'{name}' | puts 'Hello name' |
wlang/ruby | puts 'Hello &'{!{name}}' | puts 'Hello O\'Neil' |
wlang/ruby | puts 'Hello ' << '{name}' | puts 'Hello ' << 'O\'Neil' |
wlang/sql | ... WHERE name='{name}' | ... WHERE name='O\'Neil' |
wlang/sql/sybase | ... WHERE name='{name}' | ... WHERE name='O''Neil' |
Instantiating conditionally and iterating collection elements are common code generation tasks. The Imperative dialect provides these features.
signature | name | definition |
---|---|---|
?{wlang/hosted}{...}{...} | conditional (third block is optional) |
Instantiates #1, looking for an expression in the hosting language. Evaluates it, looking for a boolean value (according to boolean semantics of the hosting language). If true, instantiates #2, otherwise instantiates #3 if present, returning instantiation as replacement value. |
*{wlang/hosted <as x>?}{...}{...} | enumeration (third block is optional) |
Instantiates #1, looking for an expression in the hosting language. Evaluates it, looking for an enumerable. Iterates all its elements, instantiating #2 for each of them (the iterated value is set under name x in the scope). If #3 is present, it is instantiated between elements. Replacement is the concatenation of all these instantiations. |
dialect | wlang expression | replacement value |
---|---|---|
wlang/* | ?{true}{then}{else} | then |
wlang/* | ?{/th/ =~ "not tat"}{then}{else} | else |
wlang/* | ?{authors.include? "blambeau"}{yes}{no} | yes |
wlang/* | [*{authors as a}{"{a}"}{, }] | ["blambeau", "llambeau", "ancailliau"] |
Complex templates come with specific needs. The ability to manipulate the context and the current scope is provided by the Context ruleset. All are variants of ‘saving previous instantiations’ in scope variables…
signature | name | definition |
---|---|---|
={wlang/hosted <as x>}{...} | assignment (second block is optional) |
Instantiates #1, looking for an expression in the hosting language. Evaluates it, looking for any object. Without second block, expands the current scope with ‘x’ being bound to evaluation result. Otherwise, branches the current scope for the second block instantiation only and bind ‘x’ the same way (i.e. x will not be available outside the second block). Returns an empty string as replacement value. |
%={wlang/active-string <as x>}{...}{...} | modulo-assignment (third block is optional) |
Instantiates #1, looking for a dialect qualified name. Instantiates #2 according to the rules defined by that dialect. Without third block, expands the current scope with ‘x’ being bound to #2’s instantiation. Otherwise, branches the current scope for the third block instantiation only and binds ‘x’ the same way (i.e. x will not be available outside the third block). Returns an empty string as replacement value. |
#={wlang/active-string}{...}{...} | block-assignment (third block is optional) |
%={+{@parser.current_dialect} as #1}{#2}{#3} |
^={wlang/active-string <as x>}{...}{...} | encoding-assignment (third block is optional) |
%={+{@parser.current_dialect} as x}{^{#1}{#2}}{#3} |
dialect | wlang expression | replacement value |
---|---|---|
wlang/* | ={name as n}{Hello !{n}} | Hello O'Neil |
wlang/* | ={name as n}Hello !{n} | Hello O'Neil |
wlang/* | #={name}{blambeau}{Hello !{name}} and !{name} | Hello blambeau and O'Neil |
wlang/* | #={name}{blambeau}Hello !{name} and !{name} | Hello blambeau and blambeau |
wlang/* | ={author as name}{Hello !{name}} and !{name} | Hello blambeau and O'Neil |
wlang/* | ={author as name}Hello !{name} and !{name} | Hello blambeau and blambeau |
wlang/* | %={wlang/dummy as hello}{Hello !{name}}{!{hello}} | Hello !{name} |
wlang/* | ^={plain-text/upcase as name}{!{author}}{Hello !{name}} and !{name} | Hello BLAMBEAU and O'Neil |
wlang/* | ^={plain-text/upcase as name}{!{author}}Hello !{name} and !{name} | Hello BLAMBEAU and BLAMBEAU |
The Buffering ruleset is probably one of the more useful. It allows you to load text and data files, to change the current output buffer (for generating multiple files for example) and even to start the instantiation on other templates.
signature | name | definition |
---|---|---|
<<{wlang/uri} | input | Instantiates #1, looking for an uri. Returns the text content of the found uri (#1) as replacement value. |
>>{wlang/uri}{...} | output | Instantiates #1, looking for an uri. Instantiates #2 in the current dialect, using the file found in #1 as output buffer. Returns an empty string as replacement value. |
<<={wlang/uri <as x>}{...} | data-assignment | Instantiates #1, looking for an uri. Loads data provided by this uri, based on the file extension (typically .yml or .rb). Without second block, expands the current scope with ‘x’ being bound to the data. Otherwise, branches the current scope for the second block instantiation only and binds ‘x’ the same way (i.e. x will not be available outside the second block). Returns an empty string as replacement value. |
<<+{wlang/uri <using>? <with>?} | input-inclusion | Instantiates #1, looking for an uri. Instantiates the wlang template at this location (the dialect is infered from the file extension) in a fresh new scope built from the with expression. Returns this instantiation as replacement value. |
Includes Basic, Imperative, Buffering, Context
Includes Basic
Includes Basic, Encoding, Imperative, SQL
Includes Basic, Encoding, Imperative, SQL
Includes Basic, Imperative
Includes Basic, Encoding, Imperative, Buffering, Context, XHtml
Includes Basic, Encoding, Imperative, Buffering, Context, YAML
Includes Basic, Encoding, Imperative, Buffering, Context, Ruby
Includes Basic, Encoding, Imperative, Context, Hosted
term | definition | example |
---|---|---|
template | Source code respecting the wlang grammar, and attached to a given wlang dialect. | Hello ${name} |
dialect | Basically, dialect is used as a synonym for (programming) language. However wlang uses a tree of dialects, allowing specializations: sql/sybase for example is the qualified name of a sub-dialect ‘sybase’ of the ‘sql’ dialect. Dialects come with associated encoders. | sql/sybase |
wlang dialect | When we talk about a wlang dialect, we are actually refering to some specialization of the wlang tag-based grammar: wlang/xhtml for example is the templating language wlang proposes to generate xhtml pages. An example of source code in that dialect has been shown before. In addition to its encoders a wlang dialect comes with its sets of tags and associated rules. | wlang/xhtml |
encoder | Text transformation (algorithm) applying some encoding conventions of a portion of a the target language generated by a dialect. HTML entities-encoding, SQL’s back-quoting are examples of encoders. Encoders are accessible through their qualified name (dialect/encoder). | xhtml/entities-encoding sql/single-quoting |
ruleset | Reusable set of tags associated to rules. | Imperative ruleset Encoding rulset |
wlang tag | Special tags in the template, starting with wlang symbols and a number of wlang blocks. A tag is associated with a wlang rule. | ${...} ?{...}{...}{...} |
rule | Transformation semantics of a given tag. When wlang instantiates a template it simply replaces wlang tags by some replacement value (which is always a string). This value is computed by the rule attached to the tag. Rule definition (see Rulesets tab on top of the page) explicitly describes the number of blocks it expects, in which dialect they are parsed and instantiated and the way the replacement value is computed. | ^{wlang/active-string}{...} Instantiates #1, looking for an encoder qualified name. Instantiates #2 in the current dialect. Encode #2’s instantiation using encoder found in (#1) and return the result as replacement value. |
context | Some rules allow code to be executed in the hosting language (the definition explicitly announce it by putting wlang/hosted in the corresponding block). When doing so, this code is in fact executed in a given context that provides the execution semantics. | |
hosting language | language (or framework) that executes wlang. More precisely, the hosting language is the one that rules what is considered as an executable expression in tags that relies on some execution semantics (like !{…} for example). See the ‘Hosting language’ section to learn more. | ruby |
name | symbol | meaning | remark |
---|---|---|---|
exclamation mark | ! | execution | should never be overrided as single |
caret/circumflex | ^ | explicit encoding | should never be overrided as single |
percent | % | modulation | should never be overrided as single |
double quote | " | double-quoting | |
dollar | $ | main-encoding | |
ampersand | & | encoding | |
single quote | ' | single-quoting | |
asterisk | * | iteration | |
plus | + | inclusion | |
question mark | ? | condition | |
at symbol | @ | linking | |
tilde | ~ | matching | |
number sign | # | ||
comma | , | ||
minus (dash) | - | ||
dot | . | ||
forward slash | / | ||
colon | : | ||
semi-colon | ; | ||
equal sign | = | ||
less than | < | ||
greater than | > | ||
vertical bar | | | ||
underscore | _ | cannot be used as tag symbol; reserved for escaping in future versions | |
back slash | \ | cannot be used as tag symbol; reserved for escaping in current version | |
left parenthesis | ( | cannot be used as tag symbol; reserved for block delimiter | |
right parenthesis | ) | cannot be used as tag symbol; reserved for block delimiter | |
left bracket | [ | cannot be used as tag symbol; reserved for block delimiter | |
right bracket | ] | cannot be used as tag symbol; reserved for block delimiter | |
left brace | { | cannot be used as tag symbol; reserved for block delimiter | |
right brace | } | cannot be used as tag symbol; reserved for block delimiter |