Створіть препроцесор С


18

Метою є створення препроцесора для мови С, якомога менший за розміром вихідного коду в байтах , на бажаній вам мові. Його вхід буде вихідним файлом C, а його вихід буде попередньо обробленим вихідним кодом.

Елементи, які потрібно буде обробити, це: видалення коментарів (рядок / блок), директиви #include (відкриваючи файли на відносних шляхах та замінюючи текст у потрібній точці), #define, #undef, #if, #elif, #else, #endif, #ifdef, #ifndef та визначено (). Інші директиви C препроцесора, такі як #pragmas або #errors, можуть ігноруватися.

Немає необхідності обчислювати арифметичні вирази чи оператори порівняння в директивах #if, ми припускаємо, що вираз буде оцінюватись як істинне, доки воно містить ціле число, відмінне від нуля (його основне використання буде для визначеної () директиви). Наступні приклади можливих вхідних та вихідних даних (можливі додаткові пробіли у вихідних файлах були оброблені для кращого вигляду; для цього немає необхідності у вашому коді). Програма, здатна правильно обробити наступні приклади, буде вважатися достатньою.

----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

1
Скільки #ifпотрібно підтримувати? тобто чи повинен препроцесор підтримувати вирази арифметичними, побітовими операціями тощо?
Hasturkun

ок, приклади введення / виводу та додаткових пояснень додано
Thanasis Papoutsidakis

Відповіді:


8

Flex, 1170 + 4 = 1174

1170 символів у флексі-коді + 4 символи для прапора компіляції. Щоб створити виконуваний файл, запустіть flex pre.l ; gcc lex.yy.c -lfl. Запис просочується пам'яттю, як сито, і не закриває файли, що входять. Але в іншому випадку вона повинна бути повністю функціональною відповідно до специфікації.

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

Деякі пояснення:

  • aі bє темпами для утримування рядків із вхідних даних. aтакож використовується як параметр для функціонування f.
  • vмістить назви макросів і Vмістить «V» значення макросів
  • tі Tє 'тимчасовими власниками, коли ми ростемо vіV
  • i є 'i'ncrementer для циклів
  • s є 's'ize макросистеми
  • o- це підрахунок 'o'pen ifs всередині помилкового умовного
  • g() 'g'row макро масиви
  • f()«F'inds макрос з тим же значенням в vякостіa
  • d(y)'d'опускає останні yсимволи з поточного вводу
  • стан Dдля всередині 'D'efine
  • держава F- це ігнорування 'F'alse умовно
  • state Ifor 'Ignoring else/ elifпісля того, як було знайдено справжнє умовне.

EDIT1: очищено багато витоків пам'яті та реалізовано закриття файлів

EDIT2: модифікований код для більш правильної обробки вкладених макросів

EDIT3: шалена кількість гольфу

EDIT4: більше гольфу

EDIT5: більше гольфу; Я також помітив, що мій дзвінок до fclose () викликає проблеми на деяких комп’ютерах ... вивчаючи це.


Наразі це працює дуже добре в більшості випадків ... чомусь він кидає помилку сегментації, коли я #includeнаповнюю, але я думаю, що це пов'язано з помилкою в редакції №5. Крім того, він не замінює макроси, навіть якщо він успішно обробляє блоки #if - якщо я щось не так роблю ... але в цілому це виглядає дуже добре, і це дає грубе уявлення про те, що може зробити лексер, оскільки Я можу зрозуміти це навіть у його гольф-формі. Спробуйте перевірити, чи можна виправити помилки, якщо це не нормально, як код добре пояснює, ймовірно, це буде обрана відповідь, оскільки інших записів немає.
Thanasis Papoutsidakis
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.