Лекции по построению компилятора на Pascal

       

БУЛЕВА ЛОГИКА


Следующий шаг также должен быть вам знаком. Мы должны добавить булевы выражения и операторы отношений. Снова, так как мы работали с ними не один раз, я не буду подробно разбирать их за исключением моментов, в которых они отличаются от того, что мы делали прежде. Снова, мы не будем просто копировать их из других файлов потому что я немного изменил некоторые вещи. Большинство изменений просто включают изоляцию машинно-зависимых частей как мы делали для арифметических операций. Я также несколько изменил процедуру NotFactor для соответствия структуре FirstFactor. Наконец я исправил ошибку в объектном коде для операторов отношений: в инструкции Scc я использовал только младшие 8 бит D0. Нам нужно установить логическую истину для всех 16 битов поэтому я добавил инструкцию для  изменения младшего байта.

Для начала нам понадобятся несколько подпрограмм распознавания:

{--------------------------------------------------------------}

{ Recognize a Boolean Orop }

function IsOrop(c: char): boolean;

begin

   IsOrop := c in ['|', '~'];

end;

{--------------------------------------------------------------}

{ Recognize a Relop }

function IsRelop(c: char): boolean;

begin

   IsRelop := c in ['=', '#', '<', '>'];

end;

{--------------------------------------------------------------}

Также нам понадобятся несколько подпрограмм генерации кода:

{---------------------------------------------------------------}

{ Complement the Primary Register }

procedure NotIt;

begin

   EmitLn('NOT D0');

end;

{---------------------------------------------------------------}

.

.

.

{---------------------------------------------------------------}

{ AND Top of Stack with Primary }

procedure PopAnd;

begin

   EmitLn('AND (SP)+,D0');

end;

{---------------------------------------------------------------}

{ OR Top of Stack with Primary }

procedure PopOr;

begin

   EmitLn('OR (SP)+,D0');

end;

{---------------------------------------------------------------}


{ XOR Top of Stack with Primary }

procedure PopXor;

begin

   EmitLn('EOR (SP)+,D0');

end;

{---------------------------------------------------------------}

{ Compare Top of Stack with Primary }

procedure PopCompare;

begin

   EmitLn('CMP (SP)+,D0');

end;

{---------------------------------------------------------------}

{ Set D0 If Compare was = }

procedure SetEqual;

begin

   EmitLn('SEQ D0');

   EmitLn('EXT D0');

end;

{---------------------------------------------------------------}

{ Set D0 If Compare was != }

procedure SetNEqual;

begin

   EmitLn('SNE D0');

   EmitLn('EXT D0');

end;

{---------------------------------------------------------------}

{ Set D0 If Compare was > }

procedure SetGreater;

begin

   EmitLn('SLT D0');

   EmitLn('EXT D0');

end;

{---------------------------------------------------------------}

{ Set D0 If Compare was < }

procedure SetLess;

begin

   EmitLn('SGT D0');

   EmitLn('EXT D0');

end;

{---------------------------------------------------------------}

Все это дает нам необходимые инструменты. БНФ для булевых выражений такая:

     <bool-expr> ::= <bool-term> ( <orop> <bool-term> )*

     <bool-term> ::= <not-factor> ( <andop> <not-factor> )*

     <not-factor> ::= [ '!' ] <relation>

     <relation> ::= <expression> [ <relop> <expression> ]

Зоркие читатели могли бы заметить, что этот синтаксис не включает нетерминал "bool-factor" используемый в ранних версиях. Тогда он был необходим потому, что я также разрешал булевы константы TRUE и FALSE. Но не забудьте, что в TINY нет никакого различия между булевыми и арифметическими типами... они могут свободно смешиваться. Так что нет нужды в этих предопределенных значениях... мы можем просто использовать -1 и 0 соответственно.



В терминологии C мы могли бы всегда использовать определения:

     #define TRUE -1

     #define FALSE 0

 (Так было бы, если бы TINY имел препроцессор.)  Позднее, когда мы разрешим объявление констант, эти два значения будут предопределены языком.

Причина того, что я заостряю на этом ваше внимание, в том что я пытался использовать альтернативный путь, который заключался в использовании TRUE и FALSE как ключевых слов. Проблема с этим подходом в том, что он требует лексического анализа каждого имени переменной в каждом выражении. Как вы помните, я указал в главе 7, что это значительно замедляет компилятор. Пока ключевые слова не могут быть в выражениях нам нужно выполнять сканирование только в начале каждого нового оператора... значительное улучшение. Так что использование вышеуказанного синтаксиса не только упрощает синтаксический анализ, но также ускоряет сканирование.

Итак, если мы удовлетворены синтаксисом, представленным выше, то соответствующий код показан ниже:

{---------------------------------------------------------------}

{ Recognize and Translate a Relational "Equals" }

procedure Equals;

begin

   Match('=');

   Expression;

   PopCompare;

   SetEqual;

end;

{---------------------------------------------------------------}

{ Recognize and Translate a Relational "Not Equals" }

procedure NotEquals;

begin

   Match('#');

   Expression;

   PopCompare;

   SetNEqual;

end;

{---------------------------------------------------------------}

{ Recognize and Translate a Relational "Less Than" }

procedure Less;

begin

   Match('<');

   Expression;

   PopCompare;

   SetLess;

end;

{---------------------------------------------------------------}

{ Recognize and Translate a Relational "Greater Than" }

procedure Greater;

begin

   Match('>');



   Expression;

   PopCompare;

   SetGreater;

end;

{---------------------------------------------------------------}

{ Parse and Translate a Relation }

procedure Relation;

begin

   Expression;

   if IsRelop(Look) then begin

      Push;

      case Look of

       '=': Equals;

       '#': NotEquals;

       '<': Less;

       '>': Greater;

      end;

   end;

end;

{---------------------------------------------------------------}

{ Parse and Translate a Boolean Factor with Leading NOT }

procedure NotFactor;

begin

   if Look = '!' then begin

      Match('!');

      Relation;

      NotIt;

      end

   else

      Relation;

end;

{---------------------------------------------------------------}

{ Parse and Translate a Boolean Term }

procedure BoolTerm;

begin

   NotFactor;

   while Look = '&' do begin

      Push;

      Match('&');

      NotFactor;

      PopAnd;

   end;

end;

{--------------------------------------------------------------}

{ Recognize and Translate a Boolean OR }

procedure BoolOr;

begin

   Match('|');

   BoolTerm;

   PopOr;

end;

{--------------------------------------------------------------}

{ Recognize and Translate an Exclusive Or }

procedure BoolXor;

begin

   Match('~');

   BoolTerm;

   PopXor;

end;

{---------------------------------------------------------------}

{ Parse and Translate a Boolean Expression }



procedure BoolExpression;

begin

   BoolTerm;

   while IsOrOp(Look) do begin

      Push;

      case Look of

       '|': BoolOr;

       '~': BoolXor;

      end;

   end;

end;

{--------------------------------------------------------------}

Чтобы связать все это вместе не забудьте изменить обращение к Expression в процедурах Factor и Assignment на вызов BoolExpression.

Хорошо, если вы набрали все это, откомпилируйте и погоняйте эту версию. Сначала удостоверьтесь, что вы все еще можете анализировать обычные арифметические выражения. Затем попробуйте булевские. Наконец удостоверьтесь, что вы можете присваивать результат сравнения. Попробуйте к примеру:

     pvx,y,zbx=z>ye.

что означает

     PROGRAM

     VAR X,Y,Z

     BEGIN

     X = Z > Y

     END.

Видите как происходит присваивание булевского значения X?


Содержание раздела