コンテンツへスキップ

パーサー

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に一致します。異なる点は次のとおりです。

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を参照してください。