Към съдържанието

Парсер

Phasis предоставя своя парсер JavaScript като самостоятелен PHP API. Можете да парсирате изходния текст в типизиран AST, да го обходите и да го експортирате като масиви във формата ESTree или JSON, без да създавате Engine или да изпълнявате каквото и да било.

parseSource

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

Едно извикване към входната точка на Phasis\Parser\Parser. Парсира $source и връща коренния възел 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 избира граматичната цел, наименувана като същата опция в парсерите JS:

Стойност Цел
script класически скрипт. import/export на най-горно ниво са синтактични грешки, await е идентификатор
module ES модул. Строг режим по подразбиране, парсират се import/export и await на най-горно ниво

Всяка друга стойност предизвиква \InvalidArgumentException.

За по-прецизен контрол (предварително задаване на строг режим за контекст на директен eval, прочитане на директивата //# sourceURL) създайте парсера директно: new Parser($source) плюс setModuleMode(), setStrictMode() и parse().

Възли

Всеки AST клас наследява Phasis\Ast\Node и се намира под Phasis\Ast\{Declaration, Expression, Pattern, Statement}. Възлите са обикновени обекти със стойности само за четене:

  • $node->type() връща низа за типа на възела, който е базовото име на класа ('BinaryExpression', 'ArrowFunction', ...).
  • $node->location е Phasis\Lexer\SourceLocation с line (започва от 1), column (започва от 0) и offset (отместване в байтове). Съхраняват се само началните позиции; възлите нямат крайно отместване.
  • Всички останали публични полета са дъщерните възли и атрибутите на възела ($binary->left, $binary->operator, $binary->right).

Повечето имена на типове съвпадат с ESTree. Разликите:

Тип Phasis Тип ESTree
ArrowFunction ArrowFunctionExpression
TaggedTemplate TaggedTemplateExpression
ClassMethod MethodDefinition
ClassProperty PropertyDefinition
AssignmentProperty Property (в ObjectPattern)
ExportDeclaration ExportNamedDeclaration / ExportDefaultDeclaration / ExportAllDeclaration
ImportSpecifier ImportSpecifier / ImportDefaultSpecifier / ImportNamespaceSpecifier

Някои форми също се различават: телата на класовете са обикновен масив от елементи във възела на класа (без обвивка ClassBody), имената за import/export, етикетите на операторите и имената FunctionExpression се съхраняват като низове, а не като вложени възли Identifier, и super се парсира като Identifier с име super (няма клас възел Super). Phasis\Ast\EstreeSerializer преобразува всичко това във формата ESTree.

Синтактични грешки

Невалидният вход предизвиква Phasis\Exceptions\SyntaxError (обикновено неговия подклас Phasis\Parser\ParseError). Съобщението за изключението завършва с позицията, а структурираното местоположение е достъпно в изключението:

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
}

Обхождач

Phasis\Ast\Walker обхожда дървото в дълбочина с обратни извиквания при влизане и излизане, по модела на estraverse:

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

И двете обратни извиквания получават (Node $node, ?Node $parent). Връщането на Walker::SKIP от $enter пропуска дъщерните възли на възела; неговото собствено $leave все пак се изпълнява.

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

Обхождането е само за четене. Възлите са обекти със стойности само за четене, така че замяна или промяна не се поддържа.

Сериализатор

Phasis\Ast\Serializer преобразува типизираното AST в масиви или JSON, като използва имената на типовете и подредбата на полетата от Phasis и премахва вътрешните за парсера полета от кеша:

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

Това е форматът, който bin/phasis --ast отпечатва.

EstreeSerializer

Phasis\Ast\EstreeSerializer вместо това преобразува типизираното AST в масиви със структурата на ESTree: имена на типове от ESTree (според таблицата с отклонения по-горе), имена и подредба на полетата от ESTree, синтезирани възли ClassBody / specifier / source-Literal и отместване в байтове start за всеки възел, парснат от изходния код. Няма отмествания end, защото възлите съхраняват само началните позиции, а низовите възли Literal нямат текст 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() извежда детерминистично обобщение с по един ред за възел (тип плюс атрибути name= / value= / op= / kind=, с отстъп от два интервала за всяко ниво). Форматът е идентичен до байт с обобщението на AST, което acorn fixture в tests/Popular/acorn генерира, така че същият oracle проверява както пътя на парсера JS, така и този експорт 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 парсва файл (с цел script) и отпечатва дъмпа Serializer JSON с отстъп от два интервала. Вижте CLI за пълния списък с флагове.