创建一个C预处理程序


18

目的是为C语言创建一个预处理器,以您喜欢的语言在源代码大小(以字节为单位)方面尽可能地小。它的输入将是C源文件,其输出将是经过预处理的源代码。

它需要处理的项目为:注释删除(行/块),#include指令(通过在相对路径中打开文件并在需要的位置替换文本),#define,#undef,#if, #elif,#else,#endif,#ifdef,#ifndef和define()。其他C预处理程序指令,例如#pragmas或#errors,可以忽略。

无需在#if指令中计算算术表达式或比较运算符,我们假设该表达式只要包含非零的整数(它的主要用途将用于define()指令),就将计算为true。可能的输入和输出示例如下(为了更好的显示,输出文件中可能的多余空格被剪裁了,不需要您的代码这样做)。能够正确处理以下示例的程序将被认为是足够的。

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

您可以提供输入/输出样本吗?
Florent 2014年

向我们提供测试代码。没有例子几乎是不可能的。
Ismael Miguel

嗯,我会的。请耐心等待,因为时间和工作量限制,我不能很快。
Thanasis Papoutsidakis 2014年

1
怎样的多少#if需要得到支持?即预处理器是否需要使用算术,按位运算等来支持表达式?
Hasturkun 2014年

好的,示例输入/输出和更多说明已添加
Thanasis Papoutsidakis

Answers:


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

一些解释:

  • ab临时保留输入中的字符串。 a也用作功能的参数f
  • v保留宏的名称并V保留宏的“ V”值
  • t并且T是我们成长v和成长的“临时”持有人V
  • i 是循环的'i'ncrementer
  • s 是宏数组的'​​s'ize
  • oif错误条件中“ o”的计数
  • g() 'g'行宏数组
  • f()“f'inds与同一值的宏va
  • d(y)'d'裁剪y当前输入中的最后一个字符
  • 状态D是在“ D”定义内
  • 状态F是忽略“ F”条件的条件
  • 状态I是为'我正在忽略else/ elif找到了真正的条件之后。

EDIT1:清理了许多内存泄漏并实现了文件关闭

EDIT2:修改代码以更正确地处理嵌套的宏

EDIT3:疯狂的打高尔夫球

EDIT4:更多打高尔夫球

EDIT5:更多打高尔夫球;我还注意到,我对fclose()的调用在某些计算机上引起了问题……对此进行了调查。


到目前为止,它在大多数情况下都运行良好...由于某种原因,在我#include填充时会引发分段错误,但是我想这与编辑#5中的错误有关。此外,即使它成功处理了#if块,它也不会替代宏-除非我做错了事...但是总体而言,它看起来非常好,并且给出了词法分析器可以做什么的一个粗略概念,因为即使是打高尔夫球的形式,我也能理解。尝试查看是否可以修复错误,如果还可以,请参见代码本身,因为没有其他条目,因此可能会选择答案。
Thanasis Papoutsidakis 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.