สร้างตัวประมวลผลล่วงหน้า C


18

เป้าหมายคือการสร้างตัวประมวลผลล่วงหน้าสำหรับภาษา C ซึ่งมีขนาดเล็กที่สุดเท่าที่เป็นไปได้ในแง่ของขนาดซอร์สโค้ดเป็นไบต์ในภาษาที่คุณต้องการ อินพุตจะเป็นไฟล์ต้นฉบับ 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));
}

คุณสามารถให้ตัวอย่างอินพุต / เอาต์พุตได้หรือไม่?
Florent

ให้รหัสทดสอบแก่เรา เกือบจะเป็นไปไม่ได้เลยหากไม่มีตัวอย่าง
Ismael Miguel

เอ่อแน่นอนฉันจะ ขอให้อดทนสักหน่อยเพราะฉันไม่สามารถทำได้เร็วเนื่องจากข้อ จำกัด ด้านเวลาและภาระงาน
Thanasis Papoutsidakis

1
เท่าใดของ#ifความต้องการที่จะได้รับการสนับสนุน? เช่นตัวประมวลผลล่วงหน้าจำเป็นต้องสนับสนุนนิพจน์ที่มีเลขคณิตการดำเนินการระดับบิตและอื่น ๆ หรือไม่
Hasturkun

ตกลงตัวอย่างอินพุต / เอาต์พุตและคำอธิบายเพิ่มเติมเพิ่ม
Thanasis Papoutsidakis

คำตอบ:


8

งอ, 1170 + 4 = 1174

1170 ตัวอักษรใน flex code + 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 ที่จะเก็บสตริงจากอินพุต นอกจากนี้ยังใช้เป็นพารามิเตอร์เพื่อฟังก์ชั่นaf
  • vเก็บชื่อของมาโครและVเก็บ 'V'alues ​​ของมาโคร
  • tและTเป็น 'ผู้ถือชั่วคราว' เมื่อเราเติบโตvและV
  • i เป็น 'i'ncrementer สำหรับลูป
  • s เป็น 's'ize ของอาร์เรย์มาโคร
  • oคือจำนวนของ 'o'pen ifs ภายในเงื่อนไขที่ผิดพลาด
  • g() 'g'rows อาร์เรย์มาโคร
  • f()'f'inds แมโครที่มีค่าเดียวกันในvขณะที่a
  • d(y)'ครอบตัดyอักขระตัวสุดท้ายจากอินพุตปัจจุบัน
  • สถานะDสำหรับภายใน 'D'efine
  • รัฐFไม่สนใจเงื่อนไข 'F'alse
  • สถานะIสำหรับ 'ฉันกำลังelse/ elifหลังจากพบเงื่อนไขที่แท้จริง

EDIT1: ล้างการรั่วไหลของหน่วยความจำจำนวนมากและนำไฟล์ไปปิด

EDIT2: รหัสที่แก้ไขเพื่อจัดการกับแมโครที่ซ้อนกันอย่างถูกต้องมากขึ้น

EDIT3: การเล่นกอล์ฟที่บ้าคลั่ง

EDIT4: เล่นกอล์ฟได้มากขึ้น

EDIT5: เล่นกอล์ฟได้มากขึ้น ฉันได้สังเกตเห็นด้วยว่าการเรียกร้องให้ fclose () ทำให้เกิดปัญหากับคอมพิวเตอร์บางเครื่อง ...


มันใช้งานได้ดีมากในกรณีส่วนใหญ่ ... ด้วยเหตุผลบางอย่างที่ทำให้เกิดข้อผิดพลาดในการแบ่งส่วนเมื่อฉันทำ#includeสิ่งต่าง ๆ แต่ฉันเดาว่าสิ่งนี้เกี่ยวข้องกับข้อผิดพลาดในการแก้ไข # 5 นอกจากนี้มันไม่สามารถแทนที่มาโครได้แม้ว่ามันจะประมวลผลบล็อก #if ได้สำเร็จ - ยกเว้นว่าฉันทำอะไรผิดไป ... แต่โดยทั่วไปแล้วมันดูดีมาก ฉันสามารถเข้าใจได้แม้จะอยู่ในรูปแบบของสนามกอล์ฟ ลองดูว่าข้อผิดพลาดสามารถแก้ไขได้หรือไม่ถ้าไม่เป็นไรเพราะรหัสอธิบายตัวเองได้ดีอาจเป็นคำตอบที่เลือกเนื่องจากไม่มีรายการอื่น
Thanasis Papoutsidakis
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.