Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

4. Expressions

This section defines Korrin's expression forms and how they are grouped. Runtime behaviour of operators (arithmetic, comparison, truthiness) is in §3 and §10; this section is about syntax and structure.

4.1 Precedence and associativity

From loosest to tightest binding:

PrecedenceOperatorsFormAssociativity
1ora or bleft
2anda and bleft
3notnot aprefix
4== != < <= > >=a == bnon-associative
5+ -a + bleft
6* / %a * bleft
7--aprefix
8f(...) a.b a[i]postfixleft

"Non-associative" means a comparison may not be chained: a < b < c is a syntax error (E0101). Write (a < b) and (b < c).

print(2 + 3 * 4)
print(-(1 + 2) * 3)
# => 14
# => -9

There is no exponentiation operator; the full operator list is in §1.8.

4.2 Primary expressions

Literals

INT, FLOAT, STRING (§1.7), and the keyword literals true, false, nil.

String interpolation

A string literal with one or more { expression } holes (§1.7) is itself an expression. Evaluating it evaluates each hole left to right, renders each result with display semantics — the same form str() and print produce, so a string is spliced as-is and not re-quoted — and concatenates the literal text and the rendered holes into one str.

name = "Ada"
n = 3
print("{name} has {n} item{"s" }")
print("{n} + {n} = {n + n}")
# => Ada has 3 items
# => 3 + 3 = 6

A hole is a full expression, including calls, indexing, and further strings:

row = {"user": "grace", "score": 90}
print("{row["user"]}: {row["score"]}%")
# => grace: 90%

Names

A bare NAME refers to the nearest binding in scope (ADR 0011). An unbound name is E0301 when evaluated.

self

Inside a method, self is just the first parameter — an ordinary name (ADR 0005). Outside a method it is an unbound name like any other.

Parentheses

( expression ) groups without producing a distinct value or node; it only overrides precedence.

List literals

[ e1, e2, ... ], with an optional trailing comma. [] is the empty list. Elements are evaluated left to right.

Map literals

{ k1: v1, k2: v2, ... }, with an optional trailing comma. {} is the empty map (there is no set type). Keys and values are arbitrary expressions, evaluated in source order, key before value.

pair = {"x": 1 + 1, "y": [3, 4]}
print(pair["x"])
# => 2

4.3 Operator expressions

Unary

-a negates a number. not a produces true if a is falsy and false otherwise (ADR 0009).

Binary arithmetic and comparison

+ - * / % and == != < <= > >=. Both operands are always evaluated. Types and results are defined in §3; note / always yields a float (ADR 0010).

and / or

These short-circuit and return one of their operands, not a coerced boolean:

  • a and b evaluates a; if a is falsy it is the result, otherwise b is evaluated and is the result.
  • a or b evaluates a; if a is truthy it is the result, otherwise b.
name = nil
print(name or "anonymous")
# => anonymous

4.4 Postfix expressions

Call — callee(args)

callee is evaluated, then each argument left to right, then the call happens. callee must be a function, a builtin, or a class (§6, §7); otherwise E0302. Argument count must match the callable's arity (E0303). A trailing comma in the argument list is allowed.

Attribute — object.name

Reads name from object:

  • on an instance, a field or a method (§7);
  • on a str, list, or map, a built-in method (§8).

The result of reading a method is a callable bound to object; it is almost always called immediately (items.push(x)). An absent attribute is E0309; reading a method name without calling it is allowed and yields a function.

Index — object[index]

Reads from a list (integer index, E0307 if out of range), a map (E0310 if the key is absent), or a string. Indexing anything else is E0308.

super.method

Only valid inside a method whose class has a parent (ADR 0005). super.method resolves method starting from the parent class, bound to the current self. It is essentially always immediately called: super.method(args).