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

на главную

Жанры

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

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

Шрифт:

 begin

OpSymb := S[P];

Inc(P);

case OpSymb of

'+': Result := Result + Term(S, P);

'-': Result := Result - Term(S, P);

end;

 end;

 if P <= Length(S) then

raise ESyntaxError.Create(

'Некорректный символ в позиции ' + IntToStr(Р));

end;

Если вы разобрались с предыдущими примерами,

приведенный здесь код будет вам понятен. Некоторых комментариев требует только функция
Term
. Она выделяет, начиная с заданного символа, ту часть строки, которая соответствует определению
<Term>
. Вызвавшая ее функция
Expr
должна продолжить разбор выражения со следующего за этой подстрокой символа, поэтому функция
Term
, как и
Number
, имеет параметр-переменную
P
, которая на входе содержит номер первого символа слагаемого, а на выходе — номер первого после этого слагаемого символа.

Пример калькулятора, учитывающего приоритет операций, находится на компакт-диске под именем PrecedenceCalcSample. Поэкспериментировав с ним, легко убедиться, что теперь вычисление "2+2*2" дает правильное значение 6.

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

4.6. Выражения со скобками

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

Выражение, заключенное в скобки, допустимо везде, где допускается появление отдельного числа (из этого, в частности, следует, что допускаются вложенные скобки). Таким образом, мы должны расширить нашу грамматику так, чтобы аргументом операций сложения и умножения могли служить не только числа, но и выражения, заключенные в скобки. Это автоматически позволит использовать такие выражения и в качестве слагаемых, потому что слагаемое — это последовательность из одного или нескольких множителей, разделенных знаками умножения и деления. На языке БНФ все сказанное иллюстрирует листинг 4.6.

Листинг 4.6. Грамматика выражения со скобками (первое приближение)

<Expr> ::= <Term> {<Operation1> <Term>}

<Term> ::= <Factor> {<Operation2> <Factor>}

<Factor> ::= <Number> | ' (' <Expr> ')'

В этих определениях появилась рекурсия, т.к. в определении

<Expr>
используется (через
<Term>
) символ <Factor>, а в определении
<Factor>
<Term>
. Соответственно, подобная грамматика будет реализовываться рекурсивными функциями.

Наша грамматика не учитывает, что перед скобками может стоять знак унарной операции "

+
" или "
", хотя общепринятые правила записи выражений вполне допускают выражения типа
3*-(2+4)
. Поэтому, прежде чем приступить к созданию нового калькулятора, введем правила, допускающие такой синтаксис. Можно было бы модифицировать определение
<Factor>
таким образом:

<Factor> ::= <Number> | [Sign] '(' <Expr> ')'

Однако

такой подход страдает отсутствием общности. В дальнейшем мы усложним наш синтаксис, введя другие типы множителей (функции, переменные). Перед каждым из них, в принципе, может стоять знак унарной операции, поэтому логичнее определить синтаксис таким образом, чтобы унарная операция допускалась вообще перед любым множителем. В этом случае можно будет слегка упростить определение
<Number>
, т.к. знак "
+
" или "
" в начале числа можно будет трактовать не как часть числа, а как унарный оператор, стоящий перед множителем, представленным в виде числовой константы.

С учетом этого новая грамматика запишется следующим образом (листинг 4.7).

Листинг 4.7. Окончательный вариант грамматики выражения со скобками

<Expr> ::= <Term> {<Operation1> <Term>}

<Term> ::= <Factor> {<Operation2> <Factor>}

<Factor> ::= <UnaryOp> <Factor> | <Number> | '(' <Expr> ')'

<Number> ::= <Digit> {<Digit>} [<Separator> <Digit> {<Digit>}]

 [<Exponent> [<Sign>] <Digit> {<Digit>}]

<UnaryOp> ::= '+' | '-'

Здесь опущены определения некоторых вспомогательных символов, которые не изменились.

Мы видим, что грамматика стала "более рекурсивной", т.е. в определении символа

<Factor>
используется он сам. Соответственно, функция
Factor
будет вызывать саму себя.

Символ

<UnaryOp>
, определение которого совпадает с определениями
<Operator1>
и
<Sign>
, мы делаем независимым нетерминальным символом по тем же причинам, что и ранее: в принципе, синтаксис может допускать унарные операции (как, например,
not
в Delphi), которые не являются ни знаками, ни допустимыми бинарными операциями.

Побочным эффектом нашей грамматики стало то, что, например,

– 5
воспринимается как множитель, а потому перед ним допустимо поставить унарный оператор, т. е. выражение
– -5
также является корректным множителем и трактуется как
– (-5)
. А перед
– -5
, в свою очередь, можно поставить еще один унарный оператор. И так — до бесконечности. Это может показаться не совсем правильным, но, тем не менее, такая грамматика широко распространена. Легко, например, убедиться, что компилятор Delphi считает допустимым выражение
2+-+-2
, трактуя его как
2+(-(+(-2)))
. Листинг 4.8 иллюстрирует реализацию данной грамматики.

Листинг 4.8. Реализация калькулятора со скобками

// Так как грамматика рекурсивна, функция Expr

// должна быть объявлена заранее

function Expr(const S: string; var Р: Integer): Extended; forward;

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

// и ее вычисление

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

begin

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

Ученичество. Книга 2

Понарошку Евгений
2. Государственный маг
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Ученичество. Книга 2

Император поневоле

Распопов Дмитрий Викторович
6. Фараон
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Император поневоле

Лорд Системы 7

Токсик Саша
7. Лорд Системы
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Лорд Системы 7

Целитель. Книга вторая

Первухин Андрей Евгеньевич
2. Целитель
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Целитель. Книга вторая

Темный Патриарх Светлого Рода 3

Лисицин Евгений
3. Темный Патриарх Светлого Рода
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Темный Патриарх Светлого Рода 3

Случайная свадьба (+ Бонус)

Тоцка Тала
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Случайная свадьба (+ Бонус)

Прометей: каменный век II

Рави Ивар
2. Прометей
Фантастика:
альтернативная история
7.40
рейтинг книги
Прометей: каменный век II

Жребий некроманта. Надежда рода

Решетов Евгений Валерьевич
1. Жребий некроманта
Фантастика:
фэнтези
попаданцы
6.50
рейтинг книги
Жребий некроманта. Надежда рода

Возвышение Меркурия

Кронос Александр
1. Меркурий
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия

Не грози Дубровскому! Том Х

Панарин Антон
10. РОС: Не грози Дубровскому!
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Не грози Дубровскому! Том Х

Идеальный мир для Лекаря 12

Сапфир Олег
12. Лекарь
Фантастика:
боевая фантастика
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 12

Хочу тебя навсегда

Джокер Ольга
2. Люби меня
Любовные романы:
современные любовные романы
5.25
рейтинг книги
Хочу тебя навсегда

СД. Том 15

Клеванский Кирилл Сергеевич
15. Сердце дракона
Фантастика:
героическая фантастика
боевая фантастика
6.14
рейтинг книги
СД. Том 15

Смерть может танцевать 3

Вальтер Макс
3. Безликий
Фантастика:
боевая фантастика
5.40
рейтинг книги
Смерть может танцевать 3