- •Лабораторные работы
- •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
- •Указания по оформлению псевдокода
Лабораторная работа № 9. Генератор кода 5.2.
Генерация кода для простейших арифметических операций, работа с ассемблерным стеком. Генерация загрузки и сохранения переменных. Разбор и генерация оператора печати.
Для реализации арифметических (и не только) операций в языке ассемблер используется работа с регистровой памятью. Так, чтобы поместить значение (числовую константу или переменную) в регистр, используется инструкция mov. Команды для арифметических операций приведены в таблице ниже.
Операция |
Инструкция ассемблера |
+ |
add |
- |
sub |
* |
mul |
/ |
div |
Таким образом, код для выражения a + b будет выглядеть следующим образом:
mov ax, a
mov bx b
add ax, bx
Результат этой последовательности команд будет сохранен в регистре ax. Но в ситуации со сложными выражениями необходимо где-то сохранять промежуточные результаты. Для этих целей мы будем использовать ассемблерный стек: инструкция push - помещение значения регистра на верхушку стека, pop – извлечение из стека в регистр. После каждой атомарной операции мы будем сохранять результат в стек, чтобы не потерять его. Ниже приведен доработанный псевдокод методов синтаксического анализатора: добавленные строки выделены.
разобратьИнструкцию()
{
если( LexicalAnalyzer.текущаяЛексема == Lexems. Идентификатор )
{
Идентификатор Х = NameTable.найтиПоИмени( LexicalAnalyzer.текущееИмя );
если( !Х. эквивалентно( новый Идентификатор() ) )
{
разобратьИнструкциюПрисваивания();
CodeGenerator.добавитьИнструкцию( "pop ax" );
CodeGenerator.добавитьИнструкцию( "mov " + X. Имя + ", ax" );
}
иначе
ошибка();
}
}
tType разобратьСложениеИлиВычитание()
{
tType т;
Lexems оператор;
если( LexicalAnalyzer.текущаяЛексема == Lexems.Плюс
или LexicalAnalyzer.текущаяЛексема == Lexems.Минус )
{
оператор = LexicalAnalyzer.текущаяЛексема;
LexicalAnalyzer.разобратьСледующуюЛексему();
т = разобратьУмножениеИлиДеление();
}
иначе
т = разобратьУмножениеИлиДеление();
если( LexicalAnalyzer.текущаяЛексема == Lexems.Плюс
или LexicalAnalyzer.текущаяЛексема == Lexems.Минус )
{
выполнять
{
оператор = LexicalAnalyzer.текущаяЛексема;
LexicalAnalyzer.разобратьСледующуюЛексему();
т = разобратьУмножениеИлиДеление();
switch( оператор )
{
case Lexems.Плюс:
CodeGenerator.добавитьИнструкцию( "pop bx" );
CodeGenerator.добавитьИнструкцию( "pop ax" );
CodeGenerator.добавитьИнструкцию( "add ax, bx" );
CodeGenerator.добавитьИнструкцию( "push ax" );
break;
case Lexems.Минус:
CodeGenerator.добавитьИнструкцию( "pop bx" );
CodeGenerator.добавитьИнструкцию( "pop ax" );
CodeGenerator.добавитьИнструкцию( "sub ax, bx" );
CodeGenerator.добавитьИнструкцию( "push ax" );
break;
}
}
пока( LexicalAnalyzer.текущаяЛексема == Lexems.Плюс
или LexicalAnalyzer.текущаяЛексема == Lexems.Минус );
}
вернуть т;
}
tType разобратьУмножениеИлиДеление()
{
Lexems оператор;
tType т = разобратьПодвыражение();
если( LexicalAnalyzer.текущаяЛексема == Lexems.Умножить
или LexicalAnalyzer.текущаяЛексема == Lexems.Делить )
{
выполнять
{
оператор = LexicalAnalyzer.текущаяЛексема;
LexicalAnalyzer.разобратьСледующуюЛексему();
т = разобратьПодвыражение();
switch( оператор )
{
case Lexems.Умножить:
CodeGenerator.добавитьИнструкцию( "pop bx" );
CodeGenerator.добавитьИнструкцию( "pop ax" );
CodeGenerator.добавитьИнструкцию( "mul bx" );
CodeGenerator.добавитьИнструкцию( "push ax" );
break;
case Lexems.Делить:
CodeGenerator.добавитьИнструкцию( "pop bx" );
CodeGenerator.добавитьИнструкцию( "pop ax" );
CodeGenerator.добавитьИнструкцию( "cwd" );
CodeGenerator.добавитьИнструкцию( "div bl" );
CodeGenerator.добавитьИнструкцию( "push ax" );
break;
}
}
пока( LexicalAnalyzer.текущаяЛексема == Lexems.Умножить
или LexicalAnalyzer.текущаяЛексема == Lexems.Делить );
}
вернуть т;
}
tType разобратьПодвыражение()
{
Идентификатор х;
tType т = tType.None;
если( LexicalAnalyzer.текущаяЛексема == Lexems. Идентификатор )
{
х = NameTable.найтиПоИмени( LexicalAnalyzer.текущееИмя );
если( !Х. эквивалентно( новый Идентификатор() ) и х.категория == tCat.Var )
{
CodeGenerator.добавитьИнструкцию( "mov ax, " + LexicalAnalyzer.текущееИмя);
CodeGenerator.добавитьИнструкцию( "push ax" );
LexicalAnalyzer. разобратьСледующуюЛексему ();
вернуть х.тип;
}
иначе
ошибка();
}
иначе если( LexicalAnalyzer.текущаяЛексема == Lexems.Число )
{
CodeGenerator.добавитьИнструкцию( "mov ax, " + LexicalAnalyzer.текущееЧисло );
CodeGenerator.добавитьИнструкцию( "push ax" );
LexicalAnalyzer.разобратьСледующуюЛексему();
вернуть tType.Int;
}
иначе если( LexicalAnalyzer.текущаяЛексема == Lexems.ЛеваяСкобка )
{
LexicalAnalyzer.разобратьСледующуюЛексему();
т = разобратьВыражение();
проверитьЛексему( Lexems.ПраваяСкобка );
}
иначе
ошибка();
вернуть т;
}
После доработки текущих методов наш транслятор будет генерировать ассемблерный код, однако, проверить работоспособность этого кода можно проверить разве что отладчиком, ведь в синтаксисе нашего языка до сих пор нет оператора вывода на печать. Закрепим за оператором вывода на печать ключевое слово print. В синтаксическом анализаторе необходимо реализовать метод разбора этого оператора.
разобратьИнструкциюВыводаНаПечать()
{
проверитьЛексему( Lexems.ВыводНаПечать );
если( LexicalAnalyzer.текущаяЛексема == Lexems.Идентификатор )
{
Идентификатор X = NameTable.НайтиПоИмени( LexicalAnalyzer.текущееИмя );
CodeGenerator.добавитьИнструкцию( "push ax" );
CodeGenerator.добавитьИнструкцию( "mov ax, " + LexicalAnalyzer.текущееИмя );
CodeGenerator.добавитьИнструкцию( "CALL PRINT" );
CodeGenerator.добавитьИнструкцию( "pop ax" );
LexicalAnalyzer.разобратьСледующуюЛексему();
}
иначе
ошибка();
}
Внутри этого метода мы сохраняем содержимое регистра аккумулятора в стек, вызываем процедуру вывода на печать, о которой речь пойдет далее, и восстанавливаем содержимое аккумулятора. Далее дополним метод разобратьИнструкцию().
разобратьИнструкцию()
{
если( LexicalAnalyzer.текущаяЛексема == Lexems. Идентификатор )
{
Идентификатор Х = NameTable.найтиПоИмени( LexicalAnalyzer.текущееИмя );
если( !Х. эквивалентно( новый Идентификатор() ) )
разобратьИнструкциюПрисваивания();
иначе
ошибка();
}
иначе если( LexicalAnalyzer.текущаяЛексема == Lexems.ВыводНаПечать )
разобратьИнструкциюВыводаНаПечать();
}
В генераторе кода реализуем объявление процедуры вывода на печать, а в методе компилировать() синтаксического анализатора вызовем реализованный метод.
декларироватьПроцедуруВыводаНаПечать()
{
добавитьИнструкцию( "PRINT PROC NEAR" );
добавитьИнструкцию( "MOV CX, 10" );
добавитьИнструкцию( "MOV DI, BUFEND - PRINT_BUF" );
добавитьИнструкцию( "PRINT_LOOP:" );
добавитьИнструкцию( "MOV DX, 0" );
добавитьИнструкцию( "DIV CX" );
добавитьИнструкцию( "ADD DL, '0'" );
добавитьИнструкцию( "MOV [PRINT_BUF + DI - 1], DL" );
добавитьИнструкцию( "DEC DI" );
добавитьИнструкцию( "CMP AL, 0" );
добавитьИнструкцию( "JNE PRINT_LOOP" );
добавитьИнструкцию( "LEA DX, PRINT_BUF" );
добавитьИнструкцию( "ADD DX, DI" );
добавитьИнструкцию( "MOV AH, 09H" );
добавитьИнструкцию( "INT 21H" );
добавитьИнструкцию( "RET" );
добавитьИнструкцию( "PRINT ENDP" );
}
компилировать()
{
………………………………………………………………………………………………………
CodeGenerator.декларироватьЗавершениеОсновнойПроцедуры();
CodeGenerator. декларироватьПроцедуруВыводаНаПечать();
CodeGenerator.декларироватьЗавершениеКода();
}
Для тестирования работы транслятора можно использовать любой код на исходном языке, например, такой:
int a,b,c
begin
a=7
b=4
a=a*a-(7*b)
c=(a-b)*a+b
print c
end
После компиляции, линкования и запуска сгенерированного ассемблерного кода программа должна вывести на печать правильный результат – 361.
Примеры кода:
Файлы CodeGenerator.cs, SyntaxAnalyzer.cs в проекте.
