Le design pattern Interpreter
Description
Le design pattern Interpreter représente sous la forme d’objets la grammaire d’un langage afin d’évaluer, en les interprétant, des expressions écrites dans ce langage.
Exemple
Nous voulons créer un petit moteur de recherche des véhicules basé sur la recherche par mot-clé dans la description des véhicules à l’aide d’expressions booléennes selon la grammaire définie ci-dessous :
expression ::= terme || mot-clé || (expression)
terme ::= facteur 'ou' facteur
facteur ::= expression 'et' expression
mot-clé ::= 'a'..'z','A'..'Z' {'a'..'z','A'..'Z'}*
Les symboles entre apostrophes sont des symboles terminaux. Les symboles non terminaux sont expression, terme, facteur et mot-clé. Le symbole de départ est expression.
Nous mettons en œuvre le design pattern Interpreter afin de pouvoir représenter toute expression répondant à cette grammaire sous la forme d’un arbre syntaxique constitué d’objets.
Un tel arbre n’est constitué que de symboles terminaux. Pour simplifier, nous considérons qu’un mot-clé constitue un symbole terminal en tant que chaîne de caractères.
L’expression (rouge ou gris) et récent et diesel va être traduite par l’arbre syntaxique de la figure 20.1.
Figure 20.1 - Arbre syntaxique correspondant à l’expression (rouge ou gris) et récent et diesel
L’évaluation d’un...
Structure
1. Diagramme de classes
La figure 20.3 détaille la structure générique du design pattern Interpreter.
Ce diagramme de classes montre qu’il existe deux types de sous-expression, à savoir :
-
Les éléments terminaux qui peuvent être des noms de variable, des entiers, des nombres réels.
-
Les opérateurs qui peuvent être binaires comme dans l’exemple, unaires (opérateur « - ») ou prenant plus d’arguments comme des fonctions.
Figure 20.3 - Structure du design pattern Interpreter
2. Participants
Les participants au design pattern Interpreter sont les suivants :
-
Expression (AbstractExpression) est une classe abstraite représentant tout type d’expression, c’est-à-dire tout nœud de l’arbre syntaxique.
-
OperateurAbstrait (AbstractOperateurBinaire) est également une classe abstraite. Elle décrit tout nœud de type opérateur, c’est-à-dire possédant des opérandes qui sont des sous-arbres de l’arbre syntaxique.
-
OperateurConcret1 et OperateurConcret2 (OperateurEt, OperateurOu) sont des implémentations d’OperateurAbstrait décrivant totalement la sémantique de l’opérateur et donc capables de l’évaluer.
-
ElementTerminal est une classe abstraite décrivant tout nœud correspondant à un élément terminal....
Domaines d’application
Le design pattern Interpreter est utilisé pour interpréter des expressions représentées sous la forme d’arbres syntaxiques. Il s’applique principalement dans les cas suivants :
-
La grammaire des expressions est simple.
-
L’évaluation n’a pas besoin d’être rapide.
Si la grammaire est complexe, il vaut mieux se tourner vers des analyseurs syntaxiques spécialisés.
Exemple en PHP
Nous fournissons le code complet d’un exemple en PHP qui non seulement permet l’évaluation d’un arbre syntaxique mais le construit également.
La construction de l’arbre syntaxique, appelée analyse syntaxique, est également répartie dans les classes, à savoir celles de la figure 20.2 sous la forme de méthodes de classe (méthodes statiques, préfixées du mot-clé static en PHP).
Le code source de la classe AbstractExpression est donné à la suite. La partie relative à l’évaluation se limite à la déclaration de la signature de la méthode evalue.
Les méthodes prochainJeton, analyse et parse sont dédiées à l’analyse syntaxique. La méthode analyse est utilisée pour parser une expression entière alors que parse est dédiée à l’analyse soit d’un mot-clé soit d’une expression mise entre parenthèses.
<?php
declare(strict_types=1);
namespace ENI\DesignPatterns\Interpreter;
abstract class AbstractExpression
{
protected static string $source;
protected static int $index;
protected static ?string $jeton;
protected static function prochainJeton(): void
{
while ((self::$index < strlen(self::$source)) &&
(self::$source[self::$index] == ' ')) {
self::$index++;
}
if (self::$index == strlen(self::$source)) {
self::$jeton = null;
} elseif ((self::$source[self::$index] == '(') ||
(self::$source[self::$index] == ')')) {
self::$jeton = substr(self::$source, self::$index, 1);
self::$index++;
} else {
...