Складіть реджекси


17

У цьому завданні ви повинні написати програму, яка читає регулярний вираз і генерує іншу програму, яка виводить, чи приймається введення рядка цим регулярним виразом. Вихід повинен бути програмою, написаною тією ж мовою, що і ваша публікація.

Вхідні дані

Вхід - це регулярний вираз r, що відповідає наступному ABNF (початкове правило виробництва REGEX):

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

Якщо введення не відповідає цій граматиці, поведінка вашої програми не визначена.

Інтерпретація

Інтерпретуйте вхід як регулярний вираз, де *зірка Клінова (що означає повтор лівого аргументу нульовий або більше разів ), |є альтернативою, (а )група і жоден оператор зовсім не об'єднані. Групування має перевагу над зіркою, зірка має перевагу над конкатенацією, конкатенація має перевагу над альтернативною.

Кажуть, що рядок прийнято, якщо регулярний вираз відповідає всій рядку.

Вихідні дані

Вихід програми - це інша програма, написана на тій самій мові, що і ваша заявка, яка читає рядок s у визначеному для виконання режимі під час виконання, виводить, чи приймає r , а потім припиняє. Вихід може бути виконаний визначеним користувачем способом, хоча для прийнятих та відхилених програм повинно бути лише два різних виходи.

Ви можете припустити, що вхід вашої вихідної програми ніколи не перевищує 2 16 -1 байт.

Обмеження

Ні ваша програма, ні будь-яка програма, породжена вашим поданням, не можуть використовувати вбудовані функціональні можливості або бібліотеки

  • відповідні регекси
  • перетворити регулярні вирази
  • складати регулярні вирази
  • генерувати парсери з граматики
  • спростіть проблему таким чином, щоб ваше подання стало банальним

Оцінка балів

Оцінка вашого подання - це кількість символів. Виграє подання з найменшим балом.

Тестові шафи

Усі тестові масиви містять регулярний вираз, набір прийнятих рядків, набір відхилених рядків та приклад програми на C99, яка є дійсним висновком (гіпотетичного) подання C99.

(порожній регулярний вираз)

Приймаються рядки

  1. (порожній вхід)

Відхилені рядки

  1. foo
  2. бар
  3. баз
  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. 10А
  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: попередження: несумісне неявне декларування вбудованої функції 'вихід' [увімкнено за замовчуванням]
$ ./floatprog '1A00001' && echo недійсне || дійсне дійсне
дійсне
$ ./floatprog '100A010' && echo недійсне || відлуння дійсний
недійсний

Будь-яка програма, якщо не надати аргумент командного рядка, виводить нульовий вказівник, викликаючи помилку сегментації. Досить тривалий регулярний вираз буде переповнювати буфери цього подання, а розмір вводу в генеровану програму обмежений розміром стека. Однак усі тестові справи працюють.

Зауважте, що e(g=h++,C=h++,0,0);вводиться невизначена поведінка. Наприклад, якщо згенеровані програми не компілюються, ви можете спробувати замінити оператор на h+=2;e(g=h-1,C=h-2,0,0);, який на п'ять символів довший.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.