正規表現のコンパイル


17

このタスクでは、正規表現を読み取り、その正規表現が入力文字列を受け入れるかどうかを出力する別のプログラムを生成するプログラムを作成する必要があります。出力は、提出物と同じ言語で書かれたプログラムでなければなりません。

入力

入力は、次のABNFに一致する正規表現rです(最初の生産規則はですREGEX)。

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

入力がこの文法と一致しない場合、プログラムの動作は未定義です。

解釈

入力を正規表現として解釈します。ここ*で、Kleene-star(左引数を0回以上繰り返すことを意味します|は代替で(あり、)グループであり、演算子はまったく連結されていません。グループ化はスターより優先され、スターは連結より優先され、連結は代替より優先されます。

正規表現が文字列全体と一致する場合、文字列は受け入れられると言われます。

出力

プログラムの出力は、文字列読み込み、あなたの投稿と同じ言語で書かれた別のプログラムであるかどうか、実行時に方法を定義した実装では、出力rは受け入れを終了。出力はユーザー定義の方法で実行できますが、受け入れられたプログラムと拒否されたプログラムの出力は2つだけでなければなりません。

出力プログラムの入力が2 16 -1バイトより長くなることはないと想定できます。

制限事項

提出物も、提出物によって生成されたプログラムも、組み込み機能またはライブラリを使用できません。

  • 正規表現に一致
  • 正規表現を変換する
  • 正規表現をコンパイルする
  • 文法からパーサーを生成する
  • 提出物が簡単になるように問題を簡素化する

得点

提出のスコアは、その文字の数です。最低スコアの提出が勝ちです。

テストケース

すべてのテストケースには、正規表現、受け入れられた文字列のセット、拒否された文字列のセット、および(仮想的な)C99提出の有効な出力であるC99のサンプルプログラムが含まれています。

(空の正規表現)

受け入れられた文字列

  1. (空の入力)

拒否された文字列

  1. foo
  2. バー
  3. バズ
  4. クックス

サンプルプログラム

#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) (2進浮動小数点数)

受け入れられた文字列

  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

@ beary605ええ、あなたは正しいです。セクション制限を参照してください:提出物も、提出物によって生成されたプログラムも、組み込み機能または正規表現に一致するライブラリを使用できません(...)。
FUZxxl

私はそれが外側のスイッチの周りにループを欠けている、あなたの第二の例のプログラムが間違っていると思う
Hasturkun

回答:


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

編集:コメントに記載されているPleaseStandバグを修正するための移行を追加しました。状態の初期化も変更されました。


011正規表現の入力(0|1(0|1)*)(|A(0|1)*1)結果はtrue-であるべきですfalse
プリーズスタンド

@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 || 有効
なエコー
$ ./floatprog '100A010' &&エコー無効|| エコー有効
無効

どちらのプログラムも、コマンドライン引数の提供に失敗すると、nullポインターを逆参照し、セグメンテーション違反を引き起こします。十分に長い正規表現はこの送信のバッファをオーバーフローさせ、生成されたプログラムへの入力のサイズはスタックのサイズによって制限されます。ただし、すべてのテストケースは機能します。

e(g=h++,C=h++,0,0);未定義の動作が導入されることに注意してください。たとえば、生成されたプログラムがコンパイルされない場合は、ステートメントをh+=2;e(g=h-1,C=h-2,0,0);5文字長のに置き換えてみてください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.