Domanda

Sto scrivendo un compilatore per un motore di shading e tutto ha funzionato bene finché non ho raggiunto la parte di analisi delle istruzioni.

Ho usato un albero sintattico astratto definito con classi per fare tutto il lavoro (per semplificare il controllo del tipo e la generazione di codice intermedio).quindi ho una classe di antenati ASTNode e tutte le classi discendenti piacciono ASTFloat, ASTExpression, ASTIdentifier e così via..

In .y file sono in grado di creare l'AST nel modo comune:

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); }

e funziona abbastanza bene, ma poi ho provato a generare istruzioni di un ambito (ad esempio il corpo di un Se dichiarazione) in questo modo:Ho usato una classe ASTStatements che ha un elenco di ASTNode* che deve essere riempito dal parser con ogni istruzione incontrata.

Quindi l'approccio sarebbe qualcosa di simile a questo:

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

Il problema è che l'elemento dovrebbe essere inizializzato solo una volta per blocco di istruzioni ma non so come farlo.Utilizzando if ($$ == null) è un trucco che ho provato ma non funziona perché yylval può contenere qualunque cosa fino a quel momento.

Qual è il modo normale/migliore per gestire questo tipo di situazioni utilizzando Bison?

È stato utile?

Soluzione 3

Ho risolto questo problema generando non un elenco di istruzioni ma un albero degenerato.Quindi l'oggetto classe coinvolto è:

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
}

utilizzando le regole in .y nel seguente modo:

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

E in effetti questo è ricorsivo a sinistra, consentendo di ridurre le istruzioni il prima possibile senza ingombrare lo stack.Ho seguito lo stesso approccio per qualsiasi altro tipo di "lista di simboli" coinvolta nella mia lingua..

Altri suggerimenti

Prova una grammatica aumentata come la seguente:

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

Non sono sicuro che questo possa essere d'aiuto.

Ci sono vari motivi per preferire le regole ricorsive a sinistra per yacc, per prima cosa puoi quindi ridurle il più presto possibile nell'input.

In ogni caso, quando lo fai, puoi utilizzare uno schema come questo:

statements:                { $$ = new ... }
    | statements statement { /* now $1 and $2 do just what you want */ }
    ;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top