Aller au contenu

Analyseur syntaxique

Phasis expose son analyseur JavaScript sous la forme d’un PHP API autonome. Vous pouvez analyser du code source pour obtenir un AST typé, le parcourir et l’exporter sous forme de tableaux compatibles avec ESTree ou de JSON, sans construire de Engine ni rien exécuter.

parseSource

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

Le point d’entrée qui effectue l’opération en un seul appel sur Phasis\Parser\Parser. Analyse $source et renvoie le nœud racine 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 sélectionne l’objectif grammatical, nommé d’après l’option équivalente dans les analyseurs JS :

Valeur Objectif
script Script classique. Les import/export au niveau supérieur sont des erreurs de syntaxe ; await est un identifiant
module Module ES. Strict implicitement ; import/export et await au niveau supérieur sont analysés

Toute autre valeur déclenche \InvalidArgumentException.

Pour un contrôle plus fin (activer le mode strict à l’avance pour un contexte d’évaluation directe, lire le pragma //# sourceURL), construisez directement l’analyseur : new Parser($source) ainsi que setModuleMode(), setStrictMode() et parse().

Nœuds

Chaque classe AST étend Phasis\Ast\Node et se trouve sous Phasis\Ast\{Declaration, Expression, Pattern, Statement}. Les nœuds sont de simples objets valeur en lecture seule :

  • $node->type() renvoie la chaîne de type du nœud, qui correspond au nom de base de sa classe ('BinaryExpression', 'ArrowFunction', ...).
  • $node->location est un Phasis\Lexer\SourceLocation avec line (à partir de 1), column (à partir de 0) et offset (décalage en octets). Seules les positions de début sont stockées ; les nœuds ne comportent pas de décalage de fin.
  • Tous les autres champs publics sont les enfants et attributs du nœud ($binary->left, $binary->operator, $binary->right).

La plupart des noms de type correspondent à ceux d’ESTree. Voici les différences :

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

Quelques formes diffèrent également : les corps de classe sont un simple tableau d’éléments sur le nœud de classe (sans wrapper ClassBody) ; les noms d’import/export, les étiquettes d’instruction et les noms FunctionExpression sont stockés sous forme de chaînes plutôt que de nœuds Identifier imbriqués ; et super est analysé comme un Identifier nommé super (il n’existe pas de classe de nœud Super). Phasis\Ast\EstreeSerializer remodèle tout cela selon le format ESTree.

Erreurs de syntaxe

Une entrée invalide déclenche Phasis\Exceptions\SyntaxError (généralement sa sous-classe Phasis\Parser\ParseError). Le message d’exception se termine par la position, et l’exception contient l’emplacement structuré :

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
}

Parcoureur

Phasis\Ast\Walker parcourt un arbre en profondeur, avec des rappels à l’entrée et à la sortie, selon le modèle d’estraverse :

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

Les deux rappels reçoivent (Node $node, ?Node $parent). Renvoyer Walker::SKIP depuis $enter ignore les enfants du nœud ; le rappel $leave du nœud lui-même est néanmoins exécuté.

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

Le parcours est en lecture seule. Les nœuds sont des objets valeur en lecture seule ; il n’est donc pas possible de les remplacer ni de les modifier.

Sérialiseur

Phasis\Ast\Serializer convertit l’AST typé en tableaux ou en JSON en utilisant les noms de type et la structure des champs de Phasis, après suppression des champs de cache internes à l’analyseur :

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

C’est le format affiché par bin/phasis --ast.

EstreeSerializer

Phasis\Ast\EstreeSerializer convertit plutôt l’AST typé en tableaux au format ESTree : noms de type ESTree (selon le tableau des différences ci-dessus), noms et ordre des champs ESTree, nœuds ClassBody / spécificateur / source-Literal synthétisés, et décalage en octets start pour chaque nœud analysé depuis le code source. Il n’y a pas de décalages end, car les nœuds ne stockent que les positions de début ; les nœuds de chaîne Literal ne contiennent pas de texte 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() produit un résumé déterministe avec une ligne par nœud (type et attributs name= / value= / op= / kind=, indentation de deux espaces par niveau). Le format est identique octet pour octet au résumé AST produit par la fixture acorn dans tests/Popular/acorn ; le même oracle sert donc à vérifier le parcours d’analyse JS et cet export 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 analyse un fichier (objectif Script) et affiche le dump Serializer JSON avec une indentation de deux espaces. Consultez CLI pour la référence complète des options.