Лекции по построению компилятора на Pascal
составляют операторы отношений >=, <= и <>. Было бы позором обрабатывать все
операторы как строки и выполнять сравнение строк когда почти всегда удовлетворит
сравнение одиночных символов. Второе, и более важное, программа не работает, когда два
оператора появляются вместе как в (a+b)*(c+d). Здесь строка после b была бы
интерпретирована как один оператор ")*(".
Можно устранить эту проблему. К примеру мы могли бы просто дать GetOp список
допустимых символов и обрабатывать скобки как отличный от других тип операторов. Но
это хлопотное дело.
К счастью, имеется лучший способ, который решает все эти проблемы. Так как почти все
операторы одно-символьные, давайте просто позволим GetOp получать только один символ
одновременно. Это не только упрощает GetOp, но также немного ускоряет программу. У нас
все еще остается проблема операторов отношений, но мы в любом случае обрабатывали их
как специальные случаи.
Так что вот финальная версия GetOp:
{--------------------------------------------------------------}
{ Get an Operator }
procedure GetOp;
begin
## SkipWhite;
## Token := Look;
## Value := Look;
## GetChar;
end;
{--------------------------------------------------------------}
Обратите внимание, что я все еще присваиваю Value значение. Если вас действительно
затрагивает эффективность, вы могли бы это опустить. Когда мы ожидаем оператор, мы в
любом случае будем проверять только Token, так что значение этой строки не будет иметь
значение. Но мне кажется хорошая практика дать ей значение на всякий случай.
Испытайте эту версию с каким-нибудь реалистично выглядящим кодом. Вы должны быть
способны разделять любую программу на ее индивидуальные токены, но предупреждаю, что
двух символьные операторы отношений будут отсканированы как два раздельных токена.
Это нормально... мы будем выполнять их синтаксический анализ таким способом.
Теперь, в главе 7 функция Next была объединена с процедурой Scan, которая также сверяла
каждый идентификатор со списком ключевых слов и кодировала каждый найденный. Как я
упомянул тогда, последнее, что мы захотели бы сделать - использовать такую процедуру в
местах, где ключевые слова не должны появляться, таких как выражения. Если бы мы
сделали это, список ключевых слов просматривался бы для каждого идентификатора,
появляющегося в коде. Нехорошо.
Правильней было бы в этом случае просто разделить функции выборки токенов и поиска
ключевых слов. Версия Scan, показанная ниже, только проверяет ключевые слова. Обратите
внимание, что она оперирует текущим токеном и не продвигает входной поток.
{--------------------------------------------------------------}
{ Scan the Current Identifier for Keywords }
procedure Scan;
begin
## if Token = 'x' then
##### Token := KWcode[Lookup(Addr(KWlist), Value, NKW) + 1];
end;
{--------------------------------------------------------------}
Последняя деталь. В компиляторе есть несколько мест, в которых мы должны фактически
проверить строковое значение токена. В основном это сделано для того, чтобы различать
разные END, но есть и пара других мест. (Я должен заметить, между прочим, что мы могли
бы навсегда устранить потребность в сравнении символов END кодируя каждый из них
различными символами. Прямо сейчас мы определенно идем маршрутом ленивого человека.)