# Expressions

> Operators, property access, calls, filters and tests.

Expressions appear inside `{{ }}` and in tags. They look and behave much like JavaScript.

## Literals

```kiln
null  true  false
42  3.14  1_000_000
"double"  'single'  "with #{ interpolation }"
[1, 2, 3]
{ title: "Home", "data-id": 1, (key): value }
```

- Hash keys can be names, strings, numbers or any expression in brackets.
- `{ title }` is short for `{ title: title }`.
- `...` spreads an array or hash into a new one: `[...posts, extra]` or `{ ...defaults, size: "lg" }`.
- Trailing commas are allowed.

## Operators

| Operators | Meaning |
| --- | --- |
| `and` `or` `not` | Logic. Always return `true` or `false` |
| `==` `!=` | Strict equality, like JavaScript's `===`. Null and undefined are equal |
| `<` `>` `<=` `>=` | Compare two numbers, two strings or two dates |
| `+` `-` `*` `/` `%` `**` | Arithmetic. Numbers only |
| `//` | Divide and round down |
| `~` | Join two values as strings |
| `in` `not in` | Whether an array or set contains a value, a string contains a substring, or a hash has a key |
| `starts with` `ends with` | Compare the start or end of two strings |
| `a..b` | An array of whole numbers from `a` to `b`, including both |
| `a ? b : c` | If `a` is true, `b`, otherwise `c`. Without `: c`, null |
| `a ?: b` | `a` if it is true, otherwise `b` |
| `a ?? b` | `a` unless it is null or missing, otherwise `b` |

page.kiln:

```kiln
{{ "Total: " ~ price * quantity }}
{{ user.role in ["admin", "editor"] ? "Can edit" : "Read only" }}
{{ path starts with "/blog/" }}
{{ user.nickname ?? user.name }}
{{ (1..5)|join(", ") }}
```

Output:

```html
Total: 36
Can edit
true
Ada
1, 2, 3, 4, 5
```

`+` never joins strings, and adding a number to a string is an error. Use `~` to join text. It runs after arithmetic, so `"Total: " ~ price * quantity` works without brackets.

## Reading properties

Use `.` or `[]` to read a property. They read the same things, so use whichever fits.

```kiln
{{ page.title }}
{{ page["title"] }}
{{ page[fieldName] }}
{{ items[0] }}
{{ page?.author?.name }}
```

`?.` returns null when the left side is null or missing, and stops there. Method calls work the same way, so `post?.summary()` is null when there is no `post`. Nothing after it runs, so keys in brackets and method arguments later in the chain are not evaluated. In [strict mode](https://kilnlang.com/docs/language/strict-mode/), reading a missing property is an error unless you guard it with `?.`, `??`, `default` or `is defined`.

## Calling functions

Call a function by name with brackets. Arguments can be positional, named, or both.

```kiln
{{ range(1, 10, 2)|join(", ") }}
{% set page = document("page", slug: route.path) %}
```

Named arguments are gathered into one hash at the end. `document("page", slug: route.path)` passes exactly the same arguments as `document("page", { slug: route.path })`. Built-in filters and functions also accept their parameter names, such as `round(precision: 2)`.

`...` spreads an array into positional arguments: `max(...prices)`.

## Filters

A filter changes a value. Write it after a `|`. Filters run left to right, and brackets are optional when there are no arguments.

page.kiln:

```kiln
{{ title|trim|title }}
{{ tags|join(", ", " and ") }}
```

Output:

```html
Kiln Templates
fast, safe and async
```

Filters bind tighter than a leading minus sign, so `-1|abs` is `-(1|abs)`. Write `(-1)|abs` instead. See the [filter reference](https://kilnlang.com/docs/filters/strings/) for every built-in filter.

## Tests

A test checks a value and returns `true` or `false`. Write it after `is` or `is not`.

```kiln
{% if user is defined %}
{% if loop.index is divisible by(3) %}
{% if items is not empty %}
```

See [Tests](https://kilnlang.com/docs/tests/) for every built-in test.

## Arrow functions

Filters such as `map`, `filter`, `sort` and `reduce` take a short function. Write its parameters, then `=>`, then one expression.

page.kiln:

```kiln
{{ posts|filter(p => p.published)|map(p => p.title)|join(", ") }}
{{ posts|reduce((total, p) => total + p.price, 0) }}
```

Output:

```html
Launch, Update
30
```

Arrow functions can only be passed to filters and functions. You can't store them with `set` or print them.
