Лабораторная работа № 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 в проекте.

Содержание отчета

3.1. Название работы и ее исполнители.

3.2. Цель работы.

3.3. Краткое (по 2-3 предложения) описание процедур (функций) программы генерации кода.

3.4. Листинг программы.

3.5. В случае необходимости, информация о доработке программ лексического и синтаксического анализа.

3.6. Распечатки контрольных примеров и результатов их выполнения.

3.7. Выводы по проделанной работе.

Лабораторная работа № 10. Тестирование приложения.

ТЕСТИРОВАНИЕ ПРОЕКТА

Тестирование проходит по ошибкам, целенаправленно внесенным во входной файл. Ниже приведены возможные варианты ошибок в различных блоках транслятора.


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



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