Crear un preprocesador C

18

El objetivo es crear un preprocesador para el lenguaje C, lo más pequeño posible en términos de tamaño del código fuente en bytes , en su idioma preferido. Su entrada será un archivo fuente C y su salida será el código fuente preprocesado.

Los elementos que deberá poder procesar serán: eliminación de comentarios (línea / bloque), # incluir directivas (abriendo archivos en rutas relativas y reemplazando texto en el punto necesario), #define, #undef, #if, #elif, #else, #endif, #ifdef, #ifndef y defined (). Se pueden ignorar otras directivas de preprocesador de C como #pragmas o #errors.

No es necesario calcular expresiones aritméticas u operadores de comparación en las directivas #if, asumimos que la expresión se evaluará como verdadera siempre que contenga un número entero distinto de cero (su uso principal será para la directiva definida ()). A continuación se muestran ejemplos de posibles entradas y salidas (se recortaron posibles espacios en blanco adicionales en los archivos de salida para una mejor apariencia, no es necesario que su código lo haga). Un programa capaz de procesar los siguientes ejemplos correctamente se considerará suficiente.

----Input file: foo.c (main file being preprocessed)

#include "bar.h" // Line may or may not exist

#ifdef NEEDS_BAZZER
#include "baz.h"
#endif // NEEDS_BAZZER

#ifdef _BAZ_H_

int main(int argc, char ** argv)
{
    /*  Main function.
        In case that bar.h defined NEEDS_BAZ as true,
        we call baz.h's macro BAZZER with the length of the
        program's argument list. */
    return BAZZER(argc);
}

#elif defined(_BAR_H_)

// In case that bar.h was included but didn't define NEEDS_BAZ.
#undef _BAR_H_
#define NEEDS_BARRER
#include "bar.h"

int main(int argc, char ** argv)
{
    return BARRER(argc);
}

#else

// In case that bar.h wasn't included at all.
int main()
{return 0;}

#endif // _BAZ_H_

----Input file bar.h (Included header)

#ifndef _BAR_H_
#define _BAR_H_

#ifdef NEEDS_BARRER

int bar(int * i)
{
    *i += 4 + *i;
    return *i;
}

#define BARRER(i) (bar(&i), i*=2, bar(&i))

#else
#define NEEDS_BAZZER // Line may or may not exist
#endif // NEEDS_BARRER

#endif // _BAR_H_

----Input file baz.h (Included header)

#ifndef _BAZ_H_
#define _BAZ_H_

int baz(int * i)
{
    *i = 4 * (*i + 2);
    return *i;
}

#define BAZZER(i) (baz(&i), i+=2, baz(&i))

#endif // _BAZ_H_

----Output file foopp.c (no edits)

int baz(int * i)
{
    *i = 4 * (*i + 2);
    return *i;
}

int main(int argc, char ** argv)
{
    return (baz(&argc), argc+=2, baz(&argc));
}

----Output file foopp2.c (with foo.c's first line removed)

int main()
{return 0;}

----Output file foopp3.c (with bar.h's line "#define NEEDS_BAZZER" removed)

int bar(int * i)
{
    *i += 4 + *i;
    return *i;
}

int main(int argc, char ** argv)
{
    return (bar(&argc), argc*=2, bar(&argc));
}
Thanasis Papoutsidakis
fuente
¿Puede proporcionar muestras de entrada / salida?
Florent
Danos un código de prueba. Es casi imposible sin ejemplos.
Ismael Miguel
Uh seguro, lo haré. Solo sea un poco paciente ya que no puedo ser muy rápido debido a limitaciones de tiempo y carga de trabajo.
Thanasis Papoutsidakis
1
¿Cuánto de las #ifnecesidades deben ser apoyadas? es decir, ¿el preprocesador necesita admitir expresiones con operaciones aritméticas, bit a bit, etc.?
Hasturkun
ok, ejemplo de entrada / salida y más explicaciones agregadas
Thanasis Papoutsidakis

Respuestas:

8

Flex, 1170 + 4 = 1174

1170 caracteres en el código flexible + 4 caracteres para un indicador de compilación. Para producir un ejecutable, ejecute flex pre.l ; gcc lex.yy.c -lfl. La entrada pierde memoria como un tamiz y no cierra los archivos incluidos. Pero de lo contrario, debería ser completamente funcional según las especificaciones.

%{
#define M malloc
#define X yytext
#define A a=X
#define B(x) BEGIN x;
#define Y YY_CURRENT_BUFFER
*a,*b,**v,**V,**t,**T,i,s=1,o;
g(){t=M(++s);T=M(s);for(i=1;i<s-1;i++)t[i]=v[i],T[i]=V[i];free(v);free(V);v=t;V=T;}
f(){for(i=1;i<s;i++)if(!strcmp(v[i],a))return i;return 0;}
d(y){X[yyleng-y]=0;}
%}
%x D F I
N .*\n
%%
"//".*
"/*"([^\*]|\*[^\/])*"*/"
\"(\\.|[^\\"])*\" ECHO;
^"#include "\"[^\"]*\" d(1),yypush_buffer_state(yy_create_buffer(fopen(X+10,"r"),YY_BUF_SIZE));
^"#define "[^ ]* {B(D)strcpy(a=M(yyleng),X+8);}
<D>" "?{N} {b=M(yyleng);d(1);f(strcpy(b,X+(X[0]==32)))?free(V[i]),V[i]=b:g(),v[s-1]=a,V[s-1]=b;B(0)}
^"#undef "{N} d(1),v[f(A+7)][0]=0;
^"#if defined(".*")\n" h(2,12);
^"#ifdef "{N} h(1,7);
^"#if "{N} {d(1);if(!atoi(X+4))B(F)}
^"#ifndef "{N} {d(1);if(f(A+8))B(F)}
<F>^"#if"{N} o++;
<F>^"#endif"{N} if(!o--)B(++o)
<F>^"#else"{N} if(!o)B(0)
<F>^"#elif defined(".*")\n" if(!o){d(2);if(f(A+14))B(0)}
<F>^"#elif "{N} if(!o){d(1);if(atoi(X+6))B(0)}
<F>{N}
^"#endif"{N}
^"#el"("se"|"if"){N} B(I)
<I>^"#endif"{N} B(0)
<I>{N}
[a-zA-Z_][a-zA-Z_0-9]* printf(f(A)?V[i]:a);
<<EOF>> {a=Y;yypop_buffer_state();if(!Y)exit(0);fclose(a);}
%%
h(x,y){d(x);if(!f(A+y))B(F)}

Alguna explicación:

  • ay bson temporales para contener cadenas de la entrada. atambién se usa como parámetro para funcionar f.
  • vcontiene los nombres de las macros y Vcontiene los valores "V" de las macros
  • ty Tsomos titulares temporales de cuando crecemos vyV
  • i es un 'i'ncrementer para bucles
  • s es el tamaño de la matriz de macros
  • oes el recuento de los 'o'pen ifs dentro de un condicional falso
  • g() 'G'rows las matrices de macros
  • f()'f' encuentra una macro con el mismo valor vquea
  • d(y)'quita los últimos ycaracteres de la entrada actual
  • estado Des para dentro de un 'D'efine
  • estado Fes para ignorar un 'F'alse condicional
  • El estado Ies para 'Ignorar else/ elifdespués de que se haya encontrado un verdadero condicional.

EDITAR1: limpió muchas de las pérdidas de memoria e implementó el cierre de archivos

EDIT2: código modificado para manejar macros anidadas más correctamente

EDIT3: cantidad loca de golf

EDIT4: más golf

EDIT5: más golf; También he notado que mi llamado a fclose () causa problemas en algunas computadoras ... investigando esto.

Josh
fuente
Funciona muy bien hasta ahora en la mayoría de los casos ... por alguna razón, arroja una falla de segmentación cuando #includerelleno, pero supongo que esto está relacionado con el error en la edición # 5. Además, no sustituye las macros, a pesar de que procesa con éxito los bloques #if, a menos que esté haciendo algo mal ... pero en general se ve muy bien y da una idea aproximada de lo que puede hacer un lexer, ya que Puedo entenderlo incluso en su forma de golf. Intente ver si los errores se pueden corregir, de lo contrario, está bien, ya que el código se explica bien, probablemente esta sea la respuesta elegida, ya que no hay otras entradas.
Thanasis Papoutsidakis