Содержание материала

 

   Пара комментариев:

· Форма синтаксического анализатора выражений, использующего FirstTerm и т.п., немного отличается от того, что вы видели ранее. Это еще одна вариация на ту же самую тему. Не позволяйте им вертеть вами... изменения необязательны для того, что будет дальше.
· Заметьте, что как обычно я добавил вызовы Fin в стратегических местах для поддержки множественных строк.

Прежде чем приступить к добавлению сканера, сначала скопируйте этот файл и проверьте, что он действительно корректно выполняет анализ. Не забудьте "кода": "i" для IF, "l" для ELSE и "e" для ELSE или ENDIF.

Если программа работает, тогда давайте поспешим. При добавлении модулей сканера в программу поможет систематический план. Во всех синтаксических анализаторах, которые мы написали до этого времени, мы придерживались соглашения, что текущий предсказывающий символ должен всегда быть непустым символом. Мы предварительно загружали предсказывающий символ в Init и после этого оставляли "помпу запущенной". Чтобы позволить программе работать правильно с новыми строками мы должны ее немного модифицировать и обрабатывать символ новой строки как допустимый токен.

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

Много символьная версия показана ниже. Чтобы получить ее я сделал следующие изменения:

· Добавлены переменные Token и Value и определения типов, необходимые для Lookup.
· Добавлено определение KWList и KWcode.
· Добавлен Lookup.
· GetName и GetNum заменены их много символьными версиями. (Обратите внимание, что вызов Lookup был перемещен из GetName, так что он не будет выполняться внутри выражений).
· Создана новая, рудиментарная Scan, которая вызывает GetName затем сканирует ключевые слова.
· Создана новая процедура MatchString, которая ищет конкретное ключевое слово. Заметьте, что в отличие от Match, MatchString не считывает следующее ключевое слово.
· Изменен Block для вызова Scan.
· Немного изменены вызовы Fin. Fin теперь вызывается из GetName.

Программа полностью:

Code:

program KISS;

 

{ Constant Declarations }

const TAB = ^I;

      CR  = ^M;

      LF  = ^J;

 

{ Type Declarations  }

type Symbol = string[8];

     SymTab = array[1..1000] of Symbol;

     TabPtr = ^SymTab;

 

{ Variable Declarations }

var Look  : char;              { Lookahead Character }

    Token : char;              { Encoded Token       }

    Value : string[16];        { Unencoded Token     }

    Lcount: integer;           { Label Counter       }

 

{ Definition of Keywords and Token Types }

const KWlist: array [1..4] of Symbol =

              ('IF', 'ELSE', 'ENDIF', 'END');

const KWcode: string[5] = 'xilee';

 

{ Read New Character From Input Stream }

procedure GetChar;

begin

   Read(Look);

end;

 

{ Report an Error }

procedure Error(s: string);

begin

   WriteLn;

   WriteLn(^G, 'Error: ', s, '.');

end;

 

{ Report Error and Halt }

procedure Abort(s: string);

begin

   Error(s);

   Halt;

end;

 

{ Report What Was Expected }

procedure Expected(s: string);

begin

   Abort(s + ' Expected');

end;

 

{ Recognize an Alpha Character }

function IsAlpha(c: char): boolean;

begin

   IsAlpha := UpCase(c) in ['A'..'Z'];

end;

 

{ Recognize a Decimal Digit }

function IsDigit(c: char): boolean;

begin

   IsDigit := c in ['0'..'9'];

end;

 

{ Recognize an AlphaNumeric Character }

function IsAlNum(c: char): boolean;

begin

   IsAlNum := IsAlpha(c) or IsDigit(c);

end;

 

{ Recognize an Addop }

function IsAddop(c: char): boolean;

begin

   IsAddop := c in ['+', '-'];

end;

 

{ Recognize a Mulop }

function IsMulop(c: char): boolean;

begin

   IsMulop := c in ['*', '/'];

end;

 

{ Recognize White Space }

function IsWhite(c: char): boolean;

begin

   IsWhite := c in [' ', TAB];

end;

 

{ Skip Over Leading White Space }

procedure SkipWhite;

begin

   while IsWhite(Look) do

      GetChar;

end;

 

{ Match a Specific Input Character }

procedure Match(x: char);

begin

   if Look <> x then Expected('''' + x + '''');

   GetChar;

   SkipWhite;

end;

 

{ Skip a CRLF }

procedure Fin;

begin

   if Look = CR then GetChar;

   if Look = LF then GetChar;

   SkipWhite;

end;

 

{ Table Lookup }

function Lookup(T: TabPtr; s: string; n: integer): integer;

var i: integer;

    found: boolean;

begin

   found := false;

   i := n;

   while (i > 0) and not found do

      if s = T^[i] then

         found := true

      else

         dec(i);

   Lookup := i;

end;

 

{ Get an Identifier }

procedure GetName;

begin

   while Look = CR do

      Fin;

   if not IsAlpha(Look) then Expected('Name');

   Value := '';

   while IsAlNum(Look) do begin

     Value := Value + UpCase(Look);

     GetChar;

   end;

   SkipWhite;

end;

 

{ Get a Number }

procedure GetNum;

begin

   if not IsDigit(Look) then Expected('Integer');

   Value := '';

   while IsDigit(Look) do begin

     Value := Value + Look;

     GetChar;

   end;

   Token := '#';

   SkipWhite;

end;

 

{ Get an Identifier and Scan it for Keywords }

procedure Scan;

begin

   GetName;

   Token := KWcode[Lookup(Addr(KWlist), Value, 4) + 1];

end;

 

{ Match a Specific Input String }

procedure MatchString(x: string);

begin

   if Value <> x then Expected('''' + x + '''');

end;

 

{ 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;

 

{ Output a String with Tab }

procedure Emit(s: string);

begin

   Write(TAB, s);

end;

 

{ Output a String with Tab and CRLF }

procedure EmitLn(s: string);

begin

   Emit(s);

   WriteLn;

end;

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

{ Parse and Translate an Identifier }

procedure Ident;

begin

   GetName;

   if Look = '(' then begin

      Match('(');

      Match(')');

      EmitLn('BSR ' + Value);

      end

   else

      EmitLn('MOVE ' + Value + '(PC),D0');

end;

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

{ Parse and Translate a Math Factor }

procedure Expression; Forward;

procedure Factor;

begin

   if Look = '(' then begin

      Match('(');

      Expression;

      Match(')');

      end

   else if IsAlpha(Look) then

      Ident

   else begin

      GetNum;

      EmitLn('MOVE #' + Value + ',D0');

   end;

end;

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

{ Parse and Translate the First Math Factor }

procedure SignedFactor;

var s: boolean;

begin

   s := Look = '-';

   if IsAddop(Look) then begin

      GetChar;

      SkipWhite;

   end;

   Factor;

   if s then

      EmitLn('NEG D0');

end;

 

{ Recognize and Translate a Multiply }

procedure Multiply;

begin

   Match('*');

   Factor;

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

end;

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

{ Recognize and Translate a Divide }

procedure Divide;

begin

   Match('/');

   Factor;

   EmitLn('MOVE (SP)+,D1');

   EmitLn('EXS.L D0');

   EmitLn('DIVS D1,D0');

end;

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

{ Completion of Term Processing  (called by Term and FirstTerm }

procedure Term1;

begin

   while IsMulop(Look) do begin

      EmitLn('MOVE D0,-(SP)');

      case Look of

       '*': Multiply;

       '/': Divide;

      end;

   end;

end;

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

{ Parse and Translate a Math Term }

procedure Term;

begin

   Factor;

   Term1;

end;

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

{ Parse and Translate a Math Term with Possible Leading Sign }

procedure FirstTerm;

begin

   SignedFactor;

   Term1;

end;

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

{ Recognize and Translate an Add }

procedure Add;

begin

   Match('+');

   Term;

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

end;

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

{ Recognize and Translate a Subtract }

procedure Subtract;

begin

   Match('-');

   Term;

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

   EmitLn('NEG D0');

end;

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

{ Parse and Translate an Expression }

procedure Expression;

begin

   FirstTerm;

   while IsAddop(Look) do begin

      EmitLn('MOVE D0,-(SP)');

      case Look of

       '+': Add;

       '-': Subtract;

      end;

   end;

end;

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

{ Parse and Translate a Boolean Condition }

{ This version is a dummy }

Procedure Condition;

begin

   EmitLn('Condition');

end;

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

{ Recognize and Translate an IF Construct }

procedure Block; Forward;

procedure DoIf;

var L1, L2: string;

begin

   Condition;

   L1 := NewLabel;

   L2 := L1;

   EmitLn('BEQ ' + L1);

   Block;

   if Token = 'l' then begin

      L2 := NewLabel;

      EmitLn('BRA ' + L2);

      PostLabel(L1);

      Block;

   end;

   PostLabel(L2);

   MatchString('ENDIF');

end;

 

{ Parse and Translate an Assignment Statement }

procedure Assignment;

var Name: string;

begin

   Name := Value;

   Match('=');

   Expression;

   EmitLn('LEA ' + Name + '(PC),A0');

   EmitLn('MOVE D0,(A0)');

end;

 

{ Recognize and Translate a Statement Block }

procedure Block;

begin

   Scan;

   while not (Token in ['e', 'l']) do begin

      case Token of

       'i': DoIf;

       else Assignment;

      end;

      Scan;

   end;

end;

 

{ Parse and Translate a Program }

procedure DoProgram;

begin

   Block;

   MatchString('END');

   EmitLn('END')

end;

 

{ Initialize }

procedure Init;

begin

   LCount := 0;

   GetChar;

end;

 

{ Main Program }

begin

   Init;

   DoProgram;

end.

 

 

 

Сравните эту программу с ее одно-символьным вариантом. Я думаю вы согласитесь, что различия минимальны.

ЗАКЛЮЧЕНИЕ

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

Мы очень близки к получению всех элементов, необходимых для построения настоящего, функционального компилятора. Есть еще несколько отсутствующих вещей, особенно вызовы процедур и определения типов. Мы будем работать с ними на следующих нескольких уроках. Прежде чем сделать это, однако, я подумал что было бы забавно превратить транслятор в настоящий компилятор. Это то, чем мы займемся в следующей главе.

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

Добавить комментарий

Не использовать не нормативную лексику.

Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.

ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!


Защитный код
Обновить