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

этом никакой проблемы и возвратит его синтаксическому анализатору, правильно закодировав как "IF".

С таким подходом, мы в действительности не используем всю информацию, имеющуюся в нашем распоряжении. В середине выражения, например, синтаксический анализатор "знает", что нет нужды искать ключевое слово, но он не имеет никакой возможности сказать это сканеру. Так что сканер продолжает делать это. Это, конечно, замедляет компиляцию.

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

Альтернативой является поиск какого-то способа для использования контекстной информации, которая исходит из знания того, где мы находимся в синтаксическом анализаторе. Это возвращает нас обратно к понятию распределенного сканера, в котором различные части сканера вызываются в зависимости от контекста.

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

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

Даже в этой точке, единственным видом лексемы, который мы можем принять, является идентификатор. Нам необходимо только определить, является ли этот идентификатор ключевым словом или левой частью оператора присваивания.

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

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

ОБЪЕДИНЕНИЕ СКАНЕРА И ПАРСЕРА

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

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



program KISS;

{ Constant Declarations } const TAB =

CR =

LF = *J;

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

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

TabPtr = SymTab;

{ Variable Declarations }

var Look : char; { Lookahead Character }

Lcount: integer; { Label Counter }

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

{ Get an Identifier } function GetName: char; begin

while Look = CR do Fin;

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

Getname := UpCase(Look);

GetChar;

SkipWhite;



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