closure-builder
Version:
Simple Closure, Soy and JavaScript Build system
409 lines (265 loc) • 11.6 kB
Markdown
# Expressions
Expressions are written within templates to reference template data, variables,
or compute intermediate values. `Closure Templates` uses a language-neutral
expression syntax. This section describes the expression grammar, including how
to reference data, write literals for all the primitive types, and use basic
operators.
[TOC]
## Where are expressions evaluated?
Expressions show up in lots of different places in `Closure Templates`. The most
common place is in [print commands](print.md), but they are also used in [if
commands](control-flow.md#if), [let declarations](let.md) and many other places.
Here are a few examples:
```soy
{template .foo}
{@param p: ?}
{if $p > 2}
{call .bar}{param p : $p == 3 ? 'a' : 'b' /}{/call}
{/if}
{/template}
```
In the above example, we can see a parameter `p` being used in several
expressions for several different `Closure Templates` commands.
## Literals
### bool
Boolean literals have two values: `false` and `true`
### int
Integer literals can be specified using either hexadecimal or decimal notation.
Examples:
* `0xNN`: hexadecimal number
* `0xFF`, `Ox00FF00FF`
* `NNN`: decimal number
* `1`, `45`
`Closure Templates` integers are limited to what is representable in JavaScript.
This means that in practice, values above 2^53 are not guaranteed to be
precisely represented. See
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
for more information.
### float
Floating point literals can be written using either decimal or scientific
format.
For example,
* `NNN.NN`: decimal format
* `22.1`, `1.0`
* `NN.NeNN`: scientific notation
* `6.03e23`
### string {#string-literal}
String literals are delimited by either single or double quote characters and
support standard escape sequences:
* Standard escapes: `\\ ` `\'` `\"` `\n` `\r` `\t` `\b` `\f`
* Unicode escapes: `\u####` (backslash, "u", four hex digits )
* Hex escapes: `\x##` (backslash, "x", two hex digits )
* Octal escapes: `\NNN` (backslash, followed by 2-3 octal digits )
Examples:
* `''`, `""` empty string
* `'abc'` simple literal
* `'aa\\bb\'cc\ndd'` literal with escaped quotation marks and backslashes
* `'\u263a'` unicode escape
Long string literals can be split across lines with the `+` operator:
```soy
"abc" +
"def"
```
or a [`let` command](let.md) using the `text` kind:
```soy
{let $longString kind="text"}
abc
def
{/let}
```
### list
list literals are delimited by square brackets and contain a comma separated
list of expressions.
For example:
* `[]` empty list
* `['a', 'b']` list containing two elements
### map {#map}
Map literals are delimited by `map()` and contain a comma-delimited sequence of
key-value pairs separated by `:` characters. For example,
* `map()`: the empty map
* `map(1: 'one', 2: 'two')`
These expressions create [map](types.md#map) values. For more details about the
difference between maps and legacy object maps see the [map](types.md#map)
documentation.
### record {#record}
Record literals are delimited by `record()` and contain a comma-delimited
sequence of key-value pairs separated by `:` characters. Each key must be an
identifier. For example,
* `record()`: the empty record
* `record(aaa: 'blah', bbb: 123, ccc: $foo)`
## Variables
### Parameters and locals
Parameters and locals are introduced by:
* [param declarations](templates#param)
* [inject declarations](templates#inject)
* [let declarations](let)
* [for loops](control-flow#for)
To reference a variable, use a dollar sign `$` followed by the variable name.
For example: `$foo`
### globals
A global is a reference that looks like a simple dotted identifier sequence.
`foo.bar.Baz`
Globals can be configured with the compiler via the `--compileTimeGlobalsFile`
flag, proto enum values are also represented as global references.
TIP: You can use the [`{alias ...}`](file-declarations.md#alias) directive to
abbreviate globals.
It is an error in the compiler to reference a global that doesn't have a
definition at compile time, however, if you are only compiling for JavaScript
then this is allowed for backwards compatibility reasons.
## Operators
### Precedence
Here are the supported operators, listed in decreasing order of precedence
(highest precedence at the top):
1. `( <expr> )` `.` `?.` `[]`, `?[]`
1. `-` (unary) `not`
1. `*` `/` `%`
1. `+` `-` (binary)
1. `<` `>` `<=` `>=`
1. `==` `!=`
1. `and`
1. `or`
1. `?:` (binary) `? :` (ternary)
The `Closure Templates` programming language respects the order of evaluation
indicated explicitly by parentheses and implicitly by operator precedence.
### Parenthesis `(...)`
Parenthesis have no affect other than to manipulate the order of evaluation.
### Data access operators `.` `?.` {#data-access}
The dot operator and question-dot operator are for accessing fields of a
`record` or a `proto`.
The question-dot operator is for null safe access. If the value of the preceding
operand is `null` then the access will return `null` rather than failing. This
is useful for traversing optional proto fields.
For example,
* `$foo.bar` accesses the `bar` field of the variable `$foo`
* `$foo?.bar` accesses the `bar` field of `$foo` only if `$foo` is non-`null`
### Indexed access operators `[]` `?[]` {#indexing-operators}
Indexed access operators, used for accessing elements of `maps` and `lists`.
The question-bracket operator is for null safe access. If the value of the
preceding operand is `null` then the access will return `null` rather than
failing.
For example,
* `$foo[$bar]` accesses the `$bar` index of the map `$foo`
* `$foo?[$bar]` accesses the `$bar` field of `$foo` only if `$foo` is
non-`null`
NOTE: if the index being accessed doesn't exist, `null` will be returned. There
is no 'index out of bounds' error for lists.
### Minus operator `-`
Unary minus. Used to mathematically negate a value.
For example,
* `-$foo`
* `-(3 + $bar)`
### Not operator `not`
The `not` operator, performs logical negation (i.e. `not true == false` and `not
false == true`)
For example,
* `not $bar`
* `not isLast($foo)`
### Times operator `*`
Numeric multiplication.
For example,
* `$foo * 2`
### Division operator `/`
Numeric division.
NOTE: this always performs floating point division. For integer division
consider using the [`floor`](functions#floor),
[`ceiling`](functions.md#ceiling), or [`round`](functions.md#round) functions to
process the result of a division.
For example,
* `$foo / 2`
### Modulus operator `%`
Modulus or remainder operator.
For example,
* `$foo % 2 == 0` returns `true` if `$foo` is an even number
### Plus operator `+`
The plus operator. This operator is overloaded to perform both numeric addition
and string concatenation.
* When both operands are numeric, performs addition.
* When one of the two operands is a string, the other value is coerced to a
string and the values are concatenated.
NOTE: All primitive values have string representations. The string
representation of a map or list is not well-defined, so don't print a map or
list unless you're debugging.
For example,
* `$foo + $bar`
* `'' + $foo` coerces `foo` to a string
### Subtraction operator `-`
The subtraction operator always performs numeric subtraction.
For example,
* `$foo - 1`
### Relative comparison operators `<`, `>`, `<=`, `>=`
Relative comparison operators, used for comparing numeric values.
For example,
* `$foo < $bar`
### Equality operators `==`, `!=`
Equality operators. Compares two values for equality. If one side is a `string`
and the other side is not, then it will be coerced to a `string` for the
comparison.
For example,
* `$foo == null`
* `2 == $bar`
### Logical operators `and`, `or` {#logical-operators}
Logical boolean operators.
The boolean operators are short-circuiting. When a non-boolean value is used in
a boolean context, it is coerced to a boolean.
NOTE: Each primitive type has exactly one falsy value: `null` is falsy, `false`
is falsy for booleans, `0` is falsy for integers, `0.0` is falsy for floats, and
`''` (empty string) is falsy for strings. All other primitive values are truthy.
Maps and lists are always truthy even if they're empty.
For example,
* `$foo > 2 and $foo < 5`
* `$foo <= 2 or $foo >= 5`
Using a constant expression for the `or` operator produces a compiler warning.
Using a boolean constant renders the `or` expression meaningless; using a
constant of another type means the expression does not evaluate to a boolean
type. Further, the falsy rules are different in Java and JavaScript (`''` in
Java is truthy; but in JavaScript it is falsy). Using non-boolean constants
might produce slightly different results when rendering in Java versus
JavaScript.
Rather than using the short-circuit property of the `or` operator, you should
use `?:`, the [null coalescing operator](#null-coalescing-operator). This more
clearly expresses your intent.
For example, these expressions will produce a warning:
```soy {.bad}
{param myLabel: $myProto?.label or '' /}
{param isEnabled: $isButtonVisible or false /}
{param isEnabled: $optBoolVar or false /}
```
Simplify or use `?:` for all new `Closure Templates` code.
```soy {.good}
{param myLabel: $myProto?.label ?: '' /}
{param isEnabled: $isButtonVisible /}
{param isEnabled: $optBoolVar ?: false /}
```
### Null coalescing operator `?:` {#null-coalescing-operator}
The null coalescing operator (also known as the 'elvis operator'). Returns the
left side if it is non-`null`, and the right side otherwise. This is often
useful for supplying default values.
This operator is short circuiting, if the left side is non-`null` the right side
will not be evaluated.
For example,
* `$foo ?: 0`
### Ternary operator `? :` {#ternary-operator}
Ternary conditional operator ? : uses the boolean value of one expression to
decide which of two other expressions should be evaluated.
For example,
* `$foo ? 1 : 2`
NOTE: The checks done by the binary operator `?:` and the ternary operator `? :`
are different. Specifically, `$a ?: $b` is not equivalent to `$a ? $a : $b`.
Rather, the former expression is equivalent to `isNonnull($a) ? $a : $b`.
## Function calls
Function calls consist of an identifier with a number of positional parameters:
`<IDENT>(<EXPR>,...)`
See the [dev guide](../dev/plugins.md) for how to register a custom function and
the [functions reference](functions.md) for a list of all functions that are
available by default.
For example:
* `isNonnull($foo)`
* `max($foo, $bar)`
## Proto initialization
Proto construction consists of a fully qualified proto name followed by a
sequence of key value pairs where the keys correspond to fields in the proto.
`<PROTO-NAME>(<FIELD>: <VALUE>,...)`
For example:
* `foo.bar.Baz(quux: 3)`
TIP: You can use the [`{alias ...}`](file-declarations.md#alias) directive to
abbreviate proto names used in initialization expressions.