
# アーキテクチャ

Phasisは、機会に応じてバイトコードVMを使う4段階のツリーウォーカーです。データの流れは次のとおりです。

```
JS text
  │
  ▼
┌──────────┐
│  Lexer   │  src/Lexer/
└────┬─────┘
     ▼ tokens
┌──────────┐
│  Parser  │  src/Parser/
└────┬─────┘
     ▼ AST nodes
┌──────────────┐
│ Interpreter  │  src/Runtime/
│  + Bytecode  │  src/Bytecode/
│      VM      │
└────┬─────────┘
     ▼ JsValue
PHP host
```

各段階はPHPクラスで構成され、次の段階が利用する小さなインターフェースを備えています。リンク処理はありません。パースでASTが生成され、インタープリターが直接走査します。

## 字句解析器

`src/Lexer/Lexer.php`はソーステキストを`Token`インスタンスのストリームに変換します。各トークンは読み取り専用の構造体で、型（`TokenType`列挙型、約80種類）、値、行、列を持ちます。

字句解析器が処理するもの：

- 数値リテラル - 10進数、16進数（`0x`）、8進数（`0o`）、2進数（`0b`）、指数表記、数値区切り。
- 文字列リテラル - シングルクォートとダブルクォート、すべてのエスケープシーケンス、式を入れ子にできるテンプレートリテラル。
- 正規表現リテラル - 完全な`/pattern/flags`トークン化に対応し、`/`を正規表現として扱うか除算として扱うかを判別します。
- 識別子、キーワード、句読点。
- コメント（行コメントとブロックコメント）。トリビアとして保持されます。
- Unicodeソース - UTF-8バイトをJSのUTF-16ソースとして解釈します。

出力はトークンを遅延生成するジェネレーターです。パーサーは1トークン先読みでこれを読み込みます。

## パーサー

`src/Parser/Parser.php`は手書きのPrattパーサーです。式の優先順位は`src/Parser/Precedence.php`に定義されています。文はそれぞれ独自の再帰下降パーサーで処理します。

`src/Ast/`から不変のASTノードを生成します。すべてのノードがソース位置を持つため、スタックトレースやエラー報告に利用できます。

主なノードの形：

- **式**：`Literal`、`Identifier`、`BinaryExpression`、`UnaryExpression`、`AssignmentExpression`、`CallExpression`、`MemberExpression`、`ArrowFunction`、`ObjectExpression`、`ArrayExpression`、`ConditionalExpression`、`TemplateLiteral`、`SpreadElement`、`NewExpression`、`ThisExpression`、`SequenceExpression`、`TaggedTemplate`、`ClassExpression`、`YieldExpression`、`AwaitExpression`。
- **文**：`BlockStatement`、`IfStatement`、`ForStatement`、`ForInStatement`、`ForOfStatement`、`WhileStatement`、`DoWhileStatement`、`SwitchStatement`、`ReturnStatement`、`ThrowStatement`、`TryStatement`、`BreakStatement`、`ContinueStatement`、`ExpressionStatement`。
- **宣言**：`VariableDeclaration`、`FunctionDeclaration`、`ClassDeclaration`、`ImportDeclaration`。
- **パターン**（分割代入）：`ArrayPattern`、`ObjectPattern`、`RestElement`、`AssignmentPattern`。

仕様に従い、自動セミコロン挿入はレクサーではなくパーサーで実装されています。

## インタープリター

`src/Runtime/Interpreter.php`はASTウォーカーです。各ノードを訪問し、`JsValue`（制御フローを変更する文の場合は`Completion`）を返します。

主な補助クラス：

- **`Environment`**（`src/Runtime/Environment.php`）— 字句スコープのチェーンです。関数呼び出しごとに、字句上の親を指す新しい環境が作成されます。`var`は関数スコープに巻き上げられ、`let` / `const`はTDZを持つブロックスコープです。
- **`CallStack`**（`src/Runtime/CallStack.php`）— 深さ制限付きの明示的なスタックです。関数とメソッドの呼び出しでフレームを積み、例外発生時には巻き戻します。
- **`Completion`**（`src/Runtime/Completion.php`）— `normal`、`return`、`throw`、`break`、`continue`の判別共用体に、オプションの値とラベルを加えたものです。
- **`Reference`**（`src/Runtime/Reference.php`）— 代入や`delete`における左辺評価のための、仕様上のReference型です。

## 値

JavaScript値は`src/Value/`の下でPHPオブジェクトです。それぞれが`JsValue`インターフェースを実装し、独自の型変換メソッド（`toNumber()`、`toString()`、`toBoolean()`）を備えています。

| クラス | JS型 |
|---|---|
| `JsUndefined` | `undefined`（シングルトン）|
| `JsNull` | `null`（シングルトン）|
| `JsBoolean` | `true` / `false` |
| `JsNumber` | IEEE 754倍精度浮動小数点数。NaN、±Infinity、±0を保持 |
| `JsString` | UTF-16文字列のセマンティクス |
| `JsObject` | プロパティマップとプロトタイプチェーンを持つ基本オブジェクト |
| `JsArray` | `JsObject`を継承し、長さを追跡 |
| `JsFunction` | キャプチャした`Environment`を持つクロージャー |
| `JsSymbol` | Symbolプリミティブ |
| `JsBigInt` | bcmathによる任意精度演算 |
| `JsProxy` | 13個すべてのトラップに対応し、取り消し可能 |
| `JsPromise` | 同期実行器とマイクロタスクキュー |
| `JsGenerator` | 2つのモードに対応：フレームスナップショット（バイトコードにコンパイルされた本体。詳細は[バイトコードVM § フレームスナップショットジェネレーター](https://phasis.inline0.dev/ja/docs/advanced/bytecode-vm)）と、`yield*`、try-with-yield、非同期ジェネレーター向けのPHP Fiberフォールバック |
| `JsMap`、`JsSet`、`JsWeakMap`、`JsWeakSet`、`JsWeakRef`、`JsFinalizationRegistry` | コレクション |
| `JsArrayBuffer`、`JsSharedArrayBuffer`、`JsDataView`、`JsTypedArray` | バイナリデータ |

数値処理は、過去のバグの最大の原因です。`NaN !== NaN`と`-0 === 0`が真でなければならず、ToNumberの境界ケースにはビットレベルでの厳密な対応が必要です。`Spec/TypeConversion`クラスは仕様のアルゴリズムを忠実に実装し、網羅的にテストされています。

## 組み込みライブラリー

`src/BuiltIn/`は、標準ライブラリーのすべてのオブジェクト（`Array`、`String`、`Object`、`Math`、`JSON`、`Date`、`RegExp`、`Map`、`Set`、`Promise`、`Proxy`、`Reflect`、`Symbol`、`BigInt`、`TypedArray`、`Temporal`、`Intl`、`console`）に加え、Web Platform / Fetchパック（`URL`、`Headers`、`Request`、`Response`、`Blob`、`FormData`、完全なStreams、`EventTarget`、`AbortController`/`AbortSignal`、`DOMException`、`structuredClone`、`performance`、`TextEncoder`/`TextDecoder`、`atob`/`btoa`）、`crypto` + `SubtleCrypto`、`WebSocket`、`XMLHttpRequest`、およびイベントループ（`setTimeout`、`setInterval`、`queueMicrotask`、`AsyncContext`）を提供します。

各組み込み機能は、クラス上の静的な`install()`メソッドです。`Engine`は構築時に依存関係の順にこれらを呼び出します。それぞれがプロトタイプチェーンを定義し、PHPクロージャーを`JsFunction`インスタンスとしてラップしたメソッドを登録し、コンストラクターをグローバルとして公開します。

## PHP↔JSブリッジ

境界を越える値変換は、次の2か所で行われます。

- **`Phasis\Interop\PhpToJs::convert()`** — PHP値から`JsValue`への変換です。`setGlobal()`、`call()`の引数、ホスト関数の戻り値で使われます。PHPの呼び出し可能オブジェクトは、その本体がそれを呼び出す`JsFunction`になります。
- **`PhpToJs::toPhp()`** — ホスト関数の引数について、`JsValue`からPHPに変換します。
- **`Engine::toPhp()`**（プライベート）— `eval()`と`call()`の結果について、`JsValue`からPHPに変換します。

オブジェクトブリッジはありません。呼び出し可能でないPHPオブジェクトは`undefined`に変換されます（[issue #18](https://github.com/inline0/phasis/issues/18)）。`src/Bytecode/`の`JsToPhp`クラスは相互運用とは無関係で、後述するトランスパイラーです。変換規則は[値の変換](https://phasis.inline0.dev/ja/docs/interop/values)に記載されています。

## エラー処理

捕捉されなかったJSの`throw`はPHP`Phasis\Exceptions\JsThrowable`になります。エンジン自体が発生させるエラーは`Phasis\Exceptions\TypeError`、`RangeError`、`ReferenceError`として表面化し、いずれも`Phasis\Exceptions\RuntimeError`のサブクラスです。エンジン内では、JSの`try` / `catch`が`JsThrowable`、あらゆる`RuntimeError`、および`Phasis\Exceptions\SyntaxError`を捕捉します。ホスト関数が投げたものを含む、その他のPHP例外は変換されず、PHP呼び出し元へそのまま伝播します（[issue #20](https://github.com/inline0/phasis/issues/20)）。

制限は2つあります。呼び出しスタックの上限（`CallStack::maxDepth`、4,096フレームに加えて、トランスパイルされたクロージャーには8,192フレームのガード）と、`maxLoopIterations`です。`Engine::setLimit()`で設定し、ツリーウォーカーのみが適用します（[issue #19](https://github.com/inline0/phasis/issues/19)）。どちらも`Phasis\Exceptions\InternalError`を投げます。文字列長、出力サイズ、実行時間の制限はありません。

## バイトコードVM

ホット関数は2通りに低レベル化されます。`JsToPhp`（`src/Bytecode/`内）は、数値処理やローカル変数中心の本体に対してPHPクロージャーを生成し、PHPのトレーシングJITがネイティブコードにコンパイルできるようにします。パイプラインは`new Ctor(args)`、`obj.method(args)`、`this.member`の読み取りを低レベル化するため、ループ内のクラスメソッドという標準的なパターンはVMを完全に迂回できます。それ以外はすべて、`VM::execute`が処理するフラットなバイトコードにコンパイルされます。カスタム呼び出しスタックにより、すべてのJS呼び出しを単一のPHPフレーム内に収め、プロトタイプメソッドの読み取りにはPCごとのインラインキャッシュを使います。単純なジェネレーター本体もバイトコードにコンパイルされ、`Op::YIELD`がPHP Fiberを経由する代わりに、ヒープ上のフレームスナップショットを取得します。

コンパイラーが変換しない形式（e.g. `with`、再束縛を行う catch を含む `try`、`yield*`、非同期ジェネレーター）は、ツリーウォーカーにフォールバックします。仕組みの詳細は[バイトコードVM](https://phasis.inline0.dev/ja/docs/advanced/bytecode-vm)をご覧ください。
