← 仕様書・設計書一覧

KNF インタプリタ仕様書(作成途中)

KNF(.ksc)スクリプトを実行するインタプリタの仕様書です。Op 命令セット(JSON IR)とは別系統の、テキストベーススクリプトを直接実行するランタイムです。

Phase 7-3 まで実装済み(107テスト通過)。一部の再帰テスト・大規模ループテストは未解決。

アーキテクチャ概要

.ksc スクリプト
  │
  ▼
┌───────────┐
│ Tokenizer │  式文字列をトークン列に分割
└───────────┘
  │
  ▼
┌───────────┐
│  Parser   │  行単位で分類・ブロック構造解析
└───────────┘
  │
  ▼
┌─────────────┐
│ Interpreter │  メインループで逐次実行
│  ├─ Evaluator   │  式の評価(再帰下降パーサー)
│  ├─ GameState   │  変数・スコープ・コールスタック管理
│  └─ Debugger    │  デバッグ機能
└─────────────┘
  │
  ▼
┌────────────┐
│ IEngineAPI │  プラットフォーム抽象化(描画・音声・UI)
└────────────┘

コアモジュール

Interpreter

メインの実行エンジン。.ksc スクリプトを行単位で解析・実行します。

メソッド説明
run(script)スクリプト全体をパース・実行
stop()実行を停止
jumpTo(label)指定ラベルへジャンプ
getState()現在の PC・変数・コールスタック深度を返す
getDebugger()デバッガインスタンスを取得

Parser

行レベルの構文解析を行います。

LineTypeパターン説明
DialogueStart#speakerダイアログブロック開始
DialogueEnd#ダイアログブロック終了
Label*label_nameラベル定義
Comment// ...コメント
Empty(空行)空行
Expressionその他式・コマンド

Evaluator

式を評価する再帰下降パーサーです。

種別演算子
算術+, -, *, /, %
比較==, !=, >, >=, <, <=
論理&&, ||, !
代入=, +=, -=, *=, /=
グループ(, )

演算子優先順位(低→高): || &&==/!= >/>=/</<= +/-*///% → 単項 → リテラル/変数/関数呼び出し

Tokenizer

式文字列をトークン列に分割するレキサーです。

TokenType
Number42, 3.14
String"hello", 'world'
Booleantrue, false
Identifiercount, playerName
Keywordif, else, def, sub, return, choice
Operator+, -, ==, &&
Assign=, +=, -=, *=, /=

GameState

ランタイムの全状態を管理します。

フィールド説明
variablesグローバル変数(Map)
localScopesローカルスコープのスタック(Map[])
callStackコールスタック(CallFrame[])
labelMapラベル→行番号マップ
functionsdef 定義(値を返す関数)
subroutinessub 定義(値を返さないサブルーチン)

スコープ解決: getVar はローカルスコープ(後入れ優先)→ グローバルの順で検索。

IEngineAPI

プラットフォーム非依存の描画・音声・UI インターフェースです。

メソッド説明
showDialogue(speaker, lines)ダイアログ表示
setBg(name, effect?)背景設定
showChar(name, pose, position?)キャラクター表示
hideChar(name)キャラクター非表示
moveChar(name, position, time)キャラクター移動
playBgm(name)BGM 再生
stopBgm()BGM 停止
fadeBgm(time)BGM フェードアウト
playSe(name)効果音再生
playTimeline(name)タイムライン再生
showChoice(options)選択肢表示(Promise<number>)
waitForClick()クリック待ち
wait(ms)ミリ秒待機

ビルトインコマンド

コマンド引数IEngineAPI
bg(name)背景名setBg
ch(name, pose, pos?)キャラ名, ポーズ, 位置showChar
ch_hide(name)キャラ名hideChar
bgm(name)BGM名playBgm
bgm_stop()stopBgm
se(name)SE名playSe
wait(ms)ミリ秒wait
waitclick()waitForClick
timeline(name)タイムライン名playTimeline
jump(label)ラベル名PC を移動
call(label)ラベル名コールスタックに積んでジャンプ
ret()コールスタックから復帰

スクリプト構文

ダイアログ

#太郎
こんにちは!
今日はいい天気ですね。
#

#speaker で開始、# で終了。間の行がダイアログテキストとして表示されます。

条件分岐

if (flag == 1) {
  #太郎
  フラグが立っています。
  #
} else if (flag == 2) {
  #太郎
  フラグは2です。
  #
} else {
  #太郎
  フラグは立っていません。
  #
}

選択肢

choice {
  "はい" {
    yes_count += 1
  }
  "いいえ" {
    no_count += 1
  }
  "条件付き" if (flag == 1) {
    // flag が 1 の時のみ表示
  }
}

ユーザー定義関数

def(値を返す):

def add(a, b) {
  return a + b
}

result = add(3, 5)

sub(値を返さない):

sub greet(name) {
  greeting = "Hello, " + name
}

greet("World")

再帰呼び出し対応(深度上限: 16)。ローカルスコープで引数を管理。

文字列補間

count = 42
#ナレーター
現在のカウントは{count}です。
計算結果: {count * 2 + 1}
#

デバッグ機能

変数ウォッチ

debugger.watchVariable("hp")
// ... 実行 ...
debugger.getVariableHistory("hp")
// → [{ line, oldValue, newValue, timestamp }, ...]

ブレークポイント

debugger.addBreakpoint(10)                // 行10で停止
debugger.addBreakpoint(20, "hp < 50")     // 条件付き
debugger.toggleBreakpoint(10)             // 有効/無効切替

トレースログ

debugger.enableTrace()
// ... 実行 ...
debugger.getTraceLog()
// → [{ type, line, data }, ...]

デバッグイベント

DebugEventType発火タイミング
VariableChanged変数の値が変更された
Breakpointブレークポイントに到達
StepCompleteステップ実行完了
FunctionCall関数呼び出し
FunctionReturn関数からの復帰

エラーハンドリング

ErrorType説明
SyntaxError構文エラー
ReferenceError未定義の変数・関数への参照
TypeError型の不一致
RuntimeError実行時エラー(ゼロ除算など)
StackOverflow再帰深度超過(上限: 16)
FileNotFoundファイルが見つからない

未定義の変数・関数を参照した際、Levenshtein 距離3以内の候補を「もしかして」として提案します。

実装フェーズ

Phase内容状態
Phase 1基本実行(ダイアログ、ビルトインコマンド)完了
Phase 2ラベル・ジャンプ・call/ret完了
Phase 3式評価(算術・比較・論理・代入)完了
Phase 4if/else/choice完了
Phase 5ユーザー定義関数 def/sub完了(再帰テスト一部未解決)
Phase 6文字列補間完了
Phase 7-1エラーハンドリング完了
Phase 7-2デバッグモード完了
Phase 7-3統合テスト完了(107テスト通過)

既知の問題

  • Phase 5 の再帰テスト(fibonacci 等の二重再帰呼び出し)がハングする
  • 大規模ループ + 関数呼び出しのテストがタイムアウトする
  • ダイアログ内の文字列補間統合テストが一部スキップ(TODO)
  • playTimeline() は未実装