Liquor 2.0 Language Specification

Table of Contents

1 Preface

Liquor 2.0 language is developed with several goals in mind.

This specification is primarily targeted at language implementors.

2 Overview

2.1 Introduction

Liquor 2.0 language is a templating language for text-based content, e.g. HTML pages. As Liquor is a templating language, it is useless without extension with domain-specific features from a host environment; it is similar to Lua in this aspect.

Liquor is meant to be statically compiled to another language for efficiency, typically to the one the host environment is executed in. It also provides sandbox restrictions, which allow Liquor code to invoke certain methods on the host objects, but only ones explicitly marked as scriptable.

Liquor is a statically scoped, weakly and dynamically typed imperative language with lazily evaluated expressions. As it is essentially a domain-specific language for string concatenation, it has an unusual syntax where code is embedded in a text stream, and a final result of executing a Liquor program is always a string. All language constructs are similarly centered around string manipulation.

Liquor has four basic elements: blocks, tags, interpolations and expressions. All four of these elements can be executed and return a value. Blocks, tags and interpolations always return a string value.

Liquor does not have non-local control flow constructs by itself, such as exceptions and function definitions. This was done intentionally in order to simplify the language.

Liquor has distinct compile-time and runtime error checking. There are no fatal runtime errors, i.e. a Liquor program always evaluates to some value.

2.2 Types and Values

Liquor has the following basic types: Null, Boolean, Integer, String, Tuple and External. A value of every type except External can be created from within a Liquor program. Values of type External can only be returned by the host environment.

All Liquor values are immutable; once created, a value cannot change.

There is exactly one value of type Null, and it is called null.

There are exactly two values of type Boolean, and they are called true and false.

The only values considered “falseful” in a conditional context are null and false. Every other value, including Integer 0 (zero), is considered “truthful”.

Type Integer denotes an integer value of unspecified size. Implementation may impose additional restrictions on the representable range of Integer type.

Type String denotes a sequence of Unicode codepoints. Note that codepoints are not the same as characters or graphemes; there may exist an implementation-specific way of handling composite characters. See also the relevant Unicode FAQ entry.

Type Tuple denotes a heteromorphic sequence of values.

Type External denotes an object belonging to the host environment.

2.3 Type Conversion

Liquor supports exactly one implicit type conversion. In any context where a String value is expected, an Integer value can be provided. The Integer value will then be converted to a corresponding decimal ASCII representation without any leading zeroes.

2.4 Expressions

Liquor features expressions, which can be used to perform computations with values. This section does not define a normative grammar; the full grammar is provided in section Grammar.

Order of evaluation of Liquor expressions is not defined. As every value is immutable, the value of the entire expression should not depend upon the order of evaluation. Implementation-provided functions must not access or mutate global state; implementation-provided tags may access or mutate global state, but this is highly discouraged.

2.4.1 Literals

All Liquor types except External can be specified as literals in expressions.

Identifiers null, true and false evaluate to the corresponding values.

Numeric literals evaluate to a corresponding Integer value, and always use base 10. Numeric literals can be specified with any amount of leading zeroes. There are no negative numeric literals.

String literals evaluate to a corresponding String value. String literals can be equivalently specified with single or double quotes. Strings support escaping with backslash, and there are exactly two escape sequences: one inserts a literal backslash, and the other one inserts a literal quote. More specifically, single quoted string supports escape sequences \\ and \', and double quoted string supports escape sequences \\ and \". A single backslash followed by any character not specified above is translated to a literal backslash.

Tuple literals evaluate to a corresponding Tuple value. Tuple literals are surrounded by square brackets and delimited with commas; that is, [ 1, 2, 3 ] is a tuple literal containing three integer values, one, two and three, in that exact order.

2.4.2 Operators

Liquor supports unary and binary infix operators in expressions. All operators are left-associative.

Liquor operators are listed in order of precedence, from highest to lowest, by the following table:

  1. [], ., (), .()
  2. unary -, !
  3. *, /, %
  4. +, binary -
  5. ==, !=, <, <=, >, >=
  6. &&
  7. ||

The following operators are infix and binary: *, /, %, +, -, ==, !=, <, <=, >, >=, &&, ||. The following operators are infix and unary: -, !.

The operators [], ., (), .() are not infix and are provided in this table only to define precedence rules.

2.4.2.1 Arithmetic Operators

Arithmetic operators are * (multiplication), / (division), % (modulo), + (plus) and - (minus; binary and unary).

All arithmetic operators except +, whether unary or binary, require every argument to be of type Integer. If this is not the case, a runtime error condition (type error) is signaled.

Operator + requires both arguments to be of the same type, and only accepts arguments of type Integer, String or Tuple. If any of the conditions is not satisfied, a runtime error condition (type error) is signaled. For arguments of type String or Tuple, the + operator evaluates to the concatenation of left and right arguments in that order.

If the result of an arithmetic operation, except operator + with non-Integer arguments, exceeds the range an implementation can represent, the behavior is implementation-defined.

2.4.2.2 Boolean Operators

Boolean operators are ! (not; unary), && (and) and || (or).

All boolean operators, whether unary or binary, convert each argument to type Boolean prior to evaluation. The rules of conversion are:

  1. If the value equals null or false, it is assumed to be false.
  2. Else, the value is assumed to be true.

All boolean operators return a value of type Boolean. Binary boolean operators do not provide any guarantees on order or sequence of evaluation. However, a correct implementation which does not feature functions with side effects will not suffer from this behavior.

2.4.2.3 Comparison Operators

Comparison operators are == (equals), != (not equals), < (less), <= (less or equal), > (greater) and >= (greater or equal).

Operators == and != compare values by equality, not identity. Thus, the expression [ 1, 2 ] == [ 1, 2 ] evluates to true. These operators never signal an error condition or implicitly convert types.

Operators <, <=, > and >= require both arguments to be of type Integer. If this is not the case, a runtime error condition (type error) is signaled. Otherwise, a corresponding value of type Boolean is returned.

2.4.2.4 Indexing Operator

Indexing operator is [].

Indexing operator requires its left-hand side argument to be of type Tuple or External, and right-hand side argument to be of type Integer. If this is not the case, a runtime error condition (type error) is signaled.

If the left-hand side argument is of type External, the behavior is implementation-defined. A runtime error condition (external error) is signaled if the particular external value does not support indexing.

Indexing operator of form t[n] evaluates to n-th value from tuple t with zero-based indexing. If n is negative, then n+1-th element from the end of tuple is returned. For example, t[-1] will evaluate to the last element of the tuple t.

If the requested element does not exist in the tuple, the indexing operator evaluates to null.

2.4.3 Function Calls

Identifiers can be bound to functions prior to compilation. Identifiers null, true and false cannot be bound to a function.

Functions are defined in an implementation-specific way. Functions can have zero to one unnamed formal parameters and any amount of named formal parameters. If an unnamed formal parameter is accepted, it is mandatory. Named formal parameters can be either mandatory or optional. Absence of a mandatory formal parameter will result in a compile-time error (argument error). Named formal parameter order is irrelevant.

Function calls have mandatory parentheses, and arguments are whitespace-delimited.

If a function call includes two named parameters with the same name, a compile-time error (syntax error) is raised.

If a hypothetical function substr has one unnamed formal parameter and two optional named formal parameters from and length, then all of the following expressions are syntactically valid and will not result in a compile-time error: substr("foobar"), substr("foobar" from: 1), substr("foobar" from: 1 length:(5 - 2)). The following expression, however, is syntactically valid but will result in a compile-time error: substr(from: 1).

2.4.4 Access Operators

Access operators are . and .().

The . form is syntactic sugar for .() form without any arguments. That is, e.f is completely equivalent to e.f().

Access operator requires its left-hand side argument to be of type External. If this is not the case, a runtime error condition (type error) is signaled.

Access operator of form e.f(arg kw: value) evaluates to the result of calling method f of external object e with the corresponding arguments. Argument syntax is the same as for function calls.

This evaluation is done in an implementation-defined way. Access operator can evaluate to any type.

If the requested method does not exist in the external object or cannot successfully evaluate, a runtime error condition (external error) is signaled. Errors in the called method must not interrupt execution of the calling Liquor program.

2.4.5 Variable Access

Every identifier except null, true and false which is not bound to a function name is available to be bound as a variable name. Such identifier would evaluate to a value of the variable.

Variable definition and scoping will be further discussed in section Tags.

Referencing an undefined variable will result in a compile-time error (name error).

2.4.6 Filter Expressions

Filter expressions are a syntactic sugar for chaining method calls.

Filter expressions consist of a linear chain of function calls where n-th function’s return value is passed to n+1-th function’s unnamed parameter. Named parameters may be specified without parentheses within a corresponding chain element.

All functions used in a filter expression should accept an unnamed parameter. If this is not the case, a compile-time error (argument error) is raised. Semantics of mandatory and optional named parameters are the same as for regular function calls.

In essence, e | f a: 1 | g is equivalent to g(f(e() a: 1)).

2.5 Blocks

A block is a chunk of plaintext with tags and interpolations embedded into it. Every Liquor program has at least one block: the toplevel one.

A block consisting only of plaintext would return its literal value upon execution. Thus, the famous Hello World program would be as follows:

Hello World!

This program would evaluate to a string Hello World!.

A block can have other elements embedded into it. When such a block is executed, these elements are executed in lexical order and are replaced with the value returned by the element.

2.6 Interpolations

An interpolation is a syntactic construct of form {{ expr }} which can be embedded in a block. The expression expr should evaluate to a value of type String or Null; an implicit conversion might take place. If this is not the case, a runtime error condition (type error) is signaled.

If expr evaluates to a String, the interpolation returns it. Otherwise, the interpolation returns an empty string.

An example of using an interpolation would be:

The sum of two and three is: {{ 2 + 3 }}

This program would evaluate to a string The sum of two and three is: 5.

2.7 Tags

A tag is a syntactic construct of form {% tag expr kw: arg do: %} ... {% end tag %}. A tag has a syntax similar to a function call, but it can receive blocks of code as argument values and lazily evaluate passed expressions and blocks of code.

Tags have full control upon parameter evaluation. Tags can require arguments to be of a certain lexical form, e.g. a for tag could require its unnamed formal parameter to be a lexical identifier.

To pass a block of code to a tag, the closing tag delimiter should immediately follow a parameter name. Everything from the closing tag delimiter to the matching opening tag delimiter should be parsed as a block and passed as a value of the corresponding parameter. After the matching opening tag delimiter, the parameter list is continued.

If a tag t does not include any embedded blocks, it ends after a first matching closing tag delimiter. Otherwise, the tag ends after a first matching construct of the form {% end t %}.

Unlike functions, tags can receive multiple named parameters with the same name. Named parameters of tags are a syntactic tool and should be thoroughly verified by the implementation. Specifying incorrect names or order of named parameters may result in a compile-time error (syntax error).

All of the following are examples of syntactically valid tags:

{% yield %}

{% if var > 10 do: %}
  Var is greater than 10.
{% end if %}

{% for i in: [ 1, 2, 3 ] do: %}
  Value: {{ i }}
{% end for %}

{% if length(params.test) == 1 then: %}
  Test has length 1.
{% elsif: length(params.test) == 2 then: %}
  Test has length 2.
{% else: %}
  Test has unidentified length.
{% end if %}

{% capture "buffer" do: %}
  This text will be printed twice.
{% end capture %}
{% yield from: "buffer" %}
{% yield from: "buffer" %}

3 Grammar

The following Extended Backus-Naur Form grammar is normative. The native character set of Liquor is Unicode, and every character literal specified is an explicit codepoint.

Statement a to b is equivalent to codepoint set which includes every codepoint from a to b inclusive. Statement a except b means that both a and b are tokens which consist of exactly one codepoint, and every character satisfying a and not satisfying in b is accepted. Statement lookahead a means that the current token should only be produced if the codepoint immediately following it satisfies a.

Strictly speaking, this grammar lies within GLR domain, but if, as it is usually the case, an implementation has separate lexer and parser, a LALR(1) parser could be used. This will be further explained in section Blocks.

3.1 Basic Syntax

Whitespace
U+0007 | U+000A | U+0020
Alpha
a to z | A to Z
Digit
0 to 9
Any
any Unicode character
Symbol
Alpha | _
Identifier
Symbol ( Symbol | Digit )* lookahead ( Any except : )
Keyword
Symbol ( Symbol | Digit )* :
=
IntegerLiteral
Digit+ lookahead ( Any except Symbol )
StringLiteral
" ( \\ | \" | Any except " )* "
' ( \\ | \' | Any except ' )* '
TupleLiteral
[ TupleLiteralContent ]
TupleLiteralContent
Expression , TupleLiteralContent
Expression
empty

3.2 Expressions

Operator precedence table is provided in section Operators.

PrimaryExpression
Identifier
( Expression )
Expression
IntegerLiteral
StringLiteral
TupleLiteral
Identifier FunctionArguments
PrimaryExpression [ Expression ]
Expression . Identifier FunctionArguments?
- Expression
! Expression
Expression * Expression
Expression / Expression
Expression % Expression
Expression + Expression
Expression - Expression
Expression == Expression
Expression != Expression
Expression < Expression
Expression <= Expression
Expression > Expression
Expression >= Expression
Expression && Expression
Expression || Expression
KeywordArguments
( Keyword Expression )*
FunctionArguments
( Expression? KeywordArguments )
FilterChain
Expression | FilterChainContinuation
FilterChainContinuation
FilterFunctionCall | FilterChainContinuation
FilterFunctionCall
FilterFunctionCall
Identifier FunctionKeywordArguments

3.3 Blocks

Inside a Tag or Interpolation body any Whitespace is used to separate adjacent tokens, but is otherwise ignored. The cases where naïvely removing Whitespace would cause ambiguity can be determined by watching for lookahead clauses.

The Tag, TagFirstContinuation and EndTag production rules deviate from canonical LR(1) grammar structure. To parse these rules correctly, a LALR(1) parser should maintain a stack of tag identifiers and correctly decide on ambiguous reduction of rules Identifier and EndTag.

When the parser follows the second reduction for rule TagFirstContinuation, it should push the corresponding Tag Identifier on the top of the tag stack.

When the parser is about to decide whether it should reduce the sequence satisfying Identifier to EndTag or leave it as is, it should only reduce the sequence to EndTag if the Identifier part of the EndTag rule equals the value at the top of the tag stack. If this is the case, the topmost value is popped from the tag stack.

Block
Plaintext Block
Interpolation Block
Tag Block
Comment Block
empty
Comment
{! ( Comment | Any* )+ !}
Plaintext
( Any except { | { Any except ( { | % ) )+
Interpolation
{{ ( Expression | FilterChain ) }}
Tag
{% Identifier Expression? KeywordArguments TagFirstContinuation
TagFirstContinuation
%}
TagBlock TagNextContinuation
TagNextContinuation
KeywordArguments TagBlock TagNextContinuation
EndTag %}
TagBlock
Keyword %} Block {%
EndTag
end U+0020 Identifier at the top of tag stack

4 Compile-time Behavior

Liquor compiling process consists of three distinct parts: parsing, scope resolution and translation. Each stage includes exhaustive error checking; additionally, translation and scope resolution are heavily dependent on the defined tags and their behavior.

4.1 Errors

To ease development process, an implementation generally should not stop compilation after encountering an error. As an exception to the general rule, implementation must stop parsing and abandon any intermediate result after encountering a syntax error. Rationale to this behavior is that with Liquor’s interleaved structure, successful error recovery after parsing errors is unlikely.

Every error must carry precise location information: in particular, an error location must feature line, start column and end column.

The following algorithm can be used to calculate precise location information for every Unicode character in the source code:

  1. The initial line and column numbers equal 1.
  2. For each character in the source, in order, perform the following:
    1. If the character is U+000A, increase line number by 1 and set column number to 1.
    2. If the character is U+0007, increase column number by 1 until it equals zero modulo 8. If the column number already equals zero modulo 8, increase it by 8.
    3. If the character is a combining character, the implementation may recognize this fact and do nothing.
    4. If nothing of the above applies, increase column number by 1.

This algorithm, unlike the rest of Liquor, is specified in terms of characters and not codepoints. This means that an implementation must recognize surrogate pairs and compose them into one character.

4.1.1 Syntax Error

Syntax error will be signaled upon encountering any of the following conditions:

  1. Parsing failure (section Grammar)
  2. Duplicate function keyword arguments (section Function Calls)
  3. Incorrect tag syntax (section Tags)

Syntax errors must include source location information and point to the exact token which caused the error.

4.1.2 Argument Error

Argument error will be signaled upon encountering any of the following conditions:

  1. Absence of a mandatory parameter, or presence of non-accepted parameter (sections Function Calls, Tags)

Argument errors must include source location information and point either to the exact parameter which caused the error, or to the argument list in case of a missing parameter.

4.1.3 Name Error

Name error will be signaled upon encountering any of the following conditions:

  1. Referencing an undefined variable (sections Variable Access, Scope Resolution)
  2. Referencing an undefined function (section Function Calls)
  3. Encountering an undefined tag (section Tags)
  4. Trying to bind an already bound identifier (section Scope Resolution)

Name errors must include source location information and point to the exact token which caused the error.

4.2 Scope Resolution

Tags control every aspect of scope construction and resolution.

Basically, tags can perform three scope-related actions: declare a variable, assign a variable and create a nested scope.

Declaring a variable binds the identifier to a value. To declare a variable, the identifier should not be bound in the current scope. If this is not the case, a compile-time error (name error) is raised. If the identifier is bound in an outer scope, it will be rebound in the current scope. Such a binding ceases to exist when the current scope is left.

Assigning a variable, similarly to accessing, requires the variable to be declared in any of the scopes. Assigning a variable changes its value in the innermost scope.

Creating a nested scope allows for shadowing of the variables. Tags must only execute contents of the passed blocks in a nested scope. Passed expressions are always executed in the tag’s scope. A tag must ensure that every scope it created will be left before the tag will finish executing.

An implementation should have a way to inject variables into the outermost scope.

5 Runtime Behavior

Evaluation of Liquor programs follows lexical order for blocks, and is undefined for expressions. As all expressions are pure, this does not result in ambiguity.

A Liquor program always evaluates to a string. Liquor recognizes the value of and attempts to produce sensible output even for partially invalid programs; to keep the codebase manageable, the runtime environment must report all runtime errors to the programmer.

5.1 Type Error

A type error arises when a value of certain type(s) is expected in a context, but a value of a different type is provided. In this case, the runtime records the error and substitutes the value for a zero value, respectively for every type:

5.2 External Error

An external error arises when an unknown external method is called, or there is a problem evaluating the external method. In this case, the runtime records the error and returns null instead.

5.3 The dummy external

The dummy external is an external object which performs no operation when any method is called on it and returns null.

6 Builtins

Implementations must implement every builtin tag and function mentioned in this section. Implementations may implement any additional tags, but must not alter behavior of the described ones.

6.1 Required tags

6.1.1 declare

Tag declare has one valid syntactic form:

{% declare var = expr %}

Declare binds the name var to the result of executing expr in the current scope. If var is already bound in current scope, declare mutates the binding. If var is already bound in an outer scope, declare creates a new binding in the current scope.

The declare tag itself evaluates to an empty string.

6.1.2 assign

Tag assign has one valid syntactic form:

{% assign var = expr %}

Assign binds the name var to the result of executing expr in the current scope. If var is already bound, assign mutates the binding.

The assign tag itself evaluates to an empty string.

6.1.3 for

Tag for has two valid syntactic forms:

{% for var in: list do: %}
  code
{% end for %}
{% for var from: lower-limit to: upper-limit do: %}
  code
{% end for %}

In the for..in form, this tag invokes code with var bound to each element of list sequentally. If list is not a Tuple, a runtime error condition (type error) is signaled.

In the for..from..to form, this tag invokes code with var bound to each integer between lower-limit and upper-limit, inclusive. If lower-limit or upper-limit is not an Integer, a runtime error condition (type error) is signaled.

Both forms of the tag also bind a special var_loop variable to the [for loop external]{#for-external}.

The for tag evaluates to the concatenation of the values its code has evaluated to.

6.1.3.1 for loop external

The for loop external is an External that allows to query the current state of the loop. It provides the following parameterless methods:

6.1.4 if

Tag if has one valid syntactic form:

{% if cond-1 then: %}
  code-1
[{% elsif: cond-2 then: %}
  code-2] ...
[{% else: %}
  code-else]
{% end if %}

This tag can optionally have any amount of elsif clauses and only one else clause.

The if tag sequentally evaluates each passed condition cond-1, cond-2, … until a truthful value is computed. Then, it executes the corresponding code. If none of the conditions evaluate to a truthful value, the tag executes code-else if it exists.

The if tag returns the result of evaluating the corresponding code block, or an empty string if none of the blocks were executed.

6.1.5 unless

Tag unless has one valid syntactic form:

{% unless cond then: %}
  code
{% end unless %}

The unless tag evaluates cond. Unless it yields a truthful, code is also evaluated.

The unless tag returns the result of evaluating code, or an empty string.

6.1.6 capture

Tag capture has one valid syntactic form:

{% capture var = %}
  code
{% end capture %}

The capture tag evaluates code and binds the name var to the result. If var is already bound, capture mutates the binding.

The capture tag returns an empty string.

6.1.7 content_for

Tag content_for has one valid syntactic form:

{% content_for "handle" capture: %}
  code
{% end content_for %}

The content_for tag accepts a String handle as an immediate value. It evaluates code and assigns the result to the handle handle, which must be stored in an implementation-specific way.

The content_for tag returns an empty string.

See also notes on Layout implementation.

6.1.8 yield

Tag yield has three valid syntactic forms:

{% yield %}

In this form, the yield tag evaluates to the content of inner template.

{% yield "handle" %}
{% yield "handle" if_none: %}
  code
{% end yield %}

The yield tag accepts a String handle as an immediate value. If a string with handle handle was captured previously with {% content_for %}, then yield returns that string. If there is no captured string with that handle, yield either returns the result of evaluating if_none block if it exists, or an empty string.

See also notes on Layout implementation.

6.1.9 include

Tag include has one valid syntactic form:

{% include "partial_name" %}

The include tag accepts a String partial_name as an immediate value. It lexically includes the code of partial template partial_name in a newly created scope.

The include tag must not allow infinite recursion to happen. If such a condition is encountered, a compile-time error (syntax error) is signaled.

See also notes on Layout implementation.

6.2 Functions

Liquor offers a number of builtin functions. Their formal parameters are described using a shorthand notation:

fn_name(unnamed-arg-type kwarg1: kwarg1-type [kwarg2: kwarg2-type, kwarg2-alt-type])

In this case, function fn_name has an unnamed parameter accepting value of type unnamed-arg-type, a mandatory keyword parameter kwarg1 accepting values of type kwarg1-type, and an optional keyword parameter kwarg2 accepting values of either type kwarg2-type or kwarg2-alt-type.

6.2.1 Universal functions

6.2.1.1 is_empty

is_empty(Any)

Returns true iff the unnamed argument is one of null, "”, [].

6.2.1.2 size

size(String, Tuple)

Returns the length of the unnamed argument as an integer.

6.2.2 Conversion functions

6.2.2.1 strftime

size(String format: String)

Parses the unnamed argument as time in ISO8601 format, and reformats it using an implementation-defined strftime alike function.

6.2.2.2 to_number

to_number(String, Integer)

If the unnamed argument is an Integer, returns it. If it is a string, parses it as a decimal number, possibly with leading minus sign.

6.2.3 Integer functions

6.2.3.1 is_even

is_even(Integer)

Returns true if the unnamed argument is even, false otherwise.

6.2.3.2 is_odd

is_odd(Integer)

Returns true if the unnamed argument is odd, false otherwise.

6.2.4 String functions

6.2.4.1 downcase

downcase(String)

Returns the unnamed argument, converted to lowercase, using the Unicode case folding.

6.2.4.2 upcase

upcase(String)

Returns the unnamed argument, converted to uppercase, using the Unicode case folding.

6.2.4.3 capitalize

capitalize(String)

Returns the unnamed argument with its first character converted to uppercase, using the Unicode case folding.

6.2.4.4 starts_with

starts_with(String pattern: String)

Returns true if the unnamed argument starts with pattern, false otherwise. No normalization is performed.

6.2.4.5 strip_newlines

strip_newlines(String)

Returns the unnamed argument without any U+000A characters.

6.2.4.6 join

join(Tuple with: String)

Returns the concatenation of elements of the unnamed argument (which all must be Strings) interpsersed with the value of with.

6.2.4.7 split

split(String by: String)

Returns a tuple of fragments of the unnamed argument, extracted between occurences of by. If the unnamed argument is "”, returns [].

6.2.4.8 replace

replace(String pattern: String replacement: String)

Returns the unnamed argument with all occurences of pattern replaced with replacement.

6.2.4.9 replace_first

replace_first(String pattern: String replacement: String)

Returns the unnamed argument with the first occurence of pattern replaced with replacement.

6.2.4.10 remove

remove(String pattern: String)

Returns the unnamed argument with all occurences of pattern removed.

6.2.4.11 remove_first

remove_first(String pattern: String)

Returns the unnamed argument with the first occurences of pattern removed.

6.2.4.12 newline_to_br

newline_to_br(String)

Returns the unnamed argument with <br> inserted before every U+000A character.

6.2.4.13 url_escape

url_escape(String)

Returns the unnamed argument, processed using the application/x-www-form-urlencoded encoding algorithm.

6.2.4.14 html_escape

html_escape(String)

Returns the unnamed argument with &, <, >, ', " and / escaped to the correpsonding HTML entities.

6.2.4.15 html_escape_once, h

html_escape(String)

h(String)

Like html_escape, but does not affect & that is a part of an HTML entity.

6.2.4.16 strip_html

strip_html(String)

Returns the unnamed argument with all HTML tags and comments removed.

6.2.4.17 decode_html_entities

decode_html_entities(String)

Returns the unnamed argument with all HTML entities replaced by the corresponding Unicode character.

6.2.5 Tuple functions

6.2.5.1 compact

compact(Tuple)

Returns the unnamed argument without null elements.

6.2.5.2 reverse

reverse(Tuple)

Returns the unnamed argument, reversed.

6.2.5.3 uniq

reverse(Tuple)

Returns the unnamed argument with only first instance of non-unique elements left.

6.2.5.4 min

min(Tuple [by: String])

Returns the minimal element of the unnamed argument. The ordering between values of different types is implementation-defined. Including an External in the unnamed argument may lead to a runtime error (type error).

If by is passed, the unnamed argument must consist only of External values. In this case, the ordering is performed by calling the method specified by by.

6.2.5.5 max

max(Tuple [by: String])

See the min function.

6.2.5.6 in_groups_of

in_groups_of(Tuple size: Integer [fill_with: String, Boolean])

Returns the unnamed argument, split into tuples of size elements. If fill_with is passed, appends the value of fill_with to the last tuple, so that it is size elements big.

6.2.5.7 in_groups

in_groups(Tuple count: Integer [fill_with: String, Boolean])

Returns the unnamed argument, split into count equally-sized (except the last one) tuples. If fill_with is passed, appends the value of fill_with to the last tuple, so that it is as big as the others.

6.2.5.8 includes

includes(Tuple, External element: Any)

Returns true if the unnamed argument contains element, false otherwise. Not all externals support this operation; if an unsupported external is passed, runtime error condition (type error) is signaled.

6.2.5.9 index_of

index_of(Tuple, External element: Any)

Returns the index of element in the unnamed argument or null if it does not contain element. Not all externals support this operation; if an unsupported external is passed, runtime error condition (type error) is signaled.

6.2.6 Truncation functions

6.2.6.1 truncate

truncate(String [length: Integer omission: String])

Returns the unnamed argument, truncated to length (50 by default) characters and with omission (... by default) appended.

6.2.6.2 truncate_words

truncate_words(String [length: Integer omission: String])

Returns the unnamed argument, truncated to length (15 by default) words and with omission (... by default) appended.

6.2.6.3 html_truncate

html_truncate(String [length: Integer omission: String])

Returns the unnamed argument, truncated to length (50 by default) characters inside HTML text node and with omission (... by default) appended to the last HTML text node.

6.2.6.4 html_truncate_words

html_truncate_words(String [length: Integer omission: String])

Returns the unnamed argument, truncated to length (15 by default) words inside HTML text node and with omission (... by default) appended to the last HTML text node.

7 Layouts

In order to support the content_for and yield tags, the runtime maintains a dictionary associating the handles provided to content_for with the content. This dictionary is shared between all layout(s) and the innermost template; the templates are evaluated from the innermost to outermost.