Biên dịch Regexes


17

Trong tác vụ này, bạn phải viết một chương trình đọc biểu thức chính quy và tạo một chương trình khác để xem liệu chuỗi đầu vào có được chấp nhận bởi biểu thức chính quy đó không. Đầu ra phải là một chương trình được viết bằng cùng ngôn ngữ với nội dung bạn gửi.

Đầu vào

Đầu vào là biểu thức chính quy r khớp với ABNF sau (quy tắc sản xuất ban đầu là REGEX):

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

Nếu đầu vào không khớp với ngữ pháp này, hành vi của chương trình của bạn không được xác định.

Diễn dịch

Giải thích đầu vào dưới dạng một biểu thức chính quy, trong đó *ngôi sao Kleene (có nghĩa là lặp lại đối số bên trái bằng 0 lần trở lên ), |là một thay thế, ()nhóm và không có toán tử nào được nối. Nhóm được ưu tiên hơn sao, sao được ưu tiên hơn so với ghép, ghép được ưu tiên thay thế.

Một chuỗi được cho là được chấp nhận nếu regex khớp với toàn bộ chuỗi.

Đầu ra

Đầu ra của chương trình là một chương trình khác được viết bằng cùng ngôn ngữ với nội dung gửi của bạn đọc một chuỗi s theo cách xác định trong thời gian chạy, xuất ra liệu r có chấp nhận s và sau đó chấm dứt. Đầu ra có thể được thực hiện theo cách do người dùng xác định mặc dù chỉ có hai đầu ra riêng biệt cho các chương trình được chấp nhận và bị từ chối.

Bạn có thể cho rằng đầu vào của chương trình đầu ra của bạn không bao giờ dài hơn 2 16 -1 byte.

Những hạn chế

Việc gửi của bạn cũng như bất kỳ chương trình nào được tạo bởi trình của bạn đều có thể sử dụng chức năng dựng sẵn hoặc thư viện

  • chế độ phù hợp
  • biến đổi biểu thức chính quy
  • biên dịch biểu thức chính quy
  • tạo các trình phân tích cú pháp từ một ngữ pháp
  • đơn giản hóa vấn đề theo cách mà trình của bạn trở nên tầm thường

Chấm điểm

Điểm của bài nộp của bạn là số lượng ký tự. Bài nộp với số điểm thấp nhất sẽ thắng.

Tủ thử

Tất cả các testcase đều chứa một biểu thức chính quy, một chuỗi các chuỗi được chấp nhận, một chuỗi các chuỗi bị từ chối và một chương trình ví dụ trong C99 là đầu ra hợp lệ của một đệ trình C99 (giả thuyết).

(biểu thức chính quy trống)

Chuỗi được chấp nhận

  1. (đầu vào trống)

Chuỗi bị từ chối

  1. foo
  2. quán ba
  3. baz
  4. quux

Chương trình ví dụ

#include <stdio.h>

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

    return input[0] != 0;
}

(b|)(ab)*(a|)( abxen kẽ)

chuỗi được chấp nhận

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

chuỗi bị từ chối

  1. afba
  2. foo
  3. babba

chương trình ví dụ

#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) (số dấu phẩy động nhị phân)

chuỗi được chấp nhận

  1. 10110100
  2. 0
  3. 1A00001

chuỗi bị từ chối

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

1
Tôi đoán rằng có một chương trình như thế return (regex.match(stdin) is not null)là không được phép.
beary605

1
Bạn nói rằng "Đầu ra phải là một chương trình được viết bằng cùng ngôn ngữ với đầu vào", nhưng đầu vào là một biểu thức chính quy. Và ngữ pháp bạn cung cấp không bao gồm NHÓM quy tắc, có lẽ xác định dấu ngoặc đơn.
Peter Taylor

@Peter Xin lỗi, tôi có nghĩa là viết chúng cùng ngôn ngữ với bài nộp.
FUZxxl

@ beary605 Vâng, bạn nói đúng. Xem phần Hạn chế : Không phải trình của bạn cũng như bất kỳ chương trình nào được tạo bởi trình của bạn có thể sử dụng chức năng dựng sẵn hoặc thư viện phù hợp với biểu thức chính quy (...).
FUZxxl

Tôi nghĩ rằng chương trình ví dụ thứ hai của bạn là không chính xác, nó thiếu một vòng lặp xung quanh công tắc bên ngoài
Hasturkun

Câu trả lời:


8

Ruby, 641 651 543 ký tự

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

Phiên bản ruby ​​này trở nên khá dài do một số trường hợp góc trong trình phân tích cú pháp regex (có lẽ tôi nên thử một cách tiếp cận khác). Nó mong đợi biểu thức chính quy trên STDIN và xuất mã ruby ​​tương ứng cho công cụ đối sánh thành STDOUT.

Chương trình trực tiếp tạo mã cho một NFA- then sau đó được thực thi trong trình so khớp.

Trường hợp thử nghiệm 1: (đầu ra bao gồm các dòng mới và thụt lề)

>>>

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

Trường hợp thử nghiệm 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]!=[]

Một vi dụ khac:

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

Chỉnh sửa: Đã thêm một chuyển đổi để sửa lỗi Xin vui lòng lưu ý trong các bình luận. Cũng thay đổi khởi tạo của nhà nước.


Đầu vào 011cho (0|1(0|1)*)(|A(0|1)*1)kết quả regex trong true- nó nên được false.
Xin vui lòng vào

@PleaseStand Đã sửa. Xin vui lòng xem chỉnh sửa của tôi.
Howard

12

C, 627 ký tự

Chương trình này coi đối số dòng lệnh đầu tiên của nó là đầu vào và tạo mã C làm đầu ra của nó.

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

Đây là đầu ra của nó cho (0|1(0|1)*)(|A(0|1)*1)(có thêm dòng mới):

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

Nếu bạn cung cấp đầu vào hợp lệ làm đối số dòng lệnh đầu tiên, nó sẽ trả về trạng thái thoát 1. Nếu không, nó sẽ trả về trạng thái thoát 0.

$ ./regexcompiler '(0 | 1 (0 | 1) *) (| A (0 | 1) * 1)'> floatprog.c
$ gcc -o floatprog.c
floatprog.c: Trong chức năng 'chính':
floatprog.c: 1: 519: cảnh báo: khai báo ngầm không tương thích của hàm tích hợp 'exit' [được bật theo mặc định]
$ ./floatprog '1A00001' && echo không hợp lệ || echo hợp lệ hợp
lệ
$ ./floatprog '100A010' && echo không hợp lệ | | echo hợp
lệ không hợp lệ

Cả hai chương trình, nếu bạn không cung cấp đối số dòng lệnh, hãy hủy bỏ một con trỏ null, gây ra lỗi phân đoạn. Một regex đủ dài sẽ tràn bộ đệm của bài nộp này và kích thước của đầu vào cho một chương trình được tạo bị giới hạn bởi kích thước của ngăn xếp. Tuy nhiên, tất cả các trường hợp thử nghiệm làm việc.

Lưu ý rằng e(g=h++,C=h++,0,0);giới thiệu hành vi không xác định. Ví dụ, nếu các chương trình được tạo không biên dịch, bạn có thể thử thay thế câu lệnh bằng h+=2;e(g=h-1,C=h-2,0,0);, dài hơn năm ký tự.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.