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

Это должно работать. Испытайте ее и проверьте, что вы действительно можете использовать многосимвольные имена переменных.

СНОВА ОПЕРАТОРЫ ОТНОШЕНИЙ

У нас осталось последнее односимвольное ограничение - ограничение операторов отношений. Некоторые из операторов отношений действительно состоят из одиночных символов, но другие требуют двух. Это <= и >=. Я также предпочитаю Паскалевское <> для "не равно" вместо #.

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

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

Требуемые изменения влияют только на подпрограммы генерации кода и процедуры Relation и ее друзей. Сперва, нам понадобятся еще две подпрограммы генерации кода:

{ Set D0 If Compare was <= } procedure SetLessOrEqual; begin

EmitLnCSGE D0);

EmitLnCEXT D0); end;

{ Set D0 If Compare was >= } procedure SetGreaterOrEqual; begin

EmitLnCSLE D0);

EmitLnCEXT D0); end;

Затем измените подпрограммы анализа отношений как показано ниже:

{ Recognize and Translate a Relational "Less Than or Equal" }

procedure LessOrEqual;

begin

MatchC = );

Expression;

PopCompare;

SetLessOrEqual; end;

{ Recognize and Translate a Relational "Not Equals" }

procedure NotEqual;

begin

MatchC>);

Expression;

PopCompare;

SetNEqual; end;



procedure Less; begin

Match(<); case Look of

=: LessOrEqual; >: NotEqual; else begin

Expression; PopCompare; SetLess; end;

end; end;

{ Recognize and Translate a Relational "Greater Than" }

procedure Greater;

begin

Match(>);

if Look = = then begin

Match(=);

Expression;

PopCompare;

SetGreaterOrEqual;

end else begin

Expression;

PopCompare;

SetGreater; end; end;

Это все, что требуется. Теперь вы можете обрабатывать все операторы отношений. Попробуйте.

ВВОД/ВЫВОД

Теперь у нас есть полный, работающий язык, за исключением одного небольшого смущающего факта: у нас нет никакого способа получить или вывести данные. Нам нужны подпрограммы ввода/вывода.

Современное соглашение, установленное в C и продолженное в Ada и Modula-2, состоит в том, чтобы вывести I/O операторы из самого языка и просто включить их в библиотеку подпрограмм. Это было бы прекрасно, за исключением того, что мы пока не имеем никаких средств поддержки подпрограмм. В любом случае, с этим подходом вы столкнетесь с проблемой переменной длины списка параметров. В Паскале I/O операторы встроены в язык, поэтому это единственные операторы, для которых список параметров может иметь переменное число элементов. В C мы примиряемся с клуджами типа scanf и printf и должны передавать количество параметров в вызываемую процедуру. В Ada и Modula-2 мы должны использовать неудобный (и медленный!) способ отдельного вызова для каждого аргумента.

Так что я думаю, что предпочитаю Паскалевский подход встраивания подпрограмм ввода/вывода, даже если мы не нуждаемся в этом.

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

{ Read Variable to Primary Register }



procedure ReadVar; begin

EmitLnCBSR READ);

Store(Value); end;

{ Write Variable from Primary Register }

procedure WriteVar;

begin

EmitLnCBSR WRITE); end;

Идея состоит в том, что READ загружает значение из входного потока в D0, а WRITE выводит его оттуда.

Эти две процедуры представляют собой нашу первую встречу с потребностью в библиотечных процедурах... компонентах Run Time Library (RTL). Конечно кто-то (а именно мы) должен написать эти подпрограммы, но они не являются непосредственно частью компилятора. Я даже не буду беспокоиться о том, чтобы показать здесь эти подпрограммы, так как они очевидно очень ОС-зависимы. Я просто скажу, что для SK*DOS они особенно просты... почти тривиальны. Одна из причин, по которым я не буду показывать их здесь в том, что вы можете добавлять новые виды возможностей, например приглашение в READ или возможность пользователю повторить ошибочный ввод.

Но это действительно отдельный от компилятора проект, так что теперь я буду подразумевать что библиотека, называемая TINYLIB.LIB, существует.

Так как нам теперь нужно загружать ее, мы должны добавить ее загрузку в процедуру Header:

{ Write Header Info } procedure Header; begin

WriteLnCWARMST, TAB, EQU $A01E); EmitLnCLIB TINYLIB); end;

Она возьмет на себя эту часть работы. Теперь нам также необходимо распознавать команды ввода и вывода. Мы можем сделать это добавив еще два ключевых слова в наш список:

{ Definition of Keywords and Token Types } const NKW = 11; NKW1 = 12;

const KWlist: array[1..NKW] of Symbol =

(IF, ELSE, ENDIF, WHILE, ENDWHILE, READ, WRITE, VAR, BEGIN, END,

PROGRAM);

const KWcode: string[NKW1] = xileweRWvbep;

(Обратите внимание, что здесь я использую кода в верхнем регистре чтобы избежать конфликта с w из WHILE.)

Затем нам нужны процедуры для обработки оператора ввода/вывода и его списка параметров:

{ Process a Read Statement }



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