| Россия, Пошатово |
Контекстно-свободные грамматики
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 либо завершаются, либо уменьшают длину непрочитанной части входа. Поскольку любой цикл вызовов включает одну из них, то зацикливание невозможно.