- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
if (Р <= Length(S)) and (S[P] = DecimalSeparator) then
begin
Inc(P);
if (Р > Length(S)) or not IsDigit(S[P]) then Dec(P)
else repeat
Inc(P);
until (P > Length(S)) or not IsDigit(S(P));
end;
// Выделяем экспоненту
if (P <= Length(S)) and (UpCase(S[P]) = 'E') then
begin
// Если мы дошли до этого места, значит, от начала строки
// и до сих пор набор символов представляет собой
// синтаксически правильное число без экспоненты.
// Прежде чем начать выделение экспоненты, запоминаем
// текущую позицию, чтобы иметь возможность вернуться к ней
// если экспоненту выделить не удастся.
RollBackPos := P;
Inc(Р);
if Р > Length(S) then P := RollBackPos
else
begin
if S[P] in ['+', '-'] then Inc(P);
if (P > Length(S)) or not IsDigit(S(P)) then P := RollbackPos
else repeat
Inc(P);
until (P > Length(S)) or not IsDigit(S[P]);
end;
end;
PutLexeme(ltNumber, InitPos, Copy(S, InitPos, P- InitPos));
end;
// Выделение слова из строки и проверка его на совпадение
// с зарезервированными словами языка
procedure TLexicalAnalyzer.Word(const S: string; var P: Integer);
var
InitPos: Integer;
ID: string;
begin
InitPos := P;
Inc(P);
while (P <= Length(S)) and
(S[P] in ['0'..'9', 'A'..'Z', 'a'..'z', '_']) do
Inc(P);
ID := Copy(S, InitPos, P - InitPos);
if AnsiCompareText(ID, 'or') = 0 then
PutLexeme(ltOr, InitPos, '')
else if AnsiCompareText(ID, 'xor') = 0 than
PutLexeme(ltXor, InitPos, '')
else if AnsiCompareText(ID, 'div') = 0 then
PutLexeme(ltDiv, InitPos, '')
else if AnsiCompareText(ID, 'mod') = 0 then
PutLexeme(ltMod, InitPos, '')
else if AnsiCompareText(ID, 'and') = 0 then
PutLexeme(ltAnd, InitPos, '')
else if AnsiCompareText(ID, 'not') = 0 then
PutLexeme(ltNot, InitPos, '')
else if AnsiCompareText(ID, 'sin') = 0 then
PutLexeme(ltSin, InitPos, '')
else if AnsiCompareText(ID, 'cos') = 0 then
PutLexeme(ltCos, InitPos, '')
else if AnsiCompareText(ID, 'ln') = 0 then
PutLexeme(ltLn, InitPos, '')
else PutLexeme(ltIdentifier, InitPos, ID);
end;
В конец списка лексем помещается специальная лексема типа ltEnd. В предыдущих примерах приходилось постоянно сравнивать указатель позиции P с длиной строки S, чтобы не допустить выход за пределы диапазона. Если бы не было лексемы ltEnd, точно так же пришлось бы проверять, не вышел ли указатель за пределы списка. Но лексема ltEnd не рассматривается как допустимая ни одной из функций синтаксического анализатора, поэтому, встретив ее, каждая из них возвращает управление вызвавшей ее функции, и заканчивается эта цепочка только на функции Expr. Таким образом, код получается более ясным и компактным.
ПримечаниеАналогичный алгоритм возможен и в предыдущих версиях калькулятора: достаточно добавить в конец строки символ, который в ней заведомо не должен был появляться (например, #1), и проверять в функции Expr или Calculate, что разбор выражения остановился именно на этом символе.
Лексический анализ выражения заключается в чередовании вызовов функций SkipWhiteSpace и ExtractLexeme. Первая из них пропускает все, что может разделять две лексемы, вторая распознает и помещает в список одну лексему.
Обратите внимание, как в лексическом анализаторе реализован метод Number. Рассмотрим выражение "1е*5". В калькуляторе без лексического анализатора функция Number, дойдя до символа "*" выдавала исключение, т.к. ожидала увидеть здесь знак "+", или число. Но лексический анализатор не должен брать на себя такую ответственность — поиск синтаксических ошибок. Поэтому в данном случае он должен, дойдя до непонятного символа в конструкции, которую он счел за экспоненту, откатиться назад, выделить из строки лексему "1" и продолжить выделение лексем с символа "е". В результате список лексем будет выглядеть так: "1, "е", "*", "5". И уже синтаксический анализатор должен потом разобраться, допустима ли такая последовательность лексем или нет.
Отметим, что для нашей грамматики непринципиально, зафиксирует ли в таком выражении ошибку лексический или синтаксический анализатор. Но в общем случае может существовать грамматика, в которой такое выражение допустимо, поэтому лексический анализатор должен действовать именно так, т.е. выполнять откат, если попытка выделить число зашла на каком-то этапе в тупик (самый простой пример — наличие в языке бинарного оператора, начинающегося с символа "е" — тогда пользователь сможет написать этот оператор после числа без пробела, и чтобы справиться с такой ситуацией, понадобится откат). Функция Number вызывается из ExtractLexeme только в том случае, когда в начале лексемы встречается цифра, а с цифры может начинаться только лексема ltNumber. Таким образом, сам факт вызова функции Number говорит о том, что в строке гарантированно обнаружена подстрока (состоящая, по крайней мере, из одного символа), которая является числом. Функции синтаксического анализатора очень похожи на аналогичные функции из предыдущих примеров, за исключением того, что работают не со строкой, а со списком лексем. Поэтому мы приведем здесь только одну из них — функцию Term (листинг 4.13).
Листинг 4.13. Пример функции, использующей лексический анализаторconst
Operator2 = (ltAsterisk, ltSlash, ltDiv, ltMod, ltAnd);
function Term(LexicalAnalyzer: TLexicalAnalyzer): Extended;
var
Operator: TLexemeType;
begin
Result := Factor(LexicalAnalyzer);
while LexicalAnalyzer.Lexeme.LexemeType in Operator2 do
begin
Operator := LexicalAnalyzer.Lexeme.LexemeType;
LexicalAnalyzer.Next;
case Operator of
ltAsterisk: Result := Result * Factor(LexicalAnalyzer);
ltSlash: Result := Result / Factor(LexicalAnalyzer);
ltDiv: Result := Trunc(Result) div Trunc(Factor(LexicalAnalyzer));
ltMod: Result := Trunc(Result) mod Trunc(Factor(LexicalAnalyzer));
ltAnd: Result := Trunc(Result) and Trunc(Factor(LexicalAnalyzer));
end;
end;
end;
Если сравнить этот вариант Term с аналогичной функцией из листинга 42, легко заметить их сходство.
Использование лексического анализатора может повысить скорость многократного вычисления одного выражения при разных значениях входящих в него переменных (например, при построении графика функции, ввезенной пользователем). Действительно, лексический анализ в этом случае достаточно выполнить один раз, а потом пользоваться готовым списком. Можно сделать такие операции еще более эффективными, переложив вычисление числовых констант на лексический анализатор. Для этого в структуру TLexeme нужно добавить поле Number типа Extended и модифицировать метод Number таким образом, чтобы он сразу преобразовывал выделенную подстроку в число. Тогда дорогостоящий вызов функции StrToFloat будет перенесен из многократно повторяющейся функции Base в однократно выполняемый метод TLexicalAnalyzer.Number. Но самое радикальное средство повышения производительности — переделка синтаксического анализатора таким образом, чтобы он не вычислял выражение самостоятельно, а формировал машинный код для его вычисления. Однако написание компилятора математических выражений выходит за рамки данной книги.
4.9. Однопроходный калькулятор и функции с несколькими переменными
В предыдущем примере выражение сначала от начала до конца просматривается лексическим анализатором и переводится в иную форму (список лексем). Затем этот список обрабатывается синтаксическим анализатором. Таким образом, калькулятор получается двухпроходным, хотя из синтаксиса и семантики выражения необходимость нескольких проходов не вытекает. Попробуем переделать его так, чтобы он стал однопроходным.
ПримечаниеВ некоторых языках многопроходность — обязательное требование к реализации компилятора. Например, в языке C++ реализацию функций класса можно вставлять в само описание класса. При этом внутри этих функций можно обращаться к тем полям и функциям класса, которые объявлены ниже. Таким образом, откомпилировать подобный код может только компилятор как минимум с двумя проходами, чтобы на первом проходе можно было найти все поля класса, а на втором — откомпилировать функции класса.
В предыдущей реализации калькулятора синтаксический анализатор работал с лексическим через процедуру Next и свойство Lexeme: процедура Next передвигала текущую позицию в списке лексем, а свойство Lexeme давало доступ к текущей лексеме. Легко видеть, что при таком алгоритме лексическому анализатору нет необходимости хранить полный список лексем, достаточно помнить текущую, а при вызове Next анализировать очередную часть строки, выделяя из нее следующую лексему и делая ее текущей. Таким образом, синтаксический и лексический анализаторы будут работать по очереди, обрабатывая каждый по одной лексеме.