логических выражений а также (если вы хотите) и смешанные выражения.

Почему не «все виды логических выражений»? Потому что пока мы не имели дела с логическим оператором «not» и с ним все становится сложнее. Логический оператор «not» кажется на первый взгляд идентичным в своем поведении унарному минусу, поэтому моей первой мыслью было позволить оператору исключающего или, '~', дублировать унарный «not». Это не работало. При моей первой попытке процедура SignedTerm просто съедала мой '~' потому что символ проходил проверку на addop но SignedTerm игнорировал все addop за исключением '-'. Было бы достаточно просто добавить другую строку в SignedTerm, но это все равно не решит проблему, потому что, заметьте, Expression принимает терм со знаком только для первого аргумента.

Математически, выражение типа:

–a * -b

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

not a and not b

В случае с этими унарными операторами выбор заставить их работать таким же самым способом кажется исскуственным принуждением, жертвованием примлемым поведением на алтаре простоты реализуемости. Хотя я полностью за сохранение реализации настолько простой, насколько возможно, я не думаю, что мы должны делать это за счет приемлемости. Исправления подобные этому, приведут к потере основной детали, которая заключается в том, чтобы логическое «not» просто не является тем же самым что унарный минус. Рассмотрим исключающее «or», которое обычно записывается так:

a~b ::= (a and not b) or (not a and b)

Если мы разрешим «not» изменять весь терм, последний терм в круглых скобках интерпретировался бы как:

not(a and b)

что совсем не то же самое. Так что ясно, что о логическом «not» нужно думать как о связанном с показателем а не термом.

Идея перегрузки оператор '~' не имеет смысла и с математической точки зрения. Применение унарного минуса эквивалентно вычитанию из нуля:

–x <=> 0-x

Фактически, в одной из моих более простых версий Expression я реагировал на ведущий addop просто предзагружая нуль, затем обрабатывая оператор как если бы это был двоичный оператор. Но «not» это не эквивалент исключающему или с нулем... которое просто возвратит исходное число. Вместо этого, это исключающее или с FFFFh или -1.

Короче говоря, кажущаяся близость между унарным «not» и унарным минусом разваливается при более близком исследованиии. «not» изменяет показатель а не терм и он не имеет отношения ни к унарному минусу, ни исключающему или. Следовательно, он заслуживает своего собственного символа для вызова. Какой символ лучше, чем очевидный, также используемый в Си символ '!'? Используя правила того как мы думаем должен вести себя «not», мы должны быть способны закодировать исключающее или (предполагая что это нам когда-нибудь понадобится) в очень естественной форме:

a & !b | !a & b

Обратите внимание, что никаких круглых скобок не требуется – выбранные нам уровни приоритета автоматически заботятся обо всем.

Если вы продолжаете учитывать уровни приоритета, это определение помещает '!' на вершину кучи. Уровни становятся:

!

– (унарный)

*, /, &

+, -, |, ~

Рассматривая этот список, конечно не трудно увидеть, почему мы имели проблему при использовании '~' как символа «not»!

Так, как мы механизируем эти правила? Таким же самым способом, как мы сделали с SignedTerm, но на уровне показателя. Мы определим процедуру NotFactor:

{–}

{ Parse and Translate a Factor with Optional «Not» }

procedure NotFactor;

begin

if Look ='!' then begin

Match('!');

Factor;

Notit;

end

else

Factor;

end;

{–}

и вызовем ее из всех мест, где мы прежде вызывали Factor, т.е. из Term, Multiply, Divide и _And. Обратите внимание на новую процедуру генерации кода:

{–}

{ Bitwise Not Primary }

procedure NotIt;

begin

EmitLn('EOR #-1,D0');

end;

{–}

Испытайте ее сейчас с несколькими простыми случаями. Фактически, попробуйте пример с исключающим или:

a&!b|!a&b

Вы должны получить код (без комментариев, конечно):

MOVE A(PC),DO ; load a

MOVE D0,-(SP) ; push it

MOVE B(PC),DO ; load b

EOR #-1,D0 ; not it

AND (SP)+,D0 ; and with a

MOVE D0,-(SP) ; push result

MOVE A(PC),DO ; load a

EOR #-1,D0 ; not it

MOVE D0,-(SP) ; push it

MOVE B(PC),DO ; load b

AND (SP)+,D0 ; and with !a

OR (SP)+,D0 ; or with first term

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

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×