Expressions are the core computational units of Scaly. Every expression evaluates to a value. Unlike many languages, Scaly uses postfix operators and does not define operator precedence — evaluation order is always explicit.
Expressions are the core computational units of Scaly. Every expression evaluates to a value.
Integer literals are sequences of decimal digits, optionally preceded by a minus sign for negative numbers.
Hexadecimal literals start with 0x or 0X followed by hexadecimal digits (0-9, a-f, A-F).
Floating-point literals contain a decimal point separating the integer and fractional parts.
String literals are enclosed in double quotes. They may span multiple lines, with line breaks preserved in the resulting string.
Character literals represent a single character, enclosed in backticks.
Operations allow combining expressions.
An operation is a sequence of operands that evaluates to a single value. Each operand is an expression optionally followed by member access. An operation ends with a colon, a line break, or anything which is not an expression. Evaluation uses a context which is empty at the start of an operation. When the context is empty, operator-shaped identifiers act as prefix functions, taking the next operand as their argument.
When the context holds a value, operators combine with operands according to standard mathematical precedence. Multiplicative operators (*, /, %, div, mod) bind tighter than additive operators (+, -). Comparison and equality operators have lower precedence than arithmetic. Operators of equal precedence associate left-to-right.
Parentheses create a empty context. This allows prefix forms inside an otherwise operator chain, and explicit grouping when needed.
Identifiers can have name shape (alphanumeric) or operator shape (symbols). The shape is purely lexical and does not determine behavior. Whether an identifier acts as a prefix function or chain operator depends on its declaration and the register state.
Let bindings introduce local variables. The syntax is let followed by a name and a value expression (no equals sign). Multiple statements are separated by colons or newlines.
Comparison operators compare two values and return a boolean result.
The equality operator (=) tests if two values are equal. The inequality operator (not equal to) tests if two values are different.
Relational operators compare the ordering of values.
Boolean operators combine boolean values.
The ampersand operator performs logical AND, the pipe operator performs logical OR.
Bitwise operators manipulate integer values at the bit level.
The left shift operator shifts bits to the left, effectively multiplying by powers of two. The right shift operator shifts bits to the right, effectively dividing by powers of two.
If expressions evaluate one of two branches based on a boolean condition. The syntax is if followed by the condition, a colon, the consequent, and optionally else with an alternative.
if true: 1 else 0 ; evaluates to 1 if false: 1 else 0 ; evaluates to 0 if 5 > 3: 10 else 20 ; evaluates to 10
If expressions can be nested:
if false: 1 else if true: 2 else 3 ; evaluates to 2
Match expressions compare a value against multiple cases. The syntax is match followed by the scrutinee, a colon, one or more case branches, and optionally else.
match 2: case 1: 10 case 2: 20 else 0 ; evaluates to 20 match 5: case 1: 10 else 0 ; evaluates to 0
Multiple cases can share the same branch:
match 2: case 1 case 2: "one or two" else "other"
Choose expressions pattern match on union types, allowing different code paths based on which variant is present.
Choose can match directly on constructed variants.
When a variant carries a value, the value can be bound to a name.
Choose can have multiple when clauses to match different variants.
Optional types use null pointer optimization (NPO) for efficient representation.
The is null expression checks if an optional value is None.
T? is syntactic sugar for Option[T].
Generic unions like Option[T] work with choose for pattern matching.
Union variants can be constructed explicitly using Type.Variant syntax.
Variants are constructed with Type.Variant(value) syntax.
The is expression tests if a union value matches a variant.