Так как наш транслятор является однопроходным, методы генератора кода будут вызываться из методов синтаксического анализатора. По мере выполнения синтаксического разбора будет генерироваться и ассемблерный код. Для удобства будем хранить его в массиве строк. В классе CodeGenerator объявим массив строк и целочисленную переменную, хранящую текущий индекс для его правильного заполнения.
private static string[] code = new string[MAX_NUMBER_STRINGS];
private static int codePointer = 0;
Ключевым методом генератора кода будет метод добавитьИнструкцию(), добавляющий одну строку ассемблерного кода.
добавитьИнструкцию(инструкция)
{
код[указатель++] = инструкция;
}
Для ассемблерных программ некоторая часть кода является общей: объявление сегментов данных, кода и стека; инструкции завершения сегмента кода и т. д. Декларирование общих участков кода вынесем в отдельные функции.
Объявление сегмента данных (секции объявления переменных):
декларироватьСегментДанных()
{
добавитьИнструкцию("data segment para public \"data\"");
|
|
}
Объявление сегментов стека и кода:
декларироватьСегментыСтекаИКода()
{
добавитьИнструкцию("PRINT_BUF DB ' ' DUP(10)");
добавитьИнструкцию("BUFEND DB '$'");
добавитьИнструкцию("data ends");
добавитьИнструкцию("stk segment stack");
добавитьИнструкцию("db 256 dup (\"?\")");
добавитьИнструкцию("stk ends");
добавитьИнструкцию("code segment para public \"code\"");
добавитьИнструкцию("main proc");
добавитьИнструкцию("assume cs:code,ds:data,ss:stk");
добавитьИнструкцию("mov ax,data");
добавитьИнструкцию("mov ds,ax");
}
Объявление завершения основной процедуры:
декларироватьЗавершениеОсновнойПроцедуры()
{
добавитьИнструкцию("mov ax,4c00h");
добавитьИнструкцию("int 21h");
добавитьИнструкцию("main endp");
}
Объявление завершения кода:
декларироватьЗавершениеКода()
{
добавитьИнструкцию("code ends");
добавитьИнструкцию("end main");
}
Далее необходимо реализовать объявление переменных в ассемблерном коде.
декларироватьПеременные()
{
УзелСвязанногоСписка<Идентификатор> узел = nameTable.получитьИдентификаторы().Первый;
пока(узел!= НУЛЛ)
{
добавитьИнструкцию(узел.Значение.Имя + " dw 1");
узел = узел.Следующий;
}
}
Последним штрихом будет доработка метода SyntaxAnalyzer.компилировать(). Дополним его вызовом реализованных методов генератора кода.
компилировать()
{
CodeGenerator.декларироватьСегментДанных();
LexicalAnalyzer.инициализировать();
разобратьОбъявлениеПеременных();
CodeGenerator.декларироватьПеременные();
CodeGenerator.декларироватьСегментыСтекаИКода();
проверитьЛексему(Lexems.ПереносСтроки);
если(LexicalAnalyzer.текущаяЛексема == Lexems.Begin)
LexicalAnalyzer.разобратьСледующуюЛексему();
|
|
LexicalAnalyzer.разобратьСледующуюЛексему();
LexicalAnalyzer. разобратьПоследовательностьИнструкций();
проверитьЛексему(Lexems.End);
CodeGenerator.декларироватьЗавершениеОсновнойПроцедуры();
CodeGenerator.декларироватьЗавершениеКода();
}
Замечания по коду:
1) Вывод ассемблерного кода на форму необходимо реализовать самостоятельно.
Примеры кода:
Файлы CodeGenerator.cs, SyntaxAnalyzer.cs в проекте.