รวบรวม Regexes


17

ในภารกิจนี้คุณต้องเขียนโปรแกรมที่อ่านนิพจน์ปกติและสร้างโปรแกรมอื่นที่แสดงว่านิพจน์ปกตินั้นยอมรับอินพุตหรือไม่ ผลลัพธ์จะต้องเป็นโปรแกรมที่เขียนด้วยภาษาเดียวกันกับที่คุณส่ง

อินพุต

อินพุตคือการแสดงออกปกติRจับคู่ ABNF ดังต่อไปนี้ (กฎการผลิตเริ่มต้นREGEX):

REGEX       = *( STAR / GROUP / LITERAL / ALTERNATIVE )
STAR        = REGEX '*'
GROUP       = '(' REGEX ')'
LITERAL     = ALPHA / DIGIT
ALTERNATIVE = REGEX '|' REGEX

หากอินพุตไม่ตรงกับไวยากรณ์นี้พฤติกรรมของโปรแกรมของคุณจะไม่ได้กำหนด

การตีความ

ป้อนข้อมูลเป็นนิพจน์ปกติโดยที่*Kleene-star (หมายถึงอาร์กิวเมนต์ซ้ายซ้ำแล้วซ้ำอีกเป็นศูนย์หรือมากกว่านั้น ) |เป็นอีกทางเลือกหนึ่ง(และ)จัดกลุ่มและไม่มีตัวดำเนินการใด ๆ ที่ต่อกัน การจัดกลุ่มจะมีความสำคัญเหนือกว่าดาวฤกษ์จะมีความสำคัญเหนือกว่าการต่อเรียงและการเรียงต่อกันจะมีความสำคัญมากกว่าทางเลือก

สตริงมีการกล่าวถึงว่าเป็นที่ยอมรับถ้า regex ตรงกับสตริงทั้งหมด

เอาท์พุต

เอาท์พุทของโปรแกรมคือโปรแกรมอื่นที่เขียนในภาษาเดียวกับการส่งของคุณที่อ่านสตริงsในวิธีการดำเนินงานที่กำหนดไว้ที่รันไทม์ผลไม่ว่าจะเป็นอาร์ยอมรับsและยุติแล้ว เอาต์พุตอาจทำได้ในลักษณะที่ผู้ใช้กำหนดแม้ว่าจะต้องมีเอาต์พุตที่แตกต่างกันเพียงสองตัวสำหรับโปรแกรมที่ยอมรับและถูกปฏิเสธ

คุณอาจสมมติว่าอินพุตของโปรแกรมเอาต์พุตของคุณไม่เกิน 2 16 -1 Bytes

ข้อ จำกัด

ทั้งการส่งของคุณหรือโปรแกรมใด ๆ ที่สร้างขึ้นโดยการส่งของคุณอาจใช้ฟังก์ชันการทำงานในตัวหรือไลบรารีที่

  • จับคู่ regexes
  • แปลงการแสดงออกปกติ
  • รวบรวมการแสดงออกปกติ
  • สร้างตัวแยกวิเคราะห์จากไวยากรณ์
  • ลดความซับซ้อนของปัญหาในวิธีที่การส่งของคุณกลายเป็นเรื่องไม่สำคัญ

เกณฑ์การให้คะแนน

คะแนนการส่งของคุณคือจำนวนตัวอักษร การส่งที่มีคะแนนต่ำสุดชนะ

Testcases

ชุดทดสอบทั้งหมดประกอบด้วยนิพจน์ทั่วไปชุดของสตริงที่ยอมรับชุดของสตริงที่ถูกปฏิเสธและโปรแกรมตัวอย่างใน C99 ซึ่งเป็นเอาต์พุตที่ถูกต้องของการส่ง C99 (แบบ Hyptothetical)

(ว่างเปล่านิพจน์ปกติ)

สตริงที่ยอมรับ

  1. (อินพุตว่าง)

สตริงที่ถูกปฏิเสธ

  1. foo
  2. บาร์
  3. baz
  4. quux

โปรแกรมตัวอย่าง

#include <stdio.h>

int main() {
    char input[65536];
    gets(input);

    return input[0] != 0;
}

(b|)(ab)*(a|)( aและbสลับ)

สตริงที่ยอมรับ

  1. a
  2. ba
  3. abababababa
  4. abab

ปฏิเสธสตริง

  1. afba
  2. foo
  3. babba

โปรแกรมตัวอย่าง

#include <stdio.h>

int main() {
  char input[65536];
  int state = 0;

  for (;;) switch (state) {
    case 0: switch (getchar()) {
      case 'a': state = 1; break;
      case 'b': state = 2; break;
      case EOF: return 0;
      default:  return 1;
    } break;
    case 1: switch (getchar()) {
      case 'b': state = 2; break;
      case EOF: return 0;
      default:  return 1;
    } break;
    case 2: switch (getchar()) {
      case 'a': state = 1; break;
      case EOF: return 0;
      default:  return 1;
    } break;
}

(0|1(0|1)*)(|A(0|1)*1) (ตัวเลขทศนิยมเลขฐานสอง)

สตริงที่ยอมรับ

  1. 10110100
  2. 0
  3. 1A00001

ปฏิเสธสตริง

  1. 011
  2. 10A
  3. 1A00
  4. 100A010

1
ฉันคิดว่าการมีโปรแกรมเช่นreturn (regex.match(stdin) is not null)นี้ไม่ได้รับอนุญาต
beary605

1
คุณบอกว่า "ผลลัพธ์จะต้องเป็นโปรแกรมที่เขียนในภาษาเดียวกันกับอินพุต" แต่อินพุตนั้นเป็น regex และไวยากรณ์ที่คุณให้ไว้ไม่ได้รวมกฏ GROUP ซึ่งน่าจะเป็นตัวกำหนดที่แท้จริง
ปีเตอร์เทย์เลอร์

@Peter ขออภัยฉันต้องการเขียนภาษาเดียวกับที่ส่งมา
FUZxxl

@ beary605 ใช่คุณพูดถูก ดูข้อ จำกัดส่วน: ทั้งการส่งของคุณและโปรแกรมใด ๆ ที่สร้างโดยการส่งของคุณอาจใช้ฟังก์ชันการทำงานในตัวหรือไลบรารีที่ตรงกับ regexes (... )
FUZxxl

ฉันคิดว่าโปรแกรมตัวอย่างที่สองของคุณไม่ถูกต้องมันขาดการวนรอบสวิตช์ด้านนอก
Hasturkun

คำตอบ:


8

Ruby, 641 651 543 ตัวละคร

H=Hash.new{|h,k|[k]}
d=[[i=0,0,[]]]
o=[?(]
L="t,u,v=d.pop;q,r,s=d.pop;o.pop<?|&&(H[r]<<=t)||(H[q]<<=t;H[r]<<=u);d<<[q,u,s+v]"
gets.chop.chars.map{|c|c==?*&&(q,r,s=d.pop;H[r]|=[q,i+=1];d<<=[r,i,s];next)
eval(L)while/[|)]/=~c ?o[-1]>?(:o[-1]==?.
/[|(]/=~c&&d<<[i+=1,i,o<<c&&[]]||c!=?)&&d<<[i+=1,i+1,["s==#{o<<?.;i}&&c=='#{c}'&&#{i+=1}"]]||o[-1]=?.}
eval(L)while o.size>1
H.map{H.map{|k,v|v.map{|v|H[k]|=H[v]}}}
t,u,v=d[0]
$><<"s=#{H[t]};gets.chop.chars.map{|c|s=s.map{|s|#{v*'||'}}-[!0];#{H.map{|k,v|"s&[#{k}]!=[]&&s|=#{v}"}*?;}};p s&[#{u}]!=[]"

ทับทิมรุ่นนี้ค่อนข้างยาวเพราะมีหลายมุมในตัวแยกวิเคราะห์ regex (บางทีฉันควรลองใช้วิธีอื่น) มันคาดว่าการแสดงออกปกติใน STDIN และส่งออกรหัสทับทิมที่สอดคล้องกันสำหรับ matcher เพื่อ STDOUT

โปรแกรมสร้างรหัสโดยตรงสำหรับNFA-εซึ่งจะถูกดำเนินการใน matcher

กรณีทดสอบ 1: (เอาต์พุตรวมถึงการขึ้นบรรทัดใหม่และการเยื้องเพิ่มเติม)

>>>

s=[0];
gets.chop.chars.map{|c|
  s=s.map{|s|}-[!0];
};
p s&[0]!=[]

กรณีทดสอบ 2:

>>> (b|)(ab)*(a|)

s=[0, 1, 2, 4, 9, 5, 10, 6, 11, 12, 14];
gets.chop.chars.map{|c|
  s=s.map{|s|s==2&&c=='b'&&3||s==6&&c=='a'&&7||s==8&&c=='b'&&9||s==12&&c=='a'&&13}-[!0];
  s&[1]!=[]&&s|=[1, 2, 4, 9, 5, 10, 6, 11, 12, 14];
  s&[3]!=[]&&s|=[3, 4, 9, 5, 10, 6, 11, 12, 14];
  s&[0]!=[]&&s|=[0, 1, 2, 4, 9, 5, 10, 6, 11, 12, 14];
  s&[5]!=[]&&s|=[5, 6];
  s&[7]!=[]&&s|=[7, 8];
  s&[9]!=[]&&s|=[9, 5, 10, 6, 11, 12, 14];
  s&[4]!=[]&&s|=[4, 9, 5, 10, 6, 11, 12, 14];
  s&[11]!=[]&&s|=[11, 12, 14];
  s&[13]!=[]&&s|=[13, 14];
  s&[10]!=[]&&s|=[10, 11, 12, 14]
};
p s&[14]!=[]

ตัวอย่างอื่น:

>>> a|bc

s=[0, 1, 3, 4];
gets.chop.chars.map{|c|
  s=s.map{|s|s==1&&c=='a'&&2||s==4&&c=='b'&&5||s==6&&c=='c'&&7}-[!0];
  s&[0]!=[]&&s|=[0, 1, 3, 4];
  s&[3]!=[]&&s|=[3, 4];
  s&[5]!=[]&&s|=[5, 6];
  s&[2]!=[]&&s|=[2, 7]
};
p s&[7]!=[]

แก้ไข:เพิ่มช่วงการเปลี่ยนภาพเพื่อแก้ไขข้อบกพร่องPleaseStand ที่ระบุไว้ในความคิดเห็น เปลี่ยนสถานะเริ่มต้นด้วยเช่นกัน


การป้อนข้อมูล011สำหรับ regex (0|1(0|1)*)(|A(0|1)*1)ผลลัพธ์ในtrue- falseมันควรจะเป็น
กรุณามาถึง

@ โปรดยืนแก้ไข โปรดดูการแก้ไขของฉัน
ฮาวเวิร์ด

12

C, 627 ตัวอักษร

โปรแกรมนี้ใช้อาร์กิวเมนต์บรรทัดคำสั่งแรกเป็นอินพุตและสร้างรหัส C เป็นเอาต์พุต

#define A(v) F[v]+strlen(F[v])
#define S sprintf
char*B="&&f%d(s)||f%d(s)",*J="&&f%d(s+%d)",*r,F[256][65536];h=2;e(f,n,o,R,C,O,t,g){for(C=O=f;*r;++r)switch(*r){case 40:r++;e(g=h++,C=h++,0,0);r[1]^42?t=g:(t=C,S(A(t),B,g,C=h++),r++);o=!S(A(O),J,t,o);O=C;break;case 41:goto L;case'|':S(A(C),J,n,o);o=!S(A(O=f),"||1");break;default:r[1]^42?S(A(C),"&&s[%d]==%d",o++,*r,O^f||R++):(o=!S(A(O),J,t=h++,o),O=C=h++,g=h++,S(A(g),"&&*s==%d&&f%d(s+1)",*r++,t),S(A(t),B,g,C));}L:S(A(C),J,n,o);}main(int c,char**v){r=v[1];for(e(1,!S(*F,"&&!*s"),0,0);h--;)printf("f%d(char*s){return 1%s;}",h,F[h]);puts("main(int c,char**v){exit(f1(v[1]));}");}

นี่คือผลลัพธ์สำหรับ(0|1(0|1)*)(|A(0|1)*1)(โดยเพิ่มบรรทัดใหม่):

f11(char*s){return 1&&s[0]==49&&f7(s+1);}
f10(char*s){return 1&&s[0]==48&&f9(s+1)||1&&s[0]==49&&f9(s+1);}
f9(char*s){return 1&&f10(s)||f11(s);}
f8(char*s){return 1&&f7(s+0)||1&&s[0]==65&&f9(s+1);}
f7(char*s){return 1&&f0(s+0);}
f6(char*s){return 1&&f2(s+0);}
f5(char*s){return 1&&s[0]==48&&f4(s+1)||1&&s[0]==49&&f4(s+1);}
f4(char*s){return 1&&f5(s)||f6(s);}
f3(char*s){return 1&&s[0]==48&&f2(s+1)||1&&s[0]==49&&f4(s+1);}
f2(char*s){return 1&&f8(s+0);}
f1(char*s){return 1&&f3(s+0);}
f0(char*s){return 1&&!*s;}
main(int c,char**v){exit(f1(v[1]));}

หากคุณระบุอินพุตที่ถูกต้องเป็นอาร์กิวเมนต์บรรทัดคำสั่งแรกมันจะส่งกลับสถานะการออก 1 มิฉะนั้นจะส่งกลับสถานะการออก 0

$ ./regexcompiler '(0 | 1 (0 | 1) *) (| A (0 | 1) * 1)'> floatprog.c
$ gcc -o floatprog floatprog.c
floatprog.c: ในฟังก์ชั่น 'main':
floatprog.c: 1: 519: คำเตือน: การประกาศโดยนัยที่เข้ากันไม่ได้ของฟังก์ชันในตัว 'ออก' [เปิดใช้งานโดยค่าเริ่มต้น]
$ ./floatprog '1A00001' && echo ไม่ถูกต้อง || echo ที่ถูกต้องถูก
ต้อง
$ ./floatprog '100A010' && echo ไม่ถูกต้อง || echo
ไม่ถูกต้อง

โปรแกรมใดโปรแกรมหนึ่งหากคุณไม่สามารถระบุอาร์กิวเมนต์ของบรรทัดคำสั่งให้ทำการพอยน์เตอร์พอยน์เตอร์แบบ null ทำให้เกิดความผิดพลาดในการแบ่งเซกเมนต์ regex ที่ยาวพอจะโอเวอร์โฟลว์บัฟเฟอร์ของการส่งนี้และขนาดของอินพุตไปยังโปรแกรมที่สร้างจะถูก จำกัด โดยขนาดของสแต็ก อย่างไรก็ตามกรณีทดสอบทั้งหมดใช้งานได้

โปรดทราบว่าe(g=h++,C=h++,0,0);แนะนำพฤติกรรมที่ไม่ได้กำหนด ตัวอย่างเช่นหากโปรแกรมที่สร้างขึ้นไม่คอมไพล์คุณสามารถลองแทนที่คำสั่งด้วยh+=2;e(g=h-1,C=h-2,0,0);ซึ่งมีความยาวห้าอักขระ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.