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

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

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

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

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

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

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

Интервал:

Закладка:

Сделать

function IsWhite(c: char): boolean;

begin

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

end;

{–}

{ Skip Over Leading White Space }

procedure SkipWhite;

begin

while IsWhite(Look) do

GetChar;

end;

{–}

{ Skip Over an End-of-Line }

procedure Fin;

begin

if Look = CR then begin

GetChar;

if Look = LF then

GetChar;

end;

end;

{–}

{ Match a Specific Input Character }

procedure Match(x: char);

begin

if Look = x then GetChar

else Expected('''' + x + '''');

SkipWhite;

end;

{–}

{ Get an Identifier }

function GetName: char;

begin

if not IsAlpha(Look) then Expected('Name');

GetName := UpCase(Look);

GetChar;

SkipWhite;

end;

{–}

{ Get a Number }

function GetNum: char;

begin

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

GetNum := Look;

GetChar;

SkipWhite;

end;

{–}

{ Output a String with Tab }

procedure Emit(s: string);

begin

Write(TAB, s);

end;

{–}

{ Output a String with Tab and CRLF }

procedure EmitLn(s: string);

begin

Emit(s);

WriteLn;

end;

{–}

{ Post a Label To Output }

procedure PostLabel(L: string);

begin

WriteLn(L, ':');

end;

{–}

{ Load a Variable to the Primary Register }

procedure LoadVar(Name: char);

begin

CheckVar(Name);

EmitLn('MOVE ' + Name + '(PC),D0');

end;

{–}

{ Store the Primary Register }

procedure StoreVar(Name: char);

begin

CheckVar(Name);

EmitLn('LEA ' + Name + '(PC),A0');

EmitLn('MOVE D0,(A0)')

end;

{–}

{ Initialize }

procedure Init;

var i: char;

begin

GetChar;

SkipWhite;

for i := 'A' to 'Z' do

ST[i] := ' ';

end;

{–}

{ Parse and Translate an Expression }

{ Vestigial Version }

procedure Expression;

begin

LoadVar(GetName);

end;

{–}

{ Parse and Translate an Assignment Statement }

procedure Assignment;

var Name: char;

begin

Name := GetName;

Match('=');

Expression;

StoreVar(Name);

end;

{–}

{ Parse and Translate a Block of Statements }

procedure DoBlock;

begin

while not(Look in ['e']) do begin

Assignment;

Fin;

end;

end;

{–}

{ Parse and Translate a Begin-Block }

procedure BeginBlock;

begin

Match('b');

Fin;

DoBlock;

Match('e');

Fin;

end;

{–}

{ Allocate Storage for a Variable }

procedure Alloc(N: char);

begin

if InTable(N) then Duplicate(N);

ST[N] := 'v';

WriteLn(N, ':', TAB, 'DC 0');

end;

{–}

{ Parse and Translate a Data Declaration }

procedure Decl;

var Name: char;

begin

Match('v');

Alloc(GetName);

end;

{–}

{ Parse and Translate Global Declarations }

procedure TopDecls;

begin

while Look <> 'b' do begin

case Look of

'v': Decl;

else Abort('Unrecognized Keyword ' + Look);

end;

Fin;

end;

end;

{–}

{ Main Program }

begin

Init;

TopDecls;

BeginBlock;

end.

{–}

Обратите внимание, что у нас есть таблица идентификаторов и есть логика для проверки допустимости имени переменной. Также стоит обратить внимание на то, что я включил код, который вы видели ранее для поддержки пробелов и переносов строк. Наконец заметьте, что основная программа ограничена как обычно операторными скобками BEGIN-END.

Если вы скопировали программу в Turbo, первым делом нужно откомпилировать ее и удостовериться что она работает. Сделайте несколько объявлений а затем блок begin. Попробуйте что-нибудь вроде:

va (для VAR A)

vb (для VAR B)

vc (для VAR C)

b (для BEGIN)

a=b

b=c

e. (для END.)

Как обычно, вы должны сделать некоторые преднамеренные ошибки и проверить, что программа правильно их отлавливает.

Объявление процедуры

Если вы удовлетворены, как работает наша маленькая программа, тогда пришло время поработать с процедурами. Так как мы еще не говорили о параметрах мы начнем с рассмотрения только таких процедур которые не имеют списка параметров.

Для начала, давайте рассмотрим простую программу с процедурой и подумаем о коде который мы хотели бы увидеть для нее сгенерированным:

PROGRAM FOO;

.

.

PROCEDURE BAR; BAR:

BEGIN .

. .

. .

END; RTS

BEGIN { MAIN PROGRAM } MAIN:

. .

. .

FOO; BSR BAR

. .

. .

END. END MAIN

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

Ключ к работе с телом процедуры – понимание того, что хотя процедура может быть очень длинной, ее объявление в действительности не отличается от объявления переменной. Это просто еще один вид объявлений. Мы можем записать БНФ:

::= |

Это означает, что можно легко изменить TopDecl для работы с процедурами. Как насчет синтаксиса процедуры? Хорошо, вот предлагаемый синтаксис, который по существу такой же как и в Pascal:

::= PROCEDURE

Здесь практически не требуется никакой генерации кода., кроме генерации внутри блока begin. Мы должны только выдать метку в начале процедуры и RTS в конце.

Вот требуемый код:

{–}

{ Parse and Translate a Procedure Declaration }

procedure DoProc;

var N: char;

begin

Match('p');

N := GetName;

Fin;

if InTable(N) then Duplicate(N);

ST[N] := 'p';

PostLabel(N);

BeginBlock;

Return;

end;

{–}

Обратите внимание, что я добавил новую подпрограмму генерации кода Return, которая просто выдает инструкцию RTS. Создание этой подпрограммы «оставлено как упражнение студенту».

Для завершения этой версии добавьте следующую строку в оператор Case в DoBlock.

'p': DoProc;

Я должен упомянуть, что эта структура для объявлений и БНФ, которая управляет ей, отличается от стандартного Паскаля. В определении Паскаля от Дженсена и Вирта объявления переменных и, фактически, все виды объявлений, должны следовать в определенном порядке, т.е. метки, константы, типы, переменные, процедуры и основная программа. Чтобы следовать такой схеме, мы должны разделить два объявления и написать в основной программе что-нибудь вроде:

DoVars;

DoProcs;

DoMain;

Однако, большинство реализаций Паскаля, включая Turbo, не требуют такого порядка и позволяют вам свободно перемешивать различные объявления до тех пор пока вы не попробуете обратиться к чему-то прежде, чем это будет объявлено. Хотя это может быть больше эстетическим удовлетворением объявлять все глобальные переменные на вершине программы, конечно не будет никакого вреда от того, чтобы разрешить расставлять их в любом месте. Фактически, это может быть даже немного полезно, в том смысле, что это даст нам возможность выполнять небольшое элементарное скрытие информации. Переменные, к которым нужно обращатся только из основной программы, к примеру, могут быть объявлены непосредственно перед ней и будут таким образом недоступны для процедур.

Испытайте эту новую версию. Заметьте, что мы можем объявить так много процодур, как захотим (до тех пор, пока не исчерпаем односимвольные имена!) и все метки и RTS появятся в правильных местах.

Здесь стоит заметить, что я не разрешаю вложенные процедуры. В TINY все процедуры должны быть объявлены на глобальном уровне, так же как и в C. На Форуме Компьютерных Языков на CompuServe по этому поводу возникла порядочная дискуссия. Оказывается, существует значительная расплата сложностью которая должна быть заплачена за роскошь использования вложенных процедур. Более того, эта расплата платится во время выполнения, так как должен быть добавлен дополнительный код, который будет выполняться каждый раз когда процедура вызывается. Я также охотно верю что вложение это не очень хорошая идея просто на том основании, что я видел слишком много злоупотреблений этой возможностью. Прежде, чем сделать следующий шаг, также стоит обратить внимание на то, что «основная програма» в ее текущем состоянии незавершена, так как она не имеет метки и утверждения END. Давайте исправим эту небольшую оплошность:

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

Интервал:

Закладка:

Сделать


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

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




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


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


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

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