- •Лабораторные работы
- •1. Порядок выполнения работы.
- •2. Содержание отчета.
- •Лабораторная работа № 1. Лабораторная работа № 1.1. Создание каркаса транслятора.
- •Лабораторная работа № 2 Лабораторная работа № 1.2. Разбор исходного файла.
- •Лабораторная работа № 3.
- •1. Порядок выполнения работы.
- •2. Содержание отчета.
- •Лабораторная работа № 5. Лабораторная работа №3.1. Синтаксический анализатор.
- •Лабораторная работа № 6. Лабораторная работа №3.2. Синтаксический анализатор.
- •Лабораторная работа № 7. Лабораторная работа №3.3. Синтаксический анализатор.
- •1. Порядок выполнения работы.
- •2. Содержание отчета.
- •Лабораторная работа №5. Разработка генератора кода
- •1. Порядок выполнения работы.
- •2. Теоретическая часть
- •Практическая часть.
- •Лабораторная работа № 9. Генератор кода 5.2.
- •4. Содержание отчета
- •5. Содержание отчета
- •Лабораторная работа № 11. Синтаксический анализатор.
- •Лабораторная работа №12. Генератор кода 5.3.
- •Варианты к лабораторным работам
- •Вариант 1
- •Вариант 2
- •Вариант 3
- •Вариант 4
- •Вариант 5
- •Вариант 6
- •Вариант 7
- •Вариант 8
- •Вариант 9
- •Вариант 10
- •Вариант 11
- •Вариант 12
- •Вариант 13
- •Вариант 14
- •Вариант 15
- •Вариант 16
- •Курсовой проект
- •Оформление
- •Пустые строки
- •Пробелы в строке
- •Локальные переменные
- •Комментарии
- •Инструкции (statements)
- •Оформление if, if-else, if-else if-else
- •Оформление for, foreach
- •Оформление while, do-while
- •Оформление switch
- •Оформление try-catch
- •Указания по оформлению псевдокода
Лабораторная работа № 6. Лабораторная работа №3.2. Синтаксический анализатор.
Общая схема разбора. Разбор инструкции присваивания.
В ходе прошлой лабораторной работы был реализован синтаксический разбор секции объявления переменных для синтаксиса исходного языка. Следующий этап - разбор секции кода. Логичнее (и проще всего) представить блок кода как последовательность инструкций, разделенных переносом строки. Метод разобратьПоследовательностьИнструкций() в синтаксическом анализаторе будет обеспечивать необходимый функционал.
разобратьПоследовательностьИнструкций ()
{
разобратьИнструкцию();
пока( LexicalAnalyzer.текущаяЛексема == Lexems.Разделитель )
{
LexicalAnalyzer.разобратьСледующуюЛексему();
разобратьИнструкцию ();
}
}
Метод разобратьИнструкцию() будет реализовывать разбор одной строки исходного кода. На текущем этапе разработки синтаксического анализатора основной целью является разбор инструкции присваивания.
разобратьИнструкцию()
{
если( LexicalAnalyzer.текущаяЛексема == Lexems. Идентификатор )
{
Идентификатор Х = NameTable.найтиПоИмени( LexicalAnalyzer.текущееИмя );
если( !Х. эквивалентно( новый Идентификатор() ) )
разобратьИнструкциюПрисваивания();
иначе
ошибка();
}
}
разобратьИнструкциюПрисваивания()
{
LexicalAnalyzer.разобратьСледующуюЛексему();
если( LexicalAnalyzer.текущаяЛексема == Lexems.Присваивание )
{
LexicalAnalyzer.разобратьСледующуюЛексему();
разобратьВыражение();
}
иначе
ошибка();
}
Метод разобратьВыражение() будет реализован в ходе следующей лабораторной работы. Пока оставим для него заглушку.
tType разобратьВыражение()
{
пока( LexicalAnalyzer.текущаяЛексема != Lexems.Разделитель )
LexicalAnalyzer.разобратьСледующуюЛексему();
вернуть tType.Int;
}
Также необходимо доработать метод компилировать(), используя в нем реализованный функционал.
компилировать()
{
LexicalAnalyzer.инициализировать();
разобратьОбъявлениеПеременных();
проверитьЛексему( Lexems.ПереносСтроки );
если( LexicalAnalyzer.текущаяЛексема == Lexems.Begin )
LexicalAnalyzer.разобратьСледующуюЛексему();
LexicalAnalyzer.разобратьСледующуюЛексему();
LexicalAnalyzer. разобратьПоследовательностьИнструкций();
проверитьЛексему( Lexems.End );
}
Тестирование описанного функционала можно провести, вызывая метод SyntaxAnalyzer.компилировать() для разных (в том числе некорректных) вариантов секции кода.
Примеры кода:
Файл SyntaxAnalyzer.cs в проекте.
Лабораторная работа № 7. Лабораторная работа №3.3. Синтаксический анализатор.
Схема разбора сложных выражений – разбиение на подвыражения и рекурсивный спуск.
На текущем этапе разработки синтаксического анализатора остается реализовать разбор правой части инструкции присваивания – некоторого выражения. Это выражение может быть достаточно сложным, поэтому для простоты будем разбивать его на бинарные операции (то есть, операции с двумя операндами – сложение, вычитание, умножение, деление). Две из перечисленных операций – умножение и деление имеют высший приоритет, поэтому при прочих равных анализатор сначала выполнит их разбор. При встрече с круглыми скобками анализатор должен уметь разбирать их содержимое, как отдельное выражение. Таким образом, мы от сложного выражения поэтапно перейдем к цепочке простых – это и есть так называемый «рекурсивный спуск». Ниже приведен псевдокод трех методов: разобратьСложениеИлиВычитание(), разобратьУмножениеИлиДеление(), разобратьПодвыражение() – для операций с разным приоритетом.
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 разобратьВыражение()
{
вернуть разобратьСложениеИлиВычитание();
}
Тестирование синтаксического анализатора можно проводить в полном объеме - выполняя разбор для кода со сложными выражениями. Также необходимо проверить механизм обнаружения ошибок на этапе синтаксического анализа.
Замечания по коду:
Реализация методов разбора не является полной без проверки типов выражений на совместимость – эту проверку необходимо реализовать самостоятельно.
Примеры кода:
Файл SyntaxAnalyzer.cs в проекте.
ЛАБОРАТОРНАЯ РАБОТА №4.
ФОРМИРОВАНИЕ ПОСТФИКСНОЙ ЗАПИСИ
