2. 構文木(AST) 構造
カナデ(Kanade) 言語の抽象構文木(AST)仕様です。
このドキュメントは、パーサー(Parser)が生成するAST(Abstract Syntax Tree、抽象構文木)のノードごとのJSONスキーマを定義しています。カナデのインタープリタ、コンパイラ、静的解析ツールなど、すべてのツールがこの仕様を基準に実装されています。
カナデのASTは、ハリ(Hari)とまったく同じノード構造です。パーサーが日本語のキーワードを読み取ったあとに生成されるJSONは、ハリと完全に互換性があります。違いは区切り記号と語彙だけなので、各ノードの下に書いてあるカナデの書き方(『変数』、【タイプ】、〈関数〉)とJSONの対応を見れば、ハリのドキュメントもそのまま読み替えられます。語彙の対応は言語概要の表にまとめてあります。
1. プログラム (Program)
すべてのソースコードの最上位ノードです。
{
"type": "Program",
"body": [ /* Statementノードの一覧 */ ]
}
2. 宣言と代入 (Declarations & Assignments)
VariableDeclaration (変数の宣言と値の代入)
(『私たち』の)『名前』を(【タイプ】の)値にしよう/固定しよう/準備しよう
(参考:パーサーは変数の宣言と値の再代入を区別せず、どちらもこのノードとして読み取ります。新しい変数を作るのか再代入なのかは、ランタイムが決めます。)
{
"type": "VariableDeclaration",
"target": /* Identifier、またはMemberExpression(オブジェクトのプロパティに代入する場合) */,
"isStatic": false, // 『私たち』の が付いていればtrue
"accessModifier": "public", // クラスのフィールドなら"public"、"private"、"protected"のどれか(指定がなければpublic)
"typeAnnotation": { "type": "TypeReference", "name": "タイプ" }, // タイプの指定がなければnull
"value": /* Expressionノード(準備しようの場合、またはプロパティアクセサの場合はnull) */,
"isConst": false, // 固定しようの場合true
"isDeclarationOnly": false, // 準備しようの場合true
"getter": [ /* Statementノードの一覧(プロパティアクセサの「取得する時」ブロック、なければnull) */ ],
"setter": {
"param": { "type": "Identifier", "name": "新しい値" },
"body": [ /* Statementノードの一覧(プロパティアクセサの「決める時」ブロック) */ ]
} // プロパティアクセサの「決める時」ブロック、なければnull
}
Compound Assignments (複合代入)
『名前』に値を足そう/引こう など
{
"type": "CompoundAssignment",
"operator": "+=", // 足そうは"+="、引こうは"-="など
"target": /* Identifier、またはMemberExpression */,
"value": /* Expressionノード */
}
List Operations (リスト専用の操作)
『リスト』(の前に/の後に/に)値を追加しよう
{
"type": "ListPushStatement",
"target": /* Identifier、またはMemberExpression */,
"value": /* Expressionノード */,
"position": "back" // "front"(前に)、"back"(後に/既定値)
}
『リスト』(の前から/の後から/から)取り出そう
{
"type": "ListPopStatement",
"target": /* Identifier、またはMemberExpression */,
"position": "back" // "front"(前から)、"back"(後から/既定値)
}
『リスト』(の前から/の後から/から)取り出した値(Expressionノード)
{
"type": "ListPopExpression",
"target": /* Identifier、またはMemberExpression */,
"position": "back" // "front"、"back"
}
3. 制御フロー (Control Flow)
IfStatement (条件文)
もし(条件)なら: ... もしくは(条件)なら: ... それ以外ならば: ...
{
"type": "IfStatement",
"condition": /* Expressionノード */,
"consequent": [ /* Statementノードの一覧 */ ],
"elifs": [
{
"condition": /* Expressionノード */,
"consequent": [ /* Statementノードの一覧 */ ]
}
],
"alternate": [ /* Statementノードの一覧(なければ空のリスト) */ ]
}
SwitchStatement (スイッチ文)
『値』によって分けよう: 「場合1」の場合: ... 残りは: ...
{
"type": "SwitchStatement",
"discriminant": /* Expressionノード */,
"cases": [
{
"values": [ /* Expressionノードの一覧(読点で複数の条件を書ける) */ ],
"body": [ /* Statementノードの一覧 */ ]
}
],
"default": [ /* Statementノードの一覧(省略可、なければnull) */ ]
}
FallthroughStatement (スイッチの貫通)
次に続けよう
{
"type": "FallthroughStatement"
}
WhileLoop (条件付き繰り返し)
(条件)間繰り返そう:
{
"type": "WhileLoop",
"condition": /* Expressionノード */,
"body": [ /* Statementノードの一覧 */ ]
}
ForEachLoop (リストの巡回)
『リスト』の『項目』ごとに繰り返そう:
{
"type": "ForEachLoop",
"item": { "type": "Identifier", "name": "項目" }, // 1つずつ取り出す要素のIdentifierノード
"iterable": /* Expressionノード(評価結果がリストまたは文字列でなければならない) */,
"body": [ /* Statementノードの一覧 */ ]
}
ForRangeStatement (数の範囲の繰り返し)
1から10まで繰り返そう(『回数』): または 『開始』から『終了』まで繰り返そう(『回数』):
{
"type": "ForRangeStatement",
"start": /* Expressionノード */,
"end": /* Expressionノード */,
"iterator": { "type": "Identifier", "name": "回数" }, // 繰り返しの変数のIdentifierノード
"body": [ /* Statementノードの一覧 */ ]
}
BreakStatement (繰り返しの中断)
繰り返しを終わろう
{
"type": "BreakStatement"
}
4. 関数とオブジェクト指向 (Functions & OOP)
FunctionDeclaration (関数・メソッドの宣言)
【戻り値の型】を返す(『私たち』の)〈関数名〉を作ろう(【タイプ】の『引数』 = 「デフォルト値」):
{
"type": "FunctionDeclaration",
"id": "関数名",
"isStatic": false, // 『私たち』の が付いていればtrue
"accessModifier": "public", // "public"、"private"、"protected"のどれか(指定がなければpublic)
"returnType": { "type": "TypeReference", "name": "戻り値の型" }, // 指定がなければnull
"isAbstract": false, // 抽象メソッド(本体がない場合)ならtrue
"params": [
{
"type": { "type": "TypeReference", "name": "タイプ" }, // 指定がなければnull
"name": "引数の名前",
"default": /* Expressionノード(デフォルト値がなければnull) */
}
],
"body": [ /* Statementノードの一覧(isAbstractがtrueならnull) */ ]
}
ReturnStatement (return文)
『値』を返そう または 返そう
{
"type": "ReturnStatement",
"value": /* Expressionノード(値を省略した場合はnull) */
}
ClassDeclaration (クラスの宣言)
【親クラス】をもとにして【インターフェース】に従う【(タイプ)クラス名】を設計しよう/下設計しよう:
{
"type": "ClassDeclaration",
"id": "クラス名",
"typeParams": [ "タイプ" ], // ジェネリクスの型パラメータ(なければ空のリスト)
"isAbstract": false, // 抽象クラスとして宣言した場合true
"baseClass": { "type": "TypeReference", "name": "親クラス名" }, // 継承する親クラス1つ(なければnull)
"interfaces": [ /* TypeReferenceノードの一覧(なければ空のリスト) */ ], // 実装するインターフェースの一覧
"body": [ /* FunctionDeclaration、ConstructorDeclarationなど */ ]
}
ConstructorDeclaration (コンストラクタの宣言)
最初に作られる時(『引数』)次のようにしよう:
{
"type": "ConstructorDeclaration",
"id": { "type": "Identifier", "name": "最初に作られる時" },
"params": [ /* FunctionDeclarationと同じパラメータ構造 */ ],
"body": [ /* Statementノードの一覧 */ ]
}
InterfaceDeclaration (インターフェースの宣言)
【インターフェース名】を規定しよう:
{
"type": "InterfaceDeclaration",
"id": "インターフェース名",
"body": [ /* InterfaceMethodノードの一覧 */ ]
}
InterfaceMethod (インターフェースのメソッド規約)
〈メソッド名〉がなければならない(【タイプ】の『引数』)
{
"type": "InterfaceMethod",
"id": "メソッド名",
"returnType": { "type": "TypeReference", "name": "戻り値の型" }, // 指定がなければnull
"params": [ /* FunctionDeclarationと同じパラメータ構造 */ ]
}
5. 式と特殊な参照 (Expressions & Special References)
BinaryExpression (二項演算)
A + B、AがBと同じだ、AがBの一種だ など
{
"type": "BinaryExpression",
"left": /* Expressionノード */,
"operator": "+", "-", "*", "/", "%", "==", "!=", "<", ">", "<=", ">=", "instanceof" /* 演算子の記号 */,
"right": /* Expressionノード(ただし演算子が"instanceof"の場合はTypeReferenceノード) */
}
算術演算子の結び付きの順序は、普通の数学と同じです。* / %が+ -より先に結び付き、同じ段階の演算子は左から結び付きます。そのため2 + 3 * 4は、left: 2, operator: "+", right: (3 * 4)という木になります。丸括弧で囲んだ式は、それ自体で1つの項です。
LogicalExpression (論理演算)
A かつ B、A または B(短絡評価に対応)
{
"type": "LogicalExpression",
"left": /* Expressionノード */,
"operator": "かつ", "または",
"right": /* Expressionノード */
}
CallExpression (関数・メソッドの呼び出し)
〈関数名〉(引数1, 引数2)
{
"type": "CallExpression",
"callee": /* Identifier、MemberExpression、またはFunctionReference */,
"arguments": [ /* Expressionノードの一覧 */ ]
}
NewExpression (インスタンスの生成)
新しい【クラス名】(引数)
{
"type": "NewExpression",
"callee": {
"type": "TypeReference",
"name": "クラス名"
},
"arguments": [ /* Expressionノードの一覧 */ ]
}
MemberExpression (プロパティ・メソッド・インデックスへのアクセス)
『オブジェクト』の『プロパティ』、『リスト』の1番目、『辞書』の「キー」
{
"type": "MemberExpression",
"object": /* Expressionノード(または静的アクセスの場合はTypeReferenceノード) */,
"property": /* Identifier(プロパティ)、FunctionReference(メソッド)、IndexExpression、LengthLiteral、またはLiteral(文字列のキー) */
}
Special References (特殊な参照)
親、外 の予約語
// 親
{
"type": "SuperReference"
}
// 外
{
"type": "OuterReference"
}
Identifier (変数名)
『名前』
{
"type": "Identifier",
"name": "名前"
}
FunctionReference (関数の参照)
静的な参照: 〈関数名〉
動的な参照(リフレクション): 〈『変数名』〉 または 〈「文字列」〉
{
"type": "FunctionReference",
"name": "関数名", // 静的な参照なら文字列、動的な参照ならnull
"expression": /* Expressionノード(動的な参照ならIdentifierやLiteralなど、静的な参照ならnull) */
}
ExpressionStatement (式文)
〈関数〉()を実行しようのように、式そのものが1つの文(Statement)として使われるときのノードです。
{
"type": "ExpressionStatement",
"expression": /* CallExpressionなどのExpressionノード */
}
TypeReference (タイプの参照)
タイプの情報を表すノードです。
{
"type": "TypeReference",
"name": "タイプ名",
"typeArgs": [ /* TypeReferenceノードの一覧(ジェネリクスの引数、なければ空のリスト) */ ]
}
Literal (基本のリテラル)
数字、文字列、真偽値(真、偽)、空値(空っぽ)のリテラル(テンプレートを除く)
{
"type": "Literal",
"value": 42 /* 評価されたホスト言語の基本の値(例: 42, "こんにちは", true, false, null) */,
"raw": "42" /* ソースコードの文字列(例: "42", "\"こんにちは\"", "真", "空っぽ") */
}
TemplateLiteral (テンプレートリテラル)
枠「文字列 {『変数』} 文字列」
{
"type": "TemplateLiteral",
"strings": [ "文字列 ", " 文字列" ],
"expressions": [ /* 埋め込まれたExpressionノードの一覧 */ ]
}
ListLiteral (リスト)
【1, 2, 3】
{
"type": "ListLiteral",
"elements": [ /* Expressionノードの一覧 */ ]
}
DictLiteral (辞書)
{「キー」:「値」}
{
"type": "DictLiteral",
"elements": [
{
"key": /* Expressionノード */,
"value": /* Expressionノード */
}
]
}
IndexExpression (インデックスアクセス式)
1番目、『インデックス変数』番目(ここで番目はインデックスを表す必須の文法トークンです。読みやすさのために後ろに付くの値(1番目の値)のような言葉は、パーサーが無視する省略可能なトークン(シンタックスシュガー)です。)
『リスト』の『k』番目(後ろのの値まで)は、それ自体で1つの項です。そのため『リスト』の『k』番目 - 5は取り出した要素から5を引き、『リスト』の3番目 < 『リスト』の1番目は2つの要素を比べ、『格子』の2番目の1番目は2回インデックスします。
{
"type": "IndexExpression",
"index": /* Expressionノード(評価結果が必ず整数でなければならない) */
}
LengthLiteral (長さ)
長さ
{
"type": "LengthLiteral",
"value": "長さ"
}
6. モジュールと例外 (Modules & Exceptions)
ImportStatement (モジュールの読み込み)
「ファイル」から全部持ってこよう または 【モジュール】から〈関数〉と〈関数2〉を持ってこよう
{
"type": "ImportStatement",
"module": {
"kind": "user", // "user"(文字列リテラルのファイル)または"builtin"(タイプリテラルのモジュール)
"name": "ファイルやモジュールの名前"
},
"imports": null // すべてを持ってくるときはnull、特定の項目だけなら一覧 ["関数名", "変数名"]
}
現在のハリ・カナデの実装では、項目の一覧と全部かどうかを次のように持ちます。項目は〈a〉と〈b〉を持ってこようのように助詞でつなぎ、別名(〈切り上げ〉を〈丸め〉に)は項目が1つのときだけ付けられます。【モジュール】から全部持ってこようは、そのモジュールが宣言した関数・クラス・インターフェース(組み込みモジュールは関数のすべて)を持ってきます。
タイプリテラル(【名前】)で書いたモジュールは、標準ライブラリ、Hanaランタイムと一緒に配るパッケージ、インストールしたサードパーティのパッケージのどれかです。サードパーティのパッケージの名前は、【github.com/owner/repo】のようにgitのパスです。どれに当たるかは、モジュールを読み込む側(ローダー)が決めます。パーサーは【名前】をすべてbuiltinとして読み取ります。持ってきた関数がそのモジュールの中の関数を呼べること(モジュールの範囲)は、ランタイム仕様の4.3にあります。
TryStatement (例外処理文)
とりあえずやってみよう: ... 発生したら(『エラー』): ... 最後はいつも: ...
{
"type": "TryStatement",
"block": [ /* Statementノードの一覧(tryブロック) */ ],
"handlers": [ /* CatchClauseノードの配列(順に評価される) */ ],
"finalizer": [ /* Statementノードの一覧(finallyブロック) */ ] // なければnull
}
CatchClause (個別の例外処理ブロック)
【エラーの種類】が発生したら(『エラー』): または 発生したら(『エラー』):
{
"type": "CatchClause",
"catchType": { "type": "TypeReference", "name": "エラーの種類" }, // タイプの指定がなければnull(すべての例外を捕まえる)
"param": { "type": "Identifier", "name": "エラーの識別子名" },
"body": [ /* Statementノードの一覧 */ ]
}
ThrowStatement (エラーの発生)
新しい【エラー】(「メッセージ」)を発生させよう
{
"type": "ThrowStatement",
"error": /* Expressionノード(普通はNewExpressionで作った例外オブジェクト) */
}
7. 入出力とその他 (I/O & Others)
PrintStatement (出力文)
「メッセージ」を出力しよう
{
"type": "PrintStatement",
"value": /* Expressionノード */
}
PrintInlineStatement (続けて出力する文)
「メッセージ」を続けて出力しよう
{
"type": "PrintInlineStatement",
"value": /* Expressionノード */
}
InputStatement (入力文)
『変数』を【タイプ】で入力してもらおう または 『変数』を入力してもらおう
{
"type": "InputStatement",
"target": { "type": "Identifier", "name": "変数" }, // 入力値を保存する変数のIdentifierノード
"typeAnnotation": { "type": "TypeReference", "name": "タイプ" } // 期待するタイプ(省略すると既定で"文字列")
}