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


Авторские права

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

Здесь можно скачать бесплатно "Джек Креншоу - Давайте создадим компилятор!" в формате fb2, epub, txt, doc, pdf. Жанр: Программирование. Так же Вы можете читать книгу онлайн без регистрации и SMS на сайте LibFox.Ru (ЛибФокс) или прочесть описание и ознакомиться с отзывами.
Рейтинг:
Название:
Давайте создадим компилятор!
Издательство:
неизвестно
Год:
неизвестен
ISBN:
нет данных
Скачать:

99Пожалуйста дождитесь своей очереди, идёт подготовка вашей ссылки для скачивания...

Скачивание начинается... Если скачивание не началось автоматически, пожалуйста нажмите на эту ссылку.

Вы автор?
Жалоба
Все книги на сайте размещаются его пользователями. Приносим свои глубочайшие извинения, если Ваша книга была опубликована без Вашего на то согласия.
Напишите нам, и мы в срочном порядке примем меры.

Как получить книгу?
Оплатили, но не знаете что делать дальше? Инструкция.

Описание книги "Давайте создадим компилятор!"

Описание и краткое содержание "Давайте создадим компилятор!" читать бесплатно онлайн.



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






Expression := LoadNum(GetNum);

end;

{–}

(Вау, это, уверен, не причинило слишком большого вреда! Всего несколько дополнительных строк делают всю работу.)

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

Аддитивные выражения

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

Хорошо, что мы уже имеем модель для работы с этими более сложными выражениями. Все, что мы должны сделать, это удостовериться, что все процедуры, вызываемые Expression, (Term, Factor и т.д.) всегда возвращают идентификатор типа. Если мы сделаем это, то структура программы едва ли вообще изменится.

Первый шаг прост: мы должны переименовать нашу существующую версию Expression в Term, как мы делали много раз раньше и создать новую версию Expression:

{–}

{ Parse and Translate an Expression }

function Expression: char;

var Typ: char;

begin

if IsAddop(Look) then

Typ := Unop

else

Typ := Term;

while IsAddop(Look) do begin

Push(Typ);

case Look of

'+': Typ := Add(Typ);

'-': Typ := Subtract(Typ);

end;

end;

Expression := Typ;

end;

{–}

Обратите внимание, как в этой подпрограмме каждый вызов процедуры стал вызовом функции и как локальная переменная Typ модифицируется при каждом проходе.

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

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

{–}

{ Process a Term with Leading Unary Operator }

function Unop: char;

begin

Clear;

Unop := 'W';

end;

{–}

Процедура Push – это подпрограмма генерации кода, которая теперь имеет параметр, указывающий тип:

{–}

{ Push Primary onto Stack }

procedure Push(Size: char);

begin

Move(Size, 'D0', '-(SP)');

end;

{–}

Теперь давайте взглянем на функции Add и Subtract. В более старых версиях этих подпрограмм мы позволяем им вызывать подпрограммы генерации кода PopAdd и PopSub. Мы продолжим делать это, что делает сами функции чрезвачайно простыми:

{–}

{ Recognize and Translate an Add }

function Add(T1: char): char;

begin

Match('+');

Add := PopAdd(T1, Term);

end;

{–}

{ Recognize and Translate a Subtract }

function Subtract(T1: char): char;

begin

Match('-');

Subtract := PopSub(T1, Term);

end;

{–}

Но простота обманчива, поскольку мы переложили всю логику на PopAdd и PopSub, которые больше не являются просто подпрограммами генерации кода. Они также должны теперь заботиться о необходимых преобразованиях типов.

Какие это преобразования? Простые: оба аргумента должны иметь тот же самый размер и результат также такой размер. Меньший из двух параметров должен быть «приведен» до размера большего.

Но это представляет небольшую проблему. Если переводимый параметр – второй (т.е. в основном регистре D0) мы в отличной форме. Если же нет, мы в затруднении: мы не можем изменить размер данных, которые уже затолкнуты в стек.

Решение простое, но немного болезненное: мы должны отказаться от этих красивых инструкций «вытолкнуть данные и что-нибудь с ними сделать», заботливо предоставленных Motorola.

Альтернативой является назначение вторичного регистра, в качестве которого я выбрал R7. (Почему не R1? Потому, что для других регистров у меня есть планы на будущее.)

Первый шаг в этой новой структуре – представить процедуру Pop, аналогичную Push. Эта процедура будет всегда выталкивать верхний элемент стека в D7:

{–}

{ Pop Stack into Secondary Register }

procedure Pop(Size: char);

begin

Move(Size, '(SP)+', 'D7');

end;

{–}

Общая идея состоит в том, что все «Pop-Op» подпрограммы могут вызывать ее. Когда это сделано, мы будем иметь оба операнда в регистрах, поэтому мы можем перевести любой нужный нам. Для работы процедуре Convert необходим другой аргумент, имя регистра:

{–}

{ Convert a Data Item from One Type to Another }

procedure Convert(Source, Dest: char; Reg: String);

begin

if Source <> Dest then begin

if Source = 'B' then

EmitLn('AND.W #$FF,' + Reg);

if Dest = 'L' then

EmitLn('EXT.L ' + Reg);

end;

end;

{–}

Следующая функция выполняет пребразование, но только если текущий тип T1 меньше по размеру, чем желаемый тип T2. Это функция, возвращающая конечный тип, позволяющий нам знать, что она решила:

{–}

{ Promote the Size of a Register Value }

function Promote(T1, T2: char; Reg: string): char;

var Typ: char;

begin

Typ := T1;

if T1 <> T2 then

if (T1 = 'B') or ((T1 = 'W') and (T2 = 'L')) then begin

Convert(T1, T2, Reg);

Typ := T2;

end;

Promote := Typ;

end;

{–}

Наконец, следующая функция приводит два регистра к одному типу:

{–}

{ Force both Arguments to Same Type }

function SameType(T1, T2: char): char;

begin

T1 := Promote(T1, T2, 'D7');

SameType := Promote(T2, T1, 'D0');

end;

{–}

Эти новые подпрограммы дают нам заряд, необходимы нам чтобы разложить PopAdd и PopSub:

{–}

{ Generate Code to Add Primary to the Stack }

function PopAdd(T1, T2: char): char;

begin

Pop(T1);

T2 := SameType(T1, T2);

GenAdd(T2);

PopAdd := T2;

end;

{–}

{ Generate Code to Subtract Primary from the Stack }

function PopSub(T1, T2: char): char;

begin

Pop(T1);

T2 := SameType(T1, T2);

GenSub(T2);

PopSub := T2;

end;

{–}

После всех этих приготовлений, в конечном результате нет почти ничего кульминационного. Снова, вы можете видеть что логика совершенно проста. Все что делают эти две подпрограммы – выталкивают вершину стека в D7, приводят два операнда к одному размеру и затем генерируют код.

Обратите внимание на две новые подпрограммы генерации кода GenAdd и GenSub. Они являются остаточной формой оригинальных PopAdd и PopSub. Т.е. они являются чистыми генераторами кода, производящими сложение и вычитание регистров:

{–}

{ Add Top of Stack to Primary }

procedure GenAdd(Size: char);

begin

EmitLn('ADD.' + Size + ' D7,D0');

end;

{–}

{ Subtract Primary from Top of Stack }

procedure GenSub(Size: char);

begin

EmitLn('SUB.' + Size + ' D7,D0');

EmitLn('NEG.' + Size + ' D0');

end;

{–}

ОК, я соглашусь с вами: я выдал вам множество подпрограмм с тех пор, как мы в последний раз протестировали код. Но вы должны признать, что каждая новая подпрограмма довольно проста и ясна. Если вам (как и мне) не нравится тестировать так много новых подпрограмм одновременно все в порядке. Вы можете заглушить подпрограммы типа Convert, Promote и SameType так как они не считывают входной поток. Вы не получите корректный код, конечно, но программа должна работать. Затем постепенно расширяйте их.

При тестировании программы не забудьте, что вы сначала должны объявить некоторые переменные а затем начать «тело» программы с "B" в верхнем регистре (для BEGIN). Вы должны обнаружить, что синтаксический анализатор обрабатывает любые аддитивные выражения. Как только все подпрограммы преобразования будет введены, вы должны увидеть, что генерируется правильный код и код для преобразования типов вставляется в нужных местах. Попробуйте смешивать переменные различных размеров а также литералы. Удостоверьтесь, что все работает правильно. Как обычно, хорошо было бы попробовать некоторые ошибочные выражения и посмотреть, как компилятор обрабатывает их.

Почему так много процедур?

К этому моменту вы можете подумать, что я зашел слишком далеко в смысле глубоко вложенных процедур. В этом несомненно есть большие накладные расходы. Но в моем безумии есть смысл. Как в случае с UnOp, я заглядываю вперед на время, когда мы захотим генерировать лучший код. С таким способом организации кода мы можем достичь этого без значительных изменений в программе Например, в случаях, где значение помещенное в стек не должно преобразовываться, все же лучше использовать инструкцию «вытолкнуть и сложить». Если мы решим проверять такие случаи, мы можем включить дополнительные тесты в PopAdd и PopSub не изменяя что-либо еще.

Мультипликативные выражения

Процедуры для работы с мультипликативными операторами почти такие же. Фактически, на первом уровне они почти идентичны, так что я просто покажу их здесь без особых фанфар. Первая – наша общая форма для Factor, которая включает подвыражения в скобках:

{–}

{ Parse and Translate a Factor }

function Expression: char; Forward;

function Factor: char;

begin

if Look = '(' then begin

Match('(');

Factor := Expression;

Match(')');

end

else if IsAlpha(Look) then

Factor := Load(GetName)

else

Factor := LoadNum(GetNum);

end;

{–}

{ Recognize and Translate a Multiply }

Function Multiply(T1: char): char;

begin

Match('*');

Multiply := PopMul(T1, Factor);

end;

{–}

{ Recognize and Translate a Divide }

function Divide(T1: char): char;

begin

Match('/');

DIvide := PopDiv(T1, Factor);

end;

{–}

{ Parse and Translate a Math Term }

function Term: char;

var Typ: char;

begin

Typ := Factor;

while IsMulop(Look) do begin

Push(Typ);

case Look of

'*': Typ := Multiply(Typ);

'/': Typ := Divide(Typ);

end;

end;

Term := Typ;


На Facebook В Твиттере В Instagram В Одноклассниках Мы Вконтакте
Подписывайтесь на наши страницы в социальных сетях.
Будьте в курсе последних книжных новинок, комментируйте, обсуждайте. Мы ждём Вас!

Похожие книги на "Давайте создадим компилятор!"

Книги похожие на "Давайте создадим компилятор!" читать онлайн или скачать бесплатно полные версии.


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

Все книги автора Джек Креншоу

Джек Креншоу - все книги автора в одном месте на сайте онлайн библиотеки LibFox.

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

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

Отзывы читателей о книге "Давайте создадим компилятор!", комментарии и мнения людей о произведении.

А что Вы думаете о книге? Оставьте Ваш отзыв.