Анимация
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

Вы должны запомнить: лексический анализатор будет вызываться часто! Фактически один раз для каждой лексемы во всей исходной программе. Эксперименты показали, что средний компилятор тратит где-то от 20 до 40 процентов своего времени на подпрограммах лексического анализа. Если существовало когда-либо место, где эффективность заслуживает пристального рассмотрения, то это оно.

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

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

Table[1] := IF; Table[2] := ELSE;

Table[n] := END; что может получиться довольно длинным если есть много ключевых слов.

К счастью Turbo Pascal 4.0 имеет расширения, которые устраняют обе эти проблемы. Массивы-константы могут быть обьявлены с использованием средства TP "типизированные константы" а переменные размерности могут быть поддержаны с помощью Си-подобных расширений для указателей.

Сначала, измените ваши объявления подобным образом:

{ Type Declarations } type Symbol = string[8];

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

TabPtr = SymTab;

(Размерность, использованная в SymTab не настоящая... память не распределяется непосредственно этим объявлением, а размерность должна быть только "достаточно большой")

Затем, сразу после этих объявлений, добавьте следующее:

{ Definition of Keywords and Token Types } const KWlist: array [1..4] of Symbol =

(IF, ELSE, ENDIF, END);

Затем, вставьте следующую новую функцию:

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

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. Тогда новая форма для этих четырех процедур будет такой:

{ Get an Identifier } procedure GetName; var k: integer; begin

Value := ;

if not IsAlpha(Look) then Expected(Name); while IsAlNum(Look) do begin Value := Value + UpCase(Look);

GetChar; end;

k := Lookup(Addr(KWlist), Value, 4); if k = 0 then Token := Ident



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