¿Hay alguna forma de imprimir paso a paso lo que hace el preprocesador C al expandir una macro?
Por ejemplo, le daría un texto en lenguaje C (por ejemplo: archivo(s) .h) para preprocesarlo. En aras de la demostración, aquí hay un ejemplo simple:
// somefile.h #define qr #define bar(x,z) x ## z #define baz(y) qux ## y #define foo(x,y) bar(x, baz(y))Hasta ahora, eso es solo para construir una tabla de definiciones.
A continuación viene el texto para ampliar en detalle. Para esta demostración, espero que el flujo de trabajo/proceso/salida sea algo como esto:
$ magical_cpp_revealer somefile.h Please enter some preprocessor text to analyse: > foo(baz(p),q) Here are the resulting preprocessor calculations: ,----.----.---------------------------.----------------------------------------- |Step|Exp#| Expression | Reason |====|====|===========================|========================================= | 00 | 00 | foo(baz(p),q) | Original tokens. | 01 | | | Definition found for 'foo': `foo(x,y)` = "bar(x, baz(y))" | 02 | 01 | bar(x, baz(y)) | 'foo' begins expansion. Original tokens shown. | 03 | | | 'foo' Stage 1: Raw parameter replacements elided: no # or ## operators present. | 04 | | | 'foo' Stage 2: Stringification elided: no # operators present. | 05 | | | 'foo' Stage 3: Concatenation elided: no ## operators present. | 06 | | | 'foo' Stage 4: Argument scan begins. | 07 | | | Argument for parameter 'x' is "baz(p)" | 08 | 02 | baz(p) | Scanning "baz(p)" for macros to expand. | 09 | | | Definition found for 'baz': `baz(y)` = "qux ## y" | 10 | 03 | qux ## y | 'baz' begins expansion. Original tokens shown. | 11 | 04 | qux ## p | 'foo->baz' Stage 1: Raw parameter replacements performed | 12 | | | using 'y' = "p". | 13 | | | 'foo->baz' Stage 2: Stringification elided: no # operators present. | 14 | 05 | quxp | 'foo->baz' Stage 3: Concatenation performed. | 15 | | | 'foo->baz' Stage 4: Argument scan elided: no parameters present. | 16 | | | 'foo->baz' Stage 5: Expansive parameter replacements elided: no parameters present. | 17 | | | 'foo->baz' Stage 6: Rescan begins | 18 | | | No definition for 'quxp' | 19 | | | 'foo->baz' Stage 6: Rescan concludes. | 20 | 06 | quxp | 'baz' concludes expansion. Final result shown. | 21 | | | 'foo' Stage 4: Argument scan continues. | 22 | | | Currently: | 23 | | | 'x' = "quxp" | 24 | | | 'y' = To Be Determined | 25 | | | Argument for parameter 'y' is "q" | 26 | 07 | q | Scanning "q" for macros to expand. | 27 | | | Definition found for 'q': `q` = "r" | 28 | 08 | r | 'q' begins expansion. Original tokens shown. | 29 | | | 'foo->q': Stage 1: Concatenation elided: no ## operators present. | 30 | | | 'foo->q': Stage 2: Scan begins. | 31 | | | No definition for 'r' | 32 | | | 'foo->q': Stage 2: Scan concludes. | 33 | 09 | r | 'q' concludes expansion. Final result shown. | 34 | | | 'foo' Stage 4: Argument scan concludes. | 35 | 10 | bar(x, baz(y)) | 'foo': Reminder of current token sequence. | 36 | 11 | bar(quxp, baz(r)) | 'foo' Stage 5: Expansive parameter replacements performed | 37 | | | using 'x' = "quxp", | 38 | | | and 'y' = "r". | 39 | | | 'foo' Stage 6: Rescan begins | 40 | | | Definition found for 'bar': `bar(x,z)` = "x ## z" | 41 | 12 | x ## z | 'bar' begins expansion. Original tokens shown. | 42 | 13 | quxp ## baz(r) | 'foo->bar' Stage 1: Raw parameter replacements performed | 43 | | | using 'x' = "quxp", | 44 | | | and 'z' = "baz(r)". | 45 | | | 'foo->bar' Stage 2: Stringification elided: no # operators present. | 46 | 14 | quxpbaz(r) | 'foo->bar' Stage 3: Concatenation performed. | 47 | | | 'foo->bar' Stage 4: Argument scan elided: no parameters present. | 48 | | | 'foo->bar' Stage 5: Expansive parameter replacements elided: no parameters present. | 49 | | | 'foo->bar' Stage 6: Rescan begins | 50 | | | No definition for 'quxpbaz' | 51 | | | No definition for '(' | 52 | | | No definition for 'r' | 53 | | | No definition for ')' | 54 | | | 'foo->baz' Stage 6: Rescan concludes. | 55 | 15 | quxpbaz(r) | 'bar' concludes expansion. Final result shown. | 56 | | | 'foo' Stage 6: Rescan concludes | 57 | 16 | quxpbaz(r) | 'foo' concludes expansion. Final result shown. '----'----'---------------------------'-----------------------------------------(Nota al margen y advertencia para futuros lectores: escribí el trazo anterior a mano y puede que no sea 100% correcto, al menos en términos de representar cómo funciona el preprocesador).
Tenga en cuenta que intenté no solo ilustrar las decisiones positivas del preprocesador sobre qué hacer (por ejemplo, cuando encuentra una definición y comienza a expandirse), sino que también ilustré sus decisiones negativas sobre qué no hacer (por ejemplo, cuando un token no tiene definición o cuando los operadores #+## no están presentes). Eso puede sonar un poco específico, pero es importante para comprender por qué el preprocesador no hizo algo que esperaba que hiciera, a menudo con una conclusión mundana como "Escribí mal la definición o el token" o "Olvidé # incluir ese archivo".
Estaré aún más aliviado si hay una manera de revelar lo que CL.EXE de CL.EXE está pensando cuando usa la lógica de "preprocesador tradicional" para expandir mis macros.
He aquí un ejemplo de lo que no responde a la pregunta:
$ gcc -E somefile.h ... quxpbaz(r)Eso es lo que encuentro en las respuestas a preguntas como ¿ Alguna utilidad para probar expandir C/C++ #define macros? .
Cuando alguien pide ver la "expansión" de una macro, gcc -E parece una respuesta válida. Estoy buscando algo con mayor fidelidad y ya sé sobre gcc -E .
Estoy escribiendo código ISO C11, pero incluyo la etiqueta C++ en caso de que haya una herramienta o técnica en ese ecosistema con relevancia para esto.
Espero que alguien que lea esto sea quizás un escritor de compiladores que haya realizado o visto un trabajo similar (¿opciones de rastreo del compilador?), o que haya creado una herramienta como esta, o que tenga mucha más suerte con sus resultados de búsqueda que yo. O si está al tanto de todas las ofertas de lenguaje C que existen y está relativamente seguro de que esto no existe, entonces también encontraría útil una respuesta negativa, aunque me gustaría saber por qué el preprocesador C habría existido durante décadas, obtenido infamia por sus "trampas" y, sin embargo, nunca había visto una herramienta (o proceso) para abrir el preprocesador. (Espero que esto realmente exista. Crucemos los dedos )
Sugeriría encontrar un compilador/preprocesador de buena calidad y editar el preprocesador.
Evitaría GCC y clang, ya que son demasiado pesados en mi opinión. Echaría un vistazo a cparser de libfirm y este archivo en particular: https://github.com/libfirm/cparser/blob/master/src/parser/preprocessor.c
El código de libfirm es muy fácil de leer y editar, y casi no lleva tiempo construir el proyecto, en contraste con LLVM/clang o GCC.
Se ha comido todo el código C99 que le he arrojado hasta ahora.
Por cierto, no estoy afiliado, ¡solo creo que es genial! Acabo de usar el código con excelentes resultados y recibí un fantástico soporte, ayuda y orientación en el canal IRC #firm @ freenode.
EDITAR:
Sparse, tal como lo utiliza el equipo de conserjes del kernel en Linux, también es fácilmente pirateable para tales fines. También incluye un preprocesador c: https://github.com/chrisforbes/sparse