Анимация
JavaScript
|
Главная Библионтека { Recognize and Translate a Subtract } function Subtract(T1: char): char; begin Match(-); Subtract := PopSub(T1, Term); end; Но простота обманчива, поскольку мы переложили всю логику на PopAdd и PopSub, которые больше не являются просто подпрограммами генерации кода. Они также должны теперь заботиться о необходимых преобразованиях типов. Какие это преобразования? Простые: оба аргумента должны иметь тот же самый размер и результат также такой размер. Меньший из двух параметров должен быть "приведен" до размера большего. Но это представляет небольшую проблему. Если переводимый параметр - второй (т.е. в основном регистре D0) мы в отличной форме. Если же нет, мы в затруднении: мы не можем изменить размер данных, которые уже затолкнуты в стек. Решение простое, но немного болезненное: мы должны отказаться от этих красивых инструкций "вытолкнуть данные и что-нибудь с ними сделать", заботливо предоставленных Motorola. Альтернативой является назначение вторичного регистра, в качестве которого я выбрал R7. (Почему не R1? Потому, что для других регистров у меня есть планы на будущее.) Первый шаг в этой новой структуре - представить процедуру Pop, аналогичную Push. Эта процедура будет всегда выталкивать верхний элемент стека в D7: { Pop Stack into Secondary Register } procedure Pop(Size: char); begin Move(Size, (SP)+, D7); end; Общая идея состоит в том, что все "Pop-Op" подпрограммы могут вызывать ее. Когда это сделано, мы будем иметь оба операнда в регистрах, поэтому мы можем перевести любой нужный нам. Для работы процедуре Convert необходим другой аргумент, имя регистра: { Convert a Data Item from One Type to Another } procedure Convert(Source, Dest: char; Reg: String); begin if Source <> Dest then begin if Source = B then EmitLnCAND.W #$FF, + Reg); if Dest = L then EmitLnCEXT.L + Reg); end; end; Следующая функция выполняет пребразование, но только если текущий тип T1 меньше по размеру, чем желаемый тип T2. Это функция, возвращающая конечный тип, позволяющий нам знать, что она решила: { Promote the Size of a Register Value } function Promote(T1, T2: char; Reg: string): char; var Typ: char; begin Typ := T1; if T1 <> T2 then if (T1 = B) or ((T1 = W) and (T2 = L)) then begin Convert(T1, T2, Reg); Typ := T2; end; Promote := Typ; end; Наконец, следующая функция приводит два регистра к одному типу: { Force both Arguments to Same Type } function SameType(T1, T2: char): char; begin T1 := Promote(T1, T2, D7); SameType := Promote(T2, T1, D0); end; Эти новые подпрограммы дают нам заряд, необходимы нам чтобы разложить PopAdd и PopSub: { Generate Code to Add Primary to the Stack } function PopAdd(T1, T2: char): char; begin Pop(T1); T2 := SameType(T1, T2); GenAdd(T2); PopAdd := T2; end; { Generate Code to Subtract Primary from the Stack } function PopSub(T1, T2: char): char; begin Pop(T1); T2 := SameType(T1, T2); GenSub(T2); PopSub := T2; end; После всех этих приготовлений, в конечном результате нет почти ничего кульминационного. Снова, вы можете видеть что логика совершенно проста. Все что делают эти две подпрограммы - выталкивают вершину стека в D7, приводят два операнда к одному размеру и затем генерируют код. Обратите внимание на две новые подпрограммы генерации кода GenAdd и GenSub. Они являются остаточной формой оригинальных PopAdd и PopSub. Т. е. они являются чистыми генераторами кода, производящими сложение и вычитание регистров: { Add Top of Stack to Primary } procedure GenAdd(Size: char); begin EmitLn(ADD. + Size + D7,D0); end; { Subtract Primary from Top of Stack } procedure GenSub(Size: char); begin EmitLnCSUB. + Size + D7,D0); EmitLnCNEG. + Size + D0); end; ОК, я соглашусь с вами: я выдал вам множество подпрограмм с тех пор, как мы в последний раз протестировали код. Но вы должны признать, что каждая новая подпрограмма довольно проста и ясна. Если вам (как и мне) не нравится тестировать так много новых подпрограмм одновременно все в порядке. Вы можете заглушить подпрограммы типа Convert, Promote и SameType так как они не считывают входной поток. Вы не получите корректный код, конечно, но программа должна работать. Затем постепенно расширяйте их. При тестировании программы не забудьте, что вы сначала должны объявить некоторые переменные а затем начать "тело" программы с "B" в верхнем регистре (для BEGIN). Вы должны обнаружить, что синтаксический анализатор обрабатывает любые аддитивные выражения. Как только все подпрограммы преобразования будет введены, вы должны увидеть, что генерируется правильный код и код для преобразования типов вставляется в нужных местах. Попробуйте смешивать переменные различных размеров а также литералы. Удостоверьтесь, что все работает правильно. Как обычно, хорошо было бы попробовать некоторые ошибочные выражения и посмотреть, как компилятор обрабатывает их. ПОЧЕМУ ТАК МНОГО ПРОЦЕДУР? К этому моменту вы можете подумать, что я зашел слишком далеко в смысле глубоко вложенных процедур. В этом несомненно есть большие накладные расходы. Но в моем безумии есть смысл. Как в случае с UnOp, я заглядываю вперед на время, когда мы захотим генерировать лучший код. С таким способом организации кода мы можем достичь этого без значительных изменений в программе Например, в случаях, где значение помещенное в стек не должно преобразовываться, все же лучше использовать инструкцию "вытолкнуть и сложить". Если мы решим проверять такие случаи, мы можем включить дополнительные тесты в PopAdd и PopSub не изменяя что-либо еще. МУЛЬТИПЛИКАТИВНЫЕ ВЫРАЖЕНИЯ Процедуры для работы с мультипликативными операторами почти такие же. Фактически, на первом уровне они почти идентичны, так что я просто покажу их здесь без особых фанфар. Первая - наша общая форма для Factor, которая включает подвыражения в скобках: { Parse and Translate a Factor } function Expression: char; Forward; function Factor: char; begin if Look = ( then begin Match((); Factor := Expression; Match()); end else if IsAlpha(Look) then Factor := Load(GetName) 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 |