Анимация
JavaScript
|
Главная Библионтека procedure Main; begin Match(b); Prolog; Match(e); Epilog; end; Теперь единственной допустимой программой является программа: PROGRAM BEGIN END. (или pbe.) Разве мы не делаем успехи??? Хорошо, как обычно это становится лучше. Вы могли бы попробовать сделать здесь некоторые преднамеренные ошибки подобные пропуску b или e и посмотреть что случится. Как всегда компилятор должен отметить все недопустимые входные символы. ОБЪЯВЛЕНИЯ Очевидно на следующем шаге необходимо решить, что мы подразумеваем под объявлением. Я намереваюсь иметь два вида объявлений: переменных и процедур/функций. На верхнем уровне разрешены только глобальные объявления, точно как в C. Сейчас здесь могут быть только объявления переменных, идентифицируемые по ключевому слову VAR (сокращенно "v"). <top-level decls> ::= ( <data declaration> )* <data declaration> ::= VAR <var-list> Обратите внимание, что так как имеется только один тип переменных, нет необходимости объявлять этот тип. Позднее, для полной версии KISS, мы сможем легко добавить описание типа. Процедура Prog становится: { Parse and Translate a Program } procedure Prog; begin Match(p); Header; TopDecls; Main; MatchC.); end; Теперь добавьте две новые процедуры: { Process a Data Declaration } procedure Decl; begin Match(v); GetChar; end; { Parse and Translate Global Declarations } procedure TopDecls; begin while Look <> b do case Look of v: Decl; else Abort(Unrecognized Keyword + Look + ); end; end; Заметьте, что на данный момент Decl - просто заглушка. Она не генерирует никакого кода и не обрабатывает список... каждая переменная должна быть в отдельном утверждении VAR. ОК, теперь у нас может быть любое число объявлений данных, каждое начинается с "v" вместо VAR, перед блоком BEGIN. Попробуйте несколько вариантов и посмотрите, что происходит. ОБЪЯВЛЕНИЯ И ИДЕНТИФИКАТОРЫ Это выглядит довольно хорошо, но мы все еще генерируем только пустую программу. Настоящий ассемблер должен выдавать директивы ассемблера для распределения памяти под переменные. Пришло время действительно получить какой-нибудь код. С небольшим дополнительным кодом это легко сделать в процедуре Decl. Измените ее следующим образом: { Parse and Translate a Data Declaration } procedure Decl; var Name: char; begin Match(v); Alloc(GetName); end; Процедура Alloc просто выдает команду ассемблеру для распределения памяти: { Allocate Storage for a Variable } procedure Alloc(N: char); begin WriteLn(N, TAB, DC 0); end; Погоняйте программу. Попробуйте входную последовательность, которая объявляет какие-нибудь переменные, например: pvxvyvzbe. Видите, как распределяется память? Просто, да? Заметьте также, что точка входа "MAIN" появляется в правильном месте. Кстати, "настоящий" компилятор имел бы также таблицу идентификаторов для записи используемых переменных. Обычно, таблица идентификаторов необходима для записи типа каждой переменной. Но так как в нашем случае все переменные имеют один и тот же тип, нам не нужна таблица идентификаторов. Оказывается, мы смогли бы находить идентификатор даже без различия типов, но давайте отложим это пока не возникнет такая необходимость. Конечно, в действительности мы не анализировали правильный синтаксис для объявления данных, так как он включает список переменных. Наша версия разрешает только одну переменную. Это также легко исправить. БНФ для <var-list> следующая: <var-list> ::= <ident> (, <ident>)* Добавление этого синтаксиса в Decl дает новую версию: { Parse and Translate a Data Declaration } procedure Decl; var Name: char; begin Match(v); Alloc(GetName); while Look = , do begin GetChar; Alloc(GetName); end; end; ОК, теперь откомпилируйте этот код и испытайте его. Попробуйте ряд строк с объявлениями VAR, попробуйте список из нескольких переменных в одной строке и комбинации этих двух. Работает? ИНИЦИАЛИЗАТОРЫ Пока мы работали с объявлениями данных, меня беспокоила одна вещь - то, что Pascal не позволяет инициализировать данные в объявлении. Эта возможность по общему признанию является своего рода излишеством, и ее может не быть в языке, который считается минимальным языком. Но ее также настолько просто добавить, что было бы позором не сделать этого. БНФ становится: <var-list> ::= <var> ( <var> )* <var> ::= <ident> [ = <integer> ] Измените Alloc как показано ниже: { Allocate Storage for a Variable } procedure Alloc(N: char); begin Write(N, TAB, DC ); if Look = = then begin Match(=); WriteLn(GetNum); end else WriteLn(O); end; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 [ 41 ] 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |