Analizator sintactic
Phasis oferă analizatorul său JavaScript ca PHP API independent. Puteți analiza textul sursă într-un AST tipizat, îl puteți parcurge și exporta ca tablouri în format ESTree sau JSON fără să construiți un Engine și fără să executați nimic.
parseSource
public static function parseSource(string $source, string $sourceType = 'script'): Program
Punctul de intrare cu un singur apel din Phasis\Parser\Parser. Analizează $source și returnează nodul rădăcină 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 selectează obiectivul gramatical, denumit după aceeași opțiune din analizatoarele JS:
| Valoare | Obiectiv |
|---|---|
script |
script clasic. import/export de nivel superior sunt erori de sintaxă, await este un identificator |
module |
modul ES. Strict implicit; import/export și await de nivel superior sunt analizate |
Orice altă valoare aruncă \InvalidArgumentException.
Pentru control mai fin (activarea modului strict în prealabil pentru un context direct-eval, citirea directivei //# sourceURL), construiți direct analizatorul: new Parser($source) plus setModuleMode(), setStrictMode() și parse().
Noduri
Fiecare clasă AST extinde Phasis\Ast\Node și se află în Phasis\Ast\{Declaration, Expression, Pattern, Statement}. Nodurile sunt obiecte simple, doar pentru citire:
$node->type()returnează șirul tipului nodului, care este numele de bază al clasei ('BinaryExpression','ArrowFunction', ...).$node->locationeste unPhasis\Lexer\SourceLocationculine(de la 1),column(de la 0) șioffset(poziția în octeți). Sunt stocate doar pozițiile de început; nodurile nu conțin poziția de sfârșit.- Toate celelalte câmpuri publice sunt copiii și atributele nodului (
$binary->left,$binary->operator,$binary->right).
Majoritatea numelor de tip se potrivesc cu ESTree. Abaterile:
| Tip Phasis | Tip ESTree |
|---|---|
ArrowFunction |
ArrowFunctionExpression |
TaggedTemplate |
TaggedTemplateExpression |
ClassMethod |
MethodDefinition |
ClassProperty |
PropertyDefinition |
AssignmentProperty |
Property (în interiorul lui ObjectPattern) |
ExportDeclaration |
ExportNamedDeclaration / ExportDefaultDeclaration / ExportAllDeclaration |
ImportSpecifier |
ImportSpecifier / ImportDefaultSpecifier / ImportNamespaceSpecifier |
Și câteva forme diferă: corpurile claselor sunt un tablou simplu de elemente pe nodul clasei (fără wrapper ClassBody); numele de import/export, etichetele instrucțiunilor și numele FunctionExpression sunt stocate ca șiruri, nu ca noduri Identifier imbricate; iar super este analizat ca Identifier numit super (nu există o clasă de nod Super). Phasis\Ast\EstreeSerializer remodelează toate acestea în formatul ESTree.
Erori de sintaxă
O intrare nevalidă aruncă Phasis\Exceptions\SyntaxError (de obicei subclasa sa Phasis\Parser\ParseError). Mesajul excepției se termină cu poziția, iar localizarea structurată este disponibilă în excepție:
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
}
Parcurgător
Phasis\Ast\Walker parcurge arborele în adâncime, cu apeluri inverse la intrare și la ieșire, după modelul estraverse:
public static function walk(Node $node, callable $enter, ?callable $leave = null): void
Ambele apeluri inverse primesc (Node $node, ?Node $parent). Returnarea lui Walker::SKIP din $enter omite copiii nodului; apelul invers propriu al nodului pentru $leave este totuși executat.
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;
}
});
Parcurgerea este doar pentru citire. Nodurile sunt obiecte de valoare doar pentru citire, deci nu există funcționalitate de înlocuire sau modificare.
Serializator
Phasis\Ast\Serializer convertește AST-ul tipizat în tablouri sau JSON, folosind numele de tip și structura câmpurilor Phasis, după eliminarea câmpurilor cache interne analizatorului:
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
Acesta este formatul afișat de bin/phasis --ast.
EstreeSerializer
Phasis\Ast\EstreeSerializer convertește AST-ul tipizat în tablouri în format ESTree: nume de tip ESTree (conform tabelului de abateri de mai sus), nume și ordine a câmpurilor ESTree, noduri ClassBody / specifier / source-Literal sintetizate și o poziție în octeți start pentru fiecare nod analizat din sursă. Nu există poziții end deoarece nodurile stochează doar pozițiile de început, iar nodurile Literal de tip șir nu conțin textul 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() generează un rezumat determinist, cu un singur rând per nod (tip plus atributele name= / value= / op= / kind=, indentare de două spații pentru fiecare nivel de adâncime). Formatul este identic la nivel de octet cu rezumatul AST produs de fixture-ul acorn din tests/Popular/acorn, astfel încât același oracle verifică atât calea analizatorului JS, cât și acest 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 analizează un fișier (obiectiv de tip script) și afișează rezultatul Serializer JSON, cu indentare de două spații. Consultați CLI pentru referința completă a opțiunilor.