Docsの目次

文法の早見表

目次 · 初めて書くなら:入門ガイド · 関数を調べる:組み込み関数

このページは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にあります。

Nagi 0.1のドキュメントこのページのソース