# Expressions

On the previous pages we talked a lot about printable expressions and regular expressions. So let's clarify what exactly an expression is.

An expression is a Go expression that yields a value. Valid Expressions are:

1. A variable or constant
2. A Go expression/literal like `"abc"`, `123`, or `"abc" == "def"`.
3. A function call that returns a single value

Additionally, corgi adds a ternary and a "chain" expression. More on that below. And, of course, string interpolation, which you already learned about in the interpolation chapter.

{% hint style="warning" %}
**Implementation Note:** While you can use `` `backtick strings ``\` you cannot span them over multiple lines like you can in Go. Depending on your use case there are better alternatives.
{% endhint %}

## Printable Types

If your expression gets printed, e.g. the value of an attribute, you are restricted to these printable types:

1. `uint`, `uint8`, `uint16`, `uint32`, `uint64`
2. `int`, `int8`, `int16`, `int32`, `int64`
3. `string`
4. `bool`
5. `corgi.HTML`, `corgi.HTMLAttr`, `corgi.CSS`, `corgi.URL`, `corgi.Srcset`, `corgi.JS`, `corgi.JSStr,` and `corgi.JSAttr` in their appropriate contexts (more on them in the Security and Escaping chapter)
6. Any type implementing `fmt.Stringer`
7. A pointer to the above
8. A (possibly typed) `nil` which will print nothing

If you try to print something else, the generated function will return with an error.

## Ternary Expressions

Aside from the expressions listed above, corgi also supports ternary expressions in the form of a special ternary function.

```go
func ?[T any](cond bool, ifTrue, ifFalse T) T
```

`ifTrue` and `ifFalse` can be any Go expression.

`cond` can be any Go expression that yields a `bool` value.

```pug
func Ternary(authenticated, admin bool)

p(class = ?(authenticated, "authed", "anon"))
p(class = ?(authenticated, ?(admin, "admin", "authed"), "anon"))
p(class = ?(authenticated && admin, "admin", "not-admin"))
p= ?(admin, "Hello boss!", "Hello someone.")
p Hello, #{?(admin, "boss", "someone")}.
p #{"Hello " + ?(admin, "boss", "someone")}.
```

Ternary expressions can be used anywhere in a regular Go expression.

## Chain Expressions

You know the problem: You want to access `data.MyField.MySlice[3]`, but `MyField` could be `nil`, and `MySlice` might not even be of length `3`. The solution is to add `if`s that check nilness, length, and map indexes.

However, checks like these create an enormous bloat in template files. This is why corgi adds a second utility that allows you to perform those kind of checks. You can use a `?` to perform zero value checks, length/index checks, and checked type assertions for any expression that consists of only accessing fields/maps/slices.

{% hint style="info" %}
Chain Expressions cannot be embedded inside Go expressions.
{% endhint %}

```pug
// This checks if foo is not zero:
p #{foo?[0]}
// This checks if foo has an index or map entry 0:
p #{foo[0?]}
// This checks if that entry is not zero:
p #{foo[0]?}
// This performs a checked types assertion on that entry:
p #{foo[0].(string)?}
// And this performs all four checks:
p #{foo?[0?]?.(string)?}
```

You can also add a default if your checks fail by appending a `~`. Defaults can be any Go expression.

```pug
p #{foo? ~ "some default value"}
```

Depending on the context you use chain expressions, they behave differently:

1. When assigning a value to an attribute or setting the value of a mixin argument, that attribute or mixin arg will only be set if the value passes all checks. For required arguments you need to provide a default.
2. When used in interpolation and a check fails, nothing will be printed.
3. When used as condition for an `if`, the `if` will only be executed if all checks pass.
4. When used as a range expression in a `for` loop, that loop will only be executed if the value passes all checks.

{% hint style="info" %}
**Technical Note:** Corgi guarantees that it will call each function in a chain expression only once.
{% endhint %}

If the expression yields a pointer that you want to dereference, you can place an `*` at the start of the expression to dereference the resolved value. Defaults are assumed to be already dereferenced.

```pug
mixin foo(bar string) #{bar}

- s := "foo"
  s1 := &s
foo(bar=*s1? ~ "baz")
```

Here are a couple more examples, just to make sure you undestand everything:

{% tabs %}
{% tab title="Corgi" %}
```pug
mixin greet(name="world")
    Hello #{name}!

- var t struct{F *string}

a(href=*t.F?) Click me!
a(href=*t.F? ~ "bar") Click me!

p #{t.F?}

p #+greet(name=*t.F?)
p #+greet(name=*t.F? ~ "universe")

p The value is: #{t.F?}
p The value is #{t.F? ~ "unknown"}

if t.F?
  p The value is: #{t.F}
else
  p The value is not accessible.
```
{% endtab %}

{% tab title="HTML - F is set" %}
```markup



<!-- t := T{F: "foo"} -->

<a href="foo">Click me!</a>
<a href="foo">Click me!</a>

<p>foo</p>

<p>Hello world!</p>
<p>Hello universe!</p>

<p>The value is: foo</p>
<p>The value is: foo</p>


<p>The value is: foo</p>


```
{% endtab %}

{% tab title="HTML - F is not set" %}
```markup



<!-- t := T{F: nil} -->

<a>Click me!</a>
<a href="bar">Click me!</a>

<!-- not rendered -->

<p>Hello foo!</p>
<p>Hello foo!</p>

<p>The value is: </p>
<p>The value is: unknown</p>




<p>The value is not accessible.</p>
```
{% endtab %}
{% endtabs %}
