Создать препроцессор C


18

Цель состоит в том, чтобы создать как можно меньший препроцессор для языка Си с точки зрения размера исходного кода в байтах на предпочитаемом вами языке. Его ввод будет исходным файлом C, а вывод - предварительно обработанным исходным кодом.

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

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

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

Можете ли вы предоставить образцы ввода / вывода?
Флоран,

Предоставьте нам тестовый код. Это почти невозможно без примеров.
Исмаэль Мигель

Ну конечно, я буду. Просто будьте немного терпеливы, поскольку я не могу быть очень быстрым из-за нехватки времени и рабочей нагрузки.
Танасис Папуцудакис

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 ifвнутри ложного условного
  • g() g'rows макро-массивы
  • f()'f' указывает макрос с тем же значением, vчто иa
  • d(y)'d'rops последние yсимволы из текущего ввода
  • состояние Dдля внутри 'D'efine
  • состояние Fдля игнорирования условного 'F'alse
  • состояние I«Я игнорирую else/ elifпосле того, как было найдено истинное условие».

EDIT1: устранены многие утечки памяти и реализовано закрытие файлов

EDIT2: модифицированный код для более правильной обработки вложенных макросов

EDIT3: сумасшедшая игра в гольф

EDIT4: больше игры в гольф

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


Пока он работает очень хорошо в большинстве случаев ... по какой-то причине он выдает ошибку сегментации, когда я #includeпишу, но я думаю, это связано с ошибкой в ​​правке № 5. Кроме того, он не заменяет макросы, даже если он успешно обрабатывает блоки #if - если я не делаю что-то не так ... но в целом это выглядит очень хорошо, и это дает грубое представление о том, что может сделать лексер, так как Я могу понять это даже в его гольфовой форме. Попытайтесь выяснить, можно ли исправить ошибки, если нет, то все в порядке, как хорошо объясняется в коде, вероятно, это будет выбранный ответ, поскольку других записей нет.
Танасис Папуцудакис
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.