работу:

var i: char;

begin

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

ST[i] := ' ';

...

Наконец, вставьте следующие две строки в начало Alloc:

if InTable(N) then Abort('Duplicate Variable Name ' + N);

ST[N] := 'v';

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

ВЫПОЛНИМЫЕ УТВЕРЖДЕНИЯ

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

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

БНФ определение, данное раньше для основной программы, включало операторный блок, который мы пока что игнорировали:

<main> ::= BEGIN <block> END

Сейчас мы можем рассматривать блок просто как серию операций присваивания:

<block> ::= (Assignment)*

Давайте начнем с добавления синтаксического анализатора для блока. Мы начнем с процедуры- заглушки для операции присваивания:

{–}

{ Parse and Translate an Assignment Statement }

procedure Assignment;

begin

GetChar;

end;

{–}

{ Parse and Translate a Block of Statements }

procedure Block;

begin

while Look <> 'e' do

Assignment;

end;

{–}

Измените процедуру Main чтобы она вызывала Block как показано ниже:

{–}

{ Parse and Translate a Main Program }

procedure Main;

begin

Match('b');

Prolog;

Block;

Match('e');

Epilog;

end;

{–}

Эта версия все еще не генерирует никакого кода для «операций присваивания»... все что она делает это съедает символы до тех пор, пока не увидит 'e', означающее «END». Но она устанавливает основу для того, что следует дальше.

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

Однако, я понимаю, что большинство из вас используют компьютер 80x86, так что от кода, сгенерированного для 68000 вам мало пользы. Некоторые из вас спрашивали меня, что если бы машинозависимый код мог бы быть собран в одном месте, то было бы проще перенастроить его на другой ЦПУ. Ответ конечно да.

Чтобы сделать это вставьте следующие подпрограммы «генерации кода»:

{–}

{ Clear the Primary Register }

procedure Clear;

begin

EmitLn('CLR D0');

end;

{–}

{ Negate the Primary Register }

procedure Negate;

begin

EmitLn('NEG D0');

end;

{–}

{ Load a Constant Value to Primary Register }

procedure LoadConst(n: integer);

begin

Emit('MOVE #');

WriteLn(n, ',D0');

end;

{–}

{ Load a Variable to Primary Register }

procedure LoadVar(Name: char);

begin

if not InTable(Name) then Undefined(Name);

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

end;

{–}

{ Push Primary onto Stack }

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

0

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

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