Джек Креншоу - Давайте создадим компилятор!

Тут можно читать онлайн Джек Креншоу - Давайте создадим компилятор! - бесплатно полную версию книги (целиком) без сокращений. Жанр: comp-programming. Здесь Вы можете читать полную версию (весь текст) онлайн без регистрации и SMS на сайте лучшей интернет библиотеки ЛибКинг или прочесть краткое содержание (суть), предисловие и аннотацию. Так же сможете купить и скачать торрент в электронном формате fb2, найти и слушать аудиокнигу на русском языке или узнать сколько частей в серии и всего страниц в публикации. Читателям доступно смотреть обложку, картинки, описание и отзывы (комментарии) о произведении.
  • Название:
    Давайте создадим компилятор!
  • Автор:
  • Жанр:
  • Издательство:
    неизвестно
  • Год:
    неизвестен
  • ISBN:
    нет данных
  • Рейтинг:
    3.89/5. Голосов: 91
  • Избранное:
    Добавить в избранное
  • Отзывы:
  • Ваша оценка:
    • 80
    • 1
    • 2
    • 3
    • 4
    • 5

Джек Креншоу - Давайте создадим компилятор! краткое содержание

Давайте создадим компилятор! - описание и краткое содержание, автор Джек Креншоу, читайте бесплатно онлайн на сайте электронной библиотеки LibKing.Ru

Эта серия, написанная в период с 1988 по 1995 года и состоящая из шестнадцати частей, является нетехническим введением в конструирование компиляторов. Серия является руководством по теории и практике разработки синтаксических анализаторов и компиляторов языков программирования. До того как вы закончите чтение этой книги, вы раскроете каждый аспект конструирования компиляторов, разработаете новый язык программирования и создадите работающий компилятор.

Давайте создадим компилятор! - читать онлайн бесплатно полную версию (весь текст целиком)

Давайте создадим компилятор! - читать книгу онлайн бесплатно, автор Джек Креншоу
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

Само собой разумеется, мы можем добавить достаточно логики чтобы удержаться на правильном пути. Но это сложно и делает весь анализатор очень ненадежным.

Существует гораздо лучший способ – просто принять то же самое правило, которое так хорошо работало раньше, и относиться к токенам так же как одиночным сиволам. Другими словами, мы будем заранее считывать токен подобно тому, как мы всегда считывали символ. Это кажется таким очевидным как только вы подумаете об этом способе.

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

Решение

Давайте начнем решение проблемы с пересмотра двух процедуры:

{–}

{ Get an Identifier }

procedure GetName;

begin

SkipWhite;

if Not IsAlpha(Look) then Expected('Identifier');

Token := 'x';

Value := '';

repeat

Value := Value + UpCase(Look);

GetChar;

until not IsAlNum(Look);

end;

{–}

{ Get a Number }

procedure GetNum;

begin

SkipWhite;

if not IsDigit(Look) then Expected('Number');

Token := '#';

Value := '';

repeat

Value := Value + Look;

GetChar;

until not IsDigit(Look);

end;

{–}

Эти две процедуры функционально почти идентичны тем, которые я показал вам в Главе 7. Каждая из них выбирает текущий токен, или идентификатор или число, в глобальную строковую переменную Value. Они также присваивают кодированной версии, Token, соответствующий код. Входной поток останавливается на Look, содержащем первый символ, не являющийся частью токена.

Мы можем сделать то же самое для операторов, даже многосимвольных, с помощью процедуры типа:

{–}

{ Get an Operator }

procedure GetOp;

begin

Token := Look;

Value := '';

repeat

Value := Value + Look;

GetChar;

until IsAlpha(Look) or IsDigit(Look) or IsWhite(Look);

end;

{–}

Обратите внимание, что GetOps возвращает в качестве закодированного токена первый символ оператора. Это важно, потому что это означает, что теперь мы можем использовать этот одиночный символ для управления синтаксическим анализатором вместо предсказывающего символа.

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

{–}

{ Get the Next Input Token }

procedure Next;

begin

SkipWhite;

if IsAlpha(Look) then GetName

else if IsDigit(Look) then GetNum

else GetOp;

end;

{–}

Обратите внимание, что здесь я поместил SkipWhite перед вызовами а не после. Это означает в основном, что переменная Look не будет содержать значимого значения и, следовательно, мы не должны использовать ее как тестируемое значение при синтаксическом анализе, как мы делали до этого. Это большое отклонение от нашего нормального подхода.

Теперь, не забудьте, что раньше я избегал обработки символов возврата каретки (CR) и перевода строки (LF) как незаполненного пространства. Причина была в том, что так как SkipWhite вызывается последней в сканере, встреча с LF инициировала бы чтение из входного потока. Если бы мы были на последней строке программы, мы не могли бы выйти до тех пор, пока мы не введем другую строку с отличным от пробела символом. Именно поэтому мне требовалась вторая процедура NewLine для обработки CRLF.

Но сейчас, когда первым происходит вызов SkipWhite, это то поведение, которое нам нужно. Компилятор должен знать, что появился другой токен или он не должен вызывать Next. Другими словами, он еще не обнаружил завершающий END. Поэтому мы будем настаивать на дополнительных данных до тех пор, пока не найдем что-либо.

Все это означает, что мы можем значительно упростить и программу и концепции, обрабатывая CR и LF как незаполненное простанство и убрав NewLine. Вы можете сделать это просто изменив функцию IsWhite:

{–}

{ Recognize White Space }

function IsWhite(c: char): boolean;

begin

IsWhite := c in [' ', TAB, CR, LF];

end;

{–}

Мы уже пробовали аналогичные подпрограммы в Главе 7, но вы могли бы также попробовать и эти. Добавьте их к копии Cradle и вызовите Next в основной программе:

{–}

{ Main Program }

begin

Init;

repeat

Next;

WriteLn(Token, ' ', Value);

until Token = '.';

end.

{–}

Откомпилируйте и проверьте, что вы можете разделять программу на серии токенов и вы получаете правильные кода для каждого токена.

Почти работает, но не совсем. Существуют две потенциальные проблемы: Во-первых, в KISS/TINY почти все наши операторы – односимвольные. Единственное исключение составляют операторы отношений >=, <= и <>. Было бы позором обрабатывать все операторы как строки и выполнять сравнение строк когда почти всегда удовлетворит сравнение одиночных символов. Второе, и более важное, программа не работает, когда два оператора появляются вместе как в (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, показанная ниже, только проверяет ключевые слова. Обратите внимание, что она оперирует текущим токеном и не продвигает входной поток.

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать


Джек Креншоу читать все книги автора по порядку

Джек Креншоу - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки LibKing.




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


Отзывы читателей о книге Давайте создадим компилятор!, автор: Джек Креншоу. Читайте комментарии и мнения людей о произведении.


Понравилась книга? Поделитесь впечатлениями - оставьте Ваш отзыв или расскажите друзьям

Напишите свой комментарий
x