Чтение онлайн

на главную - закладки

Жанры

Давайте создадим компилятор!
Шрифт:

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

Следующая версия MatchString замещает символьно-ориентированную Match. Заметьте, что как и Match она не продвигает входной поток.

{–}

{ Match a Specific Input String }

procedure MatchString(x: string);

begin

if Value <> x then Expected('''' + x + '''');

Next;

end;

{–}

Исправление компилятора

Вооружившись этими новыми процедурами лексического анализа мы можем теперь начать исправлять компилятор. Изменения весьма незначительные, но есть довольно много

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

Прежде всего, код процедуры 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;

{–}

Не забудьте, что новая версия Scan не продвигает входной поток, она только сканирует ключевые слова. Входной поток должен продвигаться каждой процедурой, которую вызывает Block.

В общих чертах, мы должны заменить каждую проверку Look на аналогичную проверку Token. Например:

{–}

{ Parse and Translate a Boolean Expression }

procedure BoolExpression;

begin

BoolTerm;

while IsOrOp(Token) do begin

Push;

case Token of

'|': BoolOr;

'~': BoolXor;

end;

end;

end;

{–}

В процедурах типа Add мы больше не должны использовать Match. Нам необходимо только вызывать Next для продвижения входного потока:

{–}

{ Recognize and Translate an Add }

procedure Add;

begin

Next;

Term;

PopAdd;

end;

{–}

Управляющие структуры фактически более простые. Мы просто вызываем Next для продвижения через ключевые слова управляющих конструкций:

{–}

{ Recognize and Translate an IF Construct }

procedure Block; Forward;

procedure DoIf;

var L1, L2: string;

begin

Next;

BoolExpression;

L1 := NewLabel;

L2 := L1;

BranchFalse(L1);

Block;

if Token = 'l' then begin

Next;

L2 := NewLabel;

Branch(L2);

PostLabel(L1);

Block;

end;

PostLabel(L2);

MatchString('ENDIF');

end;

{–}

Это все необходимые изменения. В листинге Tiny Version 1.1, данном ниже, я также сделал ряд других «усовершенствований», которые в действительности не нужны. Позвольте мне кратко разъяснить их:

1. Я удалил две процедуры Prog и Main и объединил их функции в основной программе. Они кажется не добавляли ясности... фактически они просто немного загрязняли программу.

2. Я удалил ключевые слова PROGRAM и BEGIN из списка ключевых слов. Каждое из них появляется в одном месте, так что нет необходимости искать его.

3. Обжегшись однажды на чрезмерной дозе сообразительности, я напомнил себе, что TINY предназначен быть минималистским языком. Поэтому я заменил причудливую обработку унарного минуса на самую простую какую мог придумать. Гигантский шаг назад в качестве кода, но огромное упрощение компилятора. Для использования другой версии правильным местом был бы KISS.

4. Я добавил несколько подпрограмм проверок ошибок типа CheckTable и CheckDup и заменил встроенный код на их вызовы. Это навело порядок во многих подпрограммах.

Поделиться:
Популярные книги

Мымра!

Фад Диана
1. Мымрики
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Мымра!

Её (мой) ребенок

Рам Янка
Любовные романы:
современные любовные романы
6.91
рейтинг книги
Её (мой) ребенок

Барон не играет по правилам

Ренгач Евгений
1. Закон сильного
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Барон не играет по правилам

Хозяйка лавандовой долины

Скор Элен
2. Хозяйка своей судьбы
Любовные романы:
любовно-фантастические романы
6.25
рейтинг книги
Хозяйка лавандовой долины

Неправильный боец РККА Забабашкин 3

Арх Максим
3. Неправильный солдат Забабашкин
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Неправильный боец РККА Забабашкин 3

Его нежеланная истинная

Кушкина Милена
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Его нежеланная истинная

Убивать чтобы жить 6

Бор Жорж
6. УЧЖ
Фантастика:
боевая фантастика
космическая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 6

Морской волк. 1-я Трилогия

Савин Владислав
1. Морской волк
Фантастика:
альтернативная история
8.71
рейтинг книги
Морской волк. 1-я Трилогия

Темный Лекарь 7

Токсик Саша
7. Темный Лекарь
Фантастика:
попаданцы
аниме
фэнтези
5.75
рейтинг книги
Темный Лекарь 7

Неласковый отбор Золушки-2. Печать демонов

Волкова Светлана
2. Попала в сказку
Любовные романы:
любовно-фантастические романы
7.29
рейтинг книги
Неласковый отбор Золушки-2. Печать демонов

Наследник пепла. Книга I

Дубов Дмитрий
1. Пламя и месть
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Наследник пепла. Книга I

Найденыш

Шмаков Алексей Семенович
2. Светлая Тьма
Фантастика:
юмористическое фэнтези
городское фэнтези
аниме
5.00
рейтинг книги
Найденыш

Как я строил магическую империю 6

Зубов Константин
6. Как я строил магическую империю
Фантастика:
попаданцы
аниме
фантастика: прочее
фэнтези
5.00
рейтинг книги
Как я строил магическую империю 6

Леди для короля. Оборотная сторона короны

Воронцова Александра
3. Королевская охота
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Леди для короля. Оборотная сторона короны