Question

J'écris un compilateur pour un moteur d'ombrage et tous les a bien fonctionné jusqu'à ce que j'atteint les déclarations parsing partie.

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

Dans le fichier .y 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 de portée (par exemple le corps d'un si déclaration) ainsi: Je l'ai utilisé une ASTStatements de classe qui a une liste de ASTNode* qui doit être rempli par l'analyseur avec chaque énoncé rencontré.

Ainsi, 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. if ($$ == null) est un en utilisant bidouille j'ai essayé, mais ça ne fonctionne pas parce que yylval peut contenir tout jusqu'à ce point.

Quelle est la meilleure normale / façon de gérer ce genre de situations à l'aide Bison?

Était-ce utile?

La solution 3

Je l'ai 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 est impliqué:

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 pile. J'ai suivi la même approche pour tout autre type de « liste des symboles » impliqués dans ma langue ..

Autres conseils

Essayez une grammaire augmentée comme suit:

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

Je ne sais pas si cela vous aidera.

Il y a plusieurs raisons de préférer les règles récursif gauche pour yacc, pour une chose que vous pouvez 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 */ }
    ;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top