Pular para o conteúdo

Analisador sintático

Phasis disponibiliza seu analisador JavaScript como um PHP API independente. Você pode analisar o texto-fonte em uma AST tipada, percorrê-la e exportá-la como arrays no formato ESTree ou JSON, sem construir um Engine nem executar nada.

parseSource

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

O ponto de entrada de chamada única em Phasis\Parser\Parser. Analisa $source e retorna o nó raiz 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 seleciona o objetivo gramatical, com nomes correspondentes à mesma opção nos analisadores JS:

Valor Objetivo
script Script clássico. import/export no nível superior são erros de sintaxe; await é um identificador
module Módulo ES. Modo estrito implícito; import/export e await no nível superior são analisados

Qualquer outro valor lança \InvalidArgumentException.

Para um controle mais preciso (ativar previamente o modo estrito para um contexto de eval direto ou ler a pragma //# sourceURL), construa o analisador diretamente: new Parser($source) mais setModuleMode(), setStrictMode() e parse().

Nós

Toda classe AST estende Phasis\Ast\Node e fica em Phasis\Ast\{Declaration, Expression, Pattern, Statement}. Os nós são objetos de valor somente para leitura:

  • $node->type() retorna a string do tipo do nó, que corresponde ao nome da classe sem o namespace ('BinaryExpression', 'ArrowFunction', ...).
  • $node->location é um Phasis\Lexer\SourceLocation com line (baseado em 1), column (baseado em 0) e offset (deslocamento em bytes). Somente as posições iniciais são armazenadas; os nós não têm deslocamento final.
  • Todos os outros campos públicos são filhos e atributos do nó ($binary->left, $binary->operator, $binary->right).

A maioria dos nomes de tipo corresponde ao ESTree. Diferenças:

Tipo Phasis Tipo ESTree
ArrowFunction ArrowFunctionExpression
TaggedTemplate TaggedTemplateExpression
ClassMethod MethodDefinition
ClassProperty PropertyDefinition
AssignmentProperty Property (dentro de ObjectPattern)
ExportDeclaration ExportNamedDeclaration / ExportDefaultDeclaration / ExportAllDeclaration
ImportSpecifier ImportSpecifier / ImportDefaultSpecifier / ImportNamespaceSpecifier

Algumas formas também diferem: os corpos de classe são arrays simples de elementos no nó da classe (sem wrapper ClassBody); nomes de importação/exportação, rótulos de instruções e nomes FunctionExpression são armazenados como strings, e não como nós Identifier aninhados; e super é analisado como um Identifier denominado super (não existe uma classe de nó Super). Phasis\Ast\EstreeSerializer reformata tudo isso para o formato ESTree.

Erros de sintaxe

Uma entrada inválida lança Phasis\Exceptions\SyntaxError (geralmente sua subclasse Phasis\Parser\ParseError). A mensagem da exceção termina com a posição, e a localização estruturada está disponível na exceção:

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
}

Percursor

Phasis\Ast\Walker percorre uma árvore em profundidade, com callbacks de entrada e saída, no estilo do estraverse:

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

Ambos os callbacks recebem (Node $node, ?Node $parent). Retornar Walker::SKIP de $enter ignora os filhos do nó; ainda assim, o próprio $leave do nó é executado.

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

O percurso é somente para leitura. Os nós são objetos de valor somente para leitura, portanto não há suporte para substituí-los ou modificá-los.

Serializador

Phasis\Ast\Serializer converte a AST tipada em arrays ou JSON usando os nomes de tipo e a disposição de campos de Phasis, removendo os campos de cache internos do parser:

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

Este é o formato que bin/phasis --ast imprime.

EstreeSerializer

Phasis\Ast\EstreeSerializer converte a AST tipada em arrays no formato ESTree: nomes de tipo ESTree (conforme a tabela de divergências acima), nomes e ordenação de campos ESTree, nós ClassBody / especificador / origem-Literal sintetizados e um deslocamento em bytes start em cada nó analisado a partir da origem. Não há deslocamentos end, pois os nós armazenam apenas posições iniciais, e os nós de string Literal não contêm texto 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() emite um resumo determinístico, com uma linha por nó (tipo mais atributos name= / value= / op= / kind=, indentação de dois espaços por nível). O formato é idêntico byte a byte ao resumo da AST produzido pelo fixture do acorn em tests/Popular/acorn, portanto o mesmo oráculo verifica tanto o caminho do parser JS quanto esta exportação 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 analisa um arquivo (objetivo de script) e imprime o despejo Serializer JSON com indentação de dois espaços. Consulte CLI para ver a referência completa das opções.