Россия, Пошатово |
Контекстно-свободные грамматики
15.2.5. Используя сказанное, составить процедуру распознавания выражений для грамматики (пример 3, см. "пункт 15.1." ):

Решение. Эта грамматика не полностью подпадает под рассмотренные частные случаи: в правых частях есть комбинации терминалов и нетерминалов









procedure ReadK; begin | ReadL; | if b then begin | | ReadM; | end; | if b then begin | | ReadN; | end; end;
Для ее корректности надо, чтобы Посл(L) не пересекалось с Нач(MN) (которое равно Нач(M), если из M не выводится пустое слово, и равно объединению Нач(M) и Нач(N), если выводится), а также чтобы Посл(M) не пересекалось с Нач(N).
Аналогичным образом правила

procedure ReadK; begin | if (Next принадлежит Нач(LMN)) then begin | | ReadL; | | if b then begin ReadM; end; | | if b then begin ReadN; end; | end else if (Next принадлежит Нач(PQ)) then begin | | ReadP; | | if b then begin ReadQ; end; | end else begin | | b := true; | end; end;
корректность которой требует, чтобы Нач(LMN) не пересекалось с Нач(PQ).
Читая приведенную далее программу, полезно иметь в виду соответствие между русскими и английскими словами:

procedure ReadSymb (c: Symbol); | b := (Next = c); | if b then begin | | Move; | end; end; procedure ReadExpr; | ReadAdd; | if b then begin ReadRestExpr; end; end; procedure ReadRestExpr; | if Next = '+' then begin | | ReadSymb ('+'); | | if b then begin ReadExpr; end; | end else begin | | b := true; | end; end; procedure ReadAdd; | ReadFact; | if b then begin ReadRestAdd; end; end; procedure ReadRestAdd; | if Next = '*' then begin | | ReadSymb ('*'); | | if b then begin ReadAdd; end; | end else begin | | b := true; | end; end; procedure ReadFact; | if Next = 'x' then begin | | ReadSymb ('x'); | end else if Next = '(' then begin | | ReadSymb ('('); | | if b then begin ReadExpr; end; | | if b then begin ReadSymb (')'); end; | end else begin | | b := false; | end; end;
Осталось обсудить проблемы, связанные с взаимной рекурсивностью этих процедур (одна использует другую и наоборот). В паскале это допускается, только требуется дать предварительное описание процедур ("forward"). Как всегда для рекурсивных процедур, помимо доказательства того, что каждая процедура работает
правильно в предположении, что используемые в ней вызовы
процедур работают правильно, надо доказать отдельно, что работа
завершается. (Это не очевидно: если в грамматике есть правило , то из K ничего не
выводится, Посл(K) и Нач(K) пусты, но написанная по нашим канонам процедура
procedure ReadK; begin | ReadK; | if b then begin | | ReadK; | end; end;
не заканчивает работы.)
В данном случае процедуры ReadRestExpr, ReadRestAdd, ReadFact либо завершаются, либо уменьшают длину непрочитанной части входа. Поскольку любой цикл вызовов включает одну из них, то зацикливание невозможно.