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
Здесь я показал конструкции высокоуровневого языка слева и желаемый ассемблерный код справа. Прежде всего заметьте, что здесь несомненно нам не нужно генерировать много кода! Для большей части процедуры и основной программы наши существующие конструкции позаботятся о генерируемом коде.
Ключ к работе с телом процедуры – понимание того, что хотя процедура может быть очень длинной, ее объявление в действительности не отличается от объявления переменной. Это просто еще один вид объявлений. Мы можем записать БНФ:
<declaration> ::= <data decl> | <procedure>
Это означает, что можно легко изменить TopDecl для работы с процедурами. Как насчет синтаксиса процедуры? Хорошо, вот предлагаемый синтаксис, который по существу такой же как и в Pascal:
<procedure> ::= PROCEDURE <ident> <begin-block>
Здесь практически не требуется никакой генерации кода., кроме генерации внутри блока 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;
Я должен упомянуть, что эта структура для объявлений и БНФ, которая управляет ей, отличается от стандартного Паскаля. В определении Паскаля от Дженсена и Вирта объявления переменных и, фактически, все виды объявлений, должны следовать в определенном порядке, т.е. метки, константы, типы,
