
- •Лексический анализатор………………………………………………………….…..11
- •Синтаксический анализатор…………………………………………….……………15
- •Генератор кода………………………………………………………………..………..18
- •Теоретическая часть.
- •Транслятор.
- •Лексический анализатор.
- •Синтаксический анализатор.
- •1.4 Генератор кода.
- •Практическая часть.
- •2.1 Синтаксис языка в бнф. Терминалы, нетерминалы, начальный символ и правила.
- •2.2 Каркас транслятора.
- •2.3 Лексический анализатор.
- •2.3 Синтаксический анализатор.
- •2.4 Генератор кода.
- •Тестирование приложения.
2.4 Генератор кода.
LIT const – поместить константу в вершину стека.
LOAD n – поместить переменную, размещенную по адресу n в вершину
стека.
STO n – запись значения из вершины стека по адресу n (присваивание).
JMP k – безусловный переход к команде, расположенной по адресу k.
JEQ k – переход к команде, расположенной по адресу k в случае
равенства двух верхних элементов стека.
JLT k – переход к команде, расположенной по адресу k, если число в вершине стека меньше следующего за ним числа стека.
JLE k – переход к команде, расположенной по адресу k, если число в вершине стека меньше или равно следующему за ним числу стека.
JGT k – переход к команде, расположенной по адресу k, если число в вершине стека больше следующего за ним числа стека.
JGE k – переход к команде, расположенной по адресу k, если число в вершине стека больше или равно следующему за ним числу стека.
JNE k – переход к команде, расположенной по адресу k в случае неравенства двух верхних элементов стека.
ADR – содержимое регистра адреса данных помещается в вершину стека.
STAD – содержимое вершины стека помещается в регистр адреса данных.
ADD – сложение двух верхних элементов стека, результат помещается в вершину стека.
MUL – умножение двух верхних элементов стека, результат помещается в вершину стека.
SUB – вычитание элемента в вершине стека из следующего за ним элемента стека, результат помещается в вершину стека.
DIV – деление на элемент в вершине стека следующего за ним
элемента стека, результат помещается в вершину стека. |
||||||||||||||
AND |
– логическое |
"И" |
(логическое |
умножение) |
двух |
верхних |
||||||||
элементов стека, результат помещается в вершину стека. |
|
|
||||||||||||
OR |
– логическое |
"ИЛИ" |
(логическое |
сложение) |
двух |
верхних |
||||||||
элементов стека, результат помещается в вершину стека. |
|
|
DIV – деление на элемент в вершине стека следующего за ним
XOR – сложение по модулю 2 двух верхних элементов стека, результат помещается в вершину стека. NOT – знаковая инверсия элемента в вершине стека
NOL – поразрядная логическая инверсия элемента в вершине стека. NOP – пустая операция .
Так как наш транслятор является однопроходным, методы генератора кода будут вызываться из методов синтаксического анализатора. По мере выполнения синтаксического разбора будет генерироваться и ассемблерный код. Для удобства будем хранить его в массиве строк.
/// <summary>
/// Генератор кода
/// </summary>
public static class CodeGenerator
{
private const int MAX_NUMBER_STRINGS = 1000;
private static string[] code = new string[MAX_NUMBER_STRINGS];
private static int codePointer = 0;
private static int countLabels = 0;
/// <summary>
/// Добавить инструкцию
/// </summary>
/// <param name="instraction"></param>
public static void AddInstruction(string instraction)
{
code[codePointer++] = instraction;
}
/// <summary>
/// Добавить метку
/// </summary>
public static void AddLabel()
{
countLabels++;
}
/// <summary>
/// Вернуть текущую метку
/// </summary>
/// <returns></returns>
public static string GetCurrentLabel()
{
return "label" + countLabels.ToString();
}
/// <summary>
/// Описать сегмент данных
/// </summary>
public static void DeclairDataSegment()
{
AddInstruction("data segment para public \"data\"");
}
/// <summary>
/// Описать сегмент стэка и кода
/// </summary>
public static void DeclairSegmentOfStackAndCode()
{
AddInstruction("PRINT_BUF DB ' ' DUP(10)");
AddInstruction("BUFEND DB '$'");
AddInstruction("data ends");
AddInstruction("stk segment stack");
AddInstruction("db 256 dup (\"?\")");
AddInstruction("stk ends");
AddInstruction("code segment para public \"code\"");
AddInstruction("main proc");
AddInstruction("assume cs:code,ds:data,ss:stk");
AddInstruction("mov ax,data");
AddInstruction("mov ds,ax");
}
/// <summary>
/// Описать конец главной процедуры
/// </summary>
public static void DeclairEndMainProc()
{
AddInstruction("mov ax,4c00h");
AddInstruction("int 21h");
AddInstruction("main endp");
}
/// <summary>
/// Описать коней сегмента кода
/// </summary>
public static void DeclairCodeEnd()
{
AddInstruction("code ends");
AddInstruction("end main");
}
/// <summary>
/// Описать переменные
/// </summary>
public static void DeclairVariables()
{
LinkedListNode<Identificator> node = NameTable.GetIdentificators.First;
while (node != null)
{
if (SyntaxAnalyzer.Type == tType.Int)
AddInstruction(node.Value.name + " dw 1");
if (SyntaxAnalyzer.Type == tType.LInt)
AddInstruction(node.Value.name + " dl 1");
node = node.Next;
}
}
/// <summary>
/// Опичать процедуру вывода на печать
/// </summary>
public static void DeclairPrint()
{
AddInstruction("PRINT PROC NEAR");
AddInstruction("MOV CX, 10");
AddInstruction("MOV DI, BUFEND - PRINT_BUF");
AddInstruction("PRINT_LOOP:");
AddInstruction("MOV DX, 0");
AddInstruction("DIV CX");
AddInstruction("ADD DL, '0'");
AddInstruction("MOV [PRINT_BUF + DI - 1], DL");
AddInstruction("DEC DI");
AddInstruction("CMP AL, 0");
AddInstruction("JNE PRINT_LOOP");
AddInstruction("LEA DX, PRINT_BUF");
AddInstruction("ADD DX, DI");
AddInstruction("MOV AH, 09H");
AddInstruction("INT 21H");
AddInstruction("RET");
AddInstruction("PRINT ENDP");
}
Для реализации арифметических (и не только) операций в языке ассемблер используется работа с регистровой памятью. Так, чтобы поместить значение (числовую константу или переменную) в регистр, используется инструкция mov. Команды для арифметических операций приведены в таблице ниже.
Операция |
Инструкция ассемблера |
+ |
add |
- |
sub |
* |
mul |
/ |
div |
Таким образом, код для выражения a + b будет выглядеть следующим образом:
mov ax, a
mov bx b
add ax, bx
Результат этой последовательности команд будет сохранен в регистре ax. Но в ситуации со сложными выражениями необходимо где-то сохранять промежуточные результаты. Для этих целей мы будем использовать ассемблерный стек: инструкция push - помещение значения регистра на верхушку стека, pop – извлечение из стека в регистр. После каждой атомарной операции мы будем сохранять результат в стек, чтобы не потерять его.