triptico.com

Un naufragio personal

Artemus 5 Templating Language Overview

Artemus is a template toolkit. It filters text files, parsing, compiling and executing code surrounded by special marks (leaving the rest untouched) and concatenating everything as output. Its main purpose is to filter HTML files, but it can be used for any scripting need related to text filtering and substitution.

Basic Syntax

The Artemus 5 parser expects its code to be between the <{ and }> markers, passing the rest of its input as is.

This will be output with no modification:

 This is a text.
 It includes no Art5 markup,
 so it will pass through unmodified.

Artemus 5 tokens must be separated by freely distributed blanks (spaces, tabs or newlines). If a single token is found, it's accepted to be a template to be called. Templates can be found on disk as regular files (where they will be recursively compiled), in memory as builtin operators or functions (that will be discussed later), or as variables defined inside the Art5 program flow.

Direct template file inclusion:

 This is a text.
 If the current directory is in the template search path and
 a README file exists there, it will be directly included.
 
 README file:
 <{README}>

Builting functions:

 This is another example.
 The sum of 2 plus 3 is <{add 2 3}>,
 that will output "The sum of 2 plus 3 is 5".

Assignation and variable usage:

 This is another example.
 The TEST variable is assigned. <{= 'TEST' 'This is a test' }>
 (Note that the above code do not generate any output).
 And now it's printed: <{TEST}>

Please take note that, on assignation, the name of the variable to be assigned must be enclosed by quotes, as if it was a string literal.

Any text from a # symbol to the end of the line is assumed to be a comment and is ignored.

 <{
 # This won't appear in the output and will be ignored
 }>

A block of code, or a template invocation with arguments, must be enclosed in curly brackets. The <{ and }> markers also work as code block enclosers.

Look how a mul (multiply) operation must be set in a code stream:

 V and A are variables.
 <{"Voltage: " V "Current: " A "Power: " {mul V A}}>

In a code block, the first argument must always be an opcode, being it a builtin operator or a template name. But, if a literal is found, the default opcode ? (concatenate) is assumed. So, the following example is exactly the same as the previous one:

 <{? "Voltage: " V "Current: " A "Power: " {mul V A}}>

Variables and data types

Assignation to variables can be done by using the = operator:

 <{= 'email' 'angel@triptico.com' # assign }>
 Artemus author's email is <{email}>.

From that moment on, the email variable can be used as a template.

Strings

Strings can be specified in two ways: by enclosing them in double quotes, where backslash-prefixed codes are accepted (like \n, as in C or Perl), or by enclosing in single quotes, where they are parsed verbatim.

 <{ "This is\na test" # Output is two lines }>
 <{ 'This is\na test' # Output is one line, \n is printed }>

Translateable strings

A translateable string is one that will be internally translated just before output. They are like double quoted strings, but prefixed by the @ symbol.

 <{ @"Monday" }>

The special T operator can be used to define sets of translateable strings. For example, this code snippets translate weekday names from english to spanish:

 <{T
 "Monday"    "lunes"
 "Tuesday"   "martes"
 "Wednesday" "miercoles"
 "Thursday"  "jueves"
 "Friday"    "viernes"
 "Saturday"  "sabado"
 "Sunday"    "domingo"
 }>

So the <{ @"Monday" }> code above will output lunes.

The art5 command line tool includes an option to generate translate code files from a set of templates on disk. Any file matching lang_* will be refreshed to include all translateable strings ready to be edited. To start with new language file, just create an empty one.

Arrays

Arrays can be defined by using the & opcode.

 <{= 'weekdays'
     {&  "Monday" "Tuesday" "Wednesday" "Thursday"
         "Friday" "Saturday" "Sunday" }
  }>

Arrays can be later used in foreach loops and such. Each element of an array can be a scalar (as in the example) or another array itself. These structures can be as complicated as desired.

Arguments

Any included template can be called with arguments. For example:

 <{ link 'http://triptico.com/software/artemus.html' 'Artemus Home Page' }>

The link template will access its arguments as numbers prefixed by the $ sign, as in the example:

 <{ # generate an link
 '<a href = "' $0 '">' $1 '</a>'
 }>

External hash elements

When Artemus 5 is used as a library inside an application, an external hash can be provided at startup. The values for this hash can be accessed by prefixing them with the % sign. For example, in the Gruta Content Management System, the external hash is filled with the CGI arguments.

 The topic is <{%topic}>.

In the art5 command line tool, though, the external hash only has a key/value pair, arch, containing the system architecture (most probably the "Unix" string).

 The current architecture on this system is <{%arch}>.

Conditional and looping constructs

if

The if opcode can be used for conditional output. It has two mandatory arguments, the condition and the output if true:

 <{if accepted "Operation was accepted" # accepted should return 1 or 0 }>

It also accepts an optional third argument, to be output if the condition is false:

 Operation <{if accepted "was accepted" "was NOT accepted"}>.

As discussed above, if any of the three code blocks contain more than one opcode or one with arguments, they must be enclosed by curly brackets:

 Error msg: <{if {eq msg "OK"} "All OK" "Something bad happened"}>

 <{if {eq %user "anonymous"}
 		"No user logged" {"User " %user " logged in"}
 }>

foreach

The foreach opcode can be used for looping through arrays. It accepts up to 4 arguments, being mandatory just the first one, the array:

 Concatenates the full array without separation
 <{foreach weekdays}>

The second argument is a code block that will be called on each iteration with the arguments filled with each element of the array. For arrays with scalar elements, only the first ($0) element is filled.

 Prints each array element with a heading in its own line
 <{foreach weekdays {"Week day: " $0 "\n"}}>

If an element of the array is itself an array, arguments from $0 up to $9 will be filled with the elements of the array.

 <{= 'dataset' {& {& 'a' 1} {& 'b' 2} {& 'c' 3}}}>
 <{foreach dataset { "Value for element " $0 " is " $1 "\n" }}>

The array can be defined inline:

 The multiplying table of 2
 <{foreach {& 1 2 3 4 5 6 7 8 9 10} { $0 " by 2 is " {mul $0 2}}}>

Though, for loops like this, the seq opcode is more useful, as it generates a sequence of numbers:

 <{foreach {seq 1 10} { $0 " by 2 is " {mul $0 2}}}>

The third argument to foreach is a separator, that will be emitted as output between each call to the main code block:

 <{foreach {seq 1 10} $0 ", " }>

And the fourth one is a header; it will be emitted everytime the output generated by it is different that the previous one. This can be useful to generate subheaders everytime a field changes:

 Output will be:
 a
   1
   2
 b
   3
 <{foreach {& {& 'a' 1} {& 'a' 2} {& 'b' 3}} { "  " $1 "\n" } ""
		{ $0 "\n" } }>

It can also be used to generate a header only if the array is not empty:

 Output will be:
 Dataset
 1
 2
 3
 4
 <{foreach {& 1 2 3 4} $0 "\n" "Dataset\n" }>
 
 Output will be nothing
 <{foreach {&} $0 "\n" "Dataset\n" }>

case

The case opcode can be used as a multiple choice code generator. The first argument is the value to be compared, followed by pairs of value-output code block.

 <{case %arch
 "Unix"  { "Some Unix system (Linux, etc)" }
 "win32" { "Some version of MS Windows" }
 }>

The first argument is compared sequentially until one is found, and then the associated code block is emitted. If none is found, nothing is output.

If the number of arguments is odd, the last one is not compared, but accepted as the otherwise clause and used as output in case no value is found.

 <{case %arch
 "Unix"  { "Some Unix system (Linux, etc)" }
 "win32" { "Some version of MS Windows" }
 { "Some other strange architecture I didn't envisioned" }
 }>

Builtin operators and functions

String comparisons

The eq and ne operators can be used to test for equality or inequality of text strings.

Numerical comparisons

The gt and lt operators can be used to test greater-than and lower-than numerical comparisons.

Boolean operators

The and, or and not can be used as boolean operators. Both and and or do not return some true or false values, but the value tested itself, so they can be used as simple conditionals. For example:

 Stores in the user variable the content of %user or,
 if it's empty, the "anonymous" string
 <{= user {or %user "anonymous"}}>

Array manipulation

Array definitions and the seq operator has been seen above; other array manipulation opcodes are reverse, that does the obvious or size, that returns the number of elements in the array.

The split opcode can be used to split an array by using a separator:

 <{foreach {split "," "1,2,3,4,5"} {$0 "\n"}}>

The sort opcode can be used to sort an array. The 1 argument version is straightforward:

 Print in proper order:
 <{foreach {sort {& 3 4 2 5 6 4 3 2 4 5 6 4}} $0 ', ' }>

If a second argument is used, it's assumed to be a code block to be executed on each pair of arguments as a sort order criteria:

 <{foreach {sort {& 3 4 2 5 6 4 3 2 4 5 6 4} {sub 1000 $0}} $0 ', ' }>

Math operators

The add, sub, mul and div exist as their corresponding math operators.

Miscellaneous

The env opcode returns the value of an environment variable; if no argument is given, the full set of existing environment variables will be returned as an array.

The random opcode accepts an arbitrary number of arguments and returns one of them, at random.


Angel Ortega <angel@triptico.com>

Related

Visitor comments