ВВЕДЕНИЕ В последней главе я показал вам
основную идею нисходящей разработки компилятора. Я показал вам первые несколько
шагов этого процесса для компиляторов Pascal и C, но я остановился далеко
от его завершения. Причина была проста: если мы собираемся построить настоящий,
функциональный компилятор для какого-нибудь языка, я предпочел бы сделать
это для KISS, языка, который я определил в этой обучающей серии.
ПОДГОТОВКА Много лет назад существовали
языки, называемые Tiny BASIC, Tiny Pascal и Tiny C, каждый из которых был
подмножеством своего полного родительского языка. Tiny BASIC, к примеру,
имел только односимвольные имена переменных и глобальные переменные. Он
поддерживал только один тип данных. Звучит знакомо? К этому моменту мы
имеем почти все инструменты, необходимые для создания компилятора подобного
этому.
<program> ::= PROGRAM <top-level decl> <main> '.' Мы уже достигли решающей точки.
Моей первой мыслью было сделать основной блок необязательным. Кажется бессмысленным
писать "программу" без основной программы, но это имеет смысл, если мы
разрешим множественные модули, связанные вместе. Фактически я предполагаю
учесть это в KISS. Но тогда мы столкнемся с кучей проблем, которые я предпочел
бы сейчас не затрагивать. Например, термин "PROGRAM" в действительности
становится неправильно употребляемым. MODULE из Modula-2 или UNIT из Turbo
Pascal были бы более подходящими. Во-вторых, как насчет правил видимости?
Нам необходимо соглашение для работы с видимостью имен в модулях. На данный
момент лучше просто сохранить простоту и совершенно игнорировать эту
идею.
{--------------------------------------------------------------}
procedure Prog;
Процедура Header просто выдает инициализационный код, необходимый ассемблеру: {--------------------------------------------------------------}
procedure Header;
Процедуры Prolog и Epilog выдают код для идентификации основной программы и для возвращения в ОС: {--------------------------------------------------------------}
procedure Prolog;
{--------------------------------------------------------------}
procedure Epilog;
Основная программа просто вызывает Prog и затем выполняет проверку на чистое завершение: {--------------------------------------------------------------}
begin
Сейчас TINY примет только одну "программу" - пустую: PROGRAM . (или 'p.' в нашей стенографии). Заметьте, тем не менее, что компилятор
генерирует для этой программы корректный код. Она будет выполняться и делать
то, что можно ожидать от пустой программы, т.е. ничего кроме элегантного
возвращения в ОС.
<main> ::= BEGIN <block> END Здесь мы снова приняли решение.
Мы могли бы потребовать использовать объявление вида "PROCEDURE MAIN",
подобно C. Я должен допустить, что это совсем неплохая идея... Мне не особенно
нравится подход Паскаля так как я предпочитаю не иметь проблем с определением
местоположения основной программы в листинге Паскаля. Но альтернатива тоже
немного неудобна, так как вы должны работать с проверкой ошибок когда пользователь
опустит основную программу или сделает орфографическую ошибку в ее названии.
Здесь я использую простой выход.
BEGIN <name>
аналогично соглашению Модула-2. Это добавляет в язык немного
"синтаксического сахара". Подобные вещи легко добавлять и изменять по вашим
симпатиям если вы сами проектируете язык.
{--------------------------------------------------------------}
procedure Prog;
и добавьте новую процедуру: {--------------------------------------------------------------}
procedure Main;
Теперь единственной допустимой программой является программа: PROGRAM BEGIN END. (или 'pbe.') Разве мы не делаем успехи??? Хорошо, как обычно это становится лучше. Вы могли бы попробовать сделать здесь некоторые преднамеренные ошибки подобные пропуску 'b' или 'e' и посмотреть что случится. Как всегда компилятор должен отметить все недопустимые входные символы. ОБЪЯВЛЕНИЯ Очевидно на следующем шаге необходимо
решить, что мы подразумеваем под объявлением. Я намереваюсь иметь два вида
объявлений: переменных и процедур/функций. На верхнем уровне разрешены
только глобальные объявления, точно как в C.
<top-level decls> ::= ( <data declaration> )* <data declaration> ::= VAR <var-list> Обратите внимание, что так как
имеется только один тип переменных, нет необходимости объявлять этот тип.
Позднее, для полной версии KISS, мы сможем легко добавить описание типа.
{--------------------------------------------------------------}
procedure Prog;
Теперь добавьте две новые процедуры: {--------------------------------------------------------------}
procedure Decl;
{--------------------------------------------------------------}
procedure TopDecls;
Заметьте, что на данный момент
Decl - просто заглушка. Она не генерирует никакого кода и не обрабатывает
список... каждая переменная должна быть в отдельном утверждении VAR.
ОБЪЯВЛЕНИЯ И ИДЕНТИФИКАТОРЫ Это выглядит довольно хорошо,
но мы все еще генерируем только пустую программу. Настоящий ассемблер должен
выдавать директивы ассемблера для распределения памяти под переменные.
Пришло время действительно получить какой-нибудь код.
{--------------------------------------------------------------}
procedure Decl;
Процедура Alloc просто выдает команду ассемблеру для распределения памяти: {--------------------------------------------------------------}
procedure Alloc(N: char);
Погоняйте программу. Попробуйте входную последовательность, которая объявляет какие-нибудь переменные, например: pvxvyvzbe. Видите, как распределяется память?
Просто, да? Заметьте также, что точка входа "MAIN" появляется в правильном
месте.
<var-list> ::= <ident> (, <ident>)* Добавление этого синтаксиса в Decl дает новую версию: {--------------------------------------------------------------}
procedure Decl;
ОК, теперь откомпилируйте этот код и испытайте его. Попробуйте ряд строк с объявлениями VAR, попробуйте список из нескольких переменных в одной строке и комбинации этих двух. Работает? ИНИЦИАЛИЗАТОРЫ Пока мы работали с объявлениями данных, меня беспокоила одна вещь - то, что Pascal не позволяет инициализировать данные в объявлении. Эта возможность по общему признанию является своего рода излишеством, и ее может не быть в языке, который считается минимальным языком. Но ее также настолько просто добавить, что было бы позором не сделать этого. БНФ становится: <var-list> ::= <var> ( <var> )* <var> ::= <ident> [ = <integer> ] Измените Alloc как показано ниже: {--------------------------------------------------------------}
procedure Alloc(N: char);
Вот оно: инициализатор в шесть
дополнительных строк Pascal.
{--------------------------------------------------------------}
function GetNum: integer;
Строго говоря, мы должны разрешить выражения в поле данных инициализатора, или, по крайней мере, отрицательные значения. Сейчас давайте просто разрешим отрицательные значения изменив код для Alloc следующим образом: {--------------------------------------------------------------}
procedure Alloc(N: char);
Теперь у вас есть возможность инициализировать переменные отрицательными и/или многозначными значениями. ТАБЛИЦА ИДЕНТИФИКАТОРОВ Существует одна проблема с компилятором в его текущем состоянии: он ничего не делает для сохранения переменной когда мы ее объявляем. Так что компилятор совершенно спокойно распределит память для нескольких переменных с тем же самым именем. Вы можете легко убедиться в этом набрав строку типа pvavavabe. Здесь мы объявили переменную
A три раза. Как вы можете видеть, компилятор бодро принимает это и генерирует
три идентичных метки. Не хорошо.
var ST: array['A'..'Z'] of char; и вставьте следующую функцию: {--------------------------------------------------------------}
function InTable(n: char): Boolean;
Нам также необходимо инициализировать таблицу пробелами. Следующие строки в Init сделают эту работу: var i: char;
Наконец, вставьте следующие две строки в начало Alloc: if InTable(N) then Abort('Duplicate Variable Name ' +
N);
Это должно все решить. Теперь компилятор будет отлавливать двойные объявления. Позднее мы также сможем использовать InTable при генерации ссылок на переменные. ВЫПОЛНИМЫЕ УТВЕРЖДЕНИЯ К этому времени мы можем генерировать
пустую программу, которая имеет несколько объявленных переменных и возможно
инициализированных. Но пока мы не генерировали ни строки выполнимого кода.
<main> ::= BEGIN <block> END Сейчас мы можем рассматривать блок просто как серию операций присваивания: <block> ::= (Assignment)* Давайте начнем с добавления синтаксического анализатора для блока. Мы начнем с процедуры-заглушки для операции присваивания: {--------------------------------------------------------------}
procedure Assignment;
{--------------------------------------------------------------}
procedure Block;
Измените процедуру Main чтобы она вызывала Block как показано ниже: {--------------------------------------------------------------}
procedure Main;
Эта версия все еще не генерирует
никакого кода для "операций присваивания"... все что она делает это съедает
символы до тех пор, пока не увидит "e", означающее "END". Но она устанавливает
основу для того, что следует дальше.
{---------------------------------------------------------------}
procedure Clear;
{---------------------------------------------------------------}
procedure Negate;
{---------------------------------------------------------------}
procedure LoadConst(n: integer);
{---------------------------------------------------------------}
procedure LoadVar(Name: char);
{---------------------------------------------------------------}
procedure Push;
{---------------------------------------------------------------}
procedure PopAdd;
{---------------------------------------------------------------}
procedure PopSub;
{---------------------------------------------------------------}
procedure PopMul;
{---------------------------------------------------------------}
procedure PopDiv;
{---------------------------------------------------------------}
procedure Store(Name: char);
Приятная особенность такого подхода,
конечно, в том что мы можем перенастроить компилятор на новый ЦПУ просто
переписав эти процедуры "генератора кода".
Кроме того, позднее мы обнаружим что можем улучшить качество кода немного
подправляя эти процедуры без необходимости изменения компилятора.
{--------------------------------------------------------------}
procedure Undefined(n: string);
Итак, теперь мы наконец готовы
начать обработку выполнимого кода. Мы сделаем это заменив пустую версию
процедуры Assignment.
<assignment> ::= <ident> = <expression> <expression> ::= <first term> ( <addop> <term> )* <first term> ::= <first factor> <rest> <term> ::= <factor> <rest> <rest> ::= ( <mulop> <factor> )* <first factor> ::= [ <addop> ] <factor> <factor> ::= <var> | <number> | ( <expression> ) Эта БНФ также немного отличается
от той, что мы использовали раньше... еще одна "вариация на тему выражений".
Эта специфичная версия имеет то, что я считаю лучшей обработкой унарного
минуса. Как вы увидите позднее, это позволит нам очень эффективно обрабатывать
отрицательные константы. Здесь стоит упомянуть, что мы часто видели преимущества
"подстраивания" БНФ по ходу дела, с цель сделать язык легким для анализа.
То, что вы видете здесь, немного другое: мы подстраиваем БНФ для того,
чтобы сделать генерацию кода более эффективной! Это происходит впервые
в этой серии.
{---------------------------------------------------------------}
procedure Expression; Forward; procedure Factor;
{--------------------------------------------------------------}
procedure NegFactor;
{--------------------------------------------------------------}
procedure FirstFactor;
{--------------------------------------------------------------}
procedure Multiply;
{-------------------------------------------------------------}
procedure Divide;
{---------------------------------------------------------------}
procedure Term1;
{---------------------------------------------------------------}
procedure Term;
{---------------------------------------------------------------}
procedure FirstTerm;
{--------------------------------------------------------------}
procedure Add;
{-------------------------------------------------------------}
procedure Subtract;
{---------------------------------------------------------------}
procedure Expression;
{--------------------------------------------------------------}
procedure Assignment;
ОК, если вы вставили весь этот код, тогда откомпилируйте и проверьте его. Вы должны увидеть приемлемо выглядящий код, представляющий собой законченную программу, которая будет ассемблироваться и выполняться. У нас есть компилятор! БУЛЕВА ЛОГИКА Следующий шаг также должен
быть вам знаком. Мы должны добавить булевы выражения и операторы отношений.
Снова, так как мы работали с ними не один раз, я не буду подробно разбирать
их за исключением моментов, в которых они отличаются от того, что мы делали
прежде. Снова, мы не будем просто копировать их из других файлов потому
что я немного изменил некоторые вещи. Большинство изменений просто включают
изоляцию машинозависимых частей как мы делали для арифметических операций.
Я также несколько изменил процедуру NotFactor для соответствия структуре
FirstFactor. Наконец я исправил ошибку в объектном коде для операторов
отношений: в инструкции Scc я использовал только младшие 8 бит D0. Нам
нужно установить логическую истину для всех 16 битов поэтому я добавил
инструкцию для изменения младшего байта.
{--------------------------------------------------------------}
function IsOrop(c: char): boolean;
{--------------------------------------------------------------}
function IsRelop(c: char): boolean;
Также нам понадобятся несколько подпрограмм генерации кода: {---------------------------------------------------------------}
procedure NotIt;
procedure PopAnd;
{---------------------------------------------------------------}
procedure PopOr;
{---------------------------------------------------------------}
procedure PopXor;
{---------------------------------------------------------------}
procedure PopCompare;
{---------------------------------------------------------------}
procedure SetEqual;
{---------------------------------------------------------------}
procedure SetNEqual;
{---------------------------------------------------------------}
procedure SetGreater;
{---------------------------------------------------------------}
procedure SetLess;
Все это дает нам необходимые инструменты. БНФ для булевых выражений такая: <bool-expr> ::= <bool-term> ( <orop> <bool-term> )* <bool-term> ::= <not-factor> ( <andop> <not-factor> )* <not-factor> ::= [ '!' ] <relation> <relation> ::= <expression> [ <relop> <expression> ] Зоркие читатели могли бы заметить,
что этот синтаксис не включает нетерминал "bool-factor" используемый в
ранних версиях. Тогда он был необходим потому, что я также разрешал булевы
константы TRUE и FALSE. Но не забудьте, что в TINY нет никакого различия
между булевыми и арифметическими типами... они могут свободно смешиваться.
Так что нет нужды в этих предопределенных значениях... мы можем просто
использовать -1 и 0 соответственно.
#define TRUE -1
(Так было бы, если бы TINY имел
препроцессор.) Позднее, когда мы разрешим объявление констант, эти
два значения будут предопределены языком.
{---------------------------------------------------------------}
procedure Equals;
{---------------------------------------------------------------}
procedure NotEquals;
{---------------------------------------------------------------}
procedure Less;
{---------------------------------------------------------------}
procedure Greater;
{---------------------------------------------------------------}
procedure Relation;
{---------------------------------------------------------------}
procedure NotFactor;
{---------------------------------------------------------------}
procedure BoolTerm;
{--------------------------------------------------------------}
procedure BoolOr;
{--------------------------------------------------------------}
procedure BoolXor;
{---------------------------------------------------------------}
procedure BoolExpression;
Чтобы связать все это вместе
не забудьте изменить обращение к Expression в процедурах Factor и Assignment
на вызов BoolExpression.
pvx,y,zbx=z>ye. что означает PROGRAM
Видите как происходит присваивание булевского значения X? УПРАВЛЯЮЩИЕ СТРУКТУРЫ Мы почти дома. Имея булевы выражения легко добавить управляющие структуры. Для TINY мы разрешим только две из них, IF и WHILE: <if> ::= IF <bool-expression> <block> [ ELSE <block>] ENDIF <while> ::= WHILE <bool-expression> <block> ENDWHILE Еще раз позвольте мне разъяснить
решения, подразумевающиеся в этом синтаксисе, который сильно отличается
от синтаксиса C или Pascal. В обоих этих языках "тело" IF или WHILE расценивается
как одиночный оператор. Если вы предполагаете использовать блок из более
чем одного оператора вы должны создать составной утверждение использую
BEGIN-END (в Pascal) или '{}' (в C). В TINY (и KISS) нет таких вещей как
составное утверждение... одиночное или множественное, они являются в этом
языке просто блоками.
end { loop } или end { if } Как я объяснил в пятой части,
использование уникальных терминальных ключевых слов увеличивает размер
списка ключевых слов и, следовательно, замедляет лексический анализ, но
в данном случае это кажется небольшой ценой за дополнительную подстраховку.
Лучше обнаруживать ошибки во время компиляции, чем во время выполнения.
<bool-expression> и <block>, расположенные рядом без разделяющих ключевых слов. В Паскале
мы ожидали бы в этом месте ключевые слова THEN и DO.
{---------------------------------------------------------------}
procedure Branch(L: string);
{---------------------------------------------------------------}
procedure BranchFalse(L: string);
Исключая изоляцию подпрограмм генератора кода, код для анализа управляющих конструкций такой же, как вы видели прежде: {---------------------------------------------------------------}
procedure Block; Forward; procedure DoIf;
{--------------------------------------------------------------}
procedure DoWhile;
Чтобы связать все это вместе нам нужно только изменить процедуру Block чтобы распознавать ключевые слова IF и WHILE. Как обычно мы расширим определение блока так: <block> ::= ( <statement> )* где <statement> ::= <if> | <while> | <assignment> Соответствующий код: {--------------------------------------------------------------}
procedure Block;
Добавьте подпрограммы, которые
я дал, откомпилируйте и протестируйте их. У вас должна быть возможность
анализировать односимвольные версии любых управляющих конструкции. Выглядит
довольно хорошо!
ЛЕКСИЧЕСКИЙ АНАЛИЗ Конечно, вы знаете, что будет
дальше: Мы должны преобразовать программу так, чтобы она могла работать
с многосимвольными ключевыми словами, переводами строк и пробелами. Мы
только что прошли все это в седьмой главе. Мы будем использовать метод
распределенного сканера, который я показал вам в этой главе.
Фактическая реализация немного отличается, потому что различается способ,
которым я обрабатываю переводы строк.
{--------------------------------------------------------------}
procedure NewLine;
Заметьте, что мы видели эту процедуру
раньше в виде процедуры Fin. Я изменил имя, так как новое кажется более
соответствующим фактическому назначению. Я также изменил код чтобы учесть
множественные переносы и строки только с пробелами.
{--------------------------------------------------------------}
type Symbol = string[8]; SymTab = array[1..1000] of Symbol; TabPtr = ^SymTab;
{--------------------------------------------------------------}
var Look : char;
{ Lookahead Character }
ST: Array['A'..'Z'] of char; {--------------------------------------------------------------}
const NKW = 9;
const KWlist: array[1..NKW] of Symbol =
const KWcode: string[NKW1] = 'xilewevbep';
Затем добавьте три процедуры, также из седьмой главы: {--------------------------------------------------------------}
function Lookup(T: TabPtr; s: string; n: integer): integer;
procedure Scan;
procedure MatchString(x: string);
Теперь мы должны сделать довольно много тонких изменений в оставшихся процедурах. Сначала мы должны изменить функцию GetName на процедуру, снова как в главе 7: {--------------------------------------------------------------}
procedure GetName;
Обратите внимание, что эта процедура
оставляет свой результат в глобальной строковой переменной Value.
{---------------------------------------------------------------}
procedure BoolExpression; Forward; procedure Factor;
procedure Assignment;
procedure Decl;
(Заметьте, что мы все еще разрешаем
только односимвольные имена переменных поэтому мы используем здесь простое
решение и просто используем первый символ строки.)
{---------------------------------------------------------------}
procedure Block; Forward; procedure DoIf;
{--------------------------------------------------------------}
procedure DoWhile;
{--------------------------------------------------------------}
procedure Block;
{--------------------------------------------------------------}
procedure TopDecls;
{--------------------------------------------------------------}
procedure Main;
{--------------------------------------------------------------}
procedure Prog;
{--------------------------------------------------------------}
procedure Init;
Это должно работать. Если все
изменения сделаны правильно, вы должны теперь анализировать программы,
которые выглядят как программы. (Если вы не сделали всех изменений, не
отчаивайтесь. Полный листинг конечной формы дан ниже.)
МНОГОСИМВОЛЬНЫЕ ИМЕНА ПЕРЕМЕННЫХ Одна из них - ограничение, требующее
использования односимвольных имен переменных. Теперь, когда мы можем обрабатывать
многосимвольные ключевые слова, это ограничение начинает казаться произвольным
и ненужным. И действительно это так. В основном, единственное его достоинство
в том, что он позволяет получить тривиально простую реализацию таблицы
идентификаторов. Но это просто удобство для создателей компиляторов и оно
должно быть уничтожено.
NEntry: integer = 0; Затем измените определение таблицы идентификаторов как показано ниже: const MaxEntry = 100; var ST : array[1..MaxEntry] of Symbol; (Обратите внимание, что ST не
объявлен как SymTab. Это объявление липовое, чтобы заставить Lookup работать.
SymTab заняля бы слишком много памяти и поэтому фактически никогда не обьявляется).
{--------------------------------------------------------------}
function InTable(n: Symbol): Boolean;
Нам также необходима новая процедура AddEntry, которая добавляет новый элемент в таблицу: {--------------------------------------------------------------}
procedure AddEntry(N: Symbol; T: char);
Эта процедура вызывается из Alloc: {--------------------------------------------------------------}
procedure Alloc(N: Symbol);
Наконец, мы должны изменить все
подпрограммы, которые в настоящее время обрабатывают имена переменных как
одиночный символ. Они включают LoadVar и Store (просто измените тип с char
на string) и Factor, Assignment и Decl (просто измените Value[1] на Value).
{--------------------------------------------------------------}
procedure Init;
Это должно работать. Испытайте ее и проверьте, что вы действительно можете использовать многосимвольные имена переменных. СНОВА ОПЕРАТОРЫ ОТНОШЕНИЙ У нас осталось последнее односимвольное
ограничение - ограничение операторов отношений. Некоторые из операторов
отношений действительно состоят из одиночных символов, но другие требуют
двух. Это '<=' и '>='. Я также предпочитаю Паскалевское '<>' для
"не равно" вместо '#'.
{---------------------------------------------------------------}
procedure SetLessOrEqual;
{---------------------------------------------------------------}
procedure SetGreaterOrEqual;
Затем измените подпрограммы анализа отношений как показано ниже: {---------------------------------------------------------------}
procedure LessOrEqual;
{---------------------------------------------------------------}
procedure NotEqual;
{---------------------------------------------------------------}
procedure Less;
{---------------------------------------------------------------}
procedure Greater;
Это все, что требуется. Теперь вы можете обрабатывать все операторы отношений. Попробуйте. ВВОД/ВЫВОД Теперь у нас есть полный,
работающий язык, за исключением одного небольшого смущающего факта: у нас
нет никакого способа получить или вывести данные. Нам нужны подпрограммы
ввода/вывода.
{---------------------------------------------------------------}
procedure ReadVar;
{---------------------------------------------------------------}
procedure WriteVar;
Идея состоит в том, что READ
загружает значение из входного потока в D0, а WRITE выводит его оттуда.
{--------------------------------------------------------------}
procedure Header;
WriteLn('WARMST', TAB, 'EQU $A01E');
Она возьмет на себя эту часть работы. Теперь нам также необходимо распознавать команды ввода и вывода. Мы можем сделать это добавив еще два ключевых слова в наш список: {--------------------------------------------------------------}
const NKW = 11;
const KWlist: array[1..NKW] of Symbol =
const KWcode: string[NKW1] = 'xileweRWvbep';
(Обратите внимание, что здесь
я использую кода в верхнем регистре чтобы избежать конфликта с 'w' из WHILE.)
{--------------------------------------------------------------}
{--------------------------------------------------------------}
procedure DoWrite;
Наконец, мы должны расширить процедуру Block для поддержки новых типов операторов: {--------------------------------------------------------------}
procedure Block;
На этом все. Теперь у нас есть язык! ЗАКЛЮЧЕНИЕ К этому моменту мы полностью
определили TINY. Он не слишком значителен... в действительности игрушечный
комиплятор. TINY имеет только один тип данных и не имеет подпрограмм...
но это законченный, пригодный для использования язык. Пока что вы не имеете
возможности написать на нем другой компилятор или сделать что-нибудь еще
очень серьезное, но вы могли бы писать программы для чтения входных данных,
выполнения вычислений и вывода результатов. Не слишком плохо для игрушки.
{--------------------------------------------------------------}
{--------------------------------------------------------------}
const TAB = ^I;
LCount: integer = 0;
{--------------------------------------------------------------}
type Symbol = string[8]; SymTab = array[1..1000] of Symbol;
{--------------------------------------------------------------}
var Look : char;
{ Lookahead Character }
const MaxEntry = 100; var ST : array[1..MaxEntry] of Symbol;
{--------------------------------------------------------------}
const NKW = 11;
const KWlist: array[1..NKW] of Symbol =
const KWcode: string[NKW1] = 'xileweRWvbep';
{--------------------------------------------------------------}
procedure GetChar;
{--------------------------------------------------------------}
procedure Error(s: string);
{--------------------------------------------------------------}
procedure Abort(s: string);
{--------------------------------------------------------------}
procedure Expected(s: string);
{--------------------------------------------------------------}
procedure Undefined(n: string);
{--------------------------------------------------------------}
function IsAlpha(c: char): boolean;
{--------------------------------------------------------------}
function IsDigit(c: char): boolean;
{--------------------------------------------------------------}
function IsAlNum(c: char): boolean;
{--------------------------------------------------------------}
function IsAddop(c: char): boolean;
{--------------------------------------------------------------}
function IsMulop(c: char): boolean;
{--------------------------------------------------------------}
function IsOrop(c: char): boolean;
{--------------------------------------------------------------}
function IsRelop(c: char): boolean;
{--------------------------------------------------------------}
function IsWhite(c: char): boolean;
{--------------------------------------------------------------}
procedure SkipWhite;
{--------------------------------------------------------------}
procedure NewLine;
{--------------------------------------------------------------}
procedure Match(x: char);
{--------------------------------------------------------------}
function Lookup(T: TabPtr; s: string; n: integer): integer;
{--------------------------------------------------------------}
function Locate(N: Symbol): integer;
{--------------------------------------------------------------}
function InTable(n: Symbol): Boolean;
{--------------------------------------------------------------}
procedure AddEntry(N: Symbol; T: char);
{--------------------------------------------------------------}
procedure GetName;
{--------------------------------------------------------------}
function GetNum: integer;
{--------------------------------------------------------------}
procedure Scan;
{--------------------------------------------------------------}
procedure MatchString(x: string);
{--------------------------------------------------------------}
procedure Emit(s: string);
{--------------------------------------------------------------}
procedure EmitLn(s: string);
{--------------------------------------------------------------}
function NewLabel: string;
{--------------------------------------------------------------}
procedure PostLabel(L: string);
{---------------------------------------------------------------}
procedure Clear;
{---------------------------------------------------------------}
procedure Negate;
{---------------------------------------------------------------}
procedure NotIt;
{---------------------------------------------------------------}
procedure LoadConst(n: integer);
{---------------------------------------------------------------}
procedure LoadVar(Name: string);
{---------------------------------------------------------------}
procedure Push;
{---------------------------------------------------------------}
procedure PopAdd;
{---------------------------------------------------------------}
procedure PopSub;
{---------------------------------------------------------------}
procedure PopMul;
{---------------------------------------------------------------}
procedure PopDiv;
{---------------------------------------------------------------}
procedure PopAnd;
{---------------------------------------------------------------}
procedure PopOr;
{---------------------------------------------------------------}
procedure PopXor;
{---------------------------------------------------------------}
procedure PopCompare;
{---------------------------------------------------------------}
procedure SetEqual;
{---------------------------------------------------------------}
procedure SetNEqual;
{---------------------------------------------------------------}
procedure SetGreater;
{---------------------------------------------------------------}
procedure SetLess;
{---------------------------------------------------------------}
procedure SetLessOrEqual;
{---------------------------------------------------------------}
procedure SetGreaterOrEqual;
{---------------------------------------------------------------}
procedure Store(Name: string);
{---------------------------------------------------------------}
procedure Branch(L: string);
{---------------------------------------------------------------}
procedure BranchFalse(L: string);
{---------------------------------------------------------------}
procedure ReadVar;
{ Write Variable from Primary Register } procedure WriteVar;
{--------------------------------------------------------------}
procedure Header;
{--------------------------------------------------------------}
procedure Prolog;
{--------------------------------------------------------------}
procedure Epilog;
{---------------------------------------------------------------}
procedure BoolExpression; Forward; procedure Factor;
{--------------------------------------------------------------}
procedure NegFactor;
{--------------------------------------------------------------}
procedure FirstFactor;
{--------------------------------------------------------------}
procedure Multiply;
{-------------------------------------------------------------}
procedure Divide;
{---------------------------------------------------------------}
procedure Term1;
{---------------------------------------------------------------}
procedure Term;
{---------------------------------------------------------------}
procedure FirstTerm;
{--------------------------------------------------------------}
procedure Add;
{-------------------------------------------------------------}
procedure Subtract;
{---------------------------------------------------------------}
procedure Expression;
{---------------------------------------------------------------}
procedure Equal;
{---------------------------------------------------------------}
procedure LessOrEqual;
{---------------------------------------------------------------}
procedure NotEqual;
{---------------------------------------------------------------}
procedure Less;
{---------------------------------------------------------------}
procedure Greater;
{---------------------------------------------------------------}
procedure Relation;
{---------------------------------------------------------------}
procedure NotFactor;
{---------------------------------------------------------------}
procedure BoolTerm;
{--------------------------------------------------------------}
procedure BoolOr;
{--------------------------------------------------------------}
procedure BoolXor;
{---------------------------------------------------------------}
procedure BoolExpression;
{--------------------------------------------------------------}
procedure Assignment;
{---------------------------------------------------------------}
procedure Block; Forward;
procedure DoIf;
{--------------------------------------------------------------}
procedure DoWhile;
{--------------------------------------------------------------}
procedure DoRead;
{--------------------------------------------------------------}
procedure DoWrite;
{--------------------------------------------------------------}
procedure Block;
{--------------------------------------------------------------}
procedure Alloc(N: Symbol);
{--------------------------------------------------------------}
procedure Decl;
{--------------------------------------------------------------}
procedure TopDecls;
{--------------------------------------------------------------}
procedure Main;
{--------------------------------------------------------------}
procedure Prog;
{--------------------------------------------------------------}
procedure Init;
{--------------------------------------------------------------}
begin
|