Kagari は Rust の構文を持つ式指向の言語です。ブロックは式であり、if と match は値を生み、文は ; で終わります。この章では、パーサーが受け付けるすべての式と、コンパイラーが加える規則を挙げます。制御フローの式(if、match、ループ、break、continue、return)は制御フローとループに、.kix のフォーム(s!(…)、t![…]、column![…] など)はフォームにあります。
ブロックと文
const fn label_width(chars: Int<0, 40>) -> u32 { let n: u32 = chars.into(); let padding = 8; n * 16 + padding}ブロック { … } は文を順に実行します。最後の式の後に ; がなければ、その式の値がブロックの値です。そうでなければブロックの値は () です。ブロックはスコープを開き、その中の let 文の束縛は閉じ括弧 } で終わります。
ブロックに書けるもの:
| 文 | 形 | 備考 |
|---|---|---|
let 文 | let pat: Type = expr; | 後述 |
| 式文 | expr; | 値は捨てられます。#[must_use] な値を捨てると W3023 です |
| ブロック様の式 | if …、match …、loop、while、for、{ … }、name!{…} 形式のフォーム | ; は不要です |
| アイテム | fn、struct、enum、const、use など | ブロックの中で宣言するアイテムです。ファイルモジュール(mod name;)は E1005 です |
| 空文 | ; | 無視されます |
ブロック様の式は文を終えます。 文の先頭にあるブロック、if、match、loop、while、for、および choice!{…} のような波括弧のフォームは、; なしで完結した文になります。その後に続くものは式の続きになりません。したがって if c { a } else { b } - 1 は 2 つの文であり、{ … } (x) はブロックの後に括弧付きの式が続いたもので、呼び出しではありません。値を使うには (if c { a } else { b }) - 1 と書きます。
2 つの式文の間に ; がないのは構文エラー(E0001)です。
let と let mut
let n = 3;let mut total: u32 = 0;let (x, y) = (1, 2);let Point { x: px, y: py } = origin();let mut later: bool;let はパターンの変数を値に束縛します。
- パターンは反駁不能でなければなりません。つまり型のすべての値に一致する必要があります(
E2038)。パターンを参照してください。 - 型注釈
: Typeは省略できます。省略すると型は推論されます。 - 初期化子
= exprは省略できます。初期化子なしで宣言した束縛は、読む前に代入しなければなりません。代入前に読むと、実行時パニックでゲームが停止します。 - パターンが
mutを付けない限り、束縛は不変です。不変の束縛への代入はE3017です。初期化子なしで宣言した束縛への最初の代入も含みます。そのような束縛はlet mutで宣言してください。 pushdown fnの中では、ローカル変数は有限型の値を持ち(E3008)、クロージャや参照は持てません(E3015、E3014)。
リテラル
| リテラル | 例 | 型 |
|---|---|---|
| 整数 | 42、1_000、0xFF、0o17、0b1010、5u8、300i64 | 文脈から推論される整数型。接尾辞があれば型が決まります。数値を参照 |
| 小数 | 0.5、1.0、12.75 | Decimal |
| 文字列 | "Mio"、"line one\nline two" | Text |
| 真偽値 | true、false | bool |
| ユニット | () | () |
文字、バイト、生文字列、浮動小数点数のリテラルはなく、指数表記もありません。-5 は 5 に単項 - を適用したもので、1 つの負のリテラルとして型付けされます。トークンの正確な構文(エスケープ、接尾辞、桁区切り)は字句構造にあります。
パス
ROUTERoute::Azusastd::time::ms(500)Self::new()super::helpers::clamp(x)asset::<ImageAsset>("bg/room.png")rng_choose::<Coin>()パスは、ローカル変数、引数、アイテム(関数、定数、static、ユニット構造体、enum のバリアント)、関連アイテムを指します。セグメントは :: で区切ります。self、super、crate、Self は Rust と同じ働きをします(ソースファイルとモジュールを参照)。
式の中では、ジェネリクス引数はターボフィッシュ ::<…> で書きます。ジェネリクス引数は、型、整数リテラル(- 付きも可)、または型レベル定数を入れたブロック { … } です。
式の中のパスが値以外のもの(モジュールや名前付きフィールドを持つ構造体など)を指すと E2009 です。未知の名前は E1016 です。
演算子
優先順位と結合性
パーサーに基づく、結合の強い順の表です。
| 演算子 | 結合性 |
|---|---|
パス、リテラル、( … )、ブロック、フォーム、if、match、ループ | — |
フィールド a.b、タプルフィールド a.0、メソッド呼び出し a.m(…)、呼び出し f(…)、添字 a[i]、? | 左から右 |
単項 -、!、*(参照外し)、&、&mut | 前置 |
as(常にエラー、E2019) | 左 |
* / % | 左 |
+ - | 左 |
<< >> | 左 |
& | 左 |
^ | 左 |
| | 左 |
== != < > <= >= | なし。a < b < c は構文エラー |
&& | 左 |
|| | 左 |
.. ..= | 左。範囲は連鎖させるものではありません |
= += -= *= /= %= ^= &= |= <<= >>= | 右 |
return、break、クロージャ |…| body | できるだけ右まで伸びます |
この表は Rust と同じですが、範囲は非結合ではなく左結合です。比較演算子は連鎖できないので、a < b && b < c と書きます。
オペランドの型
| 演算子 | オペランド | 結果 |
|---|---|---|
+ - * / % | 同じ Rust 整数型の 2 値、Int の 2 値、Decimal 2 つ、Rat 2 つ | 数値を参照。Int の算術は結果の範囲を計算します |
& | ^ | 同じ Rust 整数型の 2 値、または bool 2 つ | オペランドの型 |
<< >> | Rust 整数 2 つ(型は異なってもよい) | 左オペランドの型 |
== != | Eq である 1 つの型の 2 値(そうでなければ E2017) | bool |
< > <= >= | 1 つの型の整数 2 つ、Int の 2 値、Decimal 2 つ、Rat 2 つ | bool |
&& || | bool 2 つ。右辺は必要なときだけ実行されます | bool |
単項 - | 符号付き Rust 整数、Int(範囲が反転します)、Decimal、Rat | オペランドの型 |
単項 ! | bool(論理否定)または Rust 整数(ビット反転) | オペランドの型 |
単項 * | 参照 &T または &mut T(そうでなければ E2033) | T |
&e、&mut e | 任意の式。&mut には可変な場所が必要です(E3017) | &T、&mut T |
整数型は暗黙には混ざりません。Int と Rust 整数、または異なる 2 つの Rust 整数型に演算子を適用するとエラーです(E2014、E2001)。片方を .into() か .saturate() で変換してください。ビット演算子には Rust 整数が必要で、Int に使うと E2015 です。除数の Int の範囲が 0 を含む除算や剰余は E2016 です。順序は数値にだけ定義されており、タプル、Text、enum に < を使うと E2015 です。Eq 型の等価性は構造的に比較します。言語トレイトを参照してください。
as
as は Rust と同じ優先順位で構文解析されますが、キャストはすべてエラー(E2019)です。整数の変換には x.into()(損失なし、コンパイル時に検査)か x.saturate()(範囲内に丸める)を使います。数値を参照してください。
代入と複合代入
total = 0;AFFECTION_MIO += 1;point.x = 10;scores[i] -= 1;place = value は値を格納します。place op= value は、演算子 + - * / % ^ & | << >> について place = place op value と同じです。代入は型 () の式です。
左辺は可変な場所でなければなりません(E3017)。
mutで宣言したローカル変数または引数pushdown fnの中の物語変数(static mut)- 可変な場所のフィールド
a.bまたは要素a[i] rが&mut Tであるときの*r、および&mut Tを通してたどったフィールドや要素
定数、不変の static、呼び出し結果のような一時値、&T を通してたどったものには代入できません。
Int の場所への複合代入は、その型の境界で飽和します。右辺には任意の Int 値を書けます。値 5 を持つ Int<0, 5> に AFFECTION_MIO += 1 を実行しても 5 のままです。数値を参照してください。
呼び出しとメソッド呼び出し
fade(ms(500))Confession()screen.entity(AZUSA).image()x.into()scores.get(0)f(a, b) は関数、関数ポインター、クロージャを呼び出します。f には任意の式を書けます。引数の数は一致しなければならず(E2024)、呼び出せない値を呼び出すと E2025 です。どの関数種別がどれを呼べるかは関数種別にあります。
receiver.method(args) は、レシーバーの型の impl で宣言されたメソッド、または標準トレイトが提供するメソッドを呼び出します。レシーバーは、メソッドの self、&self、&mut self に合わせて自動的に借用または参照外しされます。&mut self のメソッドには可変な場所が必要です(E3017)。Type::function(args) は関連関数を呼び出します。メソッドのジェネリクス引数はターボフィッシュで書きます: x.m::<T>(…)。未知のメソッドは E2022、self を持たない関連関数をメソッド構文で呼ぶと E2023 です。
フィールドと添字
point.xpair.0nested.0.1grid[row][col]a.nameは名前付きフィールドを読みます。a.0、a.1などはタプルやタプル構造体のフィールドを読みます。t.0.1は 2 回のフィールドアクセスです。参照は自動的に参照外しされます。未知のフィールドはE2020です。a[i]は配列またはVec<T>の要素を読みます(他の型ではE2032)。添字は整数で、整数リテラルの添字はusizeです。末尾を越える添字は実行時パニックになり、定数評価中ならE4005です。
構造体、タプル、配列の式
Point { x: 1, y: 2 }Point { x, y }Shape::Rect { w: 10, h: 20 }EntityId(216)Route::None(1, true)(5,)[1, 2, 3][false; 8]| 式 | 備考 |
|---|---|
Path { field: value, … } | 構造体、または構造体形式の enum バリアント。すべてのフィールドを与える必要があります(E2021)。未知のフィールドは E2020 です。Path { x } は Path { x: x } の省略形です。構造体更新構文 ..base はありません |
Path(a, b) | タプル構造体またはタプルバリアント。そのコンストラクターは関数です |
Path | ユニット構造体またはユニットバリアント |
(a, b)、(a,)、() | タプル。要素が 1 つのタプルには末尾のカンマが必要で、(a) は単に a です |
[a, b, c] | 列挙した要素の配列。要素の型は 1 つです |
[value; N] | N 個のコピーからなる配列。N は型レベル定数です |
構造体式は、if と while の条件、match の検査対象、for の反復対象には直接書けません。{ が本体の開始と解釈されるためです。括弧で囲んでください: if p == (Point { x: 0, y: 0 }) { … }。
範囲
0..4 // 0, 1, 2, 33..=5 // 3, 4, 5a..b(終端を含まない)と a..=b(終端を含む)は Range<T> を作ります。Range<T> は start、end、inclusive のフィールドを持つ std の構造体です。T は両端が変換できる型で、0..7 は Range<Int<0, 7>> です。範囲式には両端が必要で、a..、..b、..=b、.. は E2030 です。パターンとしては a.. と ..=b がありますが、..b はなく、.. 単独は残余パターンです。範囲パターンを参照してください。範囲は for ループで使います。制御フローとループを参照してください。
クロージャ
|x| x + 1|| nav::back()|a: u32, b: u32| a * b|mut screen| { screen.hud().text_window().change(Visibility::Hidden).apply_animation(instant()); screen }|v| -> u32 { v * 2 }|params| body はクロージャです。引数は反駁不能なパターンで、型は省略できます。|| は引数なしです。本体は任意の式ですが、戻り値の型 -> T を書く場合は本体をブロックにします。move はありません。クロージャは使う変数を常に値でキャプチャします。
クロージャにできることとその型(Fn(A) -> B、pushdown Fn(A) -> B、const Fn(A) -> B)は値、参照、クロージャにあります。要点は次のとおりです。
- 何もキャプチャしないクロージャは、その種別の関数ポインターに変換されます。レンダーをその場に書けるのはこのためです。関数ポインターが必要な場所にキャプチャするクロージャを書くと
E2036です。 pushdown fnの中では、クロージャは第二級です。引数として渡して呼び出せますが、ローカル変数への格納、返却、キャプチャはできません(E3015)。
? 演算子
fn first_score(scores: Vec<u32>) -> Option<u32> { let first = scores.get(0)?; Some(first * 10)}Option<T> に対する expr? は Some の中の T を与えるか、囲む関数から None を返します。Result<T, E> に対しては Ok の中の T を与えるか、その Err を返します。囲む関数は Option、または同じエラー型の Result を返さなければなりません。そうでない場合と、それ以外の型に使った場合は E2034 です。? は定数式にはなりません。