Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Pascal_Unkn.doc
Скачиваний:
8
Добавлен:
03.11.2018
Размер:
1.63 Mб
Скачать

Снова операторы отношений

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

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

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

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

{---------------------------------------------------------------}

{ Set D0 If Compare was <= }

procedure SetLessOrEqual; begin    EmitLn('SGE D0');    EmitLn('EXT D0'); end;

{---------------------------------------------------------------} { Set D0 If Compare was >= }

procedure SetGreaterOrEqual; begin    EmitLn('SLE D0');    EmitLn('EXT D0'); end;

{---------------------------------------------------------------}

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

{---------------------------------------------------------------}

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

procedure LessOrEqual; begin    Match('=');    Expression;    PopCompare;    SetLessOrEqual; end;

{---------------------------------------------------------------} { Recognize and Translate a Relational "Not Equals" }

procedure NotEqual; begin    Match('>');    Expression;    PopCompare;    SetNEqual; end;

{---------------------------------------------------------------} { Recognize and Translate a Relational "Less Than" }

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    EmitLn('BSR READ');    Store(Value); end;

{---------------------------------------------------------------} { Write Variable from Primary Register }

procedure WriteVar; begin    EmitLn('BSR WRITE'); end;

{--------------------------------------------------------------}

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

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

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

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

{--------------------------------------------------------------}

{ Write Header Info }

procedure Header; begin

   WriteLn('WARMST', TAB, 'EQU $A01E');    EmitLn('LIB 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 } procedure DoRead; begin    Match('(');    GetName;    ReadVar;    while Look = ',' do begin       Match(',');       GetName;       ReadVar;    end;    Match(')'); end;

{--------------------------------------------------------------} { Process a Write Statement }

procedure DoWrite; begin    Match('(');    Expression;    WriteVar;    while Look = ',' do begin       Match(',');       Expression;       WriteVar;    end;    Match(')'); end;

{--------------------------------------------------------------}

Наконец, мы должны расширить процедуру Block для поддержки новых типов операторов:

{--------------------------------------------------------------}

{ Parse and Translate a Block of Statements }

procedure Block; begin    Scan;    while not(Token in ['e', 'l']) do begin       case Token of        'i': DoIf;        'w': DoWhile;        'R': DoRead;        'W': DoWrite;       else Assignment;       end;       Scan;    end; end;

{--------------------------------------------------------------}

На этом все. Теперь у нас есть язык!

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]