Analizador
Phasis ofrece su analizador JavaScript como PHP API independiente. Puede analizar texto fuente para obtener un AST tipado, recorrerlo y exportarlo como arrays con forma de ESTree o JSON, sin crear un Engine ni ejecutar nada.
parseSource
public static function parseSource(string $source, string $sourceType = 'script'): Program
El punto de entrada de una sola llamada en Phasis\Parser\Parser. Analiza $source y devuelve el nodo raíz 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 selecciona el objetivo gramatical, cuyo nombre coincide con el de la misma opción en los analizadores de JS:
| Valor | Objetivo |
|---|---|
script |
Script clásico. import/export de nivel superior son errores de sintaxis; await es un identificador |
module |
Módulo ES. Estricto de forma implícita; se pueden analizar import/export y await de nivel superior |
Cualquier otro valor produce \InvalidArgumentException.
Para un control más preciso (preactivar el modo estricto en un contexto de evaluación directa, leer la directiva //# sourceURL), cree el analizador directamente: new Parser($source) más setModuleMode(), setStrictMode() y parse().
Nodos
Todas las clases AST extienden Phasis\Ast\Node y se encuentran bajo Phasis\Ast\{Declaration, Expression, Pattern, Statement}. Los nodos son objetos de valor simples y de solo lectura:
$node->type()devuelve la cadena de tipo del nodo, que corresponde al nombre base de la clase ('BinaryExpression','ArrowFunction', ...).$node->locationes unPhasis\Lexer\SourceLocationconline(base 1),column(base 0) yoffset(desplazamiento en bytes). Solo se almacenan las posiciones iniciales; los nodos no incluyen desplazamiento final.- Todos los demás campos públicos son los hijos y atributos del nodo (
$binary->left,$binary->operator,$binary->right).
La mayoría de los nombres de tipo coinciden con ESTree. Estas son las diferencias:
| Tipo de Phasis | Tipo de ESTree |
|---|---|
ArrowFunction |
ArrowFunctionExpression |
TaggedTemplate |
TaggedTemplateExpression |
ClassMethod |
MethodDefinition |
ClassProperty |
PropertyDefinition |
AssignmentProperty |
Property (dentro de ObjectPattern) |
ExportDeclaration |
ExportNamedDeclaration / ExportDefaultDeclaration / ExportAllDeclaration |
ImportSpecifier |
ImportSpecifier / ImportDefaultSpecifier / ImportNamespaceSpecifier |
Algunas formas también difieren: los cuerpos de clase son un array simple de elementos en el nodo de clase (sin envoltorio ClassBody); los nombres de importación/exportación, las etiquetas de instrucciones y los nombres de FunctionExpression se almacenan como cadenas en lugar de nodos Identifier anidados; y super se analiza como un Identifier llamado super (no existe una clase de nodo Super). Phasis\Ast\EstreeSerializer transforma todo esto al formato ESTree.
Errores de sintaxis
La entrada no válida produce Phasis\Exceptions\SyntaxError (normalmente su subclase Phasis\Parser\ParseError). El mensaje de excepción termina con la posición, y la ubicación estructurada está disponible en la excepción:
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
}
Recorrido
Phasis\Ast\Walker recorre un árbol en profundidad con devoluciones de llamada de entrada y salida, al estilo de estraverse:
public static function walk(Node $node, callable $enter, ?callable $leave = null): void
Ambas devoluciones de llamada reciben (Node $node, ?Node $parent). Si $enter devuelve Walker::SKIP, se omiten los hijos del nodo; aun así, se ejecuta su propio $leave.
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;
}
});
El recorrido es de solo lectura. Los nodos son objetos de valor de solo lectura, por lo que no se pueden reemplazar ni modificar.
Serializador
Phasis\Ast\Serializer convierte el AST tipado en arrays o JSON usando los nombres de tipo y la disposición de campos de Phasis, y elimina los campos de caché internos del analizador:
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
Este es el formato que imprime bin/phasis --ast.
EstreeSerializer
Phasis\Ast\EstreeSerializer convierte el AST tipado en arrays con forma de ESTree: nombres de tipo de ESTree (según la tabla de diferencias anterior), nombres y orden de campos de ESTree, nodos ClassBody / de especificador / de origen-Literal sintetizados y un desplazamiento en bytes start en cada nodo analizado a partir del código fuente. No hay desplazamientos end porque los nodos solo almacenan posiciones iniciales, y los nodos de cadena Literal no contienen texto 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() genera un resumen determinista de una línea por nodo (tipo y atributos name= / value= / op= / kind=, con sangría de dos espacios por nivel). El formato es idéntico byte por byte al resumen AST que produce el accesorio de prueba de acorn en tests/Popular/acorn, así que el mismo oráculo evalúa tanto la ruta del analizador JS como esta exportación de 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 analiza un archivo (objetivo Script) e imprime el volcado Serializer JSON con sangría de dos espacios. Consulte CLI para ver la referencia completa de opciones.