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

ОглавлениеДобавить в закладки К обложке

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

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

Хорошо, что мы уже имеем модель для работы с этими более сложными выражениями. Все, что мы должны сделать, это удостовериться, что все процедуры, вызываемые 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 }


Логин
Пароль
Запомнить меня