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

       

МНОГОСИМВОЛЬНЫЕ ТОКЕНЫ.


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

Большинство компиляторов выделяют обработку входного потока в отдельный модуль, называемый лексическим анализатором (сканером).  Идея состоит в том, что сканер работает со всей последовательностью символов во входном потоке и возвращает отдельные единицы (лексемы) потока. Возможно придет время, когда мы также захотим сделать что-то вроде этого,  но сейчас в этом нет необходимости. Мы можем обрабатывать много символьные токены, которые нам нужны, с помощью небольших локальных изменений в GetName и GetNum.

Обычно признаком идентификатора является то, что первый символ должен быть буквой, но остальная часть может быть алфавитно-цифровой (буквы и цифры). Для работы с ними нам нужна другая функция:

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

{ Recognize an Alphanumeric }

function IsAlNum(c: char): boolean;

begin

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

end;

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

Добавьте эту функцию в анализатор. Я поместил ее сразу после IsDigit. Вы можете также включить ее как постоянного члена в Cradle.

Теперь нам необходимо изменить функцию GetName так, чтобы она возвращала строку вместо символа:

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


{ Get an Identifier }

function GetName: string;

var Token: string;

begin

   Token := '';

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

   while IsAlNum(Look) do begin

      Token := Token + UpCase(Look);

      GetChar;

   end;

   GetName := Token;

end;

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

Аналогично измените GetNum следующим образом:

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

{ Get a Number }

function GetNum: string;

var Value: string;

begin

   Value := '';

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

   while IsDigit(Look) do begin

      Value := Value + Look;

      GetChar;

   end;

   GetNum := Value;

end;

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

Достаточно удивительно, что это фактически все необходимые изменения! Локальная переменная Name в процедурах Ident и Assignment были первоначально объявлены как "char" и теперь должны быть объявлены как string[8]. (Ясно, что мы могли бы сделать длину строки больше, если бы захотели, но большинство ассемблеров в любом случае ограничивают длину.) Внесите эти изменения и затем откомпилируйте и протестируйте. Сейчас вы верите, что это просто?


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