Kompilasi Regex

17

Dalam tugas ini Anda harus menulis sebuah program yang membaca ekspresi reguler dan menghasilkan program lain yang menampilkan apakah string input diterima oleh ekspresi reguler itu. Outputnya harus berupa program yang ditulis dalam bahasa yang sama dengan kiriman Anda.

Memasukkan

Input adalah ekspresi reguler r cocok dengan ABNF berikut (aturan produksi awal adalah REGEX):

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

Jika input tidak cocok dengan tata bahasa ini, perilaku program Anda tidak ditentukan.

Penafsiran

Menafsirkan input sebagai ekspresi reguler, di mana bintang *-Kleene (artinya mengulangi argumen kiri nol kali atau lebih ), |merupakan alternatif, (dan )grup dan tidak ada operator yang digabungkan. Pengelompokan lebih diutamakan daripada bintang, bintang lebih diutamakan daripada penggabungan, penggabungan lebih diutamakan daripada alternatif.

Suatu string dikatakan diterima jika regex cocok dengan keseluruhan string.

Keluaran

Output program ini adalah program lain yang ditulis dalam bahasa yang sama seperti kiriman Anda yang membaca string s dengan cara implementasi didefinisikan pada saat runtime, output apakah r menerima s dan kemudian berakhir. Output dapat dilakukan dengan cara yang ditentukan pengguna meskipun harus ada hanya dua output yang berbeda untuk program yang diterima dan ditolak.

Anda dapat mengasumsikan bahwa input dari program output Anda tidak pernah lebih dari 2 16 -1 Bytes.

Batasan

Baik kiriman Anda maupun program apa pun yang dihasilkan oleh kiriman Anda tidak dapat menggunakan fungsionalitas bawaan atau pustaka itu

  • mencocokkan regex
  • mengubah ekspresi reguler
  • kompilasi ekspresi reguler
  • menghasilkan parser dari tata bahasa
  • sederhanakan masalahnya dengan cara agar pengiriman Anda menjadi sepele

Mencetak gol

Skor kiriman Anda adalah jumlah karakternya. Pengajuan dengan skor terendah akan menang.

Testcases

Semua testcases berisi ekspresi reguler, satu set string yang diterima, satu set string yang ditolak dan contoh program di C99 yang merupakan output yang valid dari pengiriman C99 (hyptothetical) C99.

(ekspresi reguler kosong)

String yang diterima

  1. (input kosong)

String yang ditolak

  1. foo
  2. batang
  3. baz
  4. quux

Contoh program

#include <stdio.h>

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

    return input[0] != 0;
}

(b|)(ab)*(a|)( adan bberganti - ganti)

string yang diterima

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

string yang ditolak

  1. afba
  2. foo
  3. babba

contoh program

#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) (angka titik mengambang biner)

string yang diterima

  1. 10110100
  2. 0
  3. 1A00001

string yang ditolak

  1. 011
  2. 10A
  3. 1A00
  4. 100A010
FUZxxl
sumber
1
Saya berasumsi bahwa memiliki program seperti return (regex.match(stdin) is not null)tidak diperbolehkan.
beary605
1
Anda mengatakan bahwa "Output harus berupa program yang ditulis dalam bahasa yang sama dengan input", tetapi inputnya adalah regex. Dan tata bahasa yang Anda berikan tidak termasuk aturan GROUP, yang mungkin mendefinisikan tanda kurung literal.
Peter Taylor
@ Peter Maaf, saya bermaksud menulis mereka bahasa yang sama dengan kiriman.
FUZxxl
@ beary605 Ya, Anda benar. Lihat bagian Pembatasan : Baik kiriman Anda maupun program apa pun yang dihasilkan oleh kiriman Anda tidak dapat menggunakan fungsionalitas bawaan atau pustaka yang cocok dengan regexes (...).
FUZxxl
Saya pikir program contoh kedua Anda salah, ia kehilangan loop di sekitar saklar luar
Hasturkun

Jawaban:

8

Ruby, 641 651 543 karakter

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}]!=[]"

Versi ruby ​​ini menjadi cukup panjang karena beberapa kasus sudut di parser regex (mungkin saya harus mencoba pendekatan yang berbeda). Ia mengharapkan ekspresi reguler pada STDIN dan mengeluarkan kode ruby ​​yang sesuai untuk korek api ke STDOUT.

Program ini secara langsung menghasilkan kode untuk NFA-ε yang kemudian dieksekusi dalam korek api.

Test case 1: (output termasuk baris baru dan lekukan)

>>>

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

Uji kasus 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]!=[]

Contoh lain:

>>> 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]!=[]

Sunting: Menambahkan transisi untuk memperbaiki bug PleaseStand yang tercatat di komentar. Juga mengubah inisialisasi keadaan.

Howard
sumber
Input 011untuk (0|1(0|1)*)(|A(0|1)*1)hasil regex di true- seharusnya false.
PleaseStand
@ PleaseStand Fixed. Silakan lihat edit saya.
Howard
12

C, 627 karakter

Program ini memperlakukan argumen baris perintah pertama sebagai input dan menghasilkan kode C sebagai hasilnya.

#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]));}");}

Ini adalah output untuk (0|1(0|1)*)(|A(0|1)*1)(dengan baris baru ditambahkan):

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

Jika Anda memberikan input yang valid sebagai argumen baris perintah pertama, ia mengembalikan status keluar 1. Jika tidak, ia mengembalikan status keluar 0.

$ ./regexcompiler '(0 | 1 (0 | 1) *) (| A (0 | 1) * 1)'> floatprog.c
$ gcc -o floatprog floatprog.c
floatprog.c: Dalam fungsi 'main':
floatprog.c: 1: 519: peringatan: deklarasi implisit yang tidak kompatibel dari fungsi bawaan 'keluar' [diaktifkan secara default]
$ ./floatprog '1A00001' && echo invalid || menggemakan berlaku
valid
$ ./floatprog '100A010' && gema valid || gema valid
tidak valid

Program mana pun, jika Anda gagal memberikan argumen baris perintah, referensi referensi kosong, menyebabkan kesalahan segmentasi. Regex yang cukup panjang akan membanjiri buffer dari submission ini, dan ukuran input ke program yang dihasilkan dibatasi oleh ukuran stack. Namun, semua kasus uji berfungsi.

Perhatikan bahwa e(g=h++,C=h++,0,0);memperkenalkan perilaku yang tidak terdefinisi. Jika, misalnya, program yang dihasilkan tidak dikompilasi, Anda dapat mencoba mengganti pernyataan dengan h+=2;e(g=h-1,C=h-2,0,0);, yang lebih panjang lima karakter.

Mohon berdiri
sumber