Utwórz preprocesor C.


18

Celem jest stworzenie preprocesora dla języka C, możliwie jak najmniejszego pod względem rozmiaru kodu źródłowego w bajtach , w preferowanym języku. Jego dane wejściowe będą plikiem źródłowym typu C, a dane wyjściowe będą wstępnie przetworzonym kodem źródłowym.

Elementy, które będzie musiał przetworzyć to: Usuwanie komentarzy (linia / blok), #include directives (otwieranie plików we względnych ścieżkach i zastępowanie tekstu w wymaganym punkcie), #define, #undef, #if, #elif, #else, #endif, #ifdef, #ifndef i zdefiniowane (). Inne dyrektywy preprocesora C, takie jak #pragmas lub #errors, można zignorować.

Nie ma potrzeby obliczania wyrażeń arytmetycznych lub operatorów porównania w dyrektywach #if, zakładamy, że wyrażenie będzie miało wartość true, dopóki będzie zawierało liczbę całkowitą inną niż zero (jego główne zastosowanie będzie dotyczyło dyrektywy zdefiniowanej ()). Przykłady możliwych danych wejściowych i wyjściowych (możliwe dodatkowe spacje w plikach wyjściowych zostały przycięte dla lepszego wyglądu, nie ma potrzeby, aby kod to robił). Program zdolny do prawidłowego przetworzenia poniższych przykładów zostanie uznany za wystarczający.

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

Czy możesz podać próbki wejścia / wyjścia?
Florent

Podaj nam kod testowy. To prawie niemożliwe bez przykładów.
Ismael Miguel

Jasne, że tak. Po prostu bądź trochę cierpliwy, ponieważ nie mogę być bardzo szybki z powodu ograniczeń czasu i obciążenia.
Thanasis Papoutsidakis

1
Ile #iftrzeba wspierać? tzn. czy preprocesor musi obsługiwać wyrażenia za pomocą operacji arytmetycznych, bitowych itp.?
Hasturkun

ok, dodano przykładowe dane wejściowe / wyjściowe i dalsze wyjaśnienia
Thanasis Papoutsidakis

Odpowiedzi:


8

Flex, 1170 + 4 = 1174

1170 znaków w kodzie flex + 4 znaki dla flagi kompilacji. Aby utworzyć plik wykonywalny, uruchom flex pre.l ; gcc lex.yy.c -lfl. Wpis przecieka pamięć jak sito i nie zamyka dołączonych plików. Ale w przeciwnym razie powinien być całkowicie funkcjonalny zgodnie ze specyfikacją.

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

Niektóre wyjaśnienia:

  • ai bsą temps do przechowywania ciągów z wejścia. asłuży również jako parametr do działania f.
  • vprzechowuje nazwy makr i Vprzechowuje wartości „V” makr
  • ti Tsą współczesnymi posiadaczami, kiedy dorastamy viV
  • i jest narzędziem do tworzenia pętli
  • s jest „wielkością” tablicy makr
  • ojest liczbą „otwartych” ifwewnątrz fałszywego warunku
  • g() „zmniejsza tablice makr
  • f()„f” oznacza makro o tej samej wartości vcoa
  • d(y)'zatrzymuje ostatnie yznaki z bieżącego wejścia
  • stan Djest wewnątrz „D”
  • stan Fsłuży do ignorowania warunku „F”
  • stan Ijest dla „Ignorowanie else/ elifpo znalezieniu prawdziwego warunku.

EDYCJA 1: wyczyścił wiele wycieków pamięci i zaimplementował zamykanie plików

EDIT2: zmodyfikowano kod, aby lepiej obsługiwał zagnieżdżone makra

EDIT3: szalona ilość gry w golfa

EDIT4: więcej golfa

EDIT5: więcej golfa; Zauważyłem również, że moje wywołanie fclose () powoduje problemy na niektórych komputerach ... patrząc na to.


Jak dotąd działa bardzo dobrze w większości przypadków ... z jakiegoś powodu zgłasza błąd segmentacji, gdy #includecoś robię , ale myślę, że jest to związane z błędem w edycji # 5. Nie zastępuje też makr, mimo że z powodzeniem przetwarza bloki # if - chyba że robię coś źle ... ale ogólnie wygląda to bardzo dobrze i daje przybliżone wyobrażenie o tym, co może zrobić leksykon, ponieważ Rozumiem to nawet w formie golfa. Spróbuj sprawdzić, czy błędy można naprawić, jeśli nie jest w porządku, ponieważ kod wyjaśnia się dobrze, prawdopodobnie zostanie wybrana odpowiedź, ponieważ nie ma innych wpisów.
Thanasis Papoutsidakis
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.