Pregunta

Estoy escribiendo un compilador para un motor de sombreado y todo funcionó bien hasta que llegué a la parte de análisis de declaraciones.

utilicé un árbol de sintaxis abstracta definido con clases para hacer todo el trabajo (para simplificar la verificación de tipos y la generación de código intermedio).entonces tengo una clase de antepasado ASTNode y todas las clases descendentes como ASTFloat, ASTExpression, ASTIdentifier etcétera..

En .y archivo puedo construir el AST de la manera común:

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

y funciona bastante bien, pero luego intenté generar declaraciones de un alcance (por ejemplo, el cuerpo de un si declaración) de esta manera:he usado una clase ASTStatements que tiene una lista de ASTNode* que debe ser completado por el analizador con cada declaración encontrada.

Entonces el enfoque sería algo similar a esto:

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

El problema es que el elemento debe inicializarse solo una vez por bloque de declaraciones pero no sé cómo hacerlo.Usando if ($$ == null) es un truco que probé pero no funciona porque yylval puede contener lo que sea hasta ese punto.

¿Cuál es la mejor/normal manera de manejar este tipo de situaciones usando Bison?

¿Fue útil?

Solución 3

He resuelto este problema mediante la generación no es una lista de declaraciones, pero un árbol degenerado. Por lo que el objeto de la clase en cuestión es:

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
}

usando reglas en .y de la siguiente manera:

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

Y de hecho esto se deja recursiva, permitiendo declaraciones a reducirse tan pronto como sea posible sin saturar la pila. He seguido el mismo enfoque para cualquier otro tipo de "lista de símbolos" que participan en mi idioma ..

Otros consejos

Trate de una gramática aumentada como la siguiente:

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

No estoy seguro si esto ayudará.

Hay varias razones para preferir reglas izquierda-recursivo para yacc, por un lado a continuación, puede reducir lo antes posible en la entrada.

En cualquier caso, cuando se hace eso, a continuación, puede utilizar un patrón de esta manera:

statements:                { $$ = new ... }
    | statements statement { /* now $1 and $2 do just what you want */ }
    ;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top