Анимация
JavaScript


Главная  Библионтека 

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

набирать текст на клавиатуре!

ОК, откомпилируйте новую версию анализатора и испытайте его. Как обычно, проверьте что «компилятор» обрабатывает любое допустимое выражение и выдает осмысленное сообщение об ошибке для запрещенных. Четко, да? Вы можете заметить, что в нашей тестовой версии любое сообщение об ошибке выводится вместе с генерируемым кодом. Но запомните, это только потому, что мы используем экран как «выходной файл» в этих экспериментах. В рабочей версии вывод будет разделен, один в выходной файл, другой на экран.

ИСПОЛЬЗОВАНИЕ СТЕКА

В этом месте я собираюсь нарушить свое правило, что я не представлю что-либо сложное, пока это не будет абсолютно необходимо. Прошло достаточно много времени, чтобы не отметить проблему с генерируемым кодом. В настоящее время синтаксический анализатор использует D0 как «основной» регистр, и D1 для хранения частичной суммы. Эта схема работает отлично потому что мы имеем дело только с "addops" ([+ и [-) и новое число прибавляется по мере появления. Но в общем форме это не так. Рассмотрим, например выражение

1+(2-(3+(4-5)))

Если мы поместим «1 » в D1 , то где мы разместим «2»? Так как выражение в общей форме может иметь любую степень сложности, то мы очень быстро используем все регистры!

К счастью есть простое решение. Как и все современные микропроцессоры, 68000 имеет стек, который является отличным местом для хранения переменного числа элементов. Поэтому вместо того, чтобы помещать термы в D0 и D1 давайте затолкнем их в стек. Для тех кто незнаком с ассемблером 68000 - помещение в стек пишется как

-(SP)

и извлечение (SP)+. Итак, изменим EmitLn в процедуре Expression на

EmitLn(MOVE D0,-(SP));

и две строки в Add и Subtract:

EmitLn(ADD (SP)+,D0) и

EmitLn(SUB (SP)+,D0) соответственно. Теперь испытаем компилятор снова и удостоверимся что он работает.

И снова, полученный код менее эффективен, чем был до этого, но это необходимый шаг, как вы увидите.

УМНОЖЕНИЕ И ДЕЛЕНИЕ

Теперь давайте возьмемся за действительно серьезные дела. Как вы знаете, кроме операторов "addops" существуют и другие, выражения могут также иметь операторы умножения и деления. Вы также знаете, что существует неявный приоритет операторов или иерархия, связанная с выражениями, чтобы в выражениях типа

2 + 3 * 4,

мы знали, что нужно сначала умножить, а затем сложить. (Видите, зачем нам нужен стек?)

В ранние дни технологии компиляторов, люди использовали различные довольно сложные методы для того чтобы правила приоритета операторов соблюдались. Но, оказывается, все же, что ни один из них нам не нужен, эти правила могут быть очень хорошо применены в нашей технике нисходящего синтаксического анализа. До сих пор



единственной формой, которую мы применяли для терма была форма одиночной десятичной цифры. В более общей форме мы можем определить терм как произведение показателей (product of factors), то есть

<term> ::= <factor> [ <mulop> <factor ]* Что такое показатель? На данный момент это тоже, чем был раннее терм - одиночной цифрой.

Обратите внимание: терм имеет ту же форму, что и выражение. Фактически, мы можем добавить это в наш компилятор осторожно скопировав и переименовав. Но во избежание неразберихи ниже приведен полный листинг всех подпрограмм анализатора. (Заметьте способ, которым мы изменяем порядок операндов в Divide.)

{ Parse and Translate a Math Factor }

procedure Factor;

begin

EmitLn(MOVE # + GetNum + ,D0)

end;

{ Recognize and Translate a Multiply }

procedure Multiply;

begin

Match(*);

Factor;

EmitLnCMULS (SP) + ,D0); end;

{ Recognize and Translate a Divide }

procedure Divide;

begin

Match(/); Factor;

EmitLn(MOVE (SP)+,D1); EmitLn(DIVS D1,D0);

end;

{ Parse and Translate a Math Term }

procedure Term;

begin

Factor;

while Look in [*, /] do begin EmitLn(MOVE D0,-(SP)); case Look of *: Multiply; /: Divide; else Expected(Mulop); end; end; end;

{ Recognize and Translate an Add }

procedure Add;

begin

Match(+); Term;

EmitLn(ADD (SP)+,D0); end;



{ Recognize and Translate a Subtract }

procedure Subtract;

begin

MatchC-);

Term;

EmitLnCSUB (SP)+,D0); EmitLnCNEG D0); end;

{ Parse and Translate an Expression } procedure Expression; begin Term;

while Look in [+, -] do begin EmitLn(MOVE D0,-(SP)); case Look of

+: Add;

Subtract; else Expected(Addop); end; end; end;

Конфетка! Почти работающий транслятор в 55 строк Паскаля! Получаемый код начинает выглядеть действительно полезным, если не обращать внимание на неэффективность. Запомните, мы не пытаемся создавать сейчас самый компактный код.

КРУГЛЫЕ СКОБКИ

Мы можем закончить эту часть синтаксического анализатора добавив поддержку круглых скобок. Как вы знаете, скобки являются механизмом принудительного изменения приоритета операторов. Так, например, в выражении

2*(3+4) ,

скобки заставляют выполнять сложение перед умножением. Но, что гораздо более важно, скобки дают нам механизм для определения выражений любой степени сложности, как, например

(1+2)/((3+4)+(5-6))

Ключом к встраиванию скобок в наш синтаксический анализатор является понимание того, что не зависимо от того, как сложно выражение, заключенное в скобки, для остальной части мира оно выглядит как простой показатель. Это одна из форм для показателя:

<factor> ::= (<expression>)

Здесь появляется рекурсия. Выражение может содержать показатель, который содержит другое выражение, которое содержит показатель и т.д. до бесконечности.

Сложно это или нет, мы должны позаботиться об этом, добавив несколько строчек в процедуру Factor:

{ Parse and Translate a Math Factor } procedure Expression; Forward; procedure Factor; begin

if Look = ( then begin MatchCC); Expression;



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