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

Итак, теперь, когда вы знаете почему мы принялись за все это, давайте начнем. Просто для того, чтобы дать вам практику, мы начнем с пустого С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;

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