Preskočiť na obsah

Parser

Phasis sprístupňuje svoj parser JavaScript ako samostatný PHP API. Zdrojový text môžete analyzovať na typovaný AST, prechádzať ním a exportovať ho ako polia v tvare ESTree alebo JSON bez vytvárania Engine či vykonávania čohokoľvek.

parseSource

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

Vstupný bod na jedno volanie pre Phasis\Parser\Parser. Analyzuje $source a vráti koreňový uzol 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 vyberá gramatický cieľ pomenovaný rovnako ako príslušná možnosť v parseroch JS:

Hodnota Cieľ
script klasický skript. Toplevel import/export sú syntaktické chyby, await je identifikátor
module modul ES. Implicitne používa prísny režim, import/export a najvyššia úroveň await sa analyzujú

Každá iná hodnota vyvolá \InvalidArgumentException.

Ak potrebujete podrobnejšie nastavenie (napríklad vopred zapnúť prísny režim pre kontext priameho eval alebo načítať pragmu //# sourceURL), vytvorte parser priamo: new Parser($source) spolu s setModuleMode(), setStrictMode() a parse().

Uzly

Každá trieda AST rozširuje Phasis\Ast\Node a nachádza sa pod Phasis\Ast\{Declaration, Expression, Pattern, Statement}. Uzly sú obyčajné objekty s hodnotami iba na čítanie:

  • $node->type() vracia reťazec typu uzla, čo je základný názov triedy ('BinaryExpression', 'ArrowFunction', ...).
  • $node->location je Phasis\Lexer\SourceLocation s line (číslovanie od 1), column (číslovanie od 0) a offset (posun v bajtoch). Ukladajú sa iba počiatočné pozície; uzly nemajú koncovú pozíciu.
  • Všetky ostatné verejné polia obsahujú potomkov a atribúty uzla ($binary->left, $binary->operator, $binary->right).

Väčšina názvov typov zodpovedá ESTree. Odchýlky:

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

Niekoľko tvarov sa tiež líši: telá tried sú priamo v poli prvkov uzla triedy (bez obalu ClassBody), názvy importov a exportov, štítky príkazov a názvy FunctionExpression sa ukladajú ako reťazce namiesto vnorených uzlov Identifier a super sa analyzuje ako pomenovaný Identifier s názvom super (neexistuje trieda uzla Super). Phasis\Ast\EstreeSerializer toto všetko prevedie do podoby ESTree.

Syntaktické chyby

Neplatný vstup vyvolá Phasis\Exceptions\SyntaxError (zvyčajne jeho podtriedu Phasis\Parser\ParseError). Správa výnimky končí pozíciou a štruktúrované údaje o umiestnení sú dostupné vo výnimke:

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
}

Prechádzač

Phasis\Ast\Walker prechádza strom do hĺbky a pri vstupe do uzla aj pri jeho opustení volá spätné volania, podobne ako estraverse:

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

Obe spätné volania dostávajú (Node $node, ?Node $parent). Ak $enter vráti Walker::SKIP, potomkovia uzla sa preskočia; spätné volanie pre samotný uzol $leave sa napriek tomu vykoná.

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

Prechádzanie je iba na čítanie. Uzly sú objekty s hodnotami iba na čítanie, preto ich nemožno nahrádzať ani meniť.

Serializátor

Phasis\Ast\Serializer konvertuje typovaný AST na polia alebo JSON s názvami typov a rozložením polí podľa Phasis; interné polia vyrovnávacej pamäte parsera sa odstránia:

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

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

EstreeSerializer

Phasis\Ast\EstreeSerializer namiesto toho konvertuje typovaný AST na polia v tvare ESTree: názvy typov podľa odchýlok uvedených vyššie, názvy a poradie polí podľa ESTree, syntetizované uzly ClassBody / špecifikátora / zdroja Literal a posun v bajtoch start pre každý uzol analyzovaný zo zdroja. Posuny end sa neuvádzajú, pretože uzly ukladajú iba počiatočné pozície, a reťazcové 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() vytvorí deterministický súhrn s jedným riadkom na uzol (typ a atribúty name= / value= / op= / kind=, odsadenie dvoma medzerami na každú úroveň). Formát je bajtovo identický so súhrnom AST, ktorý vytvára testovacia vzorka acorn v tests/Popular/acorn, takže rovnaký referenčný nástroj overuje cestu parsera JS aj 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 analyzuje súbor (cieľ skriptu) a vypíše výpis Serializer JSON s odsadením dvoma medzerami. Úplný prehľad príznakov nájdete v CLI.