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, которая принимает произвольное число аргументов и возвращает их среднее. Для этого правила, связанные с функциями, переопределим так:Отдельного комментария требует символ
f(1,5)
— это вызов функции f
то ли с одним аргументом 1.5, то ли с двумя аргументами 1 и 5. Чтобы избежать подобных неоднозначностей, в нашей грамматике разделителем аргументов будет символ, выбранный разделителем элементов списка (в русской локализации Windows это точка с запятой). Для корректной работы программы следите, чтобы на вашем компьютере разделители элементов списка, а также целой и дробной частей не оказались одинаковыми.Особенность нашего нового синтаксиса в том, что он допускает любое число аргументов для любой функции, т. е., например, выражение sin(0, 1, 2, 4)
Для реализации новых синтаксических и семантических правил в код вносятся следующие изменения. Во-первых, появляются новые лексемы ltLog
ltMean
и ltListSeparator
, а соответствующие методы лексического анализатора модифицируются так, чтобы распознавать их. Во-вторых, модифицируется функция Func
— она сначала вычисляет все аргументы, переданные функции, а потом проверяет, является ли количество аргументов допустимым, и если да, вычисляет требуемую функцию.Для лучшего понимания работы лексического и синтаксического анализатора рекомендуем самостоятельно выполнить следующие задания (или хотя бы просто подумать, как их выполнить).
1. Расширить определение
or
, and
, xor
. При этом потребуется поддержка скобок, т. к. иначе анализатор во многих случаях не сможет отличить логические операторы с низким приоритетом от одноименных арифметических.2. Изменить грамматику таким образом, чтобы имя функции стало идентификатором, а не зарезервированным словом.
3. Сделать комментарии вложенными. Сейчас в последовательности символов "{a{b}c}" считается, что комментарий заканчивается перед символом "с", т. к. лексический анализатор игнорирует все открывающие фигурные скобки в комментариях. Сделать так, чтобы комментарий считался закрытым только тогда, когда число закрывающих скобок сравняется с числом открывающих.
4. Добавить поддержку шестнадцатеричных целых констант. Для их записи использовать, как и в Delphi, символ "$", после которого должна идти последовательность из одной или нескольких шестнадцатеричных цифр.
5. Добавить возможность изменения приоритета операций с помощью не только круглых, но и квадратных скобок. Рассмотреть два варианта: когда круглые и квадратные скобки полностью взаимозаменяемы (т. е., например, допустимо выражение 2*(2+2]