Expressions

Page Contents

When you supply values for interpolations or directive parameters you can use variables or more complex expressions. For example, if x is the number 8 and y is 5, the value of (x + y)/2 resolves to the numerical value 6.5.

Before we go into details, let's see some concrete examples:

Quick overview (cheat sheet)

This is a reminder for those of you who already know FreeMarker or are just experienced programmers:

See also: Operator precedence

Specify values directly

Often you want to specify a value directly and not as a result of some calculations.

Strings

To specify a string value directly you give the text in quotation marks, e.g.: "some text" or in apostrophe-quote, e.g. 'some text'. The two forms are equivalent. If the text itself contains the character used for the quoting (either " or ') or backslashes, you have to precede them with a backslash; this is called escaping. You can type any other character, including line breaks, in the text directly. Example:

${"It's \"quoted\" and
this is a backslash: \\"}

${'It\'s "quoted" and
this is a backslash: \\'}  

will print:

It's "quoted" and
this is a backslash: \

It's "quoted" and
this is a backslash: \  

Note

Of course, you could simply type the above text into the template, without using ${...}. But we do it here just for the sake of example, to demonstrate expressions.

This is the list of all supported escape sequences. All other usage of backlash in string literals is an error and any attempt to use the template will fail.

Escape sequence Meaning
\" Quotation mark (u0022)
\' Apostrophe (a.k.a. apostrophe-quote) (u0027)
\{ Opening curly brace: {
\\ Back slash (u005C)
\n Line feed (u000A)
\r Carriage return (u000D)
\t Horizontal tabulation (a.k.a. tab) (u0009)
\b Backspace (u0008)
\f Form feed (u000C)
\l Less-than sign: <
\g Greater-than sign: >
\a Ampersand: &
\xCode Character given with its hexadecimal Unicode code (UCS code)

The Code after the \x is 1 to 4 hexadecimal digits. For example this all put a copyright sign into the string: "\xA9 1999-2001", "\x0A9 1999-2001", "\x00A9 1999-2001". When the character directly after the last hexadecimal digit can be interpreted as hexadecimal digit, you must use all 4 digits or else FreeMarker will misunderstand you.

Note that the character sequence ${ (and #{) has special meaning. It's used to insert the value of expressions (typically: the value of variables, as in "Hello ${user}!"). This will be explained later. If you want to print ${ or #{, you should either use raw string literals as explained below, or escape the { like in "foo $\{bar}".

A special kind of string literals is the raw string literals. In raw string literals, backslash and ${ have no special meaning, they are considered as plain characters. To indicate that a string literal is a raw string literal, you have to put an r directly before the opening quotation mark or apostrophe-quote. Example:

${r"${foo}"}
${r"C:\foo\bar"}  

will print:

${foo}
C:\foo\bar  

Numbers

To specify a numerical value directly you type the number without quotation marks. You have to use the dot as your decimal separator and must not use any grouping separator symbols. You can use - or + to indicate the sign (+ is redundant). Scientific notation is not yet supported (so 1E3 is wrong). Also, you cannot omit the 0 before the decimal separator (so .5 is wrong).

Examples of valid number literals: 0.08, -5.013, 8, 008, 11, +11

Note that numerical literals like 08, +8, 8.00 and 8 are totally equivalent as they all symbolize the number eight. Thus, ${08}, ${+8}, ${8.00} and ${8} will all print exactly same.

Booleans

To specify a boolean value you write true or false. Don't use quotation marks.

Sequences

To specify a literal sequence, you list the subvariables separated by commas, and put the whole list into square brackets. For example:

<#list ["winter", "spring", "summer", "autumn"] as x>
${x}
</#list>  

will print:

winter
spring
summer
autumn
   

The items in the list are expressions, so you can do this for example: [2 + 2, [1, 2, 3, 4], "whatnot"]. Here the first subvariable will be the number 4, the second will be another sequence, and the third subvariable will be the string "whatnot".

Ranges

Ranges are just sequences, but they are created by specifying what range of whole numbers they contain, instead of specifying their items one by one. For example, 0..<m, assuming the m variable stores 5, will give a sequence that contains [0, 1, 2, 3, 4]. Ranges are primarily used for iterating over a range of numbers with <#list ...> and for slicing sequences and slicing strings.

The generic forms of range expressions are (where start and end can be any expression that evaluates to a number):

Further notes on ranges:

Hashes

To specify a hash in a template, you list the key/value pairs separated by commas, and put the list into curly brackets. The key and value within a key/value pair are separated with a colon. Here is an example: { "name": "green mouse", "price": 150 }. Note that both the names and the values are expressions. The keys must be strings. The values can be if any type.

Retrieving variables

Top-level variables

To access a top-level variable, you simply use the variable name. For example, the expression user will evaluate to the value of variable stored with name "user" in the root. So this will print what you store there:

${user}  

If there is no such top-level variable, then an error will result when FreeMarker tries to evaluate the expression, and it aborts template processing (unless programmers has configured FreeMarker differently).

In this kind of expression, the variable name can only contain letters (including non-Latin letters), digits (including non-Latin digits), underline (_), dollar ($), at sign (@). Furthermore, the first character can't be a ASCII digit (0-9). Starting from FreeMarker 2.3.22, the variable name can also contain minus (-), dot (.), and colon (:) at any position, but these must be escaped with a preceding backslash (\), or else they would be interpreted as operators. For example, to read the variable whose name is "data-id", the expression is data\-id, as data-id would be interpreted as "data minus id". (Note that these escapes only work in identifiers, not in string literals.)

Retrieving data from a hash

If we already have a hash as a result of an expression, then we can get its subvariable with a dot and the name of the subvariable. Assume that we have this data-model:

(root)
 |
 +- book
 |   |
 |   +- title = "Breeding green mouses"
 |   |
 |   +- author
 |       |
 |       +- name = "Julia Smith"
 |       |
 |       +- info = "Biologist, 1923-1985, Canada"
 |
 +- test = "title"  

Now we can read the title with book.title, since the book expression will return a hash (as explained in the last chapter). Applying this logic further, we can read the name of the author with this expression: book.author.name.

There is an alternative syntax if we want to specify the subvariable name with an expression: book["title"]. In the square brackets you can give any expression as long as it evaluates to a string. So with this data-model you can also read the title with book[test]. More examples; these are all equivalent: book.author.name, book["author"].name, book.author.["name"], book["author"]["name"].

When you use the dot syntax, the same restrictions apply regarding the variable name as with top-level variables (name can contain only letters, digits, _, $, @ but can't start with 0-9, also starting from 2.3.22 you can also use \-, \. and \:). There are no such restrictions when you use the square bracket syntax, since the name is the result of an arbitrary expression. (Note, that to help the FreeMarker XML support, if the subvariable name is * (asterisk) or **, then you do not have to use square bracket syntax.)

As with the top-level variables, trying to access a non-existent subvariable causes an error and aborts the processing of the template (unless programmers has configured FreeMarker differently).

Retrieving data from a sequence

This is the same as for hashes, but you can use the square bracket syntax only, and the expression in the brackets must evaluate to a number, not a string. For example to get the name of the first animal of the example data-model (remember that the number of the first item is 0, not 1): animals[0].name

Special variables

Special variables are variables defined by the FreeMarker engine itself. To access them, you use the .variable_name syntax.

Normally you don't need to use special variables. They are for expert users. The complete list of special variables can be found in the reference.

String operations

Interpolation (or concatenation)

If you want to insert the value of an expression into a string, you can use ${...} (and the deprecated #{...}) in string literals. ${...} in string literals behaves as in text sections (so it goes through the same locale sensitive number and date/time formatting), except that it never go through automatic escaping.

Example (assume that user is ``Big Joe''):

<#assign s = "Hello ${user}!">
${s} <#-- Just to see what the value of s is -->  

This will print:

Hello Big Joe!  

Warning!

A frequent mistake of users is the usage of interpolations in places where they needn't/shouldn't/can't be used. Interpolations work only in text sections (e.g. <h1>Hello ${name}!</h1>) and in string literals (e.g. <#include "/footer/${company}.html">). A typical WRONG usage is <#if ${big}>...</#if>, which will cause a syntactical error. You should simply write <#if big>...</#if>. Also, <#if "${big}">...</#if> is WRONG, since it converts the parameter value to string and the if directive wants a boolean value, so it will cause a runtime error.

Alternatively, you can use the + operator to achieve similar result:

<#assign s = "Hello " + user + "!">  

This gives the same result as the example with the ${...}.

Warning!

Because + follows the same rules as ${...}, the appended string is influenced by the locale, number_format, date_format, time_format, datetime_format and boolean_format, etc. settings, and thus the result targets humans and isn't in generally machine parsable. This mostly leads to problems with numbers, as many locales use grouping (thousands separators) by default, and so "someUrl?id=" + id becomes to something like "someUrl?id=1 234". To prevent this, use the ?c (for Computer audience) built-in, like in "someUrl?id=" + id?c or "someUrl?id=${id?c}", which will evaluate to something like "someUrl?id=1234", regardless of locale and format settings.

Getting a character

You can get a single character of a string at a given index similarly as you can read the subvariable of a sequence, e.g. user[0]. The result will be a string whose length is 1; FTL doesn't have a separate character type. As with sequence subvariables, the index must be a number that is at least 0 and less than the length of the string, or else an error will abort the template processing.

Since the sequence subvariable syntax and the character getter syntax clashes, you can use the character getter syntax only if the variable is not a sequence as well (which is possible because FTL supports multi-typed values), since in that case the sequence behavior prevails. (To work this around, you can use the string built-in, e.g. user?string[0]. Don't worry if you don't understand this yet; built-ins will be discussed later.)

Example (assume that user is "Big Joe"):

${user[0]}
${user[4]}  

will print (note that the index of the first character is 0):

B
J  

String slicing (substrings)

You can a slice a string in the same way as you slice a sequence (see there), only here instead of sequence items you work with characters. Some differences are:

Example:

<#assign s = "ABCDEF">
${s[2..3]}
${s[2..<4]}
${s[2..*3]}
${s[2..*100]}
${s[2..]}  

will print:

CD
CD
CDE
CDEF
CDEF  

Note

Some of the typical use-cases of string slicing is covered by convenient built-ins: remove_beginning, remove_ending, keep_before, keep_after, keep_before_last, keep_after_last

Sequence operations

Concatenation

You can concatenate sequences in the same way as strings, with +. Example:

<#list ["Joe", "Fred"] + ["Julia", "Kate"] as user>
- ${user}
</#list>  

will print:

- Joe
- Fred
- Julia
- Kate
   

Note that sequence concatenation is not to be used for many repeated concatenations, like for appending items to a sequence inside a loop. It's just for things like <#list users + admins as person>. Although concatenating sequences is fast and its speed is independently of the size of the concatenated sequences, the resulting sequence will be always a little bit slower to read than the original two sequences were. This way the result of many repeated concatenations is a sequence that is slow to read.

Sequence slicing

With seq[range], were range is a range value as described here, you can take a slice of the sequence. The resulting sequence will contain the items from the original sequence (seq) whose indexes are in the range. For example:

<#assert seq = ["A", "B", "C", "D", "E"]>
<#list seq[1..3] as i>${i}</#list>  

will print

BCD   

Furthermore, the items in the slice will be in the same order as in the range. Thus for example the above example with the 3..1 range would print DCB.

The numbers in the range must be valid indexes in the sequence, or else the processing of the template will be aborted with error. Like in the last example, seq[-1..0] would be an error as seq[-1] is invalid, also seq[1..5] would be because seq[5] is invalid. (Note that seq[100..<100] or seq[100..*0] would be valid despite that 100 is out of bounds, because those ranges are empty.)

Length limited ranges (start..*length) and right-unbounded ranges (start..) adapt to the length of the sliced sequence. They will slice out at most as many items as there is available:

<#assign seq = ["A", "B", "C"]>

Slicing with length limited ranges:
- <#list seq[0..*2] as i>${i}</#list>
- <#list seq[1..*2] as i>${i}</#list>
- <#list seq[2..*2] as i>${i}</#list> <#-- Not an error -->
- <#list seq[3..*2] as i>${i}</#list> <#-- Not an error -->

Slicing with right-unlimited ranges:
- <#list seq[0..] as i>${i}</#list>
- <#list seq[1..] as i>${i}</#list>
- <#list seq[2..] as i>${i}</#list>
- <#list seq[3..] as i>${i}</#list>  

This will print:

Slicing with length limited ranges:
- AB
- BC
- C
-

Slicing with right-unlimited ranges:
- ABC
- BC
- C
-  

Note above that slicing with length limited and right unbounded ranges allow the starting index to be past the last item by one (but no more).

Note

To split a sequence to slices of a given size, you should use the chunk built-in.

Hash operations

Concatenation

You can concatenate hashes in the same way as strings, with +. If both hashes contain the same key, the hash on the right-hand side of the + takes precedence. Example:

<#assign ages = {"Joe":23, "Fred":25} + {"Joe":30, "Julia":18}>
- Joe is ${ages.Joe}
- Fred is ${ages.Fred}
- Julia is ${ages.Julia}  

will print:

- Joe is 30
- Fred is 25
- Julia is 18  

Note that hash concatenation is not to be used for many repeated concatenations, like for adding items to a hash inside a loop. It's the same as with the sequence concatenation.

Arithmetical calculations

This is the basic 4-function calculator arithmetic plus the modulus operator. So the operators are:

Example:

${100 - x * x}
${x / 2}
${12 % 10}  

Assuming that x is 5, it will print:

75
2.5
2  

Both operands must be expressions which evaluate to a numerical value. So the example below will cause an error when FreeMarker tries to evaluate it, since "5" is a string and not the number 5:

${3 * "5"} <#-- WRONG! -->  

There is an exception to the above rule. The + operator, is used to concatenate strings as well. If on one side of + is a string and on the other side of + is a numerical value, then it will convert the numerical value to string (using the format appropriate for language of the page) and then use the + as string concatenation operator. Example:

${3 + "5"}  

will output this:

35  

Generally, FreeMarker never converts a string to a number automatically, but it may convert a number to a string automatically.

People often want only the integer part of the result of a division (or of other calculations). This is possible with the int built-in. (Built-ins are explained later):

${(x/2)?int}
${1.1?int}
${1.999?int}
${-1.1?int}
${-1.999?int}  

Assuming that x is 5, it will print:

2
1
1
-1
-1  

Comparison

Sometimes you want to know if two values are equal or not, or which value is the greater.

To show concrete examples I will use the if directive here. The usage of if directive is: <#if expression>...</#if>, where expression must evaluate to a boolean value or else an error will abort the processing of the template. If the value of expression is true then the things between the begin and end-tag will be processed, otherwise they will be skipped.

To test two values for equality you use = (or == as in Java or C; the two are absolutely equivalent.) To test two values for inequality you use !=. For example, assume that user is ``Big Joe'':

<#if user = "Big Joe">
  It is Big Joe
</#if>
<#if user != "Big Joe">
  It is not Big Joe
</#if>  

The user = "Big Joe" expression in the <#if ...> will evaluate to the boolean true, so the above will say ``It is Big Joe''.

The expressions on both sides of the = or != must evaluate to a scalar. Furthermore, the two scalars must have the same type (i.e. strings can only be compared to strings and numbers can only be compared to numbers, etc.) or else an error will abort template processing. For example <#if 1 = "1"> will cause an error. Note that FreeMarker does exact comparison, so string comparisons are case and white-space sensitive: "x" and "x " and "X" are not equal values.

For numerical and date, time and date-time values you can also use <, <=, >= and >. You can't use them for strings! Example:

<#if x <= 12>
  x is less or equivalent with 12
</#if>  

There's a problem with >= and >. FreeMarker interprets the > character as the closing character of the FTL tag. To prevent this, you can use lt instead of <, lte instead of <=, gt instead of > and gte instead of >=, like in <#if x gt y>. Another trick it to put the expression into parentheses like in <#if (x > y)>, although it's considered to be less elegant.

Note

FreeMarker supports some other alternatives too, but these are deprecated:

  • Writing &gt; and &lt; on the place of the problematic relation marks, like in: <#if x &gt; y> or <#if x &gt;= y> . Note that in general FTL does not support entity references (the &...; things) in FTL tags; it's just an exception with the arithmetical comparisons.

  • \lt, \lte, \gt and \gte which are the same as the ones without the backslash

Logical operations

Just the usual logical operators:

The operators will work with boolean values only. Otherwise an error will abort the template processing.

Example:

<#if x < 12 && color = "green">
  We have less than 12 things, and they are green.
</#if>
<#if !hot> <#-- here hot must be a boolean -->
  It's not hot.
</#if>  

Built-ins

Built-ins are like methods that are added to the objects by FreeMarker. To prevent name clashes with actual methods and other sub-variables, instead of dot (.), you separate them from the parent object with question mark (?). For example, if you want to ensure that path has an initial / then you could write path?ensure_starts_with('/'). The Java object behind path (a String most certainly) doesn't have such method, FreeMarker adds it. For brevity, if the method has no parameters, you must omit the (), like, to get the length of path, you have to write path?length, not path?length(). The other reason why built-ins are crucial is that normally (though it depends on configuration settings), FreeMarker doesn't expose the Java API of the objects. So despite that Java's String has a length() method, it's hidden from the template, you have to use path?length instead. The advantage of that is that thus the template doesn't depend on the exactly type of the underlying Java objects. (Like path it's maybe a java.nio.Path behind the scenes, but if the programmers has configure FreeMarker to expose Path objects as FTL strings, the template won't be aware of that, and ?length will work, despite that java.nio.Path has no similar method.)

You can find the complete list of built-ins in the Reference. For now, just a few of the more important ones:

Example:

${test?html}
${test?upper_case?html}  

Assuming that test stores the string ``Tom & Jerry'', the output will be:

Tom &amp; Jerry
TOM &amp; JERRY  

Note the test?upper_case?html. Since the result of test?upper_case is a string, you can use the html built-in with it.

Another example:

${seasons?size}
${seasons[1]?cap_first} <#-- left side can by any expression -->
${"horse"?cap_first}  

Assuming that seasons stores the sequence "winter", "spring", "summer", "autumn", the output will be:

4
Spring
Horse  

Method call

If you have a method then you can use the method call operation on it. The method call operation is a comma-separated list of expressions in parentheses. These values are called parameters. The method call operation passes these values to the method which will in turn return a result. This result will be the value of the whole method call expression.

For example, assume the programmers have made available a method variable called repeat. You give a string as the first parameter, and a number as the second parameter, and it returns a string which repeats the first parameter the number of times specified by the second parameter.

${repeat("Foo", 3)}  

will print:

FooFooFoo  

Here repeat was evaluated to the method variable (according to how you access top-level variables) and then ("What", 3) invoked that method.

I would like to emphasize that method calls are just plain expressions, like everything else. So this:

${repeat(repeat("x", 2), 3) + repeat("Foo", 4)?upper_case}  

will print this:

xxxxxxFOOFOOFOOFOO  

Handling missing values

Note

These operators exist since FreeMarker 2.3.7 (replacing the default, exists and if_exists built-ins).

As we explained earlier, an error will occur and abort the template processing if you try to access a missing variable. However two special operators can suppress this error, and handle the problematic situation. The handled variable can be top-level variable, hash subvariable, or sequence subvariable as well. Furthermore these operators handle the situation when a method call doesn't return a value (from the viewpoint of Java programmers: it returns null or it's return type is void), so it's more correct to say that these operators handle missing values in general, rather than just missing variables.

For those who know what's Java null, FreeMarker 2.3.x treats them as missing values. Simply, the template language doesn't know the concept of null. For example, if you have a bean that has a maidenName property, and the value of that property is null, then that's the same as if there were no such property at all, as far as the template is concerned (assuming you didn't configured FreeMarker to use some extreme object wrapper, that is). The result of a method call that returns null is also treated as a missing variable (again, assuming that you use some usual object wrapper). See more in the FAQ.

Note

If you wonder why is FreeMarker so picky about missing variables, read this FAQ entry.

Default value operator

Synopsis: unsafe_expr!default_expr or unsafe_expr! or (unsafe_expr)!default_expr or (unsafe_expr)!

This operator allows you to specify a default value for the case when the value is missing.

Example. Assume no variable called mouse is present:

${mouse!"No mouse."}
<#assign mouse="Jerry">
${mouse!"No mouse."}  

The output will be:

No mouse.
Jerry  

The default value can be any kind of expression, so it doesn't have to be a string. For example you can write hits!0 or colors!["red", "green", "blue"]. There is no restriction regarding the complexity of the expression that specifies the default value, for example you can write: cargo.weight!(item.weight * itemCount + 10).

Warning!

If you have a composite expression after the !, like 1 + x, always use parenthesses, like ${x!(1 + y)} or ${(x!1) + y)}, depending on which interpretation you meant. That's needed because due to a programming mistake in FreeMarker 2.3.x, the precedence of ! (when it's used as default value operator) is very low at its right side. This means that, for example, ${x!1 + y} is misinterpreted by FreeMarker as ${x!(1 + y)} while it should mean ${(x!1) + y}. This programming error will be fixed in FreeMarker 2.4, so you should not utilize this wrong behavior, or else your templates will break with FreeMarker 2.4!

If the default value is omitted, then it will be empty string and empty sequence and empty hash at the same time. (This is possible because FreeMarker allows multi-type values.) Note the consequence that you can't omit the default value if you want it to be 0 or false. Example:

(${mouse!})
<#assign mouse = "Jerry">
(${mouse!})  

The output will be:

()
(Jerry)  

Warning!

Due to syntactical ambiguities <@something a=x! b=y /> will be interpreted as <@something a=x!(b=y) />, that is, the b=y will be interpreted as a comparison that gives the default value for x, rather than the specification of the b parameter. To prevent this, write: <@something a=(x!) b=y />

You can use this operator in two ways with non-top-level variables:

product.color!"red"  

This will handle if color is missing inside product (and returns "red" if so), but will not handle if product is missing. That is, the product variable itself must exist, otherwise the template processing will die with error.

(product.color)!"red"  

This will handle if product.color is missing. That is, if product is missing, or product exists but it does not contain color, the result will be "red", and no error will occur. The important difference between this and the previous example is that when surrounded with parentheses, it is allowed for any component of the expression to be undefined, while without parentheses only the last component of the expression is allowed to be undefined.

Of course, the default value operator can be used with sequence subvariables as well:

<#assign seq = ['a', 'b']>
${seq[0]!'-'}
${seq[1]!'-'}
${seq[2]!'-'}
${seq[3]!'-'}  

the outpur will be:

a
b
-
-  

A negative sequence index (as seq[-1]!'-') will always cause an error, you can't suppress that with this or any other operator.

Missing value test operator

Synopsis: unsafe_expr?? or (unsafe_expr)??

This operator tells if a value is missing or not. Depending on that, the result is either true or false.

Example. Assume no variable called mouse is present:

<#if mouse??>
  Mouse found
<#else>
  No mouse found
</#if>
Creating mouse...
<#assign mouse = "Jerry">
<#if mouse??>
  Mouse found
<#else>
  No mouse found
</#if>  

The output will be:

  No mouse found
Creating mouse...
  Mouse found  

With non-top-level variables the rules are the same as with the default value operator, that is, you can write product.color?? and (product.color)??.

Parentheses

Parentheses can be used to group any expressions. Some examples:

                               <#-- Output will be: -->
${3 * 2 + 2}                   <#-- 8 -->
${3 * (2 + 2)}                 <#-- 12 -->
${3 * ((2 + 2) * (1 / 2))}     <#-- 6 -->
${"green " + "mouse"?upper_case}    <#-- green MOUSE -->
${("green " + "mouse")?upper_case}  <#-- GREEN MOUSE -->
<#if !(color == "red" || color == "green")>
  The color is nor red nor green
</#if>  

Note that the parentheses of a method call expressions have nothing to do with the parentheses used for grouping.

White-space in expressions

FTL ignores superfluous white-space in expressions. So these are totally equivalent:

${x + ":" + book.title?upper_case}  

and

${x+":"+book.title?upper_case}  

and

${
   x
 + ":"   +  book   .   title
   ?   upper_case
      }  

Operator precedence

The following table shows the precedence assigned to the operators. The operators in this table are listed in precedence order: the higher in the table an operator appears, the higher its precedence. Operators with higher precedence are evaluated before operators with a relatively lower precedence. Operators on the same line have equal precedence. When binary operators (operators with two ``parameters'', as + and -) of equal precedence appear next to each other, they are evaluated in left-to-right order.

Operator group Operators
highest precedence operators [subvarName] [subStringRange] . ? (methodParams) expr! expr??
unary prefix operators +expr -expr !expr
multiplicative operators * / %
additive operators + -
numerical ranges .. ..< ..! ..*
relational operators < > <= >= (and equivalents: gt, lt, etc.)
equality operators == != (and equivalents: =)
logical "and" operator &&
logical "or" operator ||

For those of you who know C, Java language or JavaScript, note that the precedence rules are the same as in those languages, except that FTL has some operators that do not exist in those languages.

The default value operator (exp!exp) is not yet in the table because of a programming mistake, which will be only fixed in FreeMarker 2.4 due to backward compatibility constraints. It meant to be a "highest precedence operator", but in FreeMarker 2.3.x the precedence on its right side is very low by accident. So if you have a composite expression on the right side, always use paranthesses, etiher like x!(y + 1) or like (x!y) + 1. Never write just x!y + 1.

FreeMarker Manual -- For FreeMarker 2.3.22
HTML generated: 2015-02-28 21:34:03 GMT
Edited with XMLMind XML Editor
Here!