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
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.
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->locationjePhasis\Lexer\SourceLocationsline(číslovanie od 1),column(číslovanie od 0) aoffset(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:
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:
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á.
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:
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.
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:
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.