Erstellen Sie einen C-Präprozessor

18

Ziel ist es, einen Präprozessor für die C-Sprache zu erstellen, der hinsichtlich der Quellcodegröße in Byte so klein wie möglich ist und in der von Ihnen bevorzugten Sprache vorliegt . Seine Eingabe ist eine C-Quelldatei, und seine Ausgabe ist der vorverarbeitete Quellcode.

Folgende Elemente müssen verarbeitet werden können: Entfernen von Kommentaren (Zeile / Block), # include-Anweisungen (durch Öffnen von Dateien in relativen Pfaden und Ersetzen von Text an der erforderlichen Stelle), #define, #undef, #if, #elif, #else, #endif, #ifdef, #ifndef und defined (). Andere C-Präprozessor-Direktiven wie #pragmas oder #errors werden möglicherweise ignoriert.

Es ist nicht erforderlich, arithmetische Ausdrücke oder Vergleichsoperatoren in # if-Direktiven zu berechnen. Wir gehen davon aus, dass der Ausdruck als wahr ausgewertet wird, solange er eine Ganzzahl ungleich Null enthält (seine Hauptverwendung liegt bei der defined () -Direktive). Es folgen Beispiele für mögliche Ein- und Ausgaben (mögliche zusätzliche Leerzeichen in Ausgabedateien wurden für eine bessere Darstellung gekürzt, Ihr Code muss dies nicht tun). Ein Programm, das die folgenden Beispiele ordnungsgemäß verarbeiten kann, wird als ausreichend angesehen.

----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
quelle
Können Sie Input- / Output-Samples bereitstellen?
Florent
Stellen Sie uns einen Testcode zur Verfügung. Es ist fast unmöglich ohne Beispiele.
Ismael Miguel
Uh sicher, werde ich. Sei einfach ein bisschen geduldig, da ich aus Zeit- und Arbeitsgründen nicht sehr schnell sein kann.
Thanasis Papoutsidakis
1
Wie viel #ifmuss unterstützt werden? dh muss der Präprozessor Ausdrücke mit arithmetischen, bitweisen Operationen usw. unterstützen?
Hasturkun
OK, Beispiel Eingabe / Ausgabe und weitere Erklärungen hinzugefügt
Thanasis Papoutsidakis

Antworten:

8

Flex, 1170 + 4 = 1174

1170 Zeichen im Flexcode + 4 Zeichen für ein Kompilierungsflag. Führen Sie aus, um eine ausführbare Datei zu erstellen flex pre.l ; gcc lex.yy.c -lfl. Der Eintrag verliert den Speicher wie ein Sieb und schließt eingeschlossene Dateien nicht. Ansonsten sollte es jedoch gemäß der Spezifikation voll funktionsfähig sein.

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

Einige Erklärungen:

  • aund bsind Temps, um Strings von der Eingabe zu halten. awird auch als Parameter für die Funktion verwendet f.
  • vVEnthält die Namen von Makros und die 'V'-Werte von Makros
  • tund Tsind 't'emporary Inhaber für, wenn wir wachsen vundV
  • i ist ein i'ncrementer für Loops
  • s ist die Größe des Makro-Arrays
  • oist die Anzahl der offenen Stellen ifin einer falschen Bedingung
  • g() 'g'rows die Makro-Arrays
  • f()'f'indet ein Makro mit demselben Wert vwiea
  • d(y)'d'rops die letzten yZeichen aus der aktuellen Eingabe
  • state Dsteht für inside a 'D'efine
  • state Fdient zum Ignorieren einer 'F'-Bedingung
  • state Iist für 'I'gnoring else/ elifnachdem eine echte Bedingung gefunden wurde.

EDIT1: Bereinigt viele Speicherlecks und implementiert das Schließen von Dateien

EDIT2: Code geändert, um verschachtelte Makros korrekter zu behandeln

EDIT3: unglaublich viel Golf

EDIT4: mehr Golf

EDIT5: mehr Golfen; Mir ist auch aufgefallen, dass mein Aufruf von fclose () auf einigen Computern Probleme verursacht.

Josh
quelle
Bisher funktioniert es in den meisten Fällen sehr gut ... aus irgendeinem Grund wird beim Stoppen ein Segmentierungsfehler #includeausgegeben, aber ich denke, dies hängt mit dem Fehler in Bearbeitung 5 zusammen. Außerdem ersetzt es keine Makros, obwohl es die # if-Blöcke erfolgreich verarbeitet - es sei denn, ich mache etwas falsch ... aber im Allgemeinen sieht es sehr gut aus und es gibt eine ungefähre Vorstellung davon, was ein Lexer seitdem tun kann Ich kann es sogar in seiner Golfform verstehen. Versuchen Sie zu sehen, ob die Fehler behoben werden können. Wenn dies nicht in Ordnung ist, wie der Code selbst gut erklärt, wird dies wahrscheinlich als Antwort gewählt, da es keine anderen Einträge gibt.
Thanasis Papoutsidakis