Cプリプロセッサを作成する


18

目標は、C言語用のプリプロセッサを、ソースコードのサイズ(バイト単位)の点で、希望する言語でできるだけ小さくすることです。入力はCソースファイルになり、出力は前処理されたソースコードになります。

処理できる必要があるアイテムは、コメントの削除(行/ブロック)、#includeディレクティブ(相対パスでファイル開き、必要なポイントでテキストを置き換える)、#define、#undef、#if、 #elif、#else、#endif、#ifdef、#ifndef、defined()。#pragmasや#errorsなどの他のCプリプロセッサディレクティブは無視されます。

#ifディレクティブで算術式や比較演算子を計算する必要はありません。式がゼロ以外の整数を含んでいる限り、式はtrueと評価されます(その主な用途はdefined()ディレクティブです)。可能な入力および出力の例は次のとおりです(出力ファイル内の余分な空白は、見た目をよくするために削除されました。コードでそうする必要はありません)。次の例を適切に処理できるプログラムで十分です。

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

入出力サンプルを提供できますか?
フローレント14

テストコードを提供してください。例がなければほとんど不可能です。
イスマエルミゲル14

ええ、私はそうします。時間と作業負荷の制約のため、私はあまり速くできないので、少し辛抱してください。
タナシスパポウトシダキス14

1
どのくらいの#ifサポートが必要ですか?すなわち、プリプロセッサは、算術演算、ビット演算などの式をサポートする必要がありますか?
ハスタークン14

OK、例えば入力/出力、さらに説明を加える
Thanasis Papoutsidakis

回答:


8

フレックス、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、入力からの文字列を保持するtempsです。 aは、functionのパラメーターとしても使用されますf
  • vマクロの名前を保持し、マクロVの「V」値を保持します
  • tそしてT、私たちが成長しvV
  • i ループの 'i'ncrementer
  • s マクロ配列の「サイズ」です
  • oif偽条件内の 'o'pen のカウントです
  • g() 'g'マクロ配列を行
  • f()「内の同じ値を持つマクロをf'inds vとしてa
  • d(y)'d' yは、現在の入力から最後の文字を切り取ります
  • 状態Dは 'D'efine内
  • 状態Fは 'F'alse条件を無視するためのものです
  • state Iは 'I'gnoring else/ elif真の条件が見つかった後です。

EDIT1:メモリリークの多くをクリーンアップし、ファイルのクローズを実装しました

EDIT2:ネストされたマクロをより正しく処理するようにコードを修正

EDIT3:クレイジーな量のゴルフ

EDIT4:より多くのゴ​​ルフ

EDIT5:より多くのゴ​​ルフ; また、fclose()を呼び出すと、一部のコンピューターで問題が発生することに気づきました。


これは今のところほとんどの場合非常にうまく機能します...何かの理由で、#includeスタッフがいるときにセグメンテーションフォールトをスローしますが、これは編集#5のバグに関連していると思います。また、#ifブロックを正常に処理したとしても、マクロを置き換えません-私が何か間違ったことをしていない限り...しかし、一般的には非常によく見え、レクサーができることの大まかなアイデアを与えますゴルフの形でも理解できます。バグを修正できるかどうかを確認してください。そうでない場合は、コード自体がよく説明しているように、他のエントリがないので、おそらく選択された答えになります。
タナシスパポウトシダキス14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.