Table de symboles Population après parsing; Bâtiment du compilateur

Après avoir créé l’arbre d’parsing syntaxique, je dois maintenant renseigner la table des symboles.

Je dois stocker des informations comme

Tapez, Scope, Offset, etc. pour les identifiants.

Maintenant, comment puis-je connaître le type et la scope des identificateurs, puisque tout ce que je sais, c’est la valeur du lexème et le numéro de ligne pour cet ID particulier (après parsing lexicale).

Comment j’ai eu à propos de la chose. Merci.

Maintenant, comment puis-je connaître le type et la scope des identificateurs, puisque tout ce que je sais, c’est la valeur du lexème et le numéro de ligne pour cet ID particulier (après parsing lexicale).

Comme EJP l’a mentionné, vous devez parcourir l’arbre d’parsing. Votre arborescence doit avoir été créée pour que vous puissiez effectuer une traversée dans l’ordre, en visitant chaque nœud dans le même ordre que celui dans lequel les instructions et les expressions du code source sont évaluées. Vos nœuds d’arborescence doivent également correspondre à une construction de langage spécifique, par exemple WhileStmtNode , MethodDeclNode , etc.

Supposons que je construis la table des symboles, parcourant l’arbre de manière récursive et que je sois entré dans un noeud de corps de méthode. Je pourrais avoir quelque chose comme ce qui suit:

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

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

Mettre à jour:

Maintenant, disons, je traverse l’arbre, chaque fois que je tombe sur un ID, je dois entrer la valeur dans la table des symboles avec le type, la scope et d’autres, disons pour la scope, je vérifie si je tombe sur ‘{‘ ou le nom de la fonction, mais comment puis-je savoir quel type d’identifiant est-ce.

Chaque nœud d’arbre doit contenir toutes les informations nécessaires pour la construction entière. Si vous utilisez un générateur d’parsingur, 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.getSsortingngValue()); :}; 

Ces productions correspondraient à Foo f; et ajoutez un noeud de déclaration de variable à l’arbre. Ce noeud encapsule deux noeuds identificateurs contenant 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 mise en correspondance d’un identifiant. Je suppose que vous connaissez 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 avez un arbre de syntaxe avec des nœuds comme celui-ci, vous avez toutes les informations dont vous avez besoin.

2ème mise à jour:

Peu importe que vous utilisiez un parsingur 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. C structs fera très bien.

si c’est un non-terminal, l’info est nommée Nonterminals name, et si c’est un terminal, c’est-à-dire un jeton, alors l’info dans le jeton, c’est-à-dire la valeur lexème, le nom du jeton et le numéro de ligne sont stockés

Vous devez avoir des nœuds spécialisés dans l’arborescence, par exemple, 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é sous la forme d’un nœud d’arborescence. Aucune autre information à stocker à ce sujet ne se trouve à côté des composants qui le composent, qui sont généralement eux-mêmes des non-terminaux. Vous ne stockeriez aucune information de jeton. Il existe très peu de cas où vous stockeriez la valeur de chaîne d’un lexeme: pour un identifiant et pour un littéral chaîne / boolean / integer.

Regardez cet exemple. Lors de la première réduction lorsque S est réduit à (S + F) , vous ajoutez un ParenExprNode à la racine de l’arborescence. Vous ajoutez également un AddExprNode tant qu’enfant de ParenExprNode . Cette logique doit être codée en dur dans votre parsingur lors de l’application d’une réduction par 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; } 

Dans l’étape suivante, la parenthèse gauche est supprimée de l’entrée. Ensuite, S est au sumt de la stack et la règle 1 la réduit à F Comme 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; } 

Etc…

tout ce que je sais, c’est la valeur du lexème et le numéro de ligne pour cet ID particulier

Ce n’est pas vrai. Vous savez où il est déclaré dans l’arbre d’parsing, qui vous dit tout ce dont vous avez besoin. Vous faites cette étape en traitant l’arbre d’parsing.