<<
>>

Интерпретатор

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

Так как сейчас мы собираемся выполнять арифметические действия, то первое, что мы должны сделать – изменить функцию GetNum, которая до настоящего момента всегда возвращала символ (или строку). Лучше если сейчас она будет возвращать целое число. Сделайте копию Cradle (на всякий случай не изменяйте сам Cradle!!) и модифицируйте GetNum следующим образом:

{–}

{ Get a Number }

function GetNum: integer;

begin

if not IsDigit(Look) then Expected('Integer');

GetNum := Ord(Look) – Ord('0');

GetChar;

end;

{–}

Затем напишите следующую версию Expression:

{–}

{ Parse and Translate an Expression }

function Expression: integer;

begin

Expression := GetNum;

end;

{–}

И, наконец, вставьте

Writeln(Expression);

в конец основной программы.

Теперь откомпилируйте и протестируйте.

Все, что эта программа делает – это «анализ» и трансляция «выражения», состоящего из одиночного целого числа. Как обычно, вы должны удостовериться, что она обрабатывает числа от 0 до 9 и выдает сообщение об ошибке для чего-либо другого. Это не должно занять у вас много времени!

Теперь давайте расширим ее, включив поддержку операций сложения. Измените Expression так:

{–}

{ Parse and Translate an Expression }

function Expression: integer;

var Value: integer;

begin

if IsAddop(Look) then

Value := 0

else

Value := GetNum;

while IsAddop(Look) do begin

case Look of

'+': begin

Match('+');

Value := Value + GetNum;

end;

'-': begin

Match('-');

Value := Value – GetNum;

end;

end;

end;

Expression := Value;

end;

{–}

Структура Expression, конечно, схожа с тем, что мы делали ранее, так что мы не будем иметь слишком много проблем при ее отладке.

Тем не менее это была серьезная разработка, не так ли? Процедуры Add и Subtract исчезли! Причина в том, что для выполнения необходимых действий нужны оба аргумента операции. Я мог бы сохранить эти процедуры и передавать в них значение выражения на данный момент, содержащееся в Value. Но мне показалось более правильным оставить Value как строго локальную переменную, что означает, что код для Add и Subtract должен быть помещен вместе. Этот результат наводит на мысль, что хотя разработанная нами структура была хорошей и проверенной для нашей бесхитростной схемы трансляции, она возможно не могла бы использоваться с ленивой оценкой. Эту небольшую интересную новость нам возможно необходимо иметь в виду в будущем.

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

{–}

{ Parse and Translate a Math Term }

function Term: integer;

var Value: integer;

begin

Value := GetNum;

while Look in ['*', '/'] do begin

case Look of

'*': begin

Match('*');

Value := Value * GetNum;

end;

'/': begin

Match('/');

Value := Value div GetNum;

end;

end;

end;

Term := Value;

end;

{–}

Теперь испробуйте. Не забудьте двух вещей: во-первых мы имеем дело с целочисленным делением, поэтому, например, 1/3 выдаст ноль. Во-вторых, даже если мы можем получать на выходе многозначные числа, входные числа все еще ограничены одиночной цифрой.

Сейчас это выглядит как глупое ограничение, так как мы уже видели как легко может быть расширена функция GetNum. Так что давайте исправим ее прямо сейчас. Вот новая версия:

{–}

{ Get a Number }

function GetNum: integer;

var Value: integer;

begin

Value := 0;

if not IsDigit(Look) then Expected('Integer');

while IsDigit(Look) do begin

Value := 10 * Value + Ord(Look) – Ord('0');

GetChar;

end;

GetNum := Value;

end;

{–}

Если вы откомпилировали и протестировали эту версию интерпретатора, следующим шагом должна быть установка функции Factor, поддерживающей выражения в скобках. Мы задержимся немного дольше на именах переменных. Сначала измените ссылку на GetNum в функции Term, чтобы вместо нее вызывалась функция Factor. Теперь наберите следующую версию Factor:

{–}

{ Parse and Translate a Math Factor }

function Expression: integer; Forward;

function Factor: integer;

begin

if Look = '(' then begin

Match('(');

Factor := Expression;

Match(')');

end

else

Factor := GetNum;

end;

{–}

Это было довольно легко, а? Мы быстро пришли к полезному интерпретатору.

<< | >>
Источник: Креншоу Джек. Давайте создадим компилятор!. 1999

Еще по теме Интерпретатор:

  1. 6.2.3. Шаг второй:факты или эмоции
  2. § 2. Определение понятия владения (п. 1288-1293)
  3. Ассоциация психологического типа. «Этические принципы»
  4. а) Формы существования
  5. Введение
  6. а) Формы существования
  7. 2. Французская школа сравнительного законодательства
  8. Социально-педагогическая функция
  9. Три стадии эволюции
  10. Жизнь и деятельность
  11. 2.6. Педагогические средства в воспитании учащихся
  12. § 1. Судебная социология
  13. 1.5. НОРМАТИВНЫЕ ОСНОВЫ ДИДАКТИКИ
  14. 6.3. Толкование правовых норм
  15. Глава 1 Купальная ванна или ханауская модель
  16. Зарождение и развитие журналистики в Европе
  17. Л.О. Доліненко, В.О. Доліненко, С.О. Сарновська. Цивільне право України, 2006
  18. ЦИВІЛЬНЕ ПРАВО УКРАЇНИ
  19. ПЕРЕДМОВА