Анимация
JavaScript
|
Главная Библионтека L1: <block> L2: ... Это приводит нас к следующей синтаксически управляемой схеме перевода: <condition> { L1 = NewLabel; L2 = NewLabel; Emit(BEQ L1) } <block> ELSE { Emit(BRA L2); PostLabel(L1) } <block> ENDIF { PostLabel(L2) } Сравнение этого со случаем IF без ELSE дает нам понимание того, как обрабатывать обе эти ситуации. Код ниже выполняет это. (Обратите внимание, что использую "l" вместо "ELSE" так как "e" имеет другое назначение): { Recognize and Translate an IF Construct } procedure DoIf; var L1, L2: string; begin Match(i); Condition; L1 := NewLabel; L2 := L1; EmitLn(BEQ + L1); Block; if Look = l then begin Match(l); L2 := NewLabel; EmitLn(BRA + L2); PostLabel(L1); Block; end; Match(e); PostLabel(L2); end; Вы получили его. Законченый анализатор/транслятор в 1 9 строк кода. Сейчас протестируйте его. Испробуйте что-нибудь типа: aiblcede Работает? Теперь, только для того, чтобы убедиться, что мы ничего не испортили и случай с IF без ELSE тоже будет обрабатываться, введите aibece Теперь испробуйте несколько вложенных IF. Испытайте что-нибудь на ваш выбор, включая несколько неправильных утверждений. Только запомните, что e не является допустимым оператором "other". ОПЕРАТОР WHILE Следующий вид оператора должен быть простым, так как мы уже имеем опыт. Синтаксис, который я выбрал для оператора WHILE следующий: WHILE <condition> <block> ENDWHILE Знаю, знаю, мы действительно не нуждаемся в отдельных видах ограничителей для каждой конструкции... вы можете видеть, что фактически в нашей односимвольной версии e используется для всех из них. Но я также помню множество сессий отладки в Паскале, пытаясь отследить своенравный END который по мнению компилятора я хотел поместить где-нибудь еще. По своему опыту знаю, что специфичные и уникальные ключевые слова, хотя они и добавляются к словарю языка, дают небольшую защиту от ошибок, которая стоит дополнительной работы создателей компиляторов. Теперь рассмотрите, во что должен траслироваться WHILE: L1: <condition> BEQ L2 <block> BRA L1 Как и прежде, сравнение этих двух представлений дает нам действия, необходимые на каждом этапе: WHILE { L1 = NewLabel; PostLabel(L1) } <condition> { Emit(BEQ L2) } <block> ENDWHILE { Emit(BRA L1); PostLabel(L2) } Код выходит непосредственно из синтаксиса: { Parse and Translate a WHILE Statement } procedure DoWhile; var L1, L2: string; begin Match(w); L1 := NewLabel; L2 := NewLabel; PostLabel(L1); Condition; EmitLnCBEQ + L2); Block; MatchCe); EmitLn(BRA + L1); PostLabel(L2); end; Так как мы получили новый оператор, мы должны добавить его вызов в процедуру Block: { Recognize and Translate a Statement Block } procedure Block; begin while not(Look in [e, l]) do begin case Look of i: DoIf; w: DoWhile; else Other; end; end; end; Никаких других изменений не требуется. Хорошо, протестируйте новую программу. Заметьте, что на этот раз код <condition> находится внутри верхней метки, как раз там, где нам надо. Попробуйте несколько вложенных циклов. Испробуйте циклы внутри IF и IF внутри циклов. Если вы немного напутаете то, что вы должны набирать, не смущайтесь: вы пишите ошибки и в других языках, не правда ли? Код будет выглядеть более осмысленным, когда мы получим полные ключевые слова. Я надеюсь, что к настоящему времени вы начинаете понимать, что это действительно просто. Все, что нам необходимо было сделать для того, чтобы создать новую конструкцию, это разработать ее синтаксически-управляемый перевод. Код возникает из него, и это не влияет на другие подпрограммы. Как только вы почувствуете это, вы увидите, что можете добавлять новые конструкции почти также быстро, как вы можете их придумывать. ОПЕРАТОР LOOP Мы могли бы остановиться на этом и иметь работающий язык. Много раз было показано, что языка высокого уровня всего с двумя конструкциями IF и WHILE достаточно для написания структурного кода. Но раз уж мы начали, то давайте немного расширим репертуар. Эта конструкция даже проще, так как она совсем не имеет проверки условия... это бесконечный цикл. Имеет ли смысл такой цикл? Немного сам по себе, но позднее мы собираемся добавить команду BREAK, которая даст нам способ выхода из цикла. Она делает язык значительно более богатым, чем Паскаль, который не имеет команды выхода из цикла и также позволяет избежать забавных конструкций типа WHILE(1) или WHILE TRUE в C и Паскале. Синтаксис прост: LOOP <block> ENDLOOP Синтаксически управляемый перевод: LOOP { L = NewLabel; PostLabel(L) } <block> ENDLOOP { Emit(BRA L } Соответствующий код показан ниже. Так как мы уже использовали "l" для ELSE на этот раз я использовал последнюю букву "p" как "ключевое слово". { Parse and Translate a LOOP Statement } procedure DoLoop; var L: string; begin Match(p); L := NewLabel; PostLabel(L); Block; Match(e); EmitLn(BRA + L); end; После того, как вы вставите эту подпрограмму, не забудьте добавить строчку в Block для ее вызова. 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 |