Анимация
JavaScript
|
Главная Библионтека Как обычно добавьте этот код и испытайте "компилятор". В настоящее время существует только одна допустимая входная последовательность: px. (где х - это любая одиночная буква, имя программы). Хорошо, как обычно наша первая попытка не очень впечатляет, но я уверен к настоящему времени вы знаете, что дальше станет интересней. Есть одна важная вещь, которую следует отметить: на выходе получается работающая, законченная и выполнимая программа (по крайней мере после того, как она будет ассемблирована). Это очень важно. Приятная особенность нисходящего метода состоит в том, что на любом этапе вы можете компилировать подмножество завершенного языка и получить программу, которая будет работать на конечной машине. Отсюда, затем, нам необходимо только добавлять возможности, расширяя конструкции языка. Это очень похоже на то, что мы уже делали, за исключением того, что мы подходили к этому с другого конца. РАСШИРЕНИЕ Чтобы расширить компилятор мы должны просто работать с возможностями языка последовательно. Я хочу начать с пустой процедуры, которая ничего не делает, затем добавлять детали в пошаговом режиме. Давайте начнем с обработки блока в соответствии с его PDL выше. Мы можем сделать это в два этапа. Сначала добавьте пустую процедуру: { Parse and Translate a Pascal Block } procedure DoBlock(Name: char); begin end; и измените Prog следующим образом: { Parse and Translate A Program } procedure Prog; var Name: char; begin Match(p); Name := GetName; Prolog; DoBlock(Name); MatchC.); Epilog(Name); end; Это конечно не должно изменить поведения программы, и не меняет. Но сейчас определение Prog закончено и мы можем перейти к расширению DoBlock. Это получается прямо из его БНФ определения: { Parse and Translate a Pascal Block } procedure DoBlock(Name: char); begin Declarations; PostLabel(Name); Statements; end; Процедура PostLabel была определена в главе по ветвлениям. Скопируйте ее в вашу копию Cradle. Я возможно должен объяснить причину вставки метки. Это имеет отношение к работе SK*DOS. В отличие от некоторых других ОС, SK*DOS позволяет точке входа в основную программу находиться в любом месте программы. Все, что вы должны сделать, это дать этой точке имя. Вызов PostLabel помещает это имя как раз перед первым выполнимым утверждением в основной программе. Как SK*DOS узнает какая из множества меток является точкой входа, спросите вы? Та, которая соответствует утверждению END в конце программы. Теперь нам нужны заглушки для процедур Declarations и Statements. Сделайте их пустыми процедурами как мы делали это раньше. Программа все еще делает то же самое? Тогда мы можем перейти к следующему этапу. ОБЪЯВЛЕНИЯ БНФ для объявлений в Pascal такая: <declarations> ::= ( <label list> <constant list> <type list> <variable list> <procedure> <function> )* (Заметьте, что я использую более либеральное определение, используемое в Turbo Pascal. В определении стандартного Pascal каждая из этих частей должна следовать в определенном порядке относительно других). Как обычно давайте позволим одиночным символам представлять каждый из этих типов объявлений. Новая форма для Declarations: { Parse and Translate the Declaration Part } procedure Declarations; begin while Look in [l, C, t, v, p, f] do case Look of Ч: Labels; c: Constants; t: Types; v: Variables; p: DoProcedure; f: DoFunction; end; end; Конечно, нам нужны процедуры-заглушки для каждого из этих типов объявлений. На этот раз они не могут быть совсем пустыми процедурами, так как иначе мы останемся с бесконечным циклом While. По крайней мере каждая подпрограмма распознавания должна съедать символ, который вызывает ее. Вставьте следующие процедуры: { Process Label Statement } procedure Labels; begin MatchCl); end; { Process Const Statement } procedure Constants; begin Match(c); end; { Process Type Statement } procedure Types; begin Match(t); end; { Process Var Statement } procedure Variables; begin Match(v); end; { Process Procedure Definition } procedure DoProcedure; begin Match(p); end; { Process Function Definition } procedure DoFunction; begin Match(f); end; Теперь испытайте компилятор используя несколько характерных входных последовательностей. Вы можете смешивать объявления любым образом, каким вам нравится пока последним символом в программе не будет ".", указывающий на конец программы. Конечно, ни одно из этих объявлений фактически ничего не объявляет, так что вам не нужны (и вы не можете использовать) любые символы, кроме тех, которые обозначают ключевые слова. Мы можем расширить раздел операторов аналогичным способом. БНФ для него будет: <statements> ::= <compound statement> <compound statement> ::= BEGIN <statement>(; <statement>) END Заметьте, что утверждение может начинаться с любого идентификатора, исключая END. Так что первая пустой формой процедуры Statements будет: { Parse and Translate the Statement Part } procedure Statements; begin Match(b); while Look <> e do GetChar; Match(e); end; Сейчас компилятор примет любое число объявлений, сопровождаемое блоком BEGIN основной программы. Сам этот блок может содержать любые символы (за исключением END), но они должны присутствовать. Простейшая входная форма сейчас pxbe. Испытайте ее. Также попробуйте некоторые ее комбинации. Сделайте некоторые преднамеренные ошибки и посмотрите что произойдет. 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 |