case Look of

'b', 'w', 'l': Decl;

else Abort('Unrecognized Keyword ' + Look);

end;

Fin;

end;

end;

{–}

Внесите показанные изменения в эти процедуры и испытайте программу. Используйте одиночные символы 'b', 'w' и 'l' как ключевые слова (сейчас они должны быть в нижнем регистре). Вы увидите, что в каждом случае мы выделяем память соответствующего объема. Обратите внимание, глядя на дамп таблицы идентификаторов, что размеры также сохранены для использования позже. Какого использования? Хорошо, это тема остальной части этой главы.

ПРИСВАИВАНИЯ

Теперь, когда мы можем объявлять переменные различных размеров, очевидно что мы должны иметь возможность что-то с ними делать. На первый раз, давайте просто попробуем загружать их в наш рабочий регистр D0. Имеет смысл использовать ту же самую идею, которую мы использовали для Alloc, т.е. сделаем процедуру загрузки, которая может загружать переменные нескольких размеров. Нам также необходимо продолжать изолировать машино-зависимое содержимое. Процедура загрузки выглядит так:

{–}

{ Load a Variable to Primary Register }

procedure LoadVar(Name, Typ: char);

begin

Move(Typ, Name + '(PC)', 'D0');

end;

{–}

По крайней мере для 68000, многие команды оказываются командами MOVE. Было бы полезно создать отдельный генератор кода только для этих инструкций и затем вызывать его когда необходимо:

{–}

{ Generate a Move Instruction }

procedure Move(Size: char; Source, Dest: String);

begin

EmitLn('MOVE.' + Size + ' ' + Source + ',' + Dest);

end;

{–} 

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

Прежде всего, мы должны удостовериться, что типы, с которыми мы работаем – загружаемого типа. Это звучит как работа для другого распознавателя:

{–}

{ Recognize a Legal Variable Type }

function IsVarType(c: char): boolean;

begin

IsVarType := c in ['B', 'W', 'L'];

end;

{–}

Затем, было бы хорошо иметь подпрограмму, которая извлечет тип переменной из таблицы идентификаторов в то же время проверяя его на допустимость:

{–}

{ Get a Variable Type from the Symbol Table }

function VarType(Name: char): char;

var Typ: char;

begin

Typ := TypeOf(Name);

if not IsVarType(Typ) then Abort('Identifier ' + Name +

' is not a variable');

VarType := Typ;

end;

{–}

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

{–}

{ Load a Variable to the Primary Register }

procedure Load(Name: char);

begin

LoadVar(Name, VarType(Name));

end;

{–}

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

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

Load('A');

Load('B');

Load('C');

Load('X');

в основную программу. Таким образом, после того, как раздел объявления завершен, они будут выполнены чтобы генерировать код для загрузки. Вы можете поиграть с различными комбинациями объявлений чтобы посмотреть как обрабатываются ошибки.

Я уверен, что вы не будете удивлены, узнав, что сохранение переменных во многом подобно их загрузке. Необходимые процедуры показаны дальше:

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату