Population de la table des symboles après analyse ;Construction du compilateur
-
13-12-2019 - |
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.
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.