¿Cómo realiza un lenguaje habilitado para macros un seguimiento del código fuente para su depuración?

StackOverflow https://stackoverflow.com/questions/3214603

Pregunta

Esta es una pregunta más teórica sobre macros (creo).Sé que las macros toman código fuente y producen código objeto sin evaluarlo, lo que permite a los programadores crear estructuras sintácticas más versátiles.Si tuviera que clasificar estos dos sistemas de macros, diría que están la macro "estilo C" y la macro "estilo Lisp".

Parece que depurar macros puede ser un poco complicado porque en tiempo de ejecución, el código que realmente se ejecuta difiere del código fuente.

¿Cómo realiza el depurador un seguimiento de la ejecución del programa en términos del código fuente preprocesado?¿Existe un "modo de depuración" especial que deba configurarse para capturar datos adicionales sobre la macro?

En C, puedo entender que establecerías un interruptor de tiempo de compilación para la depuración, pero ¿cómo lo haría un lenguaje interpretado, como algunas formas de Lisp?

Me disculpo por no probar esto, pero la cadena de herramientas lisp requiere más tiempo del que tengo que dedicar para resolverlo.

¿Fue útil?

Solución

No creo que haya una diferencia fundamental entre las macros de "estilo C" y "estilo Lisp" en la forma en que se compilan.Ambos transforman la fuente antes de que el compilador la vea.La gran diferencia es que las macros de C usan el preprocesador de C (un lenguaje secundario más débil que se utiliza principalmente para la simple sustitución de cadenas), mientras que las macros de Lisp están escritas en el propio Lisp (y por lo tanto pueden hacer cualquier cosa).

(Como un aparte:Hace tiempo que no veo un Lisp no compilado…ciertamente no desde principios de siglo.Pero en todo caso, ser interpretado parece hacer que el problema de depuración de macros sea más fácil, no más difícil, ya que se dispone de más información).

Estoy de acuerdo con miguel:No he visto ningún depurador para C que maneje macros.El código que utiliza macros se transforma antes de que suceda algo.El modo "depuración" para compilar código C generalmente solo significa que almacena funciones, tipos, variables, nombres de archivos, etc. - No creo que ninguno de ellos almacene información sobre macros.

  • Para depurar programas que usar macros, Lisp es más o menos lo mismo que C aquí:Su depurador ve el código compilado, no la aplicación macro.Por lo general, las macros se mantienen simples y se depuran independientemente antes de su uso, para evitar la necesidad de esto, al igual que C.

  • Para depurar las macrosellos mismos, antes de ir y usarlo en algún lugar, LISP tiene características que lo hacen más fácil que en C, por ejemplo, el Reply ymacroexpand-1 (Aunque en C obviamente hay una forma de macroexpand un archivo completo, plenamente, a la vez).Puede ver el antes y después de una macroexpansión, justo en su editor, cuando lo escriba.

No recuerdo ninguna vez que me encontré con una situación en la que la depuración en una definición macro en sí misma habría sido útil.O es un error en la definición de la macro, en cuyo caso macroexpand-1 aísla el problema inmediatamente, o es un error debajo de eso, en cuyo caso las funciones de depuración normales funcionan bien y no me importa que haya ocurrido una macroexpansión entre dos cuadros de mi pila de llamadas.

Otros consejos

en lispworks Los desarrolladores pueden usar el stepper tool .

LispWorks proporciona un paso a paso, donde uno puede pasar por completo proceso de expansión macro .

Realmente debe analizar el tipo de soporte que racket tiene para el código de depuración con macros. Este soporte tiene dos aspectos, como menciona Ken. Por un lado, está el problema de la depuración de macros: en común Lisp la mejor manera de hacerlo es simplemente ampliar las formas macro. Con el CPP, la situación es similar, pero más primitiva, ejecutará el código a través de solo la expansión del CPP e inspeccionar el resultado. Sin embargo, ambos son insuficientes para las macros más involucradas, y esta fue la motivación para tener una macro Debugger en Racket: le muestra la expansión de la sintaxis Pasos uno por uno, con indicaciones adicionales basadas en GUI para cosas como identificadores encuadernados, etc.

En el lado de usando Macros, la raqueta siempre ha sido más avanzada que otras implementaciones de esquema y LISP. La idea es que cada expresión (como objeto sintáctica) es el código más datos adicionales que contienen su ubicación de origen. De esta manera, cuando una forma es una macro, el código ampliado que tiene partes provenientes de la macro tendrá la ubicación de la fuente correcta, desde la definición de la macro en lugar de a partir de su uso (donde los formularios no están realmente presentes). Algunas implementaciones de esquema y LISP implementarán una limitada para esto utilizando la identidad de los subformas, como mencionó Dmitry-VK.

No sé acerca de las macros de LISP (que sospecho que es probablemente bastante diferente a las macros) o depuración, pero muchos, probablemente, los depuradores más - C / C ++ no manejan la depuración de nivel de origen de las macros de preprocesadores de C / C ++.

Generalmente, los depósitos de C / C ++ no "pisan" en la definición de macro.Si una macro se expande en múltiples afirmaciones, entonces el depurador generalmente se mantendrá en la misma línea de origen (donde se invoca la macro) para la operación de cada depurador 'paso'.

Esto puede hacer que las macros de depuración sean poco más dolorosas de lo que podrían ser, sin embargo, otra razón para evitarlas en C / C ++.Si una macro se está portando mal de una manera verdaderamente misteriosa, caeré en modo de ensamblaje para depurarlo o expandiré la macro (ya sea manualmente o usando el interruptor del compilador).Es bastante raro que tengas que ir a ese extremo;Si está escribiendo macros que son complicadas, probablemente esté tomando el enfoque equivocado.

Generalmente en la depuración de nivel de origen C tiene granularidad de la línea (comando "Siguiente" o granularidad de nivel de instrucción ("Entra"). Los procesadores macro insertan directivas especiales en la fuente procesada que permiten que el compilador mapee las secuencias compiladas de instrucciones de CPU para las líneas de código fuente.

En Lisp no existe ninguna convención entre macros y compilador para rastrear el código fuente para el mapeo de códigos compilados, por lo que no siempre es posible realizar un solo paso en el código fuente.

La opción obvia es hacer un solo paso en el código macroexpanded. El compilador ya ve la versión final, expandida, la versión del código y puede rastrear el código fuente al mapeo de código de máquina.

Otra opción es usar el hecho de que las expresiones LISP durante la manipulación tienen identidad. Si la macro es simple y solo la destrucción y el código de pegado en la plantilla, entonces algunas expresiones de código ampliado serán idénticas (con respecto a la comparación de EQ) a las expresiones que se leen del código fuente. En este caso, el compilador puede asignar algunas expresiones de código expandido al código fuente.

La respuesta simple es que es complicado ;-) Hay varias cosas diferentes que contribuyen a poder depurar un programa, y ​​más aún para el seguimiento de macros.

En C y C++, el preprocesador se utiliza para expandir macros e incluirlas en el código fuente real.Los nombres de archivos originales y los números de línea se rastrean en este archivo fuente ampliado mediante directivas #line.

http://msdn.microsoft.com/en-us/library/b5w2czay(VS.80).aspx

Cuando un programa C o C++ se compila con la depuración habilitada, el ensamblador genera información adicional en el archivo objeto que rastrea las líneas fuente, los nombres de los símbolos, los descriptores de tipo, etc.

http://sources.redhat.com/gdb/onlinedocs/stabs.html

El sistema operativo tiene características que hacen posible que un depurador se adjunte a un proceso y controle la ejecución del proceso;pausar, dar un solo paso, etc.

Cuando se adjunta un depurador al programa, traduce la pila de procesos y el contador del programa nuevamente a forma simbólica buscando el significado de las direcciones del programa en la información de depuración.

Los lenguajes dinámicos normalmente se ejecutan en una máquina virtual, ya sea un intérprete o una máquina virtual de código de bytes.Es la VM la que proporciona enlaces para permitir que un depurador controle el flujo del programa e inspeccione el estado del programa.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top