
171
в этой главе я покажу вам новую структуру. Без сомнения вы будете счастливы узнать,
что хотя изменения влияют на многие процедуры, они не очень глубоки и поэтому мы
теряем не очень многое из того что было сделано до этого.
Как ни странно, новый сканер намного более стандартен чем старый и он очень похож
на более общий сканер, показанный мной ранее в главе 7. Вы должны помнить день,
когда я сказал: K-I-S-S!
ПРОБЛЕМА
Проблема начинает проявлять себя в процедуре Block, которую я воспроизвел ниже:
{ Parse and Translate a Block of Statements }
procedure Block;
begin
Scan;
while not(Token in ['e', 'l']) do begin
case Token of
'i': DoIf;
'w': DoWhile;
'R': DoRead;
'W': DoWrite;
else Assignment;
end;
Scan;
end;
end;
Как вы можете видеть, Block ориентирован на индивидуальные утверждения
программы. При каждом проходе цикла мы знаем, что мы находимся в начале
утверждения. Мы выходим из блока когда обнаруживаем END или ELSE.
Но предположим, что вместо этого мы встретили точку с запятой. Вышеуказанная
процедура не может ее обработать, так как процедура Scan ожидает и может принимать
только токены, начинающиеся с буквы.
Я повозился с этим немного чтобы найти исправление. Я нашел множество возможных
подходов, но ни один из них меня не удовлетворял. В конце концов я выяснил причину.
Вспомните, что когда мы начинали с наших односимвольных синтаксических
анализаторов, мы приняли соглашение, по которому предсказывающий символ должен
быть всегда предварительно считан. То есть, мы имели бы символ, соответствующий
нашей текущей позиции во входном потоке, помещенный в глобальной символьной
переменной Look, так что мы могли проверять его столько раз, сколько необходимо. По
правилу, которое мы приняли, каждый распознаватель, если он находил
предназначенный ему символ, перемещал бы Look на следующий символ во входном
потоке.
Это простое и фиксированное соглашение служило нам очень хорошо когда мы имели
односимвольные токены, и все еще служит. Был бы большой смысл применить то же
самое правило и к многосимвольным токенам.
Но когда мы залезли в лексический анализ, я начал нарушать это простое правило.
Сканер из Главы 10 действительно продвигался к следующему токену если он находил
идентификатор или ключевое слово, но он не делал этого если находил возврат каретки,
символ пробела или оператор.
Теперь, такой смешанный режим работы ввергает нас в глубокую проблему в
процедуре Block, потому что был или нет входной поток продвинут зависит от вида
встреченного нами токена. Если это ключевое слово или левая часть операции