- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
В реализации лексического анализатора требуются следующие изменения. Во-первых, теперь конструктор не запускает полный цикл лексического анализа, а только сохраняет переданную строку и выделяет из нее первую лексему. Во-вторых, выражение и позиция в выражении теперь должны сохраняться между вызовами методов лексического анализатора и поэтому становятся полями этого класса. В-третьих, метод Next теперь выполняет выделение очередной лексемы, которую помещает в специально созданное для этого поле, а свойство Lexeme возвращает указатель на это поле, а не на элемент списка. Остальные функции лексического анализатора изменились только в том отношении, что теперь выражение и указатель на позицию в строке получают не через параметры, а напрямую обращаются к соответствующим полям.
Пример однопроходного калькулятора с лексическим анализатором находится на компакт-диске в папке SinglePassSample. В листинге 4.14 показан код той части нового варианта класса TLexicalAnalyzer, которую понадобилось изменить, чтобы обеспечить однопроходность.
Листинг 4.14. Однопроходный вариант класса TLexicalAnalyzertype
TLexicalAnalyzer = class
private
// Выражение для вычисления
FExpr: string;
// Текущая позиция
FP: Integer;
// Текущая лексема
FCurrLexeme: TLexeme;
function GetLexeme: PLexeme;
procedure SkipWhiteSpace;
procedure ExtractLexeme;
procedure PutLexeme(LexemeType: TLexemeType; Pos: Integer; const Lexeme: string);
procedure Number;
procedure Word;
public
constructor Create(const Expr: string);
procedure Next;
property Lexeme: PLexeme read GetLexeme;
end;
constructor TLexicalAnalyzer.Create(const Expr: string);
begin
inherited Create;
FP := 1;
FExpr := Expr;
Next;
end;
// Получение указателя на текущую лексему
function TLexicalAnalyzer.GetLexeme: PLexeme;
begin
Result := @FCurrLexeme;
end;
// Получение следующей лексемы
procedure TLexicalAnalyzer.Next;
begin
if FP <= Length(FExpr) then
begin
SkipWhiteSpace;
ExtractLexeme;
end
else PutLexeme(ltEnd, FP, '');
end;
// Замещение текущей лексемы новой лексемой
procedure TLexicalAnalyzer.PutLexeme(LexemeType: TLexemeType; Pos: Integer; const Lexeme: string);
begin
FCurrLexeme.LexemeType := LexemeType;
FCurrLexeme.Pos := Pos;
FCurrLexeme.Lexeme := Lexeme;
end;
Теперь класс TLexicalAnalyzer хранит не список лексем, а только одну текущую лексему, а функция PutLexeme не добавляет лексему в список, а изменяет значение текущей лексемы. Функция Next вместо простого изменения индекса выделяет очередную лексему, т.е. выполняет одну итерацию цикла лексического анализа. Функции SkipWhiteSpace, ExtractLexeme и т.п. избавились от параметров, через которые передавалось выражение и позиция, потому что теперь выражение и позиция хранятся в полях класса.
Синтаксический анализатор при этом остается без изменений, т.к. интерфейс лексического анализатора не изменился.
Чтобы не реализовывать дважды одну и ту же грамматику, введем в наш синтаксис еще одну возможность — поддержку функций с несколькими аргументами. Конкретно — функцию с двумя аргументами Log(а, x), возвращающей логарифм x по основанию a, а также функцию Mean, которая принимает произвольное число аргументов и возвращает их среднее. Для этого правила, связанные с функциями, переопределим так:
<Function> ::= <FuncName> '(' <MathExpr> {<ListSeparator> <MathExpr>} ')'
<FuncName> ::= 'sin' | 'cos' | 'ln' | 'log' | 'mean'
Отдельного комментария требует символ <ListSeparator>, разделяющий аргументы в функции. В Delphi, как и во многих других языках программирования, таким разделителем служит запятая. Но наша грамматика определена так, что запятая, в принципе, может служить разделителем целой и дробной части числа. Как уже говорилось, в этом случае может возникнуть неоднозначность в выражениях типа f(1,5) — это вызов функции f то ли с одним аргументом 1.5, то ли с двумя аргументами 1 и 5. Чтобы избежать подобных неоднозначностей, в нашей грамматике разделителем аргументов будет символ, выбранный разделителем элементов списка (в русской локализации Windows это точка с запятой). Для корректной работы программы следите, чтобы на вашем компьютере разделители элементов списка, а также целой и дробной частей не оказались одинаковыми.
Особенность нашего нового синтаксиса в том, что он допускает любое число аргументов для любой функции, т.е., например, выражение sin(0, 1, 2, 4) синтаксически корректно (при условии, что разделителем элементов списка является запятая), хотя смысла это выражение не имеет. Можно было бы ввести отдельные синтаксические правила для функций с одним аргументом, с двумя аргументами и с произвольным числом аргументов, но такой подход встречается редко, т.к. обычно намного проще осуществить проверку на этапе семантического анализа (т.е. в нашем случае — при вычислении функции).
Для реализации новых синтаксических и семантических правил в код вносятся следующие изменения. Во-первых, появляются новые лексемы ltLog, ltMean и ltListSeparator, а соответствующие методы лексического анализатора модифицируются так, чтобы распознавать их. Во-вторых, модифицируется функция Func — она сначала вычисляет все аргументы, переданные функции, а потом проверяет, является ли количество аргументов допустимым, и если да, вычисляет требуемую функцию.
Для лучшего понимания работы лексического и синтаксического анализатора рекомендуем самостоятельно выполнить следующие задания (или хотя бы просто подумать, как их выполнить).
1. Расширить определение <Expr> таким образом, чтобы в нем можно было объединять несколько операций сравнения с помощью or, and, xor. При этом потребуется поддержка скобок, т.к. иначе анализатор во многих случаях не сможет отличить логические операторы с низким приоритетом от одноименных арифметических.
2. Изменить грамматику таким образом, чтобы имя функции стало идентификатором, а не зарезервированным словом.
3. Сделать комментарии вложенными. Сейчас в последовательности символов "{a{b}c}" считается, что комментарий заканчивается перед символом "с", т.к. лексический анализатор игнорирует все открывающие фигурные скобки в комментариях. Сделать так, чтобы комментарий считался закрытым только тогда, когда число закрывающих скобок сравняется с числом открывающих.
4. Добавить поддержку шестнадцатеричных целых констант. Для их записи использовать, как и в Delphi, символ "$", после которого должна идти последовательность из одной или нескольких шестнадцатеричных цифр.
5. Добавить возможность изменения приоритета операций с помощью не только круглых, но и квадратных скобок. Рассмотреть два варианта: когда круглые и квадратные скобки полностью взаимозаменяемы (т.е., например, допустимо выражение 2*(2+2]) и когда закрывающая скобка должна быть такой же формы, как и открывающая.
Еще одна возможность, которую даст лексический анализатор — это обработка ошибок без исключений (иногда это может быть полезно). Пусть в анализаторе есть флаг, который взводится при обнаружении ошибки. Пока этот флаг сброшен, лексический анализатор работает обычным образом. Но если он взведен, вызов функции Next не делает ничего, а свойство Lexeme всегда возвращает лексему ltEnd, независимо от того, дошел ли анализатор до конца строки или нет. После выполнения анализа проверяется этот флаг, и по его состоянию делается вывод о том, произошла ли ошибка. Соответственно, лексический анализатор должен иметь метод для установки этого флага извне. чтобы синтаксический анализатор мог его установить при обнаружении ошибки.
ПримечаниеФлагом можно сделать строковое поле, хранящее сообщение об ошибке. Пока эта строка пуста, флаг считается сброшенным, когда строка не пуста, считается, что флаг взведен. Таким образом, синтаксический анализатор формирует при необходимости сообщение об ошибке и помещает его в это поле лексического анализатора, и тот переходит в "ошибочный" режим. Так мы обеспечиваем и реализацию флага, и передачу сообщения об ошибке. В этом случае в структуре ТLexeme можно избавиться от поля Pos — позицию последней выделенной лексемы можно сделать внутренним полем лексического анализатора, и тот сам добавит номер позиции к сообщению, сформированному синтаксическим анализатором.
4.10. Еще немного теории
Теперь, познакомившись с синтаксическим анализом на практике, вернемся к теории и немного поговорим о типах грамматик и об альтернативных методах синтаксического анализа и вычисления выражений. Эти вопросы мы здесь рассмотрим только ознакомительно, а более детальное их описание можно найти в [6–8].