Wyjec is general purpose expression language that can be used in wide range of applications that extend it with domain-specific functions. This short tutorial describes most basic features of Wyjec that are common across all Wyjec applications, without going into implementation details or describing any application-specific features.
Let's start with the simplest form of an expression - arbitrary constant string. Constant strings are basic building blocks for Wyjec expressions, playing similar role to constant numbers in arithmetic expressions. During processing, constant string evaluates to itself. Below you can see an example of Wyjec expression consisting of single constant string and the result of its evaluation.
Of course, using expression language only with constant strings makes little sense (if at all). Usually you want to be able to transform these constant strings to other values that would form the result of expression evaluation. This leads us to another type of basic building blocks of Wyjec expressions: function calls.
Function call is a mechanism which allows you to process some values (e.g. constant strings) with one of functions defined in Wyjec application that processes the expression. Values passed to a function are known as function parameters. The result of function call is a value that may be used as a parameter for another function call or may be used as part of the result of expression evaluation.
In order to use function call in Wyjec expression you must use special syntax so it can be distinguished from constant string. A function call consists of the following elements occurring in specified order and immediately following each other:
The following sample expression contains a call to a function named "upper" with single parameter - constant string "Wyjec". Function "upper" is one of standard Wyjec functions. It accepts exactly one string as a parameter and returns copy of the parameter with all lower case letters converted to upper case.
Note, that sequence of parameters is optional part of function call, because some functions don't accept any parameters or don't require them. For example, standard Wyjec function named "vbar" doesn't accept any parameters and always returns string value "|". Here's an example of expression consisting of "vbar" function call.
Calls to functions accepting many parameters are not different from single-parameter case. General rule still applies: each parameter should be preceded with "|" character. One of standard Wyjec functions that accept many parameters is "+". It concatenates all strings given as parameters into single string and then returns it as the result. The following example of Wyjec expression consists of "+" function call with three parameters (the second parameter is single space character).
While it may be non-obvious, function's parameter doesn't have to contain any characters - it may be empty. Of course, even empty parameters have to be preceded with "|" character, otherwise it wouldn't be possible to recognise them. For example, you can extend the previous expression with "+" function call by adding some empty parameters.
The above expression contains function call with six parameters, four of which are empty (2nd, 3rd, 5th, 6th). The result of concatenation is the same as if all empty parameters were removed.
As I already mentioned, the result of function call is a value that may be used as a parameter for another function call. Such construct is known as nested function call and allows for arbitrarily complex expressions. You can build an expression with nested function call by combining two previously demonstrated expressions.
The above expression contains a call to "+" function with three parameters, one of which is "vbar" function call. The actual value passed as second parameter to "+" will be the result of evaluation of nested "vbar" function, which is simply "|".
As I stated before, you can freely nest any function call in another function call as a parameter so nothing prevents you from nesting previously demonstrated (nested) function call as a parameter for "upper" function.
During evaluation of the above expression, "upper" function will the receive result of "+" function call as a parameter and process it as if this value were given in the form of constant string.
Because of the special meaning of three characters used in function call constructs ("{", "|", "}") they can't be used in constant strings. If you need string value containing one of them, you can obtain it using one of the appropriate standard Wyjec functions that don't accept any parameters and always return the same string values:
Thanks to "+" function you can easily write an expression that generates a string with special and non-special characters mixed. The following expression demonstrates this.
Until now, all values used in example expressions were strings, but Wyjec language is more powerful and supports also values of other types. Here's full list of supported types with descriptions of their allowed values:
Contrary to constant strings, it's not possible to write values of other types directly in expressions. However, you can use calls to functions that return such values. For example, standard Wyjec function named "len" accepts single string parameter and returns its length (number of contained characters) as a value of integer type.
You can use integer value (such as generated by "len" function) as a parameter for another function that accepts integer. For example, standard Wyjec function named "*" accepts integer values as parameters and returns their product as the result of type integer.
Although writing constant values directly in expressions is supported only for string type (constant strings), for other types you can obtain arbitrary value by a call to the appropriate function from type-specific groups of standard Wyjec functions. For each Wyjec type there is a group of parameter-less functions that always return the same values. Names of these functions represent their values in human-readable way, so using them is straightforward.
For boolean type there are two standard Wyjec functions for generating arbitrary values: "true" and "false", which return true and false respectively.
For integer type there is a group of standard Wyjec functions with names consisting of decimal digits (from "0" to "9") optionally preceded with a sign ("+" or "-"). There's a function for each such form representing integer number between -10^{18} - 1 and 10^{18} - 1 (inclusive). Standard functions from this groups return integer values corresponding to their names.
For float type there is a group of virtually unlimited number of standard Wyjec functions with names matching common pattern for floating point numbers. Each of these names consists of up to three parts immediately following each other:
A sequence of characters forms valid name of standard function from this group if it contains the first part and at least one of the following ones. Exponent part (if present) should be interpreted as the power of 10 that should be multiplied by a number described by preceding characters to get a value returned by a function.
For date type there is a group of standard Wyjec functions with names matching one of the following patterns, where each of the letters ("Y", "M", "D", "h", "m", "d") represents any decimal digit.
Each of these functions returns date value corresponding to the part of its name after "@" character, according to ISO 8601 standard. In case of shorter form of the name, the midnight is assumed as time of the day for returned date value.
As an example, we can use the above information to write an expression that uses standard function named "+days". It takes two parameters, date and integer, adds number of days specified by the second parameter (integer) to the first one (date) and returns the result as date value.
In the preceding parts of this document I mentioned a few standard Wyjec functions such as "upper" and "vbar" and used them in sample expressions, but I didn't use any formalism to define valid parameters for these functions. In this section I'll fill this gap.
Every Wyjec function has a name and a signature. A function's name is a sequence of characters used to identify it in expressions when using function call construct. A function's signature defines types of accepted parameters (optionally with parameters' names) and a type of function's result.
A function may accept either fixed or variable number of parameters. In the latter case a function accepts some minimum number of required parameters followed by optional parameters, where all optional parameters have the same type. Full function's signature represents the following information:
If a function accepts fixed number of parameters (without optional ones), its signature will be written in the following way:
The above signature describes a function with two required parameters: "baseDate" parameter of type date and "shiftDays" parameter of type integer. The signature also says that the function returns values of type date. As you probably noticed, this signature matches "+days" function previously used in this tutorial.
If a function accepts optional parameters besides required ones, written form of its signature will contain extra information about them:
The above signature describes a function with one required parameter of type string named "value" and any number of optional parameters of type string with common name "optValues". The function returns a value of type string so it matches string concatenation function named "+", as presented earlier.
It is possible to implement a function that doesn't accept any required parameters, only optional ones, but there is no such function among Wyjec standard functions. There are however standard functions that don't accept any parameters (even optional ones), e.g. the following signature matches "true" function, as it doesn't accept any parameters and returns a value of type boolean:
It is possible to omit names of parameters in written form of a function's signature, but this approach is rarely used. For example, here's stripped down form of "+days" function's signature: (date, integer) : date
When using any Wyjec function, you will need to know its name and signature, so usually information about both of them will be written together in the following way:
The above notation is used in the official documentation of Wyjec standard functions so get used to it.
Thanks to functions' signatures which specify sets of their legal parameters, names of Wyjec functions doesn't have to be unique. Actually, there are many standard Wyjec functions with non-unique names, also known as overloaded names. This is possible because for each such function its signature defines a set of accepted parameters which is disjunctive from such sets for all other functions with the same name. When you write an expression containing a call to function with non-unique name, Wyjec implementation chooses the appropriate function using types of values passed as parameters (Wyjec guarantees that there will be at most one matching function for any given name and a sequence of parameters).
The most important example of overloaded function name is "str". There are four standard Wyjec functions named "str" and all of them accept single parameter and return a value of type string. However, signatures of these functions are different, because for each function the type of accepted parameter is different (one of: boolean, integer, float, date). For example, the following expression contains two calls to functions named "str", the innermost function call refers to function which accepts a parameter of type boolean (signature: (boolean) : string), the outermost function call refers to function which accepts a parameter of type integer (signature: (integer) : string).
At this point you should know Wyjec enough to write quite complicated expression with standard functions documentation at hand. There are however some common situations, when expressions get overly complicated for quite basic tasks. For example, let's try to use Wyjec as a calculator to multiply 2 by 3 and print the result as "2 x 3 = <result>". Multiplication of two numbers is quite trivial with standard function named "*" (signature: (integer, integer...) : integer).
The problem is that in order to receive string value in the required format you need to convert the above result to string using standard function named "str" (signature: (integer) : string) and then use string concatenation function to merge "2 x 3 = " string with the result. Here's the resulting expression.
To make writing such expressions easier, Wyjec implicitly performs some operations related to strings. If you write a sequence of function calls and/or string constants immediately following each other, Wyjec engine will transform this sequence internally to string value by performing the following steps:
With implicit type conversion and string concatenation you can write much simpler equivalent of the above expression.
The above expression contains a sequence of two elements following each other: string constant "2 x 3 = " and a call to "*" function. According to the description of internal expression transformation, "*" function call will be wrapped with a call to "str" and then both values will be used as parameters for "+" function. It means that the resulting expression will look exactly like the first version of calculator expression with explicit conversion and concatenation.
To use Wyjec language effectively, you should look through standard functions documentation and keep it at hand when writing expressions. For more formal (and compact) description of the language, see Wyjec specification.
© Copyright by Zbigniew Chyla 2005