Анимация
JavaScript
|
Главная Библионтека Другими словами, конструкция IF имеет специфический символ завершения. Это позволит избежать висячих else Паскаля и Си и также предотвращает необходимость использовать скобки {} или begin-end. Синтаксис, который я вам здесь показываю, фактически является синтаксисом языка KISS, который я буду детализировать в следующих главах. Другие конструкции также будут немного отличаться. Это не должно быть для вас большой проблемой. Как только вы увидите, как это делается, вы поймете, что в действительности не имеет большого значения, какой конкретный синтаксис используется. Как только синтаксис определен, включить его в код достаточно просто. Теперь, все конструкции, с которыми мы будем иметь дело, включают передачу управления, что на уровне ассемблера означает условные и/или безусловные переходы. К примеру простой оператор IF: IF <condition> A ENDIF B... должен быть переведен в: Если условие не выполнено то переход на L A L: B Ясно, что нам понадобятся несколько процедур, которые помогут нам работать с этими переходами. Ниже я определил две из них. Процедура NewLabel генерирует уникальные метки. Это сделано с помощью простого способа называть каждую метку Lnn, где nn - это номер метки, начинающийся с нуля. Процедура PostLabel просто выводит метки в соответствующем месте. Вот эти две подпрограммы: { Generate a Unique Label } function NewLabel: string; var S: string; begin Str(LCount, S); NewLabel := L + S; Inc(LCount); end; { Post a Label To Output } procedure PostLabel(L: string); begin WriteLn(L, :); end; Заметьте, что мы добавили новую глобальную переменную LCount, так что вы должны изменить раздел описания переменных в начале программы, следующим образом: var Look : char; { Lookahead Character } Lcount: integer; { Label Counter } Также добавьте следующий дополнительный инициализирующий код в Init: LCount := 0; (Не забудьте сделать это, иначе ваши метки будут выглядеть действительно странными!). В этом месте я также хотел бы показать вам новый вид нотации. Если вы сравните форму оператора IF, указанную выше, с ассемблерным кодом, который должен быть получен, то вы можете увидеть, что существуют некоторые определенные действия, связанные с каждым ключевым словом в операторе: IF: Сначала получить условие и выдать код для него. Затем создать уникальную метку и выдать переход если условие ложно. ENDIF: Выдать метку. Эти действия могут быть показаны очень кратко, если мы запишем синтаксис таким образом: <condition> { Condition; L = NewLabel; Emit(Branch False to L); } <block> ENDIF { PostLabel(L) } Это пример синтаксически-управляемого перевода. Мы уже делали все это... мы просто никогда прежде не записывали это таким образом. Содержимое фигурных скобок представляет собой действия, которые будут выполняться. Хорошо в этом способе представления то, что он не только показывает что мы должны распознать, но также и действия, которые мы должны выполнить и в каком порядке. Как только мы получаем такой синтаксис, код возникает почти сам собой. Почти единственное, что осталось сделать - конкретизировать то, что мы подразумеваем под "Переход если условие ложно". Я полагаю, что должен быть код, выполняющийся для <condition>, который будет выполнять булеву алгебру и вычислять некоторый результат. Он также должен установить флажки условий, соответствующие этому результату. Теперь, обычным соглашением для булевых переменных является использование 0000 для представления значения "ложь" и какого-либо другого значения (кто-то использует FFFF, кто-то 0001 ) для представления "истины". Процессор 68000 устанавливает флажки условий всякий раз, когда любые данные перемещаются или рассчитываются. Если данные равны 0000 (что соответствует условию ложь, запомните) будет установлен флажок ноль. Код для "перехода по нулю" -BEQ. Таким образом BEQ <=> Переход если ложь BNE <=> Переход если истина По природе вещей большинство ветвлений, которые мы увидим, будут BEQ... мы будем обходить вокруг кода, который должен выполняться когда условие истинно. ОПЕРАТОР IF После этого небольшого пояснения метода мы наконец готовы начать программирование синтаксического анализатора для условного оператора. Фактически, мы уже почти сделали это! Как обычно я буду использовать наш односимвольный подход, с символом "i" вместо "IF" и "e" вместо "ENDIF" (также как и END... это двойственная природа не вызывает никакого беспорядка). Я также пока полностью пропущу символ для условия ветвления, который мы все еще должны определить. Код для DoIf: { Recognize and Translate an IF Construct } procedure Block; Forward; procedure DoIf; var L: string; begin Match(i); L := NewLabel; Condition; EmitLnCBEQ + L); Block; Match(e); PostLabel(L); end; Добавьте эту подпрограмму в вашу программу и измените Block так, чтобы он ссылался на нее как показано ниже: { Recognize and Translate a Statement Block } procedure Block; begin while not(Look in [e]) do begin case Look of i: Dolf; o: Other; end; end; end; Обратите внимание на обращение к процедуре Condition. В конечном итоге мы напишем подпрограмму, которая сможет анализировать и транслировать любое логическое условие которое мы ей дадим. Но это уже тема для отдельной главы (фактически следующей). А сейчас давайте просто заменим ее макетом, который выдает некоторый текст. Напишите следующую подпрограмму: { Parse and Translate a Boolean Condition } { This version is a dummy } Procedure Condition; begin EmitLn(<condition>); end; Вставьте эту процедуру в вашу программу как раз перед DoIf. Теперь запустите программу. Испробуйте строку типа: aibece Как вы можете видеть, синтаксический анализатор, кажется, распознает конструкцию и вставляет объектный код в правильных местах. Теперь попробуйте набор вложенных IF: aibicedefe Он начинает все более походить на настоящий, не так ли? Теперь, когда у нас есть общая идея (и инструменты такие как нотация и процедуры NewLabel и PostLabel) проще пареной репы расширить синтаксический анализатор для поддержки и других конструкций. Первое (а также и одно из самых сложных) это добавление условия ELSE в IF. В БНФ это выглядит так: IF <condition> <block> [ ELSE <block>] ENDIF Сложность возникает просто потому, что здесь присутствует необязательное условие, которого нет в других конструкциях. Соответствующий выходной код должен быть таким: <condition> BEQ L1 <block> BRA L2 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 |