Je suis en train de créer mon propre langage de programmation, ce que je fais à des fins d'apprentissage. J'ai déjà écrit le lexer et un analyseur de descente récursif pour un sous-ensemble de ma langue (je supporte actuellement les expressions mathématiques, telles que + - * /
et les parenthèses). L'analyseur me rend un arbre de syntaxe abstraite, sur lequel j'appelle la Evaluate
méthode pour obtenir le résultat de l'expression. Tout fonctionne bien. Voici approximativement ma situation actuelle (exemples de code en C #, bien que cela soit à peu près indépendant du langage):
public abstract class Node
{
public abstract Double Evaluate();
}
public class OperationNode : Node
{
public Node Left { get; set; }
private String Operator { get; set; }
private Node Right { get; set; }
public Double Evaluate()
{
if (Operator == "+")
return Left.Evaluate() + Right.Evaluate();
//Same logic for the other operators
}
}
public class NumberNode : Node
{
public Double Value { get; set; }
public Double Evaluate()
{
return Value;
}
}
Cependant, je voudrais découpler l'algorithme des nœuds d'arbre parce que je veux appliquer le principe ouvert / fermé, donc je n'ai pas à rouvrir chaque classe de nœuds lorsque je veux implémenter la génération de code par exemple. J'ai lu que le modèle de visiteur est bon pour cela. J'ai une bonne compréhension du fonctionnement du modèle et de l'utilisation de la double répartition. Mais en raison de la nature récursive de l'arbre, je ne sais pas comment je dois l'approcher. Voici à quoi ressemblerait mon visiteur:
public class AstEvaluationVisitor
{
public void VisitOperation(OperationNode node)
{
// Here is where I operate on the operation node.
// How do I implement this method?
// OperationNode has two child nodes, which may have other children
// How do I work the Visitor Pattern around a recursive structure?
// Should I access children nodes here and call their Accept method so they get visited?
// Or should their Accept method be called from their parent's Accept?
}
// Other Visit implementation by Node type
}
C'est donc mon problème. Je veux y faire face immédiatement alors que ma langue ne supporte pas beaucoup de fonctionnalités pour éviter d'avoir un problème plus important plus tard.
Je n'ai pas posté ceci sur StackOverflow car je ne veux pas que vous fournissiez une implémentation. Je veux seulement que vous partagiez des idées et des concepts que j'aurais pu manquer et comment je devrais aborder cela.
la source
Réponses:
C'est à l'implémentation du visiteur de décider s'il faut visiter les nœuds enfants et dans quel ordre. C'est tout l'intérêt du modèle de visiteur.
Afin d'adapter le visiteur à plus de situations, il est utile (et assez courant) d'utiliser des génériques comme celui-ci (c'est Java):
Et une
accept
méthode ressemblerait à ceci:Cela permet de passer des paramètres supplémentaires au visiteur et d'en récupérer un résultat. Ainsi, l'évaluation de l'expression peut être implémentée comme ceci:
Le
accept
paramètre de méthode n'est pas utilisé dans l'exemple ci-dessus, mais croyez-moi: il est très utile d'en avoir un. Par exemple, il peut s'agir d'une instance de Logger à laquelle signaler des erreurs.la source
J'ai déjà implémenté le modèle de visiteur sur un arbre récursif.
Ma structure de données récursive particulière était extrêmement simple - seulement trois types de nœuds: le nœud générique, un nœud interne qui a des enfants et un nœud feuille qui a des données. C'est beaucoup plus simple que ce que j'attends de votre AST, mais peut-être que les idées peuvent évoluer.
Dans mon cas, je n'ai délibérément pas laissé l'accepter d'un nœud avec des enfants appeler Accept sur ses enfants, ou appeler visiteur.Visiter (enfant) à partir de Accept. Il est de la responsabilité de l'implémentation correcte du membre "Visit" du visiteur de déléguer les Accepte aux enfants du nœud visité. J'ai choisi cette voie parce que je voulais permettre à différentes implémentations de visiteurs de pouvoir décider de l'ordre des visites indépendamment de la représentation arborescente.
Un avantage secondaire est qu'il n'y a presque aucun artefact du modèle Visitor à l'intérieur de mes nœuds d'arbre - chaque "Accept" appelle simplement "Visit" sur le visiteur avec le type de béton correct. Cela facilite la localisation et la compréhension de la logique de visite, tout est à l'intérieur de l'implémentation du visiteur.
Pour plus de clarté, j'ai ajouté un pseudocode C ++ - ish. D'abord les nœuds:
Et le visiteur:
la source
allow different Visitor implementations to be able to decide the order of visitation
. Très bonne idée.Vous travaillez le modèle visiteur autour d'une structure récursive de la même manière que vous feriez n'importe quoi d'autre avec votre structure récursive: en visitant les nœuds de votre structure récursivement.
la source