Читаем Давайте создадим компилятор! полностью

Чтобы оттолкнуться, давайте начнем заново, применяя более Паскаль-подобный подход и просто обрабатывая булевы операторы на том же самом уровне приоритетов что и арифметические. Мы увидим, куда это нас приведет. Если это окажется тупиком, мы всегда сможем возвратиться к предыдущему подходу.

Сперва, мы добавим в Expression операторы «уровня сложения». Это легко сделать; во первых, измените функцию IsAddop в модуле Scanner чтобы включить два дополнительных оператора: '|' для «или» и "~" для «исключающее или»:

{–}

function IsAddop(c: char): boolean;

begin

IsAddop := c in ['+','-', '|', '~'];

end;

{–}

Затем, мы должны включить анализ операторов в процедуру Expression:

{–}

procedure Expression;

begin

SignedTerm;

while IsAddop(Look) do

case Look of

'+': Add;

'-': Subtract;

'|': _Or;

'~': _Xor;

end;

end;

{–}

(Символы подчеркивания необходимы, конечно, потому что «or» and «xor» являются зарезервированными словами Turbo Pascal).

Затем процедуры _Or and _Xor:

{–}

{ Parse and Translate a Subtraction Operation }

procedure _Or;

begin

Match('|');

Push;

Term;

PopOr;

end;

{–}

{ Parse and Translate a Subtraction Operation }

procedure _Xor;

begin

Match('~');

Push;

Term;

PopXor;

end;

{–}

И, наконец, новые процедуры генерации кода:

{–}

{ Or TOS with Primary }

procedure PopOr;

begin

EmitLn('OR (SP)+,D0');

end;

{–}

{ Exclusive-Or TOS with Primary }

procedure PopXor;

begin

EmitLn('EOR (SP)+,D0');

end;

{–}

Теперь давайте протестируем транслятор (вы возможно захотите изменить вызов в Main обратно на вызов Expression просто чтобы избежать необходимости набирать каждый раз «x=» для присваивания).

Пока все хорошо. Синтаксический анализатор четко обрабатывает выражения вида:

x|y~z

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

(a+b)*(c~d)

Мы говорили об этом немного в прошлом. Вообще, правила какие операции допустимы а какие нет не могут быть применены самим синтаксическим анализатором, потому что они не являются частью синтаксиса языка, а скорее его семантики. Компилятор, который не разрешает смешанные выражения такого вида должен распознать, что c и d являются булевыми переменными а не числовыми и передумать об их умножении на следующем шаге. Но такая «охрана» не может быть выполнена синтаксическим анализатором; она должна быть обработана где-то между синтаксическим анализатором и генератором кода. Мы пока не в таком положении, чтобы устанавливать такие правила, потом что у нас нет способа ни объявления типов ни таблицы идентификаторов для сохранения в ней типов. Так что, для того что у нас на данный момент работает, синтаксический анализатор делает точно то, что он предназначен делать.

В любом случае, уверены ли мы, что не хотим разрешить операции над смешанными типами? Некоторое время назад мы приняли решение (или по крайней мере я принял) чтобы принимать значение 0000 как логическую «ложь» и -1 или FFFFh как логическую «истину». Хорошо в этом выборе то, что побитовые операции работают точно таким же способом, что и логические. Другими словами, когда мы выполняем операцию с одним битом логической переменной, мы делаем это над всеми из них. Это означает, что мы не должны делать различия между логическими и поразрядными операциями, как это сделано в C операторами & и &&, и | и ||. Уменьшение числа операторов наполовину конечно не выглядит совсем плохим.

С точки зрения данных в памяти, конечно, компьютер и компилятор не слишком интересуются представляет ли число FFFFh логическую истину или число -1. Должны ли мы? Я думаю что нет. Я могу придумать множество примеров (хотя они могут быть рассмотрены как «мудреный» код) где возможность смешивать типы могла бы пригодиться. Пример, функция дельты Дирака, которая могла бы быть закодирована в одной простой строке:

–(x=0)

или функция абсолютного значения (определенно сложный код!):

x*(1+2*(x<0))

Пожалуйста, заметьте, что я не защищаю программирование подобным образом как стиль жизни. Я почти обязательно написал бы эти функции в более читаемой форме, используя IF, только для того, чтобы защитить от запутывания того, кто будет сопровождать программу в будущем. Все же возникает моральный вопрос: Имеем ли мы право осуществлять наши идеи о хорошей практике кодирования на программисте, написав язык так, чтобы он не смог сделать что-нибудь не так? Это то, что сделал Никлаус Вирт во многих местах Паскаля и Паскаль критиковался за это – как не такой «прощающий» как Си.

Перейти на страницу:

Похожие книги

Разработка приложений в среде Linux. Второе издание
Разработка приложений в среде Linux. Второе издание

Книга известных профессионалов в области разработки коммерческих приложений в Linux представляет СЃРѕР±РѕР№ отличный справочник для широкого круга программистов в Linux, а также тех разработчиков на языке С, которые перешли в среду Linux из РґСЂСѓРіРёС… операционных систем. РџРѕРґСЂРѕР±но рассматриваются концепции, лежащие в основе процесса создания системных приложений, а также разнообразные доступные инструменты и библиотеки. Среди рассматриваемых в книге вопросов можно выделить анализ особенностей применения лицензий GNU, использование СЃРІРѕР±одно распространяемых компиляторов и библиотек, системное программирование для Linux, а также написание и отладка собственных переносимых библиотек. Р

Майкл К. Джонсон , Эрик В. Троан

Программирование, программы, базы данных