5

Estoy creando un compilador con Lex y YACC (en realidad Flex y Bison). El lenguaje permite referencias directas ilimitadas a cualquier símbolo (como C#). El problema es que es imposible analizar el idioma sin saber qué es un identificador.¿Cómo implemento referencias directas en un compilador?

La única solución que conozco es leer toda la fuente, y luego hacer un análisis "ancho-primero", para que cosas de nivel superior como declaraciones de clase y declaraciones de funciones sean analizadas antes de las funciones que las usan. Sin embargo, esto requeriría una gran cantidad de memoria para archivos grandes, y sería difícil de manejar con YACC (tendría que crear gramáticas separadas para cada tipo de declaración/cuerpo). También tendría que escribir a mano el lexer (que no es un gran problema).

No me importa mucho la eficiencia (aunque sigue siendo importante), porque voy a reescribir el compilador en sí mismo una vez que lo termine, pero quiero que esa versión sea rápida (por lo tanto, si existe) son algunas técnicas generales rápidas que no se pueden hacer en Lex/YACC pero pueden hacerse a mano, por favor sugiérelas también). Entonces, en este momento, la facilidad de desarrollo es el factor más importante.

¿Hay alguna buena solución a este problema? ¿Cómo se hace esto generalmente en compiladores para lenguajes como C# o Java?

Respuesta

7

Es completamente posible analizarlo. Aunque existe una ambigüedad entre los identificadores y las palabras clave, Lex se las arreglará felizmente dando prioridad a las palabras clave.

No veo otros problemas. No necesita determinar si los identificadores son válidos durante la etapa de análisis sintáctico. Está construyendo un árbol de análisis sintáctico o un árbol de sintaxis abstracta (la diferencia es sutil, pero irrelevante para los fines de esta discusión) a medida que analiza. Después de eso, construye sus estructuras de tablas de símbolos anidados realizando un pase sobre el AST que generó durante el análisis. Luego haces otro pase sobre el AST para verificar que los identificadores utilizados sean válidos. Siga esto con uno o más análisis adicionales sobre el AST para generar el código de salida, o alguna otra estructura de datos intermedia ¡y listo!

EDITAR: Si desea ver cómo se hace, consulte el código fuente del compilador Mono C#. Esto está realmente escrito en C# en lugar de C o C++, pero usa el puerto .NET de Jay, que es muy similar a yacc.

+0

No tiene nada que ver con palabras clave. Es más como esto: es ABC (paquete AB). (Clase C), (paquete A). (Clase B). (Campo C), o (campo A). (Campo B). (Campo C), etc. – Zifre

+1

Luego se aplica el segundo párrafo de mi respuesta. No necesita saber eso para analizar. Tratar ''. como un operador en tu gramática. En sus pases AST, puede verificarlos contra la tabla de símbolos. – U62

+0

Bueno, supongo que tendré que hacer un árbol de análisis en lugar de un AST. Como dijiste, son diferentes. Si nadie viene con una mejor respuesta, lo aceptaré, pero preferiría no hacerlo de esta manera ... – Zifre

1

Una opción es tratar con las referencias avanzadas solo escaneando y almacenando tokens hasta que tocas algo que conoces como verdadero (algo así como la recuperación de error "modo de pánico"). Una vez que haya ejecutado el archivo completo, vuelva e intente analizar los bits que no se analizaron antes.

En cuanto a tener que escribir a mano el lexer; no, use lex para generar un analizador normal y simplemente lea de él a través de un calcetín escrito a mano que le permite regresar y alimentar el analizador desde un caché, así como también lo que lex hace.

En cuanto a hacer varias gramáticas, un poco de diversión con un preprocesador en el archivo yacc y usted debería ser capaz de hacer que todo fuera de la misma fuente original

+0

No me preocupa mucho escribir el lexer, no es tan difícil (podría ser ser un poco más fácil ya que mi lenguaje tiene una sangría similar a Python).Usar el preprocesador con YACC parece que podría funcionar, pero ¿hay alguna forma de cambiar el símbolo de inicio? – Zifre

+0

Re preprocesador con yacc, esa es exactamente la idea. defina la gramática completa sin definir explícitamente el símbolo de inicio y luego cambie un pequeño fragmento del archivo (a través de algo como #include o #define) para elegir el punto de inicio. Una forma de hacerlo sería Tener la regla de inicio de la forma "Root :: = MacroRule;" y reemplace MacroRule con lo que quiera para esta versión. – BCS

Cuestiones relacionadas