Читаем Давайте создадим компилятор! полностью

{–}

{ Table Lookup }

{ If the input string matches a table entry, return the entry

index. If not, return a zero. }

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;

{–}

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

{–}

{ Main Program }

begin

ReadLn(Token);

WriteLn(Lookup(Addr(KWList), Token, 4));

end.

{–}

Обратите внимание как вызывается Lookup: функция Addr устанавливает указатель на KWList, который передается в Lookup.

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

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

Итак, какие кода мы должны возвращать? В действительности есть только два приемлемых варианта. Это похоже на идеальное применения перечислимого типа Паскаля. К примеру, вы можете определить что-то типа

SymType = (IfSym, ElseSym, EndifSym, EndSym, Ident, Number, Operator);

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

Теперь добавьте два описания переменных:

Token: Symtype; { Current Token }

Value: String[16]; { String Token of Look }

Измените сканер так:

{–}

{ Lexical Scanner }

procedure Scan;

var k: integer;

begin

while Look = CR do

Fin;

if IsAlpha(Look) then begin

Value := GetName;

k := Lookup(Addr(KWlist), Value, 4);

if k = 0 then

Token := Ident

else

Token := SymType(k – 1);

end

else if IsDigit(Look) then begin

Value := GetNum;

Token := Number;

end

else if IsOp(Look) then begin

Value := GetOp;

Token := Operator;

end

else begin

Value := Look;

Token := Operator;

GetChar;

end;

SkipWhite;

end;

{–}

(Заметьте, что Scan сейчас стала процедурой а не функцией).

Наконец, измените основную программу:

{–}

{ Main Program }

begin

Init;

repeat

Scan;

case Token of

Ident: write('Ident ');

Number: Write('Number ');

Operator: Write('Operator ');

IfSym, ElseSym, EndifSym, EndSym: Write('Keyword ');

end;

Writeln(Value);

until Token = EndSym;

end.

{–}

Мы заменили строку Token, используемую раньше, на перечислимый тип. Scan возвращает тип в переменной Token и возвращает саму строку в новой переменной Value.

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

Теперь у нас все работает правильно, и было легко сгенерировать это из того, что мы имели раньше. Однако, она все равно кажется мне немного «перегруженной». Мы можем ее немного упростить, позволив GetName, GetNum, GetOp и Scan работать с глобальными переменными Token и Value, вследствие этого удаляя их локальные копии. Кажется немного умней было бы переместить просмотр таблицы в GetName. Тогда новая форма для этих четырех процедур будет такой:

Перейти на страницу:

Похожие книги

Разработка приложений в среде Linux. Второе издание
Разработка приложений в среде Linux. Второе издание

Книга известных профессионалов в области разработки коммерческих приложений в Linux представляет СЃРѕР±РѕР№ отличный справочник для широкого круга программистов в Linux, а также тех разработчиков на языке С, которые перешли в среду Linux из РґСЂСѓРіРёС… операционных систем. РџРѕРґСЂРѕР±но рассматриваются концепции, лежащие в основе процесса создания системных приложений, а также разнообразные доступные инструменты и библиотеки. Среди рассматриваемых в книге вопросов можно выделить анализ особенностей применения лицензий GNU, использование СЃРІРѕР±одно распространяемых компиляторов и библиотек, системное программирование для Linux, а также написание и отладка собственных переносимых библиотек. Р

Майкл К. Джонсон , Эрик В. Троан

Программирование, программы, базы данных