2009-10-31 11 views
0

Je suis en train d'écrire un compilateur pour un moteur d'ombrage et tout a bien fonctionné jusqu'à ce que j'aie atteint la partie d'analyse des instructions.Utilisation de bison pour analyser la liste des éléments

J'ai utilisé un arbre de syntaxe abstraite définie avec des classes à faire tout le travail (pour simplifier typage et génération de code intermédiaire) .. donc j'ai une classe ancêtre ASTNode et toutes les classes descendantes comme ASTFloat, ASTExpression, ASTIdentifier etc. .. sur

dans .y fichier, je suis en mesure de construire l'AST de la manière commune:

nexp: 
T_LPAR nexp T_RPAR { $$ = $2; } 
| nexp OP_PLUS nexp { $$ = new ASTBExpression('+', (ASTExpression*)$1, (ASTExpression*)$3); } 
| nexp OP_MINUS nexp { $$ = new ASTBExpression('-', (ASTExpression*)$1, (ASTExpression*)$3); } 
| nexp OP_TIMES nexp { $$ = new ASTBExpression('*', (ASTExpression*)$1, (ASTExpression*)$3); } 

et il fonctionne très bien, mais j'essayé de générer des déclarations o de cette façon: J'ai utilisé une classe ASTStatements qui a une liste de ASTNode* qui doit être remplie par l'analyseur avec chaque instruction rencontrée.

Donc, l'approche serait quelque chose de similaire à ceci:

statements: 
statement { if ($$ == null) $$ = new ASTStatements(); ((ASTStatements*)$$)->addStatement($1); } statements { $$->generateASM(); } 
; 

Le problème est que l'élément doit être initialisé une seule fois par bloc de déclarations, mais je ne sais pas comment le faire. L'utilisation de if ($$ == null) est un hack que j'ai essayé mais cela ne fonctionne pas car yylval peut contenir n'importe quoi jusqu'à ce point.

Quelle est la meilleure façon de gérer ce genre de situations avec Bison?

+0

avez-vous un% type statement_if etc. etc? –

+0

bien sûr, j'ai dû résoudre le problème en transformant ASTStatements * en une classe avec une partie gauche (ASTStatement *) et une partie droite (ASTStatements *). J'ai en fait transformé la liste en un arbre dégénéré .. – Jack

Répondre

1

Je résolu ce problème en générant pas une liste de déclarations, mais un arbre dégénéré. Ainsi, l'objet de classe en cause est:

ASTStatements 
{ 
    ASTStatements *m_next; 
    ASTStatement *m_statement; 

    .... 

    public: 
     ASTStatements(ASTStatement *statement) // used for last one 
     ASTStatements(ASTStatement *stat, ASTStatements *next) // used with a next one 
} 

en utilisant les règles de .y de la manière suivante:

statements: /* empty */ { $$ = null; } 
| statements statement { if ($1 == null) $$ = new ASTStatements($2); else $$ = new ASTStatements($2, (ASTStatements*)$1); } 

Et en effet, cela est laissé récursif, ce qui permet des déclarations à réduire le plus rapidement possible sans encombrer la empiler. J'ai suivi la même approche pour toute autre sorte de "liste de symboles" impliqués dans ma langue.

+0

J'aime vraiment l'idée de l'arbre dégénéré. – Vortico

1

Essayez une grammaire augmentée comme ce qui suit:

statements: statement { $$ = new ASTStatements(); 
         ((ASTStatements*)$$)->addStatement($1); }  
| statements statement { ((ASTStatements*)$$)->addStatement($2); } 

Je ne sais pas si cela vous aidera.

1

Il existe plusieurs raisons de préférer les règles récursives à gauche pour yacc, pour une chose que vous pouvez ensuite réduire le plus tôt possible dans l'entrée.

Dans tous les cas, lorsque vous faites cela, vous pouvez alors utiliser un modèle comme celui-ci:

statements:    { $$ = new ... } 
    | statements statement { /* now $1 and $2 do just what you want */ } 
    ;