编译正则表达式


17

在此任务中,您必须编写一个程序来读取正则表达式,并生成另一个程序,该程序输出该正则表达式是否接受输入字符串。输出必须是使用与提交相同的语言编写的程序。

输入值

输入是与以下ABNF匹配的正则表达式r(初始生产规则为REGEX):

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

如果输入与此语法不匹配,则程序的行为未定义。

解释

将输入解释为正则表达式,其中*的Kleene-star(表示重复左参数零次或多次|是替代方法,(并且)组且根本没有运算符串联在一起。分组优先于星形,星形优先于串联,串联优先于替代。

如果正则表达式匹配整个字符串,则认为该字符串可以接受

输出量

该程序的输出是另一个与您的提交使用相同语言编写的程序,该程序在运行时以实现定义的方式读取字符串s,输出r是否接受s然后终止。输出可以以用户定义的方式完成,尽管对于接受和拒绝的程序必须只有两个不同的输出。

您可以假定输出程序的输入从不超过2 16 -1字节。

限制条件

您的提交或提交的任何程序均不得使用内置功能或

  • 匹配正则表达式
  • 转换正则表达式
  • 编译正则表达式
  • 从语法生成解析器
  • 简化问题,使您的提交变得微不足道

计分

您提交的分数是它的字符数。得分最低的提交者获胜。

测试用例

所有测试用例均包含正则表达式,一组接受的字符串,一组拒绝的字符串以及C99中的示例程序,该程序是(假设的)C99提交的有效输出。

(空正则表达式)

接受的字符串

  1. (空输入)

拒绝弦

  1. 酒吧
  2. 巴兹
  3. ux

范例程序

#include <stdio.h>

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

    return input[0] != 0;
}

(b|)(ab)*(a|)ab交替)

接受的字符串

  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
您说“输出必须是用与输入相同的语言编写的程序”,但输入是正则表达式。而且您提供的语法不包含规则GROUP,该规则大概定义了文字括号。
彼得·泰勒

@Peter对不起,我的意思是写与提交内容相同的语言。
FUZxxl 2012年

@ beary605是的,你是对的。请参阅“ 限制:”部分:您的提交或提交所生成的任何程序均不得使用内置功能或与正则表达式(...)相匹配的库。
FUZxxl 2012年

我认为您的第二个示例程序不正确,它缺少外部开关周围的循环
Hasturkun 2012年

Answers:


8

红宝石,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}]!=[]"

由于正则表达式解析器中有一些极端的情况,因此这个红宝石版本变得相当长(也许我应该尝试其他方法)。它期望在STDIN上使用正则表达式,并将匹配器的相应红宝石代码输出到STDOUT。

该程序直接为NFA-ε生成代码,然后在匹配器中执行。

测试用例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]!=[]

编辑:添加了一个过渡,以修复请注释中指出的BugStand。也改变了状态的初始化。


011正则表达式的输入(0|1(0|1)*)(|A(0|1)*1)结果为true-应该为false
PleaseStand'8

@PleaseStand固定。请看我的编辑。
霍华德

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:警告:内置函数'exit'的隐式声明不兼容[默认启用]
$ ./floatprog'1A00001'&& echo invalid || echo有效
有效
$ ./floatprog'100A010'&& echo无效|| 回显有效
无效

如果您未能提供命令行参数,则这两个程序都会取消引用空指针,从而导致分段错误。足够长的正则表达式将使此提交的缓冲区溢出,并且所生成程序的输入大小受堆栈大小限制。但是,所有测试用例都可以工作。

注意 e(g=h++,C=h++,0,0);引入未定义的行为。例如,如果生成的程序无法编译,则可以尝试将语句替换为h+=2;e(g=h-1,C=h-2,0,0);,这又是五个字符。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.