Схема разбора сложных выражений – разбиение на подвыражения и рекурсивный спуск

На текущем этапе разработки синтаксического анализатора остается реализовать разбор правой части инструкции присваивания – некоторого выражения. Это выражение может быть достаточно сложным, поэтому для простоты будем разбивать его на бинарные операции (то есть, операции с двумя операндами – сложение, вычитание, умножение, деление). Две из перечисленных операций – умножение и деление имеют высший приоритет, поэтому при прочих равных анализатор сначала выполнит их разбор. При встрече с круглыми скобками анализатор должен уметь разбирать их содержимое, как отдельное выражение. Таким образом, мы от сложного выражения поэтапно перейдем к цепочке простых – это и есть так называемый «рекурсивный спуск». Ниже приведен псевдокод трех методов: разобратьСложениеИлиВычитание(), разобратьУмножениеИлиДеление(), разобратьПодвыражение() – для операций с разным приоритетом.

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 в проекте.



Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: