Question

Après avoir créé l'arborescence d'analyse, je dois maintenant remplir la table des symboles.

Je dois stocker des informations comme

Type, Portée, Décalage, etc. pour les identifiants.

Maintenant, comment puis-je connaître le type et la portée des identifiants, puisque tout ce que je sais, c'est la valeur du lexème et le numéro de ligne pour cet identifiant particulier (après analyse lexicale).

Comment puis-je gérer tout ça ?merci.

Était-ce utile?

La solution

Maintenant, comment puis-je connaître le type, la portée des identifiants, car tout ce que je sais est la valeur lexée et le numéro de ligne pour cet identifiant particulier (après analyse lexicale).

Comme EJP l'a mentionné, vous devez parcourir l'arborescence d'analyse.Votre arbre doit avoir été créé de manière à ce que vous puissiez effectuer un parcours dans l'ordre, en visitant chaque nœud dans le même ordre dans lequel les instructions et expressions du code source sont évaluées.Vos nœuds d'arborescence doivent également correspondre à une construction de langage spécifique, par ex. WhileStmtNode, MethodDeclNode, etc.

Supposons que je construis la table des symboles, en parcourant récursivement l’arborescence et que je viens d’entrer un nœud de corps de méthode.J'aurais peut-être quelque chose comme ceci :

public void doAction(MethodBodyNode methodBody) {
    currScope = 2;
    methodBody.getExpr().applyAction(this);
    currScope = 2;
}

Je garde une variable globale pour gérer la portée.Chaque fois que j'entre dans un bloc dont la portée change, j'incrémente currScope.De même, je maintiendrais currClass et currMethod variables à stocker avec le nom du symbole, le type, le décalage, etc.pour les phases ultérieures.

Mise à jour:

Dis maintenant, je traverse l'arbre, chaque fois que je rencontre un identifiant, je devrais saisir la table de la valeur à symbole avec le type, la portée et les autres, disons pour la portée que je vérifie si je rencontre '{' ou nom de fonction, Mais comment puis-je savoir quel type d'identification est-ce.

Chaque nœud d'arborescence doit contenir toutes les informations nécessaires pour l'ensemble de la construction.Si vous utilisez un générateur d'analyseur, comme CUP ou Bison, vous pouvez spécifier comment construire l'arborescence dans les actions de grammaire.Par exemple.

variableDeclaration::= identifier:i identifier:i2 SEMICOLON {: RESULT = new VarDeclNode(i, i2, null); :};
identifier::= ID:i {: RESULT = new IdNode(i.getLineNum(), i.getCharNum(), i.getStringValue()); :};

Ces productions correspondraient Foo f; et ajoutez un nœud de déclaration de variable à l'arborescence.Ce nœud encapsule deux nœuds d'identification qui contiennent le numéro de ligne, le numéro de caractère et la valeur de chaîne du lexème.Le premier nœud identifiant est le type et le second est le nom de la variable. ID est un symbole de terminal renvoyé par le lexer lors de la correspondance avec un identifiant.Je suppose que vous êtes familier avec cela dans une certaine mesure.

public class VarDeclNode extends StmtNode {

    private IdNode id;
    private IdNode type;
    private ExprNode expr;

    public VarDeclNode(IdNode id, IdNode type, ExprNode expr) {
        super();
        this.id = id;
        this.type = type;
        this.expr = expr;
    }

}

Lorsque vous disposez d’un arbre syntaxique avec des nœuds comme celui-ci, vous disposez de toutes les informations dont vous avez besoin.

2ème mise à jour :

Peu importe que vous utilisiez un analyseur personnalisé ou généré, il existe un point distinct où vous ajoutez un nœud dans l'arborescence lors de la mise en correspondance d'une production.Et peu importe la langue que vous utilisez.Les structures C feront très bien l’affaire.

S'il s'agit d'un non terminal, les informations ont un nom non terminal, et s'il s'agit d'un terminal, c'est-à-direun jeton, puis les informations dans le jeton, c'est-à-direla valeur lexème, le nom de jeton et le numéro de ligne sont stockés

Vous devez avoir des nœuds spécialisés dans l'arborescence, par ex.ClassNode, TypeNode, MethodDeclNode, IfStmtNode, ExprNode.Vous ne pouvez pas simplement stocker un type de nœud et y placer des non-terminaux et des terminaux.Un non-terminal est représenté comme un nœud d'arborescence, il n'y a aucune autre information à stocker à son sujet en dehors des parties qui le composent, qui sont généralement elles-mêmes des non-terminaux.Vous ne stockeriez aucune information de jeton.Il n'y a que quelques cas où vous stockeriez réellement la valeur de chaîne d'un lexème :pour un identifiant et pour un littéral chaîne/booléen/entier.

Jettes un coup d'oeil à ce exemple.Lors de la première réduction lorsque S est réduit à (S + F), vous ajoutez un ParenExprNode à la racine de l'arbre.Vous ajoutez également un AddExprNode en tant qu'enfant du ParenExprNode.Cette logique doit être codée en dur dans votre analyseur lors de l'application d'une réduction selon la règle 2 de la grammaire.

L'arbre:

    ExprNode (root)
       |
  ParenExprNode
       |
   AddExprNode
   /         \
ExprNode   ExprNode

Le code:

struct ExprNode { void* exprNode; };
struct ParenExprNode { void* exprNode; };
struct AddExprNode { void* op1, * op2; };
struct IdNode { char* val; int line; int charNum; };
struct IntLiteralNode { int val; int line; int charNum; };

void reduce_rule_2(ExprNode* expr) {

    //remove stack symbols

    //create nodes
    struct ParenExprNode* parenExpr = malloc(sizeof(struct ParenExprNode));
    struct AddExprNode* addExpr = malloc(sizeof(struct AddExprNode));
    addExpr->op1 = malloc(sizeof(struct ExprNode));
    addExpr->op2 = malloc(sizeof(struct ExprNode));

    //link them
    parenExpr->exprNode = (void*)addExpr;
    expr->exprNode = (void*)parenExpr;
}

À l'étape suivante, la parenthèse gauche est supprimée de l'entrée.Après, S est au sommet de la pile et il est réduit à F par la règle 1.Depuis F est le non-terminal d'un identifiant, il est représenté par IdNode.

L'arbre:

    ExprNode
       |
  ParenExprNode
       |
   AddExprNode
   /         \
ExprNode   ExprNode
   |
 IdNode

Le code:

reduce_rule_2(addExpr->op1);

void reduce_rule_1(ExprNode* expr) {
    //reduce stack symbols
    struct IdNode* id = malloc(sizeof(struct IdNode));
    id->val = parser_matched_text();
    id->lineNum = parser_line_num();
    id->charNum = parser_char_num();
    expr->exprNode = (void*)id;
}

Et ainsi de suite...

Autres conseils

Tout ce que je sais, c'est la valeur lexeme et le numéro de ligne de cet identifiant particulier

Ce n'est pas vrai.Vous savez où il est déclaré dans l'arbre d'analyse, qui vous dit tout ce dont vous avez besoin.Vous faites cette étape par Traitement l'arbre d'analyse.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top