Expressions¶
An expression is what appears before the first | in an expression chain (see
Transforms for the pipe itself). It is either a single operand, or exactly one
binary comparison between two operands — kBars does not chain multiple operators in a single
expression without parentheses. An operand is either a primary value or a
parenthesized sub-expression.
{{ name }} {{! operand: an identifier }}
{{ "literal" }} {{! operand: a string literal }}
{{ a == b }} {{! binary: exactly one operator }}
{{ a == b | upcase }} {{! the pipe applies to the whole expression's result }}
Primaries¶
A primary is one of:
- An identifier — see Interpolation for every recognized form.
- A string literal —
"…"or'…', with escapes\\,\",\',\n,\r,\t,\uXXXX. - A number literal —
-?[0-9]+(\.[0-9]+)?. true,false, ornull.
Binary operators¶
| Operator | Meaning |
|---|---|
or |
Logical OR (both sides coerced to truthy); case-insensitive |
and |
Logical AND (both sides coerced to truthy); case-insensitive |
== |
Equality |
!= |
Inequality (!(a == b)) |
<, <=, >, >= |
Ordering comparison |
Whitespace is required around a binary operator, matching the pattern for the pipe operator:
{{ a==b }} is not valid, {{ a == b }} is.
Parenthesized sub-expressions¶
An operand may be a parenthesized expression chain instead of a bare primary, so a binary expression can combine more than one operator by grouping:
{{#if a and (b or c) }} {{! (b or c) is evaluated first, then and'd with a }}
{{#if (a or b) and (c or d) }} {{! both sides may be grouped }}
{{#if a and (b or (c and d)) }} {{! groups can nest }}
{{ (items | size) > 0 }} {{! a group may itself carry transforms }}
Whitespace immediately inside the parentheses is optional: (b or c) and ( b or c ) both parse.
Parentheses used this way are a full expression chain, so transforms may appear inside them, as in
the last example above. What kBars still does not support is chaining operators without
parentheses — {{ a and b and c }} is a parse error regardless of grouping elsewhere in the
template; each and/or/comparison needs exactly two operands, parenthesized or not.
Equality (==, !=)¶
Equality dispatches by operand type, checking each rule in order until one applies:
- If either operand is a boolean primitive, both sides are coerced to truthy and compared as booleans.
- Otherwise, if either operand is a string primitive, both sides render to string (the same
rendering
{{ }}would produce) and are compared as strings. - Otherwise, if both operands are numeric, they're compared as numbers.
- Otherwise, structural equality: two structs are equal when they have the same keys and every value is recursively equal; two lists are equal when they have the same size and every element is recursively equal (in order); anything else compares equal only when it is the same kind of value.
!= is simply the negation of ==.
Ordering (<, <=, >, >=)¶
- If either operand is a string primitive, both sides render to string and compare lexicographically.
- Otherwise, if both operands are numeric, they compare numerically.
- Otherwise — comparing, say, a struct to a number — the comparison silently evaluates to
false.
Truthiness¶
{{#if}}, {{#unless}}, and the and/or operators all coerce their operand to a boolean using
the same rule. A value is falsy when it is:
null(an absent context, or a resolvednull)- a numeric primitive equal to
0.0(covers0,0L,0.0,-0;NaNis truthy) - a boolean primitive
false - a string primitive equal to
"" - an empty list
Everything else — including an empty struct {} — is truthy.
Where to next¶
- Blocks — where truthiness drives
{{#if}}/{{#unless}}arm selection. - Transforms — the
|pipeline that follows an expression. - Number Rendering — how numeric results (including
2vs.2.0) are rendered; equality/ordering above already treat them identically.