UNPKG

closure-builder

Version:

Simple Closure, Soy and JavaScript Build system

409 lines (265 loc) 11.6 kB
# 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.