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

{–}

{ Parse and Translate a Term with Optional Leading Sign }

procedure SignedTerm;

var Sign: char;

begin

Sign := Look;

if IsAddop(Look) then

GetChar;

Term;

if Sign = '-' then Negate;

end;

{–}

...

{–}

{ Parse and Translate an Expression }

procedure Expression;

begin

SignedTerm;

while IsAddop(Look) do

case Look of

'+': Add;

'-': Subtract;

end;

end;

{–}

Если память мне не изменяет мы однажды уже имели и процедуру SignedFactor и SignedTerm. У меня были причины сделать так в то время... они имели отношение к обработке булевой алгебры и, в частности, булевой функции «not». Но, конечно, для арифметических операций дублирование не нужно. В выражении типа:

–x*y

очевидно, что знак идет со всем термом x*y а не просто с показателем x и таким способом Expression и закодирован.

Протестируйте этот новый код, выполнив Main. Она все еще вызывает Expression, так что теперь вы должны быть способны работать с выражениями, содержащими любую из четырех арифметических операций.

Наше последнее дело, относительно выражений, это модификация процедуры Factor для разрешения выражений в скобках. Используя рекурсивный вызов Expression мы можем уменьшить необходимый код практически до нуля. Пять строк, добавленные в Factor, выполнят эту работу:

{–}

{ Parse and Translate a Factor }

procedure Factor;

begin

if Look ='(' then begin

Match('(');

Expression;

Match(')');

end

else if IsDigit(Look) then

LoadConstant(GetNumber)

else if IsAlpha(Look)then

LoadVariable(GetName)

else

Error('Unrecognized character ' + Look);

end;

{–}

К этому моменту ваш «компилятор» должен уметь обрабатывать любые допустимые выражения, которые вы ему подбросите. Еще лучше, что он должен отклонить все недопустимые!

<p>Присваивания</p>

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

{–}

{ Parse and Translate an Assignment Statement }

procedure Assignment;

var Name: string;

begin

Name := GetName;

Match('=');

Expression;

StoreVariable(Name);

end;

{–}

Присваивание вызывает еще одну подпрограмму генерации кода:

{–}

{ Store the Primary Register to a Variable }

procedure StoreVariable(Name: string);

begin

EmitLn('LEA ' + Name + '(PC),A0');

EmitLn('MOVE D0,(A0)');

end;

{–}

Теперь измените вызов в Main на вызов Assignment и вы должны увидеть полную операцию присваивания, обрабатываемую правильно. Довольно хорошо, не правда ли? И безболезненно также.

В прошлом мы всегда старались показывать БНФ уравнения для определения синтаксиса, который мы разрабатываем. Я не сделал этого здесь и давно пора это сделать. Вот эти БНФ:

 ::= | | '(' ')'

::= []

 ::= ( )*

 ::= ( )*

 ::= '='

<p>Булева алгебра</p>

Следующий шаг, как мы изучили несколько раз до этого, это добавление булевой алгебры. В прошлом этот шаг по крайней мере удваивал количество кода, который мы должны были написать. Когда я прошел эти шаги в своем уме, я обнаружил, что отклоняюсь все больше и больше от того, что мы делали в предыдущих главах. Чтобы освежить вашу память, я отметил, что Паскаль обрабатывает булевы операторы в значительной степени идентично способу, которым он обрабатывает арифметические операторы. Булево «and» имеет тот же самый уровень приоритета, что и умножение, а «or» то же что сложение. Си, с другой стороны, устанавливает их на различных уровнях приоритета, которые занимают 17 уровней. В нашей более ранней работе я выбрал что-то среднее, с семью уровнями. В результате, мы закончили на чем-то называющемся булевыми выражениями, соответствующим в большинстве деталей арифметическим выражениям, но на другом уровне приоритета. Все это, как оказалось, возникло потому, что мне не хотелось помещать скобки вокруг булевых выражений в утверждениях типа:

IF (c >= 'A') and (c <= 'Z') then ...

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

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

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

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

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

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

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