Crea un preprocessore C.


18

L'obiettivo è quello di creare un preprocessore per il linguaggio C, il più piccolo possibile in termini di dimensioni del codice sorgente in byte , nella lingua preferita. Il suo input sarà un file sorgente C e il suo output sarà il codice sorgente pre-elaborato.

Gli elementi che dovrà essere in grado di elaborare saranno: Rimozione dei commenti (linea / blocco), #include direttive (aprendo i file nei percorsi relativi e sostituendo il testo nel punto necessario), #define, #undef, #if, #elif, #else, #endif, #ifdef, #ifndef e defined (). Altre direttive del preprocessore C come #pragmas o #errors possono essere ignorate.

Non è necessario calcolare espressioni aritmetiche o operatori di confronto nelle direttive #if, supponiamo che l'espressione valuterà vero fintanto che contiene un numero intero diverso da zero (il suo uso principale sarà per la direttiva definita ()). Seguono esempi di possibili input e output (eventuali spazi bianchi extra nei file di output sono stati tagliati per un aspetto migliore, non è necessario che il codice lo faccia). Un programma in grado di elaborare correttamente i seguenti esempi sarà considerato sufficiente.

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

Potete fornire campioni di input / output?
Florent,

Forniscici un codice di prova. È quasi impossibile senza esempi.
Ismael Miguel,

Certo che lo farò. Sii solo un po 'paziente perché non posso essere molto veloce a causa dei limiti di tempo e di carico di lavoro.
Thanasis Papoutsidakis,

1
Quanta #ifnecessità deve essere supportata? cioè il preprocessore deve supportare espressioni con operazioni aritmetiche, bit per bit, ecc.?
Hasturkun,

ok, input / output di esempio e ulteriori spiegazioni aggiunte
Thanasis Papoutsidakis,

Risposte:


8

Flex, 1170 + 4 = 1174

1170 caratteri nel codice flessibile + 4 caratteri per un flag di compilazione. Per produrre un eseguibile, esegui flex pre.l ; gcc lex.yy.c -lfl. La voce perde memoria come un setaccio e non chiude i file inclusi. Ma per il resto, dovrebbe essere completamente funzionale secondo le specifiche.

%{
#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)}

Qualche spiegazione:

  • ae bsono temp per contenere stringhe dall'input. aè anche usato come parametro per funzionare f.
  • vcontiene i nomi delle macro e Vcontiene i "V" valori delle macro
  • te Tsono titolari "temporanei" per quando cresciamo veV
  • i è un 'i'ncrementer per loop
  • s è il 's'ize dell'array macro
  • oè il conteggio delle "aperture" ifall'interno di un falso condizionale
  • g() raccoglie le macro matrici
  • f()'f'inds una macro con lo stesso valore vcomea
  • d(y)'cancella gli ultimi ycaratteri dall'input corrente
  • lo stato Dè all'interno di un 'D'efine
  • stato Fè per ignorare un 'F'alse condizionale
  • lo stato Iè per 'Ignorare else/ elifdopo che è stato trovato un vero condizionale.

EDIT1: ripulito molte delle perdite di memoria e implementato la chiusura dei file

EDIT2: codice modificato per gestire le macro nidificate in modo più corretto

EDIT3: folle quantità di golf

EDIT4: più golf

EDIT5: più golf; Ho anche notato che la mia chiamata a fclose () causa problemi su alcuni computer ... esaminando questo.


Funziona molto bene finora nella maggior parte dei casi ... per qualche motivo genera un errore di segmentazione quando #includeroba, ma immagino che sia correlato al bug nella modifica # 5. Inoltre non sostituisce le macro, anche se elabora correttamente i blocchi #if - a meno che non stia facendo qualcosa di sbagliato ... ma in generale sembra molto buono e dà un'idea approssimativa di cosa può fare un lexer, dal momento che Posso capirlo anche nella sua forma da golf. Prova a vedere se i bug possono essere corretti, se non è ok, come spiega bene il codice, probabilmente questa sarà scelta come risposta in quanto non ci sono altre voci.
Thanasis Papoutsidakis,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.