LtdTemplate Manual
Version 1.0.0; 2014-05-07

Author(s)

Contents

Introduction

LtdTemplate is a Ruby Gem implementing resource-limitable, user-editable, textual templating.

Note that "user-editable" does not particularly refer to simplicity (understanding most templates will be facilitated by having a programming background), but rather that, with proper configuration, it can be used to allow sophisticated users to edit their own templates (e.g. for message localization) with limited risk of infinite execution loops or recursion, excessively long strings, or other uncontrolled resource utilization.

This document describes the template syntax. See the Gem documentation for information about the programming interface.

[Contents]

Template Syntax

A template consists of inter-mixed sections of literal text and template code. The shortest sequences between "<<" and ">>" that don't contain "<<" or ">>" are template code; the rest is literal text. Given a sequence of the form:

A<<B<<C>>D<<E>>F>>G<<<H>>>I

template code cannot begin at "<<B" because it is not allowed to contain "<<" or ">>", so the first template code is "<<C>>", the second is "<<E>>", and the last is "<<H>>". Everything else ("A<<B", "D", "F>>G<", and ">I") is treated as literal text.

If template code begins with "<<." (with a period immediately after the second "<"), it strips any trailing white space from immediately preceding literal text. Likewise, if template code ends with ".>>", it strips any leading white space from immediately following literal text. Template code consisting of only "<<.>>" is considered to meet both conditions and is equivalent to "<<..>>".

There need not be any literal text before, after, or between template code sequences. Template code sequences between delimiters may also be empty (<<>>).

Unless otherwise stated, the remainder of this document will refer to template code as the code between the "<<" (or "<<.") and ">>" (or ".>>") without reference to the delimiters themselves.

[Contents]

Template Code Syntax

Comments

Comments begin with "/*" and end at the nearest "*/".

/* this is a comment */ /* and so is /* this */

[Contents] [Template Code Syntax]

String Literals

String literals are used to generate string values. LtdTemplate supports two forms: "short" and "regular".

Short string literals begin with a single quote (') and terminate before a period (.), comma (,), opening or closing brackets, parentheses, or braces ([](){}), or white space. However, any normally terminating character may be included by preceding it with a backslash (\).

Regular string literals begin with a double quote (") and terminate at the next double quote. A double quote may be included by preceding it with a backslash.

String literals may also contain other "escape sequences" like those in Ruby double-quoted strings. Here is the full list:

SequenceMeaning
\M-\C-xmeta-control-x
\M-xmeta-x
\C-x, \Cxcontrol-x
\udddd Unicode four-digit, 16-bit hexadecimal code point dddd
\xddtwo-digit, 8-bit hexadecimal dd
\ddd one, two, or three-digit, 8-bit octal ddd
\aalert (ASCII BEL)
\bbackspace (ASCII BS)
\eescape (ASCII ESC)
\fformfeed (ASCII FF)
\nnew-line/line-feed (ASCII LF)
\rcarriage return (ASCII CR)
\sspace (ASCII SP)
\thorizontal tab (ASCII HT)
\vvertical tab (ASCII VT)
\c any other character c is just itself

Examples, with some comments added for annotation:

' /* empty */ 'short '<\< '>\> /* escaped code delimiters */ "\"regular\" string"

[Contents] [Template Code Syntax]

Numeric Literals

Numeric literals are used to generate typical integer or real (decimal) numeric values and consist of an optional minus sign (-) followed by one or more digits, and optionally followed by a decimal point (.) and one or more additional digits.

0.125 1 -2 3.0 -4.5

LtdTemplate does not currently support numeric literals in scientific notation or bases other than ten.

[Contents] [Template Code Syntax]

Variables

Variables hold references to values, and can hold references to (be bound to) different values at different times. They exist within the stack of namespaces. Referencing a variable normally searches for it beginning in the most recent or "nearest" namespace and works back to the first or "root" namespace until a variable with that name is found. If the name begins with circumflex (^), the search begins in the parent namespace (if there is one) instead of the current namespace. If the name begins with at-sign (@), only the root namespace is searched. If the variable has not been assigned, the nil value is used.

Variable names must be in one of the following formats (unless accessed as an element of a specific namespace—see last example) and are case-sensitive:

  1. A letter or an underscore (_), followed by zero or more letters, numbers, or underscores
  2. An at-sign or circumflex followed by one or more letters, numbers, or underscores
  3. An at-sign, circumflex, or dollar-sign ($) by itself
name item1 _2 ^parent @root $ @['<\<weird>\>]

[Contents] [Template Code Syntax]

Method Calls

A method call performs an action on, or returns information about, a value. A method call consists of a value expression followed by a period (.) followed by the desired method name, optionally followed by an open parenthesis "(", zero or more method call parameters separated by commas (,), and a closing parenthesis ")". Parameters may be positional (also called sequential) or named (also called random-access). Use the "dot dot" symbol (..) to terminate the positional parameters and begin the named parameters.

value_expression.method_name(positional_1, ..., positional_N .. name_1, value_1, ..., name_N, value_N)

Method names are case-sensitive and must be in one of the following formats:

  1. A letter or underscore (_) followed by zero or more letters, numbers, or underscores
  2. One or more punctuation characters that don't have any other meaning (these are often referred to as "operators" in other syntax definitions), such as:
+ - * / % & | ! < <= = == != >= >

The parentheses are optional if no parameters are to be supplied to the method call. The period is optional for "operator-style" method calls if not immediately preceded by punctuation.

3.type /* => number */
3.type.length /* "chained" method calls; => 6 */
1+(2,3,4) /* short for */ 1.+(2,3,4) /* => 10 */

[Contents] [Template Code Syntax]

Assignments

Variables (and array subscripts, covered later) support two assignment method calls: unconditional (=) and conditional (?=). A conditional assignment does nothing if the variable is already bound to (holds a reference to) a value other than the nil value. (This is a change from versions prior to 1.0.0; in those versions even a nil value prevented a conditional assignment.) Assignments do not render in the template output.

If called with a single parameter and no "dot dot" symbol, the value of the single parameter is assigned. Otherwise, all the parameters are assigned as an array. See also array values.

num=(5) num?=(6) /* num is still 5 */
empty1= empty2=() /* empty arrays */
single=(10 ..) /* one element */ double=(5, 10) /* two */
matrix=($.*(1, 0), $.*(0, 1) .. 'type, 'identity)
matrix[0, 0] /* => 1 */ matrix['type] /* => identity */

Variables beginning with circumflex (^) will be created in the parent namespace (if they do not already exist) and variables beginning with at-sign (@) will be created in the root namespace. All others will be created in the current namespace.

See also the var method for namespaces.

[Contents] [Template Code Syntax]

Pre-Defined Variables

The following variables are pre-defined by LtdTemplate:

NameDescription
$ This is the current namespace. A new namespace is automatically created each time a code block is executed and is destroyed when the code block completes.
^ This variable refers to the previous (parent) namespace if there is one, or nil if there isn't.
@ This variable refers to the first (also known as the "root" or "top-level") namespace.
_ This variable refers to the array of positional and/or named parameters passed into the template or current code block.

[Contents] [Template Code Syntax]

Code Sequences

A code sequence refers to a sequence of zero or more template code expressions. Templates, method call parameters, and code block bodies are all code sequences.

If a code sequence consists of a single value and does not constitute the entire template code, it is retained as-is. Otherwise, the string value of each element in the sequence, if any, is combined to produce the result of the code sequence.

'Hello $.true ", " num=(5) num-(4) " world!"
/* 5 and 4 are single values in their code sequences */
/* $.true and num=(5) render nothing */
/* num-(4) returns the number 1 but it's string value gets used */
/* => Hello, 1 world! */

[Contents] [Template Code Syntax]

Code Blocks And Bindings

If you surround a code sequence by braces ({ and }), you create a code block. A code block is a value which does not render directly, but can be called as a method call, executing the code sequence within the code block.

render_method=({ $.method })
render_method.hi " " render_method.there /* => hi there */

Code blocks (or other values) may be bound to values using the methods method and subsequently called like methods.

The methods method takes any number of name-and-binding pairs to be added or updated. If there are an odd number of parameters (i.e. there is a final name without a binding), the existing binding, if any, associated with that name is returned. Specifying nil ($.nil) as the binding will actually delete any existing binding for the given name.

@.methods('greet,{"Hello, "_[0]"."})
@.greet('Dave) /* => Hello, Dave. */

You can also bind code blocks to proxy objects to be used by an entire class of values. For convenience, binding to an unset variable automatically causes the variable to be set to an empty string.

@Array?=(') /* bind the Array proxy to a unique object (before 1.0.0) */
@Array.methods('list, { $.target.join(", ") }) /* bind the list method */
$.*(1, 2, 3).list /* => 1, 2, 3 */

Methods added this way are only called if there is no standard method with the same name.

[Contents] [Template Code Syntax]

Subscripts

Any expression that evaluates to an array (or tree of nested arrays) may be subscripted to select a particular value or nested array from the array. A subscript consists of an open bracket ([) followed by one or more selector expressions separated by commas followed by a closing bracket (]). Consecutive selector expressions are used to traverse progressively more deeply nested arrays.

array_var['items, 5] /* fetches item "5" (which might also be an array) from array "items" in the array referenced by array_var */
array_var['items][5] /* as above - alternate syntax */

If the requested item does not exist, the special value "nil" is used instead.

Prior to version 1.0.0, subscript syntax could also be used to bind code blocks to non-array values. The syntax is shown here for historical reference but is no longer supported:

x['greeting]=({'hello}) /* where x is not an array value */
/* this syntax is no longer supported! */

[Contents] [Template Code Syntax]

Values And Methods

Arrays

LtdTemplate does not have array literals, but you can construct arrays using the variable assignment method (see variable syntax) or via the array method on namespaces:

empty=() /* empty array */ single=(10 ..) /* one element */ double=(5, 10) /* two */
matrix=($.*(1, 0), $.*(0, 1) .. 'type, 'identity)
matrix[0, 0] /* => 1 */ matrix['type] /* => identity */

Arrays render to the concatenation of the renderings of their sequential (positional) elements; random-access (named) elements are ignored.

Standard methods:

MethodDescription
callSame as the array itself
classReturns the string "Array"
each(code_block) Executes code_block.each_seq(index, value) for each sequential index then code_block.each_rnd(key, value) for each random-access key and returns an array of results (since 0.2.1)
each_rnd(code_block) Executes code_block.each_rnd(key, value) for each random-access key and returns an array of results (since 0.2.1)
each_seq(code_block) Executes code_block.each_seq(index, value) for each sequential index and returns an array of results (since 0.2.1)
joinJoins sequential (positional) elements
join(separator)Join with separator between sequential (positional) elements
join(two, first, middle, last) Joins two sequential elements with two as the separator or more than two sequential elements with first as the first separator, last as the last separator, and middle for all other separators
pop, ->Pop the last sequential element
push(list), +>(list) Adds zero or more elements in the list to the end of the array
rnd_sizeThe number of random-access (named) elements
seq_sizeThe number of sequential (positional) elements
shift, <-Shift the first sequential element
sizeThe total number of elements in the array
typeReturns the string "array"
unshift(list), <+(list) Adds zero or more elements in the list to the beginning of the array
/Interpolate the sequential and random-access values of the array into the positional and named parameters of another method call (since 0.2.1)
% Interpolate pairs of sequential values in the array as named parameters of another method call (since 0.2.1)

Code blocks may be bound as methods for all array values as follows:

@Array.methods('method_name, { code sequence })

[Contents] [Values And Methods]

Booleans (True And False)

LtdTemplate supports boolean values (true and false). They can be accessed via the namespace method calls $.true and $.false. There is only one true value and one false value, although other values can be interpreted in boolean context (the nil value is treated as false; all others, including the number 0 and empty strings, are treated as true).

Standard methods:

MethodDescription
callSame as the boolean itself
classReturns the string "Boolean"
string, str Returns the string "true" for true and "false" for false
typeReturns the string "boolean"
+, |, or Returns the "logical or" of the value and any supplied call parameters (if any are true, the result is true, otherwise the result is false)
*, &, and Returns the "logical and" of the value and any supplied call parameters (if all are true, the result is true, otherwise the result is false)
!, not Returns the "logical not and" of the value and any supplied call parameters (if all are false, the result is true, otherwise the result is false)

Code blocks may be bound as methods for true, false, or both boolean values as follows:

$.true.methods('method_name, { code sequence })
$.false.methods('
method_name, { code sequence })
@Boolean.methods('
method_name, { code sequence })

[Contents] [Values And Methods]

Code Blocks

Standard methods:

MethodDescription
typeReturns the string "code"

All other methods execute the code block in a new namespace.

[Contents] [Values And Methods]

Namespaces

A namespace is a storage area for variables. A new namespace is created each time the template is rendered or a code block is executed. In the case of code blocks, the previously active namespace becomes the parent (^) of the new namespace ($).

Namespaces provide a number of pre-defined variables, as well as method calls for loops and conditionals that are traditionally statements in other programming environments.

Standard methods:

MethodDescription
array(elements), *(elements) Returns an anonymous array (see array values, assignments, and the var method)
falseReturns the boolean false value
if(condition_1, return_1, ..., condition_N, return_N, default) Processes each condition in turn. The return value corresponding to the first condition that evaluates to true is returned. If none of the conditions evaluate to true, the optional default value (or the nil value, in the case of an even number of parameters) is returned. Parameters can be supplied as code blocks to avoid execution unless and until needed.
loop(before, body, after) If the before condition evaluates to true, the body is executed and the optional after condition is evaluated. If the after condition is absent or evaluates to true, the loop starts over with the before condition. The body and non-constant conditions should be supplied as code blocks. Returns an array of body results (one element per loop iteration).
methodReturns the method name used to call a code block, or "render" for top-level template code
nilReturns the nil value
targetReturns the target object in a code block being called as part of an object binding
trueReturns the boolean true value
use(name) Calls the template loader to load resource name
var(name_1, ..., name_N .. set_1, value_1, ..., set_N, value_N) Creates and (re-)sets variables named by the supplied strings. The variables in the sequential parameters section, name_1 through name_N, are assigned the nil value. The variables in the random-access section, set_1 through set_N, are assigned the corresponding values from value_1 through value_N.

Code blocks may be bound as methods for all namespaces as follows:

@Namespace.methods('method_name, { code sequence })

[Contents] [Values And Methods]

Nil

LtdTemplate supports a "nil" value to represent the lack of another value. It can be accessed via the namespace method call $.nil. There is only one nil value.

Standard methods:

MethodDescription
typeReturns the string "nil"

Code blocks may be bound as methods for the nil value as follows:

$.nil.methods('method_name, { code sequence })

[Contents] [Values And Methods]

Numbers

Standard methods:

MethodDescription
abs Returns the absolute value of the number
callReturns the number itself
ceilReturns the closest greater or equal integer
classReturns the string "Number"
floorReturns the closest lesser or equal integer
flt, floatReturns the number as a floating-point (decimal) number
intReturns the integer portion of the number
str, string Returns the number as a string
typeReturns the string "number"
+(list) Returns the sum of the number and numeric items in list
- With no parameters, returns the negative of the number
-(list) Returns the difference of the number and the sum of numeric items in the non-empty list
*(list) Returns the product of the number and the numeric items in list
/(list) Returns the quotient of the number and the numeric items in list
%(list) Returns the progressive remainder of the number and the numeric items in list
&(list) Returns the bit-wise "AND" of the number and the numeric items in list
|(list) Returns the bit-wise "OR" of the number and the numeric items in list
^(list) Returns the bit-wise "exclusive OR" of the number and the numeric items in list
op or op(value) for op in <=, <, ==, !=, >, >= Returns whether the number is less than or equal to, less than, equal to, not equal to, greater than, or greater than or equal to, value. If value is not supplied or is not numeric, zero is used.

Code blocks may be bound as methods for all numeric values as follows:

@Number.methods('method_name, { code sequence })

[Contents] [Values And Methods]

Strings

Standard methods:

<
MethodDescription
callReturns the string itself
classReturns the string "String"
capcase Returns the string with each space-separated word capitalized (since 0.2.2)
downcase Returns the string entirely in lowercase (since 0.2.2)
flt, float Returns the floating-point value of the string
html Returns the HTML encoding of the string (since 0.1.4)
idx(target, offset), index(target, offset) Search for target left-to-right in the string beginning at position offset (default 0) and return the offset from the start of the string at which found (or -1 if not found)
int Returns the integer value of the string
join(list) Equivalent to $.array(list).join(string) (since 0.2.2)
len, length Returns the length of the string
match(regexp, offset)Match a string against a regular expression. Returns MatchData if the pattern matches, or nil if it doesn't.
pcte Returns the "percent encoding" of the string (for URI components; since 0.1.4)
regexp Returns a "regular expression" version of the string (only if enabled by the calling program; since 0.2.2)
rep(pattern, replacement), replace(pattern, replacement) Returns a copy of the string with all occurrences of pattern replaced with replacement; the pattern may be a regular expression (since 0.2.2)
rep1, replace1 Like rep/replace, but with only the first occurrence replaced (since 0.2.2)
split(pattern, limit) Returns an array of up to limit (default unlimited) substrings split at pattern (since 0.2.2)
ridx(string, offset), rindex(string, offset) Search for target right-to-left in the string beginning at position offset (the end of the string by default) and return the offset from the start of the string at which found (or -1 if not found)
rng(begin, end), range(begin, end) Returns characters begin through end of the string, inclusive, counting from zero. Either may be negative, in which case counting is backwards from the end of the string.
slc(begin, length), slice(begin, length) Returns length characters beginning at begin, counting from zero. Begin may be negative, in which case counting is backwards from the end of the string.
str, string Returns the string itself
typeReturns the string "string"
upcase Returns the string entirely in uppercase
+(list) Returns the concatenation of the string and the items in list
*(value) Returns the concatenation of the string repeated value times. If value is negative, the reverse of the string repeated -value times is returned. If value is omitted or not numeric, an empty string is returned.
op or op(value) for op in <=, <, ==, !=, >, >= Returns whether the string is less than or equal to, less than, equal to, not equal to, greater than, or greater than or equal to, value. If value is not supplied, the empty string is used.

Code blocks may be bound as methods for all string values as follows:

@String.methods('method_name, { code sequence })

[Contents] [Values And Methods]

Regular Expressions And Matches

"Regular expressions" ("regex" or "regexp" for short) in LtdTemplate are generated from string values using the regexp method.

Regular expressions must be enabled by the calling program. See the gem documentation for details.

Regexp-specific methods:

MethodDescription
ci, ignorecase Make pattern case-insensitive
class Returns the string "Regexp"
ext, extended Allow white space and comments in the pattern
match(string, offset) Match a string against a regular expression. Returns MatchData if the pattern matches, or nil if it doesn't.
multi, multiline Makes . match newlines too
typeReturns the string "regexp"
"Example".replace("[aeiou]".regexp.ci,'_) /* => _x_mpl_ */

Code blocks may be bound as methods for all regular expression values as follows:

@Regexp.methods('method_name, { code sequence })

Regular expression match-result methods:

MethodDescription
[index] Returns the entire (index 0) or specific sub-pattern match.
[name] Returns the named sub-pattern match.
begin(n) Returns the offset of the start of the nth match array in the string (numeric index or named sub-match).
class Returns the string "Match".
end(n) Returns the offset of the character immediately following the nth match in the string (numeric index or named sub-match).
length, size Returns the size of the match results array.
offset(n) Returns the begin and end values in a two-element array.
type Returns the string "match".

[Contents] [Values And Methods]

(Some More) Examples

This example binds a list method to arrays to include commas and "and" between elements:

@Array.methods('list, {
$.target.join(" and ", ", ", ", ", ", and ") })
$.*('Ruby).list /* => Ruby */
$.*('Perl, 'Ruby).list /* => Perl and Ruby */
$.*('Perl, 'PHP, 'Python, 'Ruby).list /* => Perl, PHP, Python, and Ruby */

This example binds a method called "en_nth" on number values to return the English "nth" for the number (e.g. 1st for 1, 2nd for 2, 3rd for 3, etc.) and then generates them from -11 to 24:

@Number.methods('en_nth, {
$.var(.. 'n, $.target) $.var(.. 'n10, n.abs%(10), 'n100, n.abs%(100))
n $.if({ n100>=(11)&(n100<=(20)) }, 'th, { n10==(1) }, 'st,
{ n10==(2) }, 'nd, { n10==(3) }, 'rd, 'th) })
n=(-11) $.loop({ n<=(24) }, { n.en_nth n=(n+(1)) }).join(", ")

[Contents]