文法の早見表
目次 · 初めて書くなら:入門ガイド · 関数を調べる:組み込み関数
このページはHigh(.nagi)の書式を引くための資料です。短い例は関数内に書く断片も含みます。各例をまとめて動かすには入門の完成コードを使ってください。
このページの目次
ファイルと字下げ
- UTF-8で保存し、拡張子を
.nagiにする。 - トップレベルには
import、class、def、async def、Rustの外部関数宣言を書く。実行する文は関数内に書く。 - ブロックの前に
:を付け、空白で字下げする。空白4つを推奨する。タブは禁止。 - コメントは
#。識別子は英字・数字・_で、数字からは始めない。日本語の文字列・コメントは使える。 ()や[]の中は複数行に分けられる。引数や要素の末尾の余分な,は未対応。
def main():
# コメント
print("Hello, Nagi!")
値と変数
| 書き方 | 意味 |
|---|---|
count = 10 |
型を推論する。整数の標準はi64 |
count: i32 = 10 |
型を指定する |
rate = 1.5 |
小数の標準はf64 |
enabled = True / False |
真偽値。true / falseも受け付ける |
name = "Nagi" / 'Nagi' |
UTF-8文字列 |
values = [1, 2, 3] |
同じ型の要素の配列 |
values: List[i64] = [] |
空配列には型注釈を付ける |
missing: i64? = None |
値がないnullable。nullも受け付ける |
present: i64? = some(42) |
値があるnullable |
count = 11 |
同じ型で再代入 |
count += 1 / -= 1 / *= 2 |
複合代入。/= / %=は未対応 |
文字列のエスケープは\n、\r、\t、\"、\'、\\です。f-string、文字列の補間、三重引用符はありません。型の一覧は型を参照してください。
関数とreturn
呼び出し先は、同名のローカル変数に入れた関数、自作関数、組み込み関数の順に決まります。自作のlenを定義すると、len(...)はその関数を呼びます。関数以外の変数は呼び出せません。
typeなどRust側の予約語も、Nagiの文法で使える位置なら名前にできます。生成Rustで必要な名前の変換はコンパイラが行い、Low・JSON・SQLiteの項目名には元の名前を残します。
def add(a: i64, b: i64) -> i64:
return a + b
def show(value: i64):
print(value)
return
引数の型は必須です。戻り値を省略するとunit。値を返す関数は全経路で戻り値が必要です。呼び出しはadd(1, 2)のように位置引数を使います。デフォルト引数、可変長引数、ユーザー定義generic関数はありません。
分岐とループ
次は関数内の断片です。
score = 80
if score >= 80:
print("合格")
else:
print("再挑戦")
for index in range(3):
print(index)
count = 0
while count < 3:
count += 1
条件はboolです。range(n)は0以上n未満で、引数は1つです。配列やviewのfor走査はprimitiveとCopy classの要素に対応します。elif、break、continue、passはありません。
演算子
優先順位が高いものから並べています。同じ段の二項演算は左から評価する形で解析します。曖昧な式には()を付けてください。
| 優先順位 | 演算子 | 例 |
|---|---|---|
| 高 | 呼び出し、フィールド、index | add(1, 2)、point.x、values[0] |
| ↓ | -(単項)、not、try、await |
-count、not enabled、try await db_open(...) |
| ↓ | *、/、% |
count * 2 |
| ↓ | +、- |
count + 1 |
| ↓ | <、>、<=、>= |
count < 10 |
| ↓ | ==、!= |
count == 10 |
| ↓ | and |
count > 0 and count < 10 |
| 低 | or |
enabled or count == 0 |
符号反転の-valueは、符号付き整数(i8 / i16 / i32 / i64)と浮動小数点数(f32 / f64)に使えます。関数やclassの値には使えません。
| 比較する値 | == / != |
< / > / <= / >= |
|---|---|---|
| 同じ型の数値・bool・str | 対応 | 対応 |
| UUID・timestamp | 対応 | 未対応 |
| view[str]・view[bytes] | 対応 | 対応 |
| view[T] | 要素Tが一致比較に対応する場合 | 要素Tが大小比較に対応する場合 |
| class・所有するList | 未対応 | 未対応 |
classの配列を借りたview[Point]も、そのまま比較できません。必要なフィールドを取り出して比較してください。配列のviewは、要素ごとの比較になります。
異なる数値型は暗黙に変換しません。i32からi64にはi64(value)を使います。i32(i64値)はResult[i32, Error]を返すため、Result関数内でtry i32(value)などと書きます。
比較の連鎖0 < count < 10は使わず、count > 0 and count < 10と書きます。整数の/は整数除算です。**、//、ビット演算は未対応です。
class、配列、view
class Point:
x: f64
y: f64
def main():
point = Point(x=1.0, y=2.0)
print(point.x)
values = [10, 20]
append(values, 30)
print(values[0])
borrowed = view(values)
duplicate = copy(borrowed)
print(len(duplicate))
classは全フィールドを名前付きで指定します。フィールド・indexへの代入、method、継承は未対応です。indexは0からで、負数や範囲外は実行時panicになります。文字列のindexは使えません。配列や文字列の区間を借りる場合はtry slice(view(data), start, end)です。
自作関数への所有文字列・配列の引き渡しはmoveです。読むだけなら引数をview[str]やview[i64]にし、view(value)で渡します。詳細は入門ガイドと所有権を参照してください。
Result、async、scope
| 書き方 | 意味 |
|---|---|
-> Result[i64, Error] |
成功なら整数、失敗ならErrorを返す |
return ok(42) |
成功を返す |
return error("理由") |
失敗を返す |
return not_found("理由") |
対象なしを返す。HTTPでは404 |
return fail(problem) |
元のErrorをそのまま返す |
value = try parse_i64("42") |
値を取り出す。失敗なら呼び出し元へ返す |
async def work(): |
非同期関数を定義する |
await sleep(10) |
非同期処理を待つ。単位はミリ秒 |
db = try await db_open(":memory:") |
非同期処理を待ち、Resultの失敗も伝える |
tryはResultを返す関数内、awaitはasync関数内で使います。その場でResultの成功・失敗を処理する場合は、次のように両方のcaseを書きます。これは関数内の断片です。
match parse_i64("42"):
case Ok(number):
print(number)
case Err(problem):
print(error_kind(problem))
使わないpayloadは_にします。matchはResultを消費し、payloadの名前はcase内だけで使えます。外側の変数と同じ名前は使えません。完全な実行例と制限はエラー処理にあります。
子taskは次の完全なコードのようにscope内でspawnします。
async def main() -> Result[unit, Error]:
async with scope:
spawn sleep(10)
spawn sleep(15)
return ok(print("完了"))
scopeを出るときに子taskを待ちます。scope内のreturn、viewを別taskへ渡すこと、値を返す子taskのspawnは現在未対応です。詳細はasyncを参照してください。
import、HTTP、Rust
| 用途 | 書き方 | 詳細 |
|---|---|---|
| ファイルを読み込む | import "models.nagi" |
import。同じ名前空間に読み込む |
| GET handlerを定義する | 関数の前に@get("/users/{id}") |
HTTP。@post、@put、@deleteもある |
| HTMLを返す | return ok(html("<h1>Hello</h1>")) |
戻り値はResult[Html, Error] |
| テキストを埋め込む | include_text("index.html") |
ソースの場所を基準に、コンパイル時に埋め込む |
| Rust関数を宣言する | @rust("native::crc32")の次行にextern def crc32(text: view[str]) -> i64 |
Rust連携。本体・末尾の:は不要 |
Pythonに似ていても違うところ
| Pythonでよく書く形 | Nagiでは |
|---|---|
def add(a, b): |
引数型を書く。戻り値があるなら-> 型も書く |
print(a, b) |
1引数ずつprint(a)、print(b) |
items.append(x) |
append(items, x) |
try: ... except: |
try 式で失敗を伝える、またはmatchでResultを分岐する |
from models import User |
import "models.nagi" |
| 辞書、tuple、内包表記、lambda | 未対応。class、配列、通常の関数・ループを使う |
str(42)、任意型へのcast |
汎用変換は未対応。直接print(42)などを使う |
nullableにはNone / some(value)がありますが、nullableを対象とするmatchや汎用のunwrap APIはありません。型を持つことと、完全な操作APIがあることは分けて考えてください。
整数のrelease演算はRust backendの固定幅演算に従い、加算などのoverflowはwrapします。debug Rust側ではpanicする場合があります。checked / wrapping演算を言語として統一することは今後の課題です。Lowの構文・差し替えはLow、実装予定はroadmapにあります。