
# パーサー

PhasisはJavaScriptパーサーを単独のPHP APIとして提供します。ソーステキストを型付きASTに解析し、走査して、ESTree形式の配列またはJSONとしてエクスポートできます。`Engine`を構築したり、何かを実行したりする必要はありません。

## parseSource

```php
public static function parseSource(string $source, string $sourceType = 'script'): Program
```

`Phasis\Parser\Parser`の1回の呼び出しで使えるエントリーポイントです。`$source`を解析し、`Phasis\Ast\Program`ルートノードを返します。

```php
use Phasis\Parser\Parser;

$program = Parser::parseSource('const x = [1, 2].map(n => n * 2);');
$module = Parser::parseSource('import { x } from "./x.js"; export default x;', 'module');
```

`$sourceType`は文法のゴールを指定します。名前はJSパーサーの同じオプションに由来します。

| 値 | ゴール |
|---|---|
| `script` | 従来のScript。トップレベルの`import`/`export`は構文エラーになり、`await`は識別子です |
| `module` | ES Module。暗黙にstrictモードとなり、`import`/`export`とトップレベルの`await`を解析できます |

その他の値を指定すると`\InvalidArgumentException`がスローされます。

より細かく制御する場合（直接evalするコンテキストでstrictモードを事前に有効化する、`//# sourceURL`プラグマを読み取るなど）は、パーサーを直接構築します。`new Parser($source)`に加え、`setModuleMode()`、`setStrictMode()`、`parse()`を指定します。

## ノード

すべてのASTクラスは`Phasis\Ast\Node`を継承し、`Phasis\Ast\{Declaration, Expression, Pattern, Statement}`配下にあります。ノードは読み取り専用の単純な値オブジェクトです。

- `$node->type()`はノードの型文字列（クラスのベース名。`'BinaryExpression'`、`'ArrowFunction'`など）を返します。
- `$node->location`は`Phasis\Lexer\SourceLocation`で、`line`（1始まり）、`column`（0始まり）、`offset`（バイトオフセット）を持ちます。保存されるのは開始位置だけで、ノードに終了オフセットはありません。
- それ以外の公開フィールドは、ノードの子要素と属性（`$binary->left`、`$binary->operator`、`$binary->right`）です。

型名の大半は[ESTree](https://github.com/estree/estree)に一致します。異なる点は次のとおりです。

| Phasisの型 | ESTreeの型 |
|---|---|
| `ArrowFunction` | `ArrowFunctionExpression` |
| `TaggedTemplate` | `TaggedTemplateExpression` |
| `ClassMethod` | `MethodDefinition` |
| `ClassProperty` | `PropertyDefinition` |
| `AssignmentProperty` | `Property`（`ObjectPattern`内） |
| `ExportDeclaration` | `ExportNamedDeclaration` / `ExportDefaultDeclaration` / `ExportAllDeclaration` |
| `ImportSpecifier` | `ImportSpecifier` / `ImportDefaultSpecifier` / `ImportNamespaceSpecifier` |

構造にもいくつか違いがあります。クラス本体はクラスノード上の要素配列で、`ClassBody`ラッパーはありません。import/export名、文ラベル、`FunctionExpression`名は、入れ子の`Identifier`ノードではなく文字列として保存されます。また、`super`は`super`という名前の`Identifier`として解析されます（`Super`ノードクラスはありません）。`Phasis\Ast\EstreeSerializer`はこれらすべてをESTree形式に変換します。

## 構文エラー

入力が無効な場合は`Phasis\Exceptions\SyntaxError`（通常はそのサブクラスの`Phasis\Parser\ParseError`）がスローされます。例外メッセージの末尾には位置が付き、構造化された位置情報は例外から取得できます。

```php
use Phasis\Exceptions\SyntaxError;
use Phasis\Parser\Parser;

try {
    Parser::parseSource("const a = 1;\nconst b = ;");
} catch (SyntaxError $e) {
    echo $e->getMessage();          // Unexpected token (got ; ";") at line 2, column 10
    echo $e->location->line;        // 2
    echo $e->location->column;      // 10
    echo $e->location->offset;      // 23
}
```

## ウォーカー

`Phasis\Ast\Walker`は、estraverseのようにenter/leaveコールバックを使って、ツリーを深さ優先で走査します。

```php
public static function walk(Node $node, callable $enter, ?callable $leave = null): void
```

両方のコールバックには`(Node $node, ?Node $parent)`が渡されます。`$enter`から`Walker::SKIP`を返すと、ノードの子要素を走査しません。ノード自身の`$leave`は引き続き呼び出されます。

```php
use Phasis\Ast\Expression\Identifier;
use Phasis\Ast\Node;
use Phasis\Ast\Walker;

$names = [];
Walker::walk($program, function (Node $node) use (&$names) {
    if ($node instanceof Identifier) {
        $names[] = $node->name;
    }
    if ($node instanceof \Phasis\Ast\Expression\ArrowFunction) {
        return Walker::SKIP;
    }
});
```

走査は読み取り専用です。ノードは読み取り専用の値オブジェクトなので、置換や変更には対応していません。

## シリアライザー

`Phasis\Ast\Serializer`は、パーサー内部のキャッシュフィールドを除き、Phasisの型名とフィールド構成を使って型付きASTを配列またはJSONに変換します。

```php
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
```

これは`bin/phasis --ast`が出力する形式です。

## EstreeSerializer

`Phasis\Ast\EstreeSerializer`は型付きASTをESTree形式の配列に変換します。ESTreeの型名（上の差分表に準拠）、ESTreeのフィールド名と順序、合成された`ClassBody` / specifier / source-`Literal`ノードを使い、ソースから解析された各ノードに`start`バイトオフセットを付与します。ノードに保存されるのは開始位置のみなので、`end`オフセットはありません。また、文字列の`Literal`ノードには`raw`テキストがありません。

```php
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
public static function summarize(Node $node): string
```

`summarize()`は、型と`name=` / `value=` / `op=` / `kind=`属性を含む、ノードごとの決定的な1行サマリーを出力します（深さごとに2スペースでインデント）。この形式は、`tests/Popular/acorn`のacornフィクスチャが生成するASTサマリーとバイト単位で一致するため、同じオラクルでJSパーサー経路とこのPHPエクスポートの両方を照合できます。

```php
use Phasis\Ast\EstreeSerializer;
use Phasis\Parser\Parser;

echo EstreeSerializer::summarize(Parser::parseSource('let x = a + 1;'));
// Program
//   VariableDeclaration kind=let
//     VariableDeclarator
//       Identifier name=x
//       BinaryExpression op=+
//         Identifier name=a
//         Literal value=1
```

## CLI

`bin/phasis --ast script.js`はファイルを解析し（scriptゴール）、`Serializer`のJSONダンプを2スペースのインデントで出力します。すべてのフラグの説明は[CLI](https://phasis.inline0.dev/ja/docs/cli)を参照してください。
