- Antril
- Referencia
- Expresiones
Esta página aún no está traducida al español. Leer la versión en inglés
Expressions
Antril's expressions - precedence and associativity, member access and null operators, calls, indexing, new, array literals, tuples, lambdas, typeof, sizeof, nameof, default, interpolated strings, casts and conversions, is, as, switch and with.
En esta página
Precedence
From the loosest to the tightest. Operators in the same row have the same precedence; the level number is the one of PrecedenceLevel, which new operator symbols use to place themselves (see Operators).
| Level | PrecedenceLevel |
Operators | Associativity |
|---|---|---|---|
| 10 | Assignment |
= += -= *= /= %= &= |= ^= <<= >>= |
right |
| 20 | Conditional |
c ? a : b |
right |
| 30 | Coalesce |
?? |
right |
| 40 | LogicalOr |
|| |
left |
| 50 | LogicalAnd |
&& |
left |
| 60 | BitwiseOr |
| |
left |
| 70 | BitwiseXor |
^ |
left |
| 80 | BitwiseAnd |
& |
left |
| 90 | Equality |
== != |
left |
| 100 | Relational |
< <= > >=, is, as |
left |
| 105 | x switch { … } |
left | |
| 110 | Range |
.. ..= |
left |
| 120 | Shift |
<< >> |
left |
| 130 | Additive |
+ - |
left |
| 140 | Multiplicative |
* / % |
left |
| 141 | Power |
(none built in; for new symbols) | |
| unary | prefix - + ! ~ ++ -- & &mut * await, casts (T)x |
right | |
| postfix | calls f(…), ., ?., […], ->, x++ x--, with { … } |
left |
Some consequences:
==binds tighter than&,|and^: write(flags & Mask) == Mask. Without the parentheses,flags & Mask == MaskcomparesMask == Maskfirst.- Comparisons do not chain:
1 < 2 < 3compares aboolwith3(AN2080). a ?? b + cisa ?? (b + c), anda ?? b ?? cgroups to the right.c ? x : d ? y : zisc ? x : (d ? y : z).- An assignment is an expression with the assigned value:
a = b = 4;sets both. -5is a negative literal, so-2147483648fits ani32.
import System;
Main()
{
i32? missing = null;
Console.WriteLine(missing ?? 2 + 3);
Console.WriteLine(1 + 2 * 3 << 1);
Console.WriteLine((6 & 3) == 2);
bool t = false;
Console.WriteLine(t ? 1 : t ? 2 : 3);
i32 x = 5;
x >>= 1;
x <<= 3;
x ^= 1;
x %= 7;
Console.WriteLine(x);
i32 y = x++ + ++x;
Console.WriteLine(y + " " + x);
Console.WriteLine(-x * 2);
Console.WriteLine(~0);
Console.WriteLine(3 is > 2 and < 5);
Console.WriteLine(7 switch { > 5 => 1, _ => 0 } + 10);
}
larva run
5
14
true
3
3
8 5
-10
-1
true
11
Operands
| Operand | Example | See |
|---|---|---|
| Literals | 42, 0xFF, 1.5f32, 'A', "text", true, null |
Lexical structure |
| Interpolated strings | $"n = {n}" |
below |
| Names | count, Console, System.Math |
below |
this, base |
this.x, base.Describe() |
Members |
| Parentheses | (a + b) |
|
| Tuples | (1, "one") |
below |
| Array literals | [1, 2, 3] |
below |
new |
new Point(1, 2), new i32[10] |
below |
| Lambdas | x => x * 2 |
below |
typeof, sizeof, nameof, default |
typeof(str), sizeof(i64) |
below |
checked, unchecked |
checked(a + b) |
below |
effect { … } |
Effect e = effect { … }; |
Reactive values |
$resource("name") |
$resource("data/table.bin") |
below |
declbits, allbits |
in a bitflag enum | Enums |
Names and member access
A name is looked up in the current function (locals and parameters), then the members of the type (and its base classes), then the namespace and its parents, then the names imported by name, and last the namespaces imported whole (see Source files). A name can always be written in full: System.Console.WriteLine.
a.b reads a member: a field, a property, a method, a nested type, or a member of a namespace. Static members are read through the type (Int32.MaxValue); reading a static member through a value is AN2038, and an instance member through a type is AN2036. A primitive keyword names its type: i32.MaxValue.
Null operators
| Operator | Value |
|---|---|
a?.b, a?.M() |
null when a is null; otherwise a.b (as a nullable). |
a ?? b |
a unless it is null, then b. |
A member of a nullable value cannot be read directly: n.Value with Node? n is AN2040 (check it first, with is, ?. or ??).
import System;
public class Node
{
public:
i32 Value;
Node? Next;
new(i32 Value);
Describe() : str => "node " + Value;
}
Main()
{
Node first = new Node(1);
first.Next = new Node(2);
Console.WriteLine(first.Next?.Value ?? -1);
Console.WriteLine(first.Next?.Next?.Value ?? -1);
Console.WriteLine(first.Next?.Describe() ?? "none");
i32? v = first.Next?.Next?.Value;
Console.WriteLine(v.HasValue);
i32? a = null, b = null;
Console.WriteLine(a ?? b ?? 9);
}
larva run
2
-1
node 2
false
9
?? needs a left side that may be null (AN2079), and comparing a value that is never null with null is AN2075.
Calls
f(a, b) calls a function, a method or a function value. The overload is chosen by the number of arguments, then by their types (see Functions). Type arguments are usually inferred; they can be written after the name, before the parentheses:
Make<Box>();
Pair<i32, str>(1, "x");
< after a name is read as type arguments only when a ( or . follows the closing >; otherwise it is a comparison.
Arguments are passed by position. A value argument is copied; an object argument passes the reference.
Warning
Not yet available: default values for parameters and named arguments (F(b: 2)). Write overloads instead.
Indexing
| Target | x[i] |
x[i] = v |
|---|---|---|
An array T[] |
the element; the index is any integer | stores the element |
A multi-dimensional array T[,] |
g[i, j]: one index per dimension (AN2043) |
stores |
A pointer T* (unsafe) |
*(p + i) |
stores through it |
A fixed buffer |
the element | stores |
| Any other object or value | calls x.At(i) |
calls x.SetAt(i, v) |
Any number of indexes can be given to At and SetAt: grid[x, y] calls grid.At(x, y). This is how List<T>, Dictionary<K, V> and str are indexed, and how your own types become indexable:
import System;
public class Board
{
i32[] cells = new i32[9];
public:
At(i32 x, i32 y) : i32 => cells[y * 3 + x];
SetAt(i32 x, i32 y, i32 value) { cells[y * 3 + x] = value; }
}
Main()
{
Board b = new Board();
b[1, 2] = 7;
b[1, 2] = b[1, 2] + 1;
Console.WriteLine(b[1, 2]);
Dictionary<str, i32> ages = new Dictionary<str, i32>();
ages["ana"] = 31;
Console.WriteLine(ages["ana"]);
}
larva run
8
31
Array indexes are checked: an index out of range stops the program with larva panic: index 3 out of bounds for length 1 and exit code 101. A type without At cannot be indexed (AN2042).
Warning
Not yet available: compound assignment on an indexed object, such as list[0] += 1 (AN2096). Write list[0] = list[0] + 1. On arrays it works.
new
| Form | What it makes |
|---|---|
new C(args) |
An object of the class C, by the constructor that takes args. |
new V(args), new V() |
A value: built in place, not on the heap. |
new T[n] |
An array of n elements, zeroed. |
new T?[n] |
An array of n nullable elements, all null. |
new T[a, b] |
A rectangular array (up to 8 dimensions). |
new T() |
With T : new() in a generic; see Generics. |
The arguments can be left out for a constructor without parameters: new Thing is new Thing(). A base class (AN2061), a static class (AN2060) and a type without a matching constructor (AN2062) cannot be made.
A new array starts zeroed, so its elements need a zero value: new str[3] is AN2022. Use new str?[3], an array literal, or a List<str>.
Warning
Not yet available: array fillers, such as new str[3]("").
Array literals
[a, b, c] makes an array. Its element type comes from where it goes (a variable, a parameter, a return), or else from its elements:
import System;
Main()
{
i64[] wide = [1, 2];
i32[] none = [];
i32[][] rows = [[1], [2, 3]];
i32[,] square = [[1, 2], [3, 4]];
Console.WriteLine(wide[1] + none.Length + rows[1].Length);
Console.WriteLine(square[1, 0] + " " + square.Rank + " " + square.GetLength(1));
loop (str word in ["alpha", "beta"])
Console.Write(word + " ");
Console.WriteLine();
}
larva run
4
3 2 2
alpha beta
- An empty literal
[]needs a target type (AN2024). - Nested literals make an array of arrays (
i32[][]), or a rectangular array when the target isT[,]; then every row has the same length.
Tuples
(a, b, …) with two or more items makes a System.ValueTuple, whose type is written (T1, T2, …). Its items are Item1, Item2… A null item needs a target type: (str?, i32) pair = (null, 1); (AN2143 otherwise). See Types.
Ranges
a..b (without b) and a..=b (with b) are not values: they go in a range loop (loop (i32 i = 0..10)) and in patterns (1..10 =>). Anywhere else they are AN2026.
Lambdas
A lambda is a function written in place: x => x * 2, (a, b) => a + b, (str s) => { … }, () => 42. Its parameter types come from the func type it goes to, or are written. See Functions.
func<i32, i32> : i32 add = (a, b) => a + b;
func<str> show = (str s) => { Console.WriteLine("> " + s); };
typeof, sizeof, nameof and default
| Expression | Value |
|---|---|
typeof(T) |
The System.Type that describes T: typeof(List<i32>).Name is "List". |
sizeof(T) |
The size of T in bytes, as an i32: sizeof(u256) is 32. A constant for types whose size is known. |
nameof(x) |
The last name written, as a constant str: nameof(count) is "count", nameof(Console.WriteLine) is "WriteLine". For a type, its name as typeof(T).Name gives it: nameof(i32[]) is "Int32[]". |
default(T) |
The zero value of T: 0, false, a zeroed value, null for a nullable type. |
nameof of a name that does not exist is AN2141. Inside a generic, nameof(T) is the name of the type argument, read at run time.
Caution
default(str) and default(C) for a class compile, but give a reference that is not valid: using it crashes the program. Use default(str?), which is null.
checked and unchecked
checked(expression) evaluates integer operations with overflow checks: a result that does not fit throws OverflowError. unchecked(expression) wraps around, which is also what happens by default. The statement forms are in Statements.
Interpolated strings
$"…" builds a string from text and holes {expression}. A hole can have an alignment after , and a format after ::
| Hole | Meaning |
|---|---|
{value} |
The value as text, as + would join it. |
{value,8} |
Padded with spaces on the left to 8 characters (right-aligned). |
{value,-8} |
Padded on the right (left-aligned). The width can be any integer expression. |
{value:F2} |
value.ToString("F2"): the format goes to the value's ToString(format). |
{value,8:F1} |
Both. |
{{ and }} write a brace. The format runs to the closing }.
import System;
Main()
{
f64 price = 3.14159;
i32 n = 42;
str name = "Ana";
Console.WriteLine($"{name} has {n} items at {price:F2}");
Console.WriteLine($"[{n,6}] [{n,-6}] [{price,8:F1}]");
Console.WriteLine($"{{braces}} {n * 2} {(n > 40 ? 1 : 0)} {n:X}");
Console.WriteLine($"{name.ToUpper()} {$"[{n}]"}");
}
larva run
Ana has 42 items at 3.14
[ 42] [42 ] [ 3.1]
{braces} 84 1 2A
ANA [42]
A ?: in a hole goes in parentheses, since : starts the format. An interpolated string is one line.
Warning
Not yet available: a string literal inside a hole, such as $"{ok ? "yes" : "no"}" (AN0007). Put the text in a variable first. An interpolated string inside a hole works.
Strings and +
+ with a string on either side joins text: the other side is turned into text with its ToString(). Numbers, bools, enums and objects are joined; evaluation goes left to right, so 1 + 2 + "x" is "3x" and "x" + 1 + 2 is "x12".
A nullable string (str?) and a type parameter without a constraint cannot be joined (AN2097): check the string for null first (name ?? "").
Casts and conversions
Conversions that cannot lose information happen by themselves:
| From | To |
|---|---|
| A signed integer | A wider signed integer. |
| An unsigned integer | A wider unsigned integer, or a wider signed one (u16 to i32). |
| An integer | Any float. |
| A float | A wider float (f32 to f64). |
T |
T?. |
| A class | Its base classes and its interfaces, and any. |
A value, a number |
An interface it implements, and any (boxed). |
&mut T |
&T. |
Every other conversion is a cast, (T)x, and without it the compiler reports AN2084 (a i64 is not a i32 (cast it: it may lose information)):
| Cast | What it does |
|---|---|
(i32)wide |
An integer to a narrower or differently signed one keeps the low bits: (i8)200 is -56, (u8)-1 is 255. |
(i32)3.9 |
A float to an integer drops the fraction: -3.9 gives -3. |
(f32)x |
Between floats, rounded. |
(i32)level, (Level)5 |
An enum to and from its base; see Enums. |
(Dog)animal |
A downcast: the object must be a Dog. |
(i32)boxed |
From any (or an interface) back to the value it holds. |
(T)x |
A conversion a type declares with op (T)(S s); see Operators. |
(u8*)p, (i64)p |
Between pointers, and between pointers and integers, in unsafe code (AN2083). |
A downcast gives a non-null object, so its target is T, not T? (AN2082). When the object is not of that class the program stops (larva panic: null reference, exit code 101); test first with is, or use as.
x as T converts an object to the class or interface T and gives a T?: null when x is not a T. It works between object types only (AN2020).
is
x is pattern tests a value against a pattern and gives a bool. A type pattern with a name declares a variable that is set where the test is true:
if (shape is Circle c && c.Radius > 1)
Console.WriteLine(c.Radius);
if (name is not null)
Console.WriteLine(name.Length);
if (point is { X: 0, Y: > 1 })
Console.WriteLine("on the axis");
Every pattern is described in Patterns.
switch expressions
value switch { pattern => result, … } gives the result of the first arm that matches. Arms are separated by commas (a last comma is allowed), and each can have a when condition:
import System;
Describe(i32 n) : str => n switch
{
< 0 => "negative",
0 => "zero",
1..10 => "small",
_ => "large",
};
Main()
{
Console.WriteLine(Describe(-3) + ", " + Describe(0) + ", " + Describe(5) + ", " + Describe(50));
}
larva run
negative, zero, small, large
The type of the expression is the type the arms have in common. A switch expression needs at least one arm (AN2085). The compiler does not check that the arms cover every value: when none matches, the program stops with larva panic: null reference (exit code 101). End with _ => ….
with
record with { Field = value, … } copies a record class or record value, replacing some fields:
import System;
public record class Person
{
public:
str Name;
i32 Age;
new(str Name, i32 Age);
}
Main()
{
Person ana = new Person("Ana", 31);
Person older = ana with { Age = 32 };
Console.WriteLine(ana);
Console.WriteLine(older);
}
larva run
Person { Name = Ana, Age = 31 }
Person { Name = Ana, Age = 32 }
with on a type that is not a record is AN2132, and a field the record does not have is AN2133.
Pointer operators
In unsafe code: &x takes an address, *p reads or writes through a pointer, p->field is (*p).field, p[i] is *(p + i), and p + n, p - q and comparisons do pointer arithmetic. Outside unsafe code they are AN2066, AN2067 and AN2121. With the borrow checker on, &x and &mut x in safe code borrow instead; see Memory.
Resources
$resource("name") is the bytes of a file built into the program, as a u8[]. The name is one listed in the RESOURCES chunk of the project's .queen; a name that is not there is AN2139.
chunk RESOURCES:
embed: data/**
u8[] table = $resource("data/table.bin");
System.Resources reads them by a name known only at run time.
Editar esta página en GitHub Reportar un problema