Přejít k obsahu

Parser

Phasis zpřístupňuje svůj parser JavaScript jako samostatné PHP API. Zdrojový text můžete parsovat do typovaného AST, procházet jej a exportovat jako pole ve tvaru ESTree nebo JSON, aniž byste vytvářeli Engine nebo cokoli spouštěli.

parseSource

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

Vstupní bod pro jedno volání v Phasis\Parser\Parser. Parsuje $source a vrací kořenový uzel 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 vybírá gramatický cíl, pojmenovaný stejně jako odpovídající volba v parserech JS:

Hodnota Cíl
script klasický Script. Toplevel import/export jsou syntaktické chyby, await je identifikátor
module modul ES. Implicitně striktní režim, parsují se import/export a nejvyšší await

Jakákoli jiná hodnota vyvolá \InvalidArgumentException.

Pro podrobnější řízení (přednastavení striktního režimu pro kontext přímého vyhodnocení, čtení direktivy //# sourceURL) vytvořte parser přímo: new Parser($source) plus setModuleMode(), setStrictMode() a parse().

Uzly

Každá třída AST rozšiřuje Phasis\Ast\Node a nachází se pod Phasis\Ast\{Declaration, Expression, Pattern, Statement}. Uzly jsou prosté objekty hodnot pouze pro čtení:

  • $node->type() vrací řetězec typu uzlu, což je název třídy bez jmenného prostoru ('BinaryExpression', 'ArrowFunction', ...).
  • $node->location je Phasis\Lexer\SourceLocation s položkami line (od 1), column (od 0) a offset (bajtový posun). Ukládají se pouze počáteční pozice; uzly nemají koncový posun.
  • Všechna ostatní veřejná pole představují potomky a atributy uzlu ($binary->left, $binary->operator, $binary->right).

Většina názvů typů odpovídá ESTree. Odchylky:

Typ Phasis Typ ESTree
ArrowFunction ArrowFunctionExpression
TaggedTemplate TaggedTemplateExpression
ClassMethod MethodDefinition
ClassProperty PropertyDefinition
AssignmentProperty Property (uvnitř ObjectPattern)
ExportDeclaration ExportNamedDeclaration / ExportDefaultDeclaration / ExportAllDeclaration
ImportSpecifier ImportSpecifier / ImportDefaultSpecifier / ImportNamespaceSpecifier

Liší se také některé tvary: těla tříd jsou na uzlu třídy prostá pole prvků (bez obalu ClassBody), názvy importů/exportů, popisky příkazů a názvy FunctionExpression jsou uloženy jako řetězce namísto vnořených uzlů Identifier a super se parsuje jako pojmenovaný Identifier typu super (třída uzlu Super neexistuje). Phasis\Ast\EstreeSerializer to vše převede do podoby ESTree.

Syntaktické chyby

Neplatný vstup vyvolá Phasis\Exceptions\SyntaxError (obvykle jeho podtřídu Phasis\Parser\ParseError). Zpráva výjimky končí pozicí a strukturované údaje o místě jsou dostupné ve výjimce:

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
}

Procházení stromu

Phasis\Ast\Walker prochází strom do hloubky pomocí zpětných volání při vstupu a odchodu, ve stylu estraverse:

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

Obě zpětná volání přijímají (Node $node, ?Node $parent). Vrácení hodnoty Walker::SKIP z $enter přeskočí potomky uzlu; vlastní $leave uzlu se přesto zavolá.

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;
    }
});

Procházení je pouze pro čtení. Uzly jsou neměnné objekty hodnot, takže jejich nahrazování ani úpravy nejsou podporovány.

Serializátor

Phasis\Ast\Serializer převádí typovaný AST na pole nebo JSON pomocí názvů typů a rozložení polí Phasis, přičemž odstraňuje interní pole mezipaměti parseru:

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

Toto je formát, který bin/phasis --ast vypisuje.

EstreeSerializer

Phasis\Ast\EstreeSerializer místo toho převádí typovaný AST na pole ve tvaru ESTree: názvy typů ESTree (podle výše uvedené tabulky odchylek), názvy a pořadí polí ESTree, syntetizované uzly ClassBody / specifier / source-Literal a posun v bajtech start u každého uzlu parsovaného ze zdroje. Posuny end neexistují, protože uzly ukládají pouze počáteční pozice, a řetězcové uzly Literal neobsahují text 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() vytváří deterministický jednořádkový souhrn pro každý uzel (typ plus atributy name= / value= / op= / kind=, odsazení o dvě mezery na každou úroveň). Formát je bajt po bajtu totožný se souhrnem AST, který vytváří acorn fixture v tests/Popular/acorn, takže stejný oracle ověřuje cestu parseru JS i tento export 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 parsuje soubor (cíl script) a vypisuje výpis Serializer JSON s odsazením o dvě mezery. Úplný přehled příznaků najdete v CLI.