Parser
Phasis biedt zijn parser van JavaScript aan als zelfstandige PHP API. Je kunt brontekst parsen naar een getypeerde AST, deze doorlopen en exporteren als arrays in ESTree-vorm of JSON, zonder een Engine te maken of iets uit te voeren.
parseSource
public static function parseSource(string $source, string $sourceType = 'script'): Program
Het eenmalige toegangspunt op Phasis\Parser\Parser. Parseert $source en retourneert het hoofdknooppunt van 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 bepaalt het grammaticadoel, genoemd naar dezelfde optie in parsers van JS:
| Waarde | Doel |
|---|---|
script |
klassiek Script. import/export op het hoogste niveau zijn syntaxfouten; await is een identifier |
module |
ES Module. Impliciet strikt; import/export en await op het hoogste niveau kunnen worden geparsed |
Elke andere waarde gooit \InvalidArgumentException.
Voor meer controle (de strikte modus vooraf instellen voor een context met directe eval, de pragma //# sourceURL lezen) kun je de parser rechtstreeks maken: new Parser($source) plus setModuleMode(), setStrictMode() en parse().
Knooppunten
Elke AST-klasse breidt Phasis\Ast\Node uit en bevindt zich onder Phasis\Ast\{Declaration, Expression, Pattern, Statement}. Knooppunten zijn gewone, alleen-lezen waardeobjecten:
$node->type()retourneert de typeaanduiding van het knooppunt als tekenreeks; dit is de basisnaam van de klasse ('BinaryExpression','ArrowFunction', ...).$node->locationis eenPhasis\Lexer\SourceLocationmetline(vanaf 1),column(vanaf 0) enoffset(byte-offset). Alleen beginposities worden opgeslagen; knooppunten bevatten geen eindoffset.- Alle overige openbare velden zijn de kinderen en kenmerken van het knooppunt (
$binary->left,$binary->operator,$binary->right).
De meeste typenamen komen overeen met ESTree. De afwijkingen:
| Type van Phasis | Type van ESTree |
|---|---|
ArrowFunction |
ArrowFunctionExpression |
TaggedTemplate |
TaggedTemplateExpression |
ClassMethod |
MethodDefinition |
ClassProperty |
PropertyDefinition |
AssignmentProperty |
Property (binnen ObjectPattern) |
ExportDeclaration |
ExportNamedDeclaration / ExportDefaultDeclaration / ExportAllDeclaration |
ImportSpecifier |
ImportSpecifier / ImportDefaultSpecifier / ImportNamespaceSpecifier |
Enkele vormen wijken ook af: klasseblokken zijn een gewone array met elementen op de klasseknoop (zonder wrapper van ClassBody); import- en exportnamen, labels van statements en namen van FunctionExpression worden als tekenreeksen opgeslagen in plaats van als geneste knooppunten van Identifier, en super wordt geparsed als een Identifier met de naam super (er bestaat geen knooppuntklasse Super). Phasis\Ast\EstreeSerializer zet dit alles om naar de vorm van ESTree.
Syntaxfouten
Ongeldige invoer gooit Phasis\Exceptions\SyntaxError (meestal de subklasse Phasis\Parser\ParseError). De positie staat aan het einde van het uitzonderingsbericht en de gestructureerde locatie is beschikbaar op de uitzondering:
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
}
Doorloper
Phasis\Ast\Walker doorloopt een boom diepte-eerst met callbacks voor het betreden en verlaten van knooppunten, volgens de stijl van estraverse:
public static function walk(Node $node, callable $enter, ?callable $leave = null): void
Beide callbacks ontvangen (Node $node, ?Node $parent). Als $enter Walker::SKIP retourneert, worden de kinderen van het knooppunt overgeslagen; de eigen callback voor $leave van het knooppunt wordt nog steeds uitgevoerd.
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;
}
});
Doorlopen is alleen-lezen. Knooppunten zijn alleen-lezen waardeobjecten, dus vervangen of aanpassen wordt niet ondersteund.
Serializer
Phasis\Ast\Serializer zet de getypeerde AST om naar arrays of JSON met de Phasis-typenamen en veldindeling, waarbij interne cachevelden van de parser worden verwijderd:
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
Dit is de indeling die bin/phasis --ast afdrukt.
EstreeSerializer
Phasis\Ast\EstreeSerializer zet de getypeerde AST om naar arrays in ESTree-indeling: ESTree-typenamen (volgens de afwijkingstabel hierboven), ESTree-veldnamen en -volgorde, aangemaakte ClassBody-, specifier- en source-Literal-nodes, en een start-byteoffset op elke node die uit broncode is geparseerd. Er zijn geen end-offsets, omdat nodes alleen beginposities opslaan, en string-Literal-nodes geen raw-tekst bevatten.
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() genereert een deterministisch overzicht met één regel per node (type plus name= / value= / op= / kind=-kenmerken, twee spaties inspringing per diepteniveau). De indeling is byte voor byte identiek aan het AST-overzicht dat de acorn-fixture in tests/Popular/acorn genereert, zodat dezelfde oracle beide parserpaden, dat van JS en deze PHP-export, controleert:
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 parseert een bestand (scriptdoel) en drukt de Serializer JSON-dump af met inspringing van twee spaties. Zie CLI voor de volledige lijst met opties.