Чтение онлайн

на главную

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

Таким образом, наш калькулятор будет распознавать и вычислять цепочки чисел, между которыми стоят знаки операции, которые над этими числами выполняются. В вырожденном случае выражение может состоять из одного числа и, соответственно, не содержать ни одного знака операции. Опишем эти правила с помощью БНФ и ранее определенного символа

<Number>
.

<Expr> ::= <Number> {<Operation> <Number>}

<Operation> ::= '+' | '-' | '*' | '/'

Примечание

В нашей грамматике не предусмотрено, что между оператором и его операндами может

находиться пробел, т.е. выражение "2 + 2", в отличие от "2+2", не удовлетворяет данной грамматике. В отсутствие лексического анализатора игнорирование пробелов и прочих разделителей (переводов строки, комментариев) является трудоемкой рутинной операцией, поэтому во всех примерах без лексического анализатора мы будем требовать, чтобы выражения не содержали пробелов.

Для написания калькулятора нам понадобятся две новых функции —

IsOperator
, которая проверяет, является ли следующий символ оператором, и
Expr
, которая получает на входе строку, анализирует ее в соответствии с указанными правилами и вычисляет результат. Кроме того, функция
IsNumber
сама по себе нам тоже больше не нужна — мы создадим на ее основе функцию
Number
, которая получает на входе строку и номер позиции, начиная с которой в этой строке должно быть расположено число, проверяет, так ли это, и возвращает это число. Кроме того, функция
Number
должна перемещать указатель на следующий после числа символ строки, чтобы функция
Expr
, вызвавшая
Number
, могла узнать, с какого символа продолжать анализ. Если последовательность символов не является корректным числом, функция
Number
возбуждает исключение
ESyntaxError
, определенное специально для указания на ошибку в записи выражения.

Сама по себе задача преобразования строки в вещественное число достаточно сложна, и чтобы не отвлекаться на ее решение, мы воспользуемся функцией

StrToFloat
из модуля
SysUtils
. Когда функция
Number
выделит из строки последовательность символов, являющуюся числом, эта последовательность передается функции
StrToFloat
, и преобразованием занимается она. Здесь следует учесть два момента. Во-первых, в нашей грамматике разделителем целой и дробной части является точка, a
StrToFloat
использует системные настройки, т.е. разделителем может быть и запятая. Чтобы обойти эту проблему, слегка изменим синтаксис и будем сравнивать аргумент функции
IsSeparator
не с символом ".", а с
DecimalSeparator
(таким образом, наш калькулятор тоже станет чувствителен к системным настройкам). Во-вторых, не всякое выражение, соответствующее нашей грамматике, будет допустимым числом с точки зрения
StrToFloat
, т.к. эта функция учитывает диапазон типа
Extended
. Например, синтаксически верное выражение "2е5000" даст исключение
EConvertError
, т.к. данное число выходит за пределы этого диапазона. Но пока мы остаемся в рамках типа
Extended
, мы вынуждены мириться с этим.

Новые функции приведены в листинге 4.3.

Листинг 4.3. Реализация простейшего калькулятора

// Выделение из строки подстроки, соответствующей

// определению <Number>, и вычисление этого числа

// S — строка, из которой выделяется подстрока

//
Р — номер позиции в строке, с которой должно

// начинаться число. После завершения работы функции

// этот параметр содержит номер первого после числа

function Number(const S: string; var P: Integer): Extended;

var

 InitPos: Integer;

begin

 // InitPos нам понадобится для выделения подстроки,

 // которая будет передана в StrToFloat

 InitPos := Р;

 if (Р <= Length(S)) and IsSign(S[P]) then Inc(P);

 if (P > Length(S)) or not IsDigit(S[P]) then

raise ESyntaxError.Create(

'Ожидается цифра в позиции ' + IntToStr(Р));

 repeat

Inc(P);

 until (P > Length(S)) or not IsDigit(S[P]);

 if (P <= Length(S)) and IsSeparator(S[P]) then begin

Inc(P);

if (P > Length(S)) or not IsDigit(S[P]) then

raise ESyntaxError.Create(

'Ожидается цифра в позиции ' + IntToStr(Р));

repeat

Inc(P);

until (P > Length(S)) or not IsDigit(S[P]);

 end;

 if (P <= Length(S)) and IsExponent(S[P]) then

 begin

Inc(P);

if Р > Length(S) then

raise ESyntaxError.Create('Неожиданный конец строки');

if IsSign(S[P]) then Inc(P);

if (P > Length(S)) or not IsDigit(S[P]) then

raise ESyntaxError.Create(

'Ожидается цифра в позиции ' + IntToStr(Р));

repeat

Inc(P);

until (P > Length(S)) or not IsDigit(S[P]);

 end;

 Result := StrToFloat(Copy(S, InitPos, P - InitPos));

end;

// Проверка символа на соответствие <Operator>

function IsOperator(Ch: Char): Boolean;

begin

 Result := Ch in ['+', '-', '*', '/'];

end;

// Проверка строки на соответствие <Expr>

// и вычисление выражения

function Expr(const S: string): Extended;

var

 P: Integer;

 OpSymb: Char;

begin

 P := 1;

 Result := Number(S, P);

 while (P <= Length(S)) and IsOperator(S[P]) do

 begin

Поделиться:
Популярные книги

Изгой. Трилогия

Михайлов Дем Алексеевич
Изгой
Фантастика:
фэнтези
8.45
рейтинг книги
Изгой. Трилогия

Старатель 2

Лей Влад
2. Старатели
Фантастика:
боевая фантастика
космическая фантастика
5.00
рейтинг книги
Старатель 2

Купеческая дочь замуж не желает

Шах Ольга
Фантастика:
фэнтези
6.89
рейтинг книги
Купеческая дочь замуж не желает

Кодекс Охотника. Книга IX

Винокуров Юрий
9. Кодекс Охотника
Фантастика:
боевая фантастика
городское фэнтези
попаданцы
5.00
рейтинг книги
Кодекс Охотника. Книга IX

Школа Семи Камней

Жгулёв Пётр Николаевич
10. Real-Rpg
Фантастика:
фэнтези
рпг
5.00
рейтинг книги
Школа Семи Камней

Тайны ордена

Каменистый Артем
6. Девятый
Фантастика:
боевая фантастика
попаданцы
7.48
рейтинг книги
Тайны ордена

Убивать чтобы жить 9

Бор Жорж
9. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 9

Мимик нового Мира 11

Северный Лис
10. Мимик!
Фантастика:
юмористическое фэнтези
постапокалипсис
рпг
5.00
рейтинг книги
Мимик нового Мира 11

Пришествие бога смерти. Том 5

Дорничев Дмитрий
5. Ленивое божество
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Пришествие бога смерти. Том 5

Аномальный наследник. Том 3

Тарс Элиан
2. Аномальный наследник
Фантастика:
фэнтези
7.74
рейтинг книги
Аномальный наследник. Том 3

Опер. Девочка на спор

Бигси Анна
5. Опасная работа
Любовные романы:
современные любовные романы
эро литература
5.00
рейтинг книги
Опер. Девочка на спор

Имя нам Легион. Том 4

Дорничев Дмитрий
4. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Имя нам Легион. Том 4

Приручитель женщин-монстров. Том 8

Дорничев Дмитрий
8. Покемоны? Какие покемоны?
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Приручитель женщин-монстров. Том 8

Личник

Валериев Игорь
3. Ермак
Фантастика:
альтернативная история
6.33
рейтинг книги
Личник