Анимация
JavaScript
|
Главная Библионтека else Token := SymType(k-1); end; { Get a Number } procedure GetNum; begin Value := ; if not IsDigit(Look) then Expected(Integer); while IsDigit(Look) do begin Value := Value + Look; GetChar; end; Token := Number; end; { Get an Operator } procedure GetOp; begin Value := ; if not IsOp(Look) then Expected(Operator); while IsOp(Look) do begin Value := Value + Look; GetChar; end; Token := Operator; end; { Lexical Scanner } procedure Scan; var k: integer; begin while Look = CR do Fin; if IsAlpha(Look) then GetName else if IsDigit(Look) then GetNum else if IsOp(Look) then GetOp else begin Value := Look; Token := Operator; GetChar; end; SkipWhite; end; ВОЗВРАЩЕНИЕ СИМВОЛА По существу, все сканер, которые я когда-либо видел и которые написаны на Паскале, использовали механизм перечислимых типов, который я только что описал. Это конечно работающий механизм, но он не кажется мне самым простым подходом. Прежде всего, список возможных типов символов может получиться довольно длинным. Здесь я использовал только один символ "Operator" для обозначения всех операторов, но я видел другие проекты, в которых фактически возвращаются различные кода для каждого. Существует, конечно, другой простой тип, который может быть возвращен как код: символ. Вместо возвращения значения "Operator" для знака "+", что неправильного в том, чтобы просто возвращать сам символ? Символ - такая же хорошая переменная для кодирования различных типов лексем, она легко может быть использована в операторах Case, и это гораздо проще набрать. Что может быть проще? Кроме того, мы уже имели опыт с идеей кодировать ключевые слова как одиночные символы. Наши предыдущие программы уже написаны таким способом, так что использование этого метода минимизирует изменения того, что мы уже сделали. Некоторые из вас могут почувствовать, что идея с возвращение символьных кодов слишком детская. Я должен допустить, что она становится немного неуклюжей для операторов типа "<=". Если вы хотите остаться с перечислимыми типами, хорошо. Для остальных я хотел бы показать как изменить то, что мы сделали выше, для поддержки такого подхода. Во-первых, сейчас вы можете удалить объявление типа SymType... он нам больше не понадобится. И вы можете изменить тип Token в char. Затем, чтобы заменить SymType, добавьте следующую константу: const KWcode: string[5] = xilee; (Я буду кодировать все идентификаторы одиночным символом x). Наконец измените Scan и его родственников следующим образом: { Get an Identifier } procedure GetName; begin Value := ; if not IsAlpha(Look) then Expected(Name); while IsAlNum(Look) do begin Value := Value + UpCase(Look); GetChar; end; Token := KWcode[Lookup(Addr(KWlist), Value, 4) + 1]; end; { Get a Number } procedure GetNum; begin Value := ; if not IsDigit(Look) then Expected(Integer); while IsDigit(Look) do begin Value := Value + Look; GetChar; end; Token := #; end; { Get an Operator } procedure GetOp; begin Value := ; if not IsOp(Look) then Expected(Operator); while IsOp(Look) do begin Value := Value + Look; GetChar; end; if Length(Value) = 1 then Token := Value[1] else Token := ?; end; { Lexical Scanner } procedure Scan; var k: integer; begin while Look = CR do Fin; if IsAlpha(Look) then GetName else if IsDigit(Look) then GetNum else if IsOp(Look) then begin GetOp else begin Value := Look; Token := ?; GetChar; end; SkipWhite; end; { Main Program } begin Init; repeat Scan; case Token of x: write(Ident ); #: Write(Number ); i, Ч, e: Write(Keyword ); else Write(Operator ); end; Writeln(Value); until Value = END; end. Эта программа должна работать также как и предыдущая версия. Небольшое различие в структуре, может быть, но она кажется мне более простой. РАСПРЕДЕЛЕННЫЕ СКАНЕРЫ ПРОТИВ ЦЕНТРАЛИЗОВАННЫХ Структура лексического анализатора, которую я только что вам показал, весьма стандартна и примерно 99% всех компиляторов используют что-то очень близкое к ней. Это, однако, не единственно возможная структура, или даже не всегда самая лучшая. Проблема со стандартным подходом состоит в том, что сканер не имеет никаких сведений о контексте. Например, он не может различить оператор присваивания "=" и оператор отношения "=" (возможно именно поэтому и C и Паскаль используют для них различные строки). Все, что сканер может сделать, это передать оператор синтаксическому анализатору, который может точно сказать исходя из контекста, какой это оператор. Точно так же, ключевое слово "IF" не может быть посредине арифметического выражения, но если ему случится оказаться там, сканер не увидит в 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 |