На текущем этапе разработки синтаксического анализатора остается реализовать разбор правой части инструкции присваивания – некоторого выражения. Это выражение может быть достаточно сложным, поэтому для простоты будем разбивать его на бинарные операции (то есть, операции с двумя операндами – сложение, вычитание, умножение, деление). Две из перечисленных операций – умножение и деление имеют высший приоритет, поэтому при прочих равных анализатор сначала выполнит их разбор. При встрече с круглыми скобками анализатор должен уметь разбирать их содержимое, как отдельное выражение. Таким образом, мы от сложного выражения поэтапно перейдем к цепочке простых – это и есть так называемый «рекурсивный спуск». Ниже приведен псевдокод трех методов: разобратьСложениеИлиВычитание(), разобратьУмножениеИлиДеление(), разобратьПодвыражение() – для операций с разным приоритетом.
tType разобратьСложениеИлиВычитание()
{
tType т;
Lexems оператор;
если(LexicalAnalyzer.текущаяЛексема == Lexems.Плюс
|
|
или LexicalAnalyzer.текущаяЛексема == Lexems.Минус)
{
оператор = LexicalAnalyzer.текущаяЛексема;
LexicalAnalyzer.разобратьСледующуюЛексему();
т = разобратьУмножениеИлиДеление();
}
иначе
т = разобратьУмножениеИлиДеление();
если(LexicalAnalyzer.текущаяЛексема == Lexems.Плюс
или LexicalAnalyzer.текущаяЛексема == Lexems.Минус)
{
выполнять
{
оператор = LexicalAnalyzer.текущаяЛексема;
LexicalAnalyzer.разобратьСледующуюЛексему();
т = разобратьУмножениеИлиДеление();
switch(оператор)
{
case Lexems.Плюс:
break;
case Lexems.Минус:
break;
}
}
пока(LexicalAnalyzer.текущаяЛексема == Lexems.Плюс
или LexicalAnalyzer.текущаяЛексема == Lexems.Минус);
}
вернуть т;
}
tType разобратьУмножениеИлиДеление()
{
Lexems оператор;
tType т = разобратьПодвыражение();
если(LexicalAnalyzer.текущаяЛексема == Lexems.Умножить
или LexicalAnalyzer.текущаяЛексема == Lexems.Делить)
{
выполнять
{
оператор = LexicalAnalyzer.текущаяЛексема;
LexicalAnalyzer.разобратьСледующуюЛексему();
т = разобратьПодвыражение();
switch(оператор)
{
case Lexems.Умножить:
break;
case Lexems.Делить:
break;
}
}
пока(LexicalAnalyzer.текущаяЛексема == Lexems.Умножить
или LexicalAnalyzer.текущаяЛексема == Lexems.Делить);
}
вернуть т;
}
tType разобратьПодвыражение()
{
Идентификатор х;
tType т = tType.None;
если(LexicalAnalyzer.текущаяЛексема == Lexems. Идентификатор)
{
х = NameTable.найтиПоИмени(LexicalAnalyzer.текущееИмя);
если(!Х. эквивалентно(новый Идентификатор()) и х.категория == tCat.Var)
{
LexicalAnalyzer. разобратьСледующуюЛексему ();
вернуть х.тип;
}
иначе
ошибка();
}
иначе если(LexicalAnalyzer.текущаяЛексема == Lexems.Число)
{
LexicalAnalyzer.разобратьСледующуюЛексему();
вернуть tType.Int;
}
иначе если(LexicalAnalyzer.текущаяЛексема == Lexems.ЛеваяСкобка)
{
LexicalAnalyzer.разобратьСледующуюЛексему();
т = разобратьВыражение();
|
|
проверитьЛексему(Lexems.ПраваяСкобка);
}
иначе
ошибка();
вернуть т;
}
Частично код этих методов будет доработан в процессе разработки генератора кода. В ходе прошлой лабораторной работы мы сделали заглушку для метода разобратьВыражение(). Теперь же в этом методе мы можем использовать реализованный функционал – просто запустим разбор вызовом метода разобратьСложениеИлиВычитание().
tType разобратьВыражение()
{
вернуть разобратьСложениеИлиВычитание();
}
Тестирование синтаксического анализатора можно проводить в полном объеме - выполняя разбор для кода со сложными выражениями. Также необходимо проверить механизм обнаружения ошибок на этапе синтаксического анализа.
Замечания по коду:
1) Реализация методов разбора не является полной без проверки типов выражений на совместимость – эту проверку необходимо реализовать самостоятельно.
Примеры кода:
Файл SyntaxAnalyzer.cs в проекте.