function Factor(const S: string; var P: Integer): Extended;
begin
if P > Length(S) then
raise ESyntaxError.Create('Неожиданный конец строки');
// По первому символу подстроки определяем,
// какой это множитель
case S[P] of
'+'; // унарный "+"
begin
Inc(Р);
Result := Factor(S, P);
end;
'-': // унарный "-"
begin
Inc(P);
Result := -Factor(S, P);
end;
else
begin
Result := Base(S, P);
if (P <= Length(S)) and (S[P] = '^') then
begin
Inc(P);
Result := Power(Result, Factor(S, P));
end;
end;
end;
end;
Пример калькулятора называется FullCalcSample. Его интерфейс (рис. 4.2) содержит новые элементы, с помощью которых пользователь может задавать значения переменных. В левой нижней части окна находится список переменных с их значениями (при запуске программы этот список пустой). Правее расположены поля ввода Имя переменнойи Значение переменной, а также кнопка Установить. В первое поле следует ввести имя переменной, во второе — ее значение. При нажатии на кнопку Установитьпеременная будет внесена в список, а если переменная с таким именем уже есть в списке, то ее значение будет обновлено. Все переменные, которые есть в списке, могут использоваться в выражении. Если требуемая переменная в списке не найдена, попытка вычислить выражение приводит к ошибке.
Рис. 4.2.Главное окно программы FullCalcSample
Заметим, что символ можно было бы определить несколько иначе:
::= [] ['^' ]
В нашем случае, когда есть только два унарных оператора и применение срезу двух (разных или одинаковых) практически бессмысленно, такой синтаксис реализовать было бы проще (пример реализации такого синтаксиса дан в программе FullCalcSample в виде комментария). При этом исчезла бы возможность ставить несколько знаков унарных операций подряд. В общем случае такой подход неверен, т.к. при большем количестве унарных операций это может пригодиться, да и выглядит естественно. Поэтому в качестве основного был выбран несколько более сложный, но и более функциональный вариант.
4.8. Калькулятор с лексическим анализатором
Прежде чем двигаться дальше, рассмотрим недостатки последней версии нашего калькулятора. Во-первых, бросается в глаза некоторое дублирование функций. Действительно, с одной стороны, выделением числа из подстроки занимается функция Number
, но в функции Base
также содержится проверка первого символа числа. Функция Identifier
тоже частично дублируется функцией Base
.
Второй недостаток — нельзя вставлять разделители, облегчающие чтение выражения. Например, строка "2 + 2" не является допустимым выражением — следует писать "2+2" (без пробелов). Если же попытаться учесть возможность вставки пробелов, придется в разные функции добавлять много однотипного рутинного кода, который существенно усложнит восприятие программы.
Третий недостаток — сложность введения новых операторов, которые обозначаются не одним символом, а несколькими, например, >=
, and
, div
. Если посмотреть функции Expr
и Term
, которые придется в этом случае модифицировать, видно, что переделка будет достаточно сложной.
Решить все эти проблемы позволяет лексический анализатор, который выделяет из строки все лексемы, пропуская пробелы и иные разделители, и определяет тип каждой лексемы, не заботясь о том, насколько уместно ее появление в данной позиции выражения. А после лексического анализа начинает работать анализатор синтаксический, который будет иметь дело не с отдельными символами строки, а с целыми лексемами
В качестве примера рассмотрим реализацию следующей грамматики (листинг 4.10).
Листинг 4.10. Грамматика калькулятора с лексическим анализатором
::= [ ]
::= '=' | '>' | '<' | '>=' | '<=' | '<>'
::= { }
::= '+' | '-' | 'or' | 'xor'
::= { }
::= '*' | '/' | 'div' | 'mod' | 'and'
::= | ['^' ]
::= '+' | '-' | 'not'
::= | | | '(' ')'
::= '(' ')'
::= 'sin' | 'cos' | 'ln'
::= { | }
::= 'A' | ... | 'Z' | 'a' | ... | 'z' | '_'
::= '0' | ... | '9'
::= {} [ {}]
(('E' | 'e') ['+' | '-'] {)]
Примечание
Здесь используется нетерминальный символ , который мы не определили. Он полагается равным точке или запятой в зависимости от системных настроек.
Читать дальше
Конец ознакомительного отрывка
Купить книгу