Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.

Предоплата всего

Подписываем
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Предоплата всего
Подписываем
Омский государственный технический университет
Кафедра ИВТ
Курсовая работа
по дисциплине:
«Системное программное обеспечение»
Выполнил: Мусайбеков К.К.
гр. ИВТ-415
Проверил: Флоренсов А.Н.
Омск 2008
Задание
Разработать на основе предикативного анализатора компилятор c языка программирования, который описательно задается следующими определениями
array имя_массива [ размерность_массива ];
где размерность_массива задаётся целым числом;
выражение операция_сравнения выражение
для выдачи результатов служат операторы:
write выражение [ , выражение, …]
для ввода оператор
read переменная [ , переменная, …]
Основным результатом работы компилятора должен быть либо исполняемый файл, либо файл объектного кода - в зависимости от опции вызова этого компилятора;
в качестве дополнительных файлов, выдаваемых компилятором при указании опций запроса для них, должны формироваться ассемблерный файл промежуточного представления и файл таблицы символических имен.
Рекомендуется проводить разработку постепенно, реализуя до получения результирующего файла сначала первые пункты его задания, затем добавляя к ним группу следующих и т.д.
Текст программы
Файл main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define IF 256 //Определяем константы, используемые далее по ходу программы
#define ELSE 257
#define REPEAT 258
#define UNTIL 259
#define READ 260
#define WRITE 261
#define START 262
#define END 263
#define INT 264
#define ARRAY 265
#define ASSUME 266
#define IDENT 267
#define NUMBER 268
#define OPERATOR 269
#define OPENBRACKET 270
#define CLOSEBRACKET 271
#define DOTCOMMA 272
#define CONTINUE 273
#define OPENSQBRACKET 274
#define CLOSESQBRACKET 275
#define BREAK 276
#define COMMA 277
#define FOR 278
#define WHILE 279
#define WRITELN 280
#define PROCEDURE 281
#define HOSTWHILE 4
#define HOSTFOR 3
#define HOSTREPEAT 2
#define HOSTOTHER 1
#define ASMEND_IF "%s_%ld:\n" //Определяем асмовские конструкции
#define ASMIF_ELSE " jmp %s_%ld\n%s_%ld:\n" //на базе которых будет строится программа
#define ASMSTATEMENT " pop dword [%s+4*%ld]\n"
#define ASMSTATEMENT1 " mov edi, [%s+4*%ld]\n pop dword [%s+4*edi]\n"
#define ASMNEGATE " pop eax\n neg eax\n push eax\n"
#define ASMASSUME_MEM " push dword [%s+4*%ld]\n"
#define ASMASSUME_NUM " push dword %ld\n"
#define ASMASSUME_MEM1 " mov edi,[%s+4*%ld]\n push dword [%s+4*edi]\n"
#define ASMCONDITION " pop ebx\n pop eax\n cmp eax,ebx\n %s near %s_%ld\n"
#define ASMREPEAT "repeat_%ld:\n"
#define ASMJMP " jmp near %s_%ld\n"
#define ASMUNTIL "until_%ld:\n"
#define ASMREAD " call stdread\n mov [%s+4*%ld],eax\n"
#define ASMREAD1 " call stdread\n mov edi,[%s+4*%ld]\n mov [%s+4*edi],eax\n"
#define ASMLN " call lfwrite\n"
#define ASMWRITE " pop eax\n call itox\n"
#define ASMFOR "for_%ld:\n"
#define ASMENDFOR " jmp near for_%ld\nendfor_%ld:\n"
#define ASMWHILE "while_%ld:\n"
#define ASMENDWHILE " jmp while_%ld\nendwhile_%ld:\n"
#define ASMPROCSTART "%s:\n push ebp\n mov ebp, esp\n"
#define ASMPROCEND " mov esp,ebp\n pop ebp\n ret\n\n\n"
#define ASMCALL " call %s\n"
#define ASMVARIABLE " mov eax, [ebp + %ld]\n mov [%s], eax\n"
#define ASMVARIABLE1 " push dword %s\n"
char CodeHeader[] = "EXTERN stdexit, stdread, itox, lfwrite\nsegment .text USE32 CLASS=CODE\n\n..start:\n call main\n call stdexit\n", //Заголовок кода
DataHeader[] = " segment .data USE32\n";
char MathOpAsm[8][100] = {
" pop ebx\n pop eax\n add eax,ebx\n push eax\n",
" pop ebx\n pop eax\n sub eax,ebx\n push eax\n",
" pop ebx\n pop eax\n mul ebx\n push eax\n",
" pop ebx\n pop eax\n xor edx,edx\n div ebx\n push eax\n",
" pop ebx\n pop eax\n xor edx,edx\n div ebx\n push edx\n",
" pop ebx\n mov eax,1\n add eax,ebx\n push eax\n",
" pop eax\n mov ebx,1\n sub eax,ebx\n push eax\n",
" pop eax\n mov ebx,eax\n mul ebx\n push eax\n"};
char OpMath[8][3] = {"+", "-", "*", "/", "%", "++", "--", "**"},
StrLogic[6][4] = {"je", "jl", "jle", "jg", "jge", "jne"},
OpLogic[6][3] = {"!=", ">=", ">", "=<", "<", "=="};
typedef struct { //В данной структуре будут храниться все токены и их значения
int token;
char *value;
int line;
} TOKEN;
typedef struct{ //В данной структуре будут храниться все
char *name; //зарегестрированные переменные и процедуры
int size;
} datahold;
struct { //Здесь хранятся зарезервированые слова
char name[32];
int type;
} KeywordList[23] = {
{"if", IF},
{"else", ELSE},
{"repeat", REPEAT},
{"until", UNTIL},
{"read", READ},
{"write", WRITE},
{"{", START},
{"}", END},
{"int", INT},
{"array", ARRAY},
{"=", ASSUME},
{"(", OPENBRACKET},
{")", CLOSEBRACKET},
{";", DOTCOMMA},
{"continue", CONTINUE},
{"[", OPENSQBRACKET},
{"]", CLOSESQBRACKET},
{"break", BREAK},
{",", COMMA},
{"for", FOR},
{"while", WHILE},
{"writeln", WRITELN},
{"procedure",PROCEDURE}
};
unsigned int count = 0; //Счетчик
FILE *inpfile, //Хэндл входного файла
*outfile; //Хэндл выходного файла
int token = 0, //Текущий токен
tokennum = 0, //Общее количество токенов
curtoken = 0, //Номер текущий токен
datanum = 0, //Общее количество зарегестрированных переменных
procnum = 0, //Общее количество процедур
ifcounter = 0, //Количсетво ифоф
rcounter = 0, //Количество репитов
wcounter = 0, //Количество вайлов
fcounter = 0; //Количество форов
char ops[] = {'+', '-', '*', '/', '<', '>', '^', '=', '!', '%', 0}, //Все операторы
outname[128]; //Путь к файлу
TOKEN *tarr = 0; //Указатель на массив с токенами
datahold *data = 0, //Указатель на массив с зарегенными данными
*proch = 0; //Указатель на массив с зареганными процедурами
long FileSize (FILE *stream); //Описываем заголовки функций
int ScanList (char *buffer);
int LexAnalyz (char *buffer);
int Logic (void);
int Condition (char *id_str,int cnt);
int GetOp (void);
int GetPar (char **name,int *index,int *value, char **name1);
int Expression (void);
int Factor (void);
int IdentCheck (datahold *data, int datanum);
int Index (char **name1);
int Variable (char **name, int *index, char **name1);
int Code (int host, int count);
int Statement (int host, int count);
int DataOut (FILE *outfile);
int Parse (void);
int RunNasmW (void);
int RunTlink32 (void);
int OtherOut (void);
int RepeatUntil (int rcount);
int IfElse (int ifcount, int host, int count);
int ForDo (int forcounter);
int WhileDo (int rcount);
long FileSize(FILE *stream) { //Функция, опеределяющая размер файла
//Входной параметр: файловый дескриптор
long curpos, length;
curpos = ftell(stream);
fseek(stream, 0L, SEEK_END);
length = ftell(stream);
fseek(stream, curpos, SEEK_SET);
return length;
}
int ScanList (char *buffer) { //Функция, сканирующая массив с ключевыми словами
//и выдающая токен ключевого слова если он найден
int i; //Входной параметр: идентификатор
for (i = 0;i < 23; i++) {
if (!strncmp(buffer, KeywordList[i].name, 32)) {
return KeywordList[i].type;
}
}
return -1;
}
int LexAnalyz(char *buffer) { //Лексический анализатор
//Входной параметр: входной поток символов
int i, //Счетчик
line = 1; //Счетчик линий в файле
char *value, //Указатель на ячейку данных
error[50]; //Строка содержащая ошибку
memset(error, 0, 50);
while (count < strlen(buffer)) { //Перебираем все символы входного потока
i = 0;
if (buffer[count] == '\n') { //Если встретили перевод строки, тогда
line++; //увеличиваем номер строки и переходим к следующему
count++; //символу
continue;
}
if (isdigit(buffer[count])) { //Если встретили цифру, тогда пытаемся выделить все число
value = malloc(128);
while (isdigit(buffer[count])) {
value[i++] = buffer[count];
if (i > 126) {
strcpy(error, "Ошибка: при лексическом анализе - переполнение\n");
break;
}
count++;
}
value[i] = 0; //Последний символ равен 0
token = NUMBER; //Токен - число
} else {
if (isalpha(buffer[count])) { //Если встретили символ, тогда выделяем весь идентификатор
value = malloc(128);
while (isalpha(buffer[count]) || isdigit(buffer[count])) { //Последующие символы могут быть и цифрой
value[i++] = buffer[count];
if (i > 126) {
strcpy(error, "Ошибка: при лексическом анализе - переполнение\n");
break;
}
count++;
}
value[i] = 0; //Последний символ 0
if ((i = ScanList(value)) == -1) { //Проверяем, ключевое ли это слово
token = IDENT; //Токен - идентификатор
} else {
token = i; //Токен - ключевое слово
}
} else {
if (strchr(ops, buffer[count])) { //Если это операция, тогда выделяем ее
value = malloc(128);
while (strchr(ops, buffer[count])) {
value[i++] = buffer[count];
if (i > 126) {
strcpy(error, "Ошибка: при лексическом анализе - переполнение\n");
break;
}
count++;
}
value[i] = 0;
if ((i = ScanList(value)) == -1) { //Не знак ли это равенства?
token = OPERATOR; //Токен - оператор
} else {
token = i; //Токен - знак равенства
}
} else { //Проверяем, не скобки ли это {} или не ; или не [] или не ,
value = malloc(2); //или не ()
value[0] = buffer[count];
value[1] = 0;
if ((i = ScanList(value)) == -1) { //Если нет, то продолжаем далее, иначе токен - ключевое слово
count ++;
continue;
} else {
token = i;
count++;
}
}
}
} //Конец while
tarr = realloc(tarr, (tokennum + 2) * sizeof(TOKEN)); //Выделяем память, чтобы записать очередной токен
tarr[tokennum].value = value;
tarr[tokennum].token = token;
tarr[tokennum].line = line;
tokennum++;
}
if (strlen(error) == 0) { //Если ошибок нет, то завершаем работу анализатора
printf("Лексческий анализ успешно выполнен\n");
return 0;
} else {
printf(error);
return -1;
}
}
int Logic() { //Функция, идентифицирующая текущую логическую операцию
int i, //Счетчик
retv = -1; //Возвращаемое значение
if (strlen(tarr[curtoken].value) > 2) {
printf("Ошибка %d: Неверная логическая операция %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
for (i=0; i < 6; i++) { //Ищем текущую операцию в массиве операций
if (!strcmp(tarr[curtoken].value, OpLogic[i])) { //Если нашли, то заканчиваем работу и возвращаем номер
retv = i; //этой операции в нашем массиве
break;
}
}
curtoken++;
if (retv == -1) {
printf("Ошибка %d: Такая логическая операция не найдена %s\n", tarr[curtoken - 1].line, tarr[curtoken - 1].value);
return -1;
}
return retv;
}
int Condition(char *id_str,int cnt) { //Функция, выделяющая логическое условие в ифе и репите
//Входные параметры: метка, которую записываем и ее номер
int op; //Номер логической операции
if (tarr[curtoken].token != OPENBRACKET) {
printf("Ошибка %d: Условия должно начинаться со скобки %s\n", tarr[curtoken].line, tarr[curtoken].line);
return -1;
}
curtoken++;
if (Expression()) { //Далее должно идти выражение
return -1;
}
if ((op = Logic()) == -1) { //Потом знак сравнения
return -1;
}
if (Expression()) { //Потом еще одно выражение
return -1;
}
if (tarr[curtoken].token != CLOSEBRACKET) {
printf("Ошибка %d: Условие должно заканчиваться скобкой %s\n", tarr[curtoken].line, tarr[curtoken].line);
return -1;
}
curtoken++;
fprintf(outfile, ASMCONDITION, StrLogic[op], id_str, cnt);
return 0;
}
int GetOp() { //Функция, идентифицирующая текущую математическую операцию
int i,
retv = -1; //Возвращаемое значение
if (strlen(tarr[curtoken].value) > 2) { //Если длина операции больше 2, тогда ошибка
return -1;
}
for (i=0; i < 8; i++) { //Ищем текущую операцию в массиве операций
if (!strcmp(tarr[curtoken].value, OpMath[i])) {
retv = i;
break;
}
}
if (retv == -1) { //Если не нашли, тогда возвращаем ошибку
return -1;
}
curtoken++;
return retv;
}
int GetPar(char **name, int *index, int *value, char **name1) { //Функция, возвращающая параметры переменной из строки
if (tarr[curtoken].token == NUMBER) { //Если переменная - число, тогда возвращаем имя переменной
*name1 = 0;
*name = 0;
*index = 0;
sscanf(tarr[curtoken].value, "%ld", value);
curtoken ++;
return 0;
} else { //Иначе возвращаем имя и индекс
return Variable(name, index, name1);
}
}
int Expression() { //Функция, разбирающая арифметическое выражение
int op; //Номер операции
if (Factor()) { //В левой части - выражение
return -1;
}
while (1) { //Завускаем бесконечный цикл, т.к. в правой части
//может бесконечное число операндов
if ((op = GetOp()) == -1) { //Между операндами математическая операция
return 0;
}
if (op < 5) { //Если код операции меньше 5, значит это одна из простых
if (Factor()) { //операций, после которой идет выражение
return -1;
}
} else {
printf("Ошибка %d: Данная операция недопустима %s\n", tarr[curtoken - 1].line, tarr[curtoken - 1].value);
return -1;
}
fprintf(outfile, MathOpAsm[op]); //Записываем результат выполнения операции
}
}
int Factor() { //Функция, разбирающая одну из частей
//выражения (левую или правую)
char *name = 0, //Имя переменной
*name1 = 0; //Имя индексной переменной
int index, //Индекс переменной
num; //Значение числа
switch (tarr[curtoken].token) { //Анализируем текущий токен
case OPENBRACKET: //Выражение начинается со скобки (, значит дальше
curtoken ++; //идет еще одно выражение
if (Expression() == -1) {
return -1;
}
if (tarr[curtoken].token != CLOSEBRACKET) {
printf("Ошибка %d: Нет закрывающей скобки %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
return 0;
case OPERATOR: //Выражение начинается со знака (-)
curtoken ++;
if (Expression() == -1) {
return -1;
}
fprintf(outfile, ASMNEGATE); //Записываем инверсию операнда
return 0;
case NUMBER: //Выражение начинается с переменной или числа
case IDENT:
if (GetPar(&name, &index, &num, &name1) == -1) { //Получаем параметры переменной или числа
return -1;
}
if (name==NULL) { //Если имени нет, тогда это число, иначе переменная
fprintf(outfile, ASMASSUME_NUM, num);
} else {
if (name1 == 0) { //Проверяем индекс - число это или переменная
fprintf(outfile, ASMASSUME_MEM, name, index);
} else {
fprintf(outfile, ASMASSUME_MEM1, name1, index, name);
}
}
return 0;
default: //По умолчанию ошибка
return 0;
}
}
int IdentCheck(datahold *data, int datanum) { //Проверка переменной на зарегенность. Входные параметры
//структура, в которой ищем и размер этой структуры
int i; //Счетчик
for (i=0; i < datanum; i++) { //Поиск в массиве зарегенных переменных входной переменной
if (!strcmp(data[i].name, tarr[curtoken].value)) {
return i;
}
}
return -1;
}
int Index(char **name1) { //Определение индекса переменной. Входной параметр
//указатель на идентификатор
int size = 0; //Размер переменной
char *name = 0; //Имя переменной
if (tarr[curtoken].token != OPENSQBRACKET) {
printf("Ошибка %d: Индексная переменная должна начинаться со скобки [ %\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
switch (tarr[curtoken].token) {
case NUMBER: //В скобках находится число
sscanf(tarr[curtoken].value, "%ld", &size); //Преобразуем число из строки
curtoken ++;
break;
case IDENT: //В скобках находится идентификатор
if (Variable(name1, &size, &name) != -1) { //Выделяем его параметры
if (name == 0) { //Если это индексная переменная, тогда ошибка
break;
}
}
default:
printf("Ошибка %d: В скобках должно быть число %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
if (tarr[curtoken].token != CLOSESQBRACKET) {
printf("%d: Индексная переменная должна заканчиваться скобкой ] %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
return size;
}
int Variable(char **name, int *index, char **name1) { //Идентификация переменной индексная она или нет. Входные
//параметры: имя переменной, индекс и имя индекса
int i;
char *name2; //Счетчик
if (tarr[curtoken].token != IDENT) {
printf("Ошибка %d: Переменная должна начинаться с буквы %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
*name = tarr[curtoken].value; //Запоминаем имя переменной
if ((i = IdentCheck(data, datanum)) == -1) {
printf("Внимание %d: Переменная не идентифицирована %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
if (tarr[curtoken].token == OPENSQBRACKET) { //Если после ИД идет открывающая скобка, значит это индексная
*index = Index(name1); //переменная. Запоминаем индекс переменной
if (*name1 == 0) {
if (*index >= data[i].size) { //Если индекс превысил допустимые границы, значит ошибка
printf("Ошибка %d: Индекс вышел за допустимые границы\n", tarr[curtoken].line);
return -1;
}
}
} else { //Иначе это число
*index = 0;
*name1 = 0;
}
return 0;
}
int Code(int host, int count) { //Обработка вложенных операторов. Входные переменные: хозяин,
//и номер метки
curtoken++; //Запускаем цикл обработки вложенных операторов
while (1) {
if (Statement(host, count) == -1) { //Обрабатываем вложенные операторы
return -1;
}
if (tarr[curtoken].token == END) { //Если закрывающая скобка }, тогда выход
break;
}
}
curtoken++;
return 0;
}
//Процедура обработки цикла Repeat-Until. Входной параметр
int RepeatUntil(int rcount) { //номер цикла в программе
curtoken ++;
rcounter ++; //Увеличиваем количество циклов на один
fprintf(outfile, ASMREPEAT, rcount); //Записываем стартовую метку
if (Statement(HOSTREPEAT, rcount) == -1) { //Обрабатываем вложенные операторы
return -1;
}
if (tarr[curtoken].token != UNTIL) {
printf("Ошибка %d: После выражения должен идти until %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
if (Condition("repeat", rcount) == -1) { //Обрабатываем условие завершения работы цикла
return -1;
}
if (tarr[curtoken].token != DOTCOMMA) {
printf("Ошибка %d: Пропущена точка с запятой %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
fprintf(outfile, ASMUNTIL, rcount, rcount); //Записываем метку окончания цикла
return 0;
}
int IfElse(int ifcount, int host, int count) { //Процедура обработки условного оператора if-else. Входные
//параметры номер ифа, хозяин и номер метки
curtoken++;
if (Condition("endif", ifcount) == -1) { //Сперва должно идти условие
return -1;
}
ifcounter++;
if (Statement(host, count) == -1) { //Далее блок операторов
return -1;
}
if (tarr[curtoken].token == ELSE) { //Если есть часть else, то обработаем и ее
fprintf(outfile, ASMIF_ELSE, "endelse", ifcount, "endif", ifcount);
curtoken ++;
if (Statement(host, count) == -1) { //Обработка блока операторов
return -1;
}
fprintf(outfile, ASMEND_IF, "endelse", ifcount); //Записываем метки окончания условного оператора
} else {
fprintf(outfile, ASMEND_IF, "endif", ifcount);
}
return 0;
}
int ForDo(int forcount) { //Процедура обработки цикла for. Входные параметры:
//номер цикла
int op; //Номер операции
curtoken++;
fcounter ++; //Увеличиваем количество for`ов
if (tarr[curtoken].token != OPENBRACKET) {
printf("Ошибка %d: После for должна идти открывающая скобка %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
curtoken ++; //Сначала идет начальное условие
if ((tarr[curtoken].token != IDENT) || (Statement(HOSTOTHER, forcount) == -1)) {
return -1;
}
fprintf(outfile, ASMFOR, forcount); //Далее идет условие выхода их цикла
if (Expression()) { //Потом должно идти выражение
return -1;
}
if ((op = Logic()) == -1) { //Потом знак сравнения
return -1;
}
if (Expression()) { //Потом еще одно выражение
return -1;
}
fprintf(outfile, ASMCONDITION, StrLogic[op], "endfor", forcount);
if (tarr[curtoken].token != DOTCOMMA) {
printf("Ошибка %d: Не хватает точки с запятой %s\n", tarr[curtoken].line, tarr[curtoken-1].value);
return -1;
}
curtoken++; //Потом выражение
if ((tarr[curtoken].token != IDENT) || (Statement(HOSTOTHER, forcount) == -1)) {
return -1;
}
if (tarr[curtoken].token != CLOSEBRACKET) {
printf("Ошибка %d: Нет закрывающей скобки %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
curtoken++;
if (Statement(HOSTFOR, forcount) == -1) { //Потом обработка блока операторов
return -1;
}
fprintf(outfile, ASMENDFOR, forcount, forcount); //Запись в файл метки окончания цикла
return 0;
}
int WhileDo(int rcount) { //Процедура обработки цикла while
//Входной параметр номер цикла
curtoken ++;
wcounter ++;
fprintf(outfile, ASMWHILE, rcount);
if (Condition("endwhile", rcount) == -1) { //Обрабатываем условие завершения цикла
return -1;
}
if (Statement(HOSTWHILE, rcount) == -1) { //Обрабатываем вложенные операторы
return -1;
}
fprintf(outfile, ASMENDWHILE, rcount, rcount);
return 0;
}
int Statement(int host, int count) { //Основная функция синтаксического анализатора
//Входные параметры: хозяин и номер метки
int type, //Тип токена
index, //Индекс переменной
op; //Номер операции
char *name = 0, //Имя индексной переменной
*name1 = 0; //Имя индекса, если там переменная
switch (tarr[curtoken].token) {
//Операторные скобки {}
case START:
return Code(host, count);
case IF: //Условный оператор if
if (IfElse(ifcounter, host, count) == -1) {
return -1;
}
return 0;
case REPEAT: //Оператор цикла repeat
if (RepeatUntil(rcounter) == -1) {
return -1;
}
return 0;
case FOR: //Оператор цикла for
if (ForDo(fcounter) == -1) {
return -1;
}
return 0;
case WHILE: //Оператор цикла while
if (WhileDo(wcounter) == -1) {
return -1;
}
return 0;
case INT: //Инициализация переменной
case ARRAY:
type = tarr[curtoken].token; //Сразу можем обработать несколько переменных
while (1) {
curtoken ++;
if (tarr[curtoken].token != IDENT) {
printf("Ошибка %d: После int и array должно идти имя переменной %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
if (IdentCheck(data, datanum) != -1) {
printf("Ошибка %d: Данная переменная уже зарегестрирована %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
name = tarr[curtoken].value;
curtoken++;
switch (type) { //Проверяем тип переменной
case INT: //Целый тип
index = 1;
break;
case ARRAY: //Или массив
if ((index = Index(&name)) == -1) {
return -1;
}
break;
}
if ((data = realloc(data, (datanum + 1) * sizeof(datahold))) == 0) {
printf("Недостаточно памяти");
return -1;
}
data[datanum].name = name; //Записываем в таблицу переменных
data[datanum].size = index; //новую переменную
datanum ++;
if (tarr[curtoken].token == COMMA) { //Проверяем, есть ли запятая, если да, то обработаем еще одну
continue; //переменную
}
break;
}
if (tarr[curtoken].token != DOTCOMMA) {
printf("%d: Нет точки с запятой %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
return -1;
}
curtoken++;
return 0;
case IDENT: //Переменная
if (Variable(&name, &index, &name1) == -1) { //Определяем индекс и имя переменной
curtoken ++; //Может быть это процедура
if (tarr[curtoken].token != OPENBRACKET) {
printf("%d: Нет открывающей скобки %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
return -1;
}
while (1) { //Обрабатываем параметры процедуры
curtoken ++;
if ((tarr[curtoken].token != IDENT) && tarr[curtoken].token != NUMBER) {
printf("Внимание %d: Возможно в скобках переменная %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
break;
}
if (Expression() == -1) { //Параметром может быть выражение
return -1;
}
if (tarr[curtoken].token != COMMA) {
printf("Внимание %d: Возможно в скобках запятая %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
break;
}
}
if (tarr[curtoken].token != CLOSEBRACKET) {
printf("%d: Нет закрывающей скобки %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
if (tarr[curtoken].token != DOTCOMMA) {
printf("%d: Нет точки с запятой %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
fprintf(outfile, ASMCALL, name);
return 0;
}
switch (tarr[curtoken].token) { //Проверка на тип операции ( ++ -- **)
case ASSUME: //далее идет = выражение
curtoken ++;
if (Expression() == -1) { //Потом идет выражение
return -1;
}
break;
case OPERATOR: //далее идет = (++ или -- или **)
if (name1 == 0) {
fprintf(outfile, ASMASSUME_MEM, name, index);
} else {
fprintf(outfile, ASMASSUME_MEM1, name1, index, name);
}
if ((op = GetOp()) != -1) { //Между ними математическая операция
if (op > 4) {
fprintf(outfile, MathOpAsm[op]);
break;
}
}
default:
printf("%d: Отсутствует знак присваивания %s %d\n", tarr[curtoken].line, tarr[curtoken].value, curtoken);
return -1;
}
if (tarr[curtoken].token != DOTCOMMA) {
printf("%d: Нет точки с запятой %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
if (name1 == 0) {
fprintf(outfile, ASMSTATEMENT, name, index);
} else {
fprintf(outfile, ASMSTATEMENT1, name1, index, name);
}
return 0;
case READ: //Операторы ввода и вывода данных
case WRITE:
type = tarr[curtoken].token;
while (1) { //Можно вывести или ввести несколько выражений
curtoken ++;
switch (type) {
case READ:
if (Variable(&name, &index, &name1) == -1) {
return -1;
}
if (name1 == 0) {
fprintf(outfile, ASMREAD, name, index);
} else {
fprintf(outfile, ASMREAD1, name1, index, name);
}
break;
case WRITE:
if (Expression() == -1) { //Опеределяем выражение и выводим его
return -1;
}
fprintf(outfile, ASMWRITE);
break;
}
if (tarr[curtoken].token == COMMA) { //Проверяем, есть ли запятая, если да, то обработаем еще одну
continue; //переменную
}
break;
}
if (tarr[curtoken].token != DOTCOMMA) {
printf("%d: Нет точки с запятой %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
return 0;
case WRITELN: //Перевод строки
fprintf(outfile, ASMLN);
curtoken ++;
return 0;
case DOTCOMMA: //Пустой оператор ;
curtoken ++;
return 0;
//Оператор continue
case CONTINUE: //Проверяем какой цикл вызывал и записываем его метку
switch (host) {
case HOSTWHILE:
fprintf(outfile, ASMJMP, "while", count);
break;
case HOSTREPEAT:
fprintf(outfile, ASMJMP, "repeat", count);
break;
case HOSTFOR:
fprintf(outfile, ASMJMP, "for", count);
break;
default:
printf("%d: Оператор continue должен находиться внутри цикла\n", tarr[curtoken - 1].line);
return -1;
}
curtoken ++;
if (tarr[curtoken].token != DOTCOMMA) {
printf("%d: Нет тоqчки с запятой %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
return 0;
case BREAK: //Оператор break
switch (host) { //Проверяем какой цикл вызывад и записываем его метку
case HOSTWHILE:
fprintf(outfile, ASMJMP, "endwhile", count);
break;
case HOSTREPEAT:
fprintf(outfile, ASMJMP, "until", count);
break;
case HOSTFOR:
fprintf(outfile, ASMJMP, "endfor", count);
break;
default:
printf("%d: Оператор break должен находиться внутри цикла\n", tarr[curtoken - 1].line);
return -1;
}
curtoken ++;
if (tarr[curtoken].token != DOTCOMMA) {
printf("%d: Нет точки с запятой %s\n", tarr[curtoken - 1].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
return 0;
default:
printf("%d: Неизвестный оператор %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
}
int DataOut(FILE *outfile) { //Запись сегмента данных
int i;
fprintf(outfile,"%s", DataHeader);
for (i=0; i < datanum; i++){
if (data[i].size == 1) {
fprintf(outfile,"%s dd 0\n", data[i].name);
} else {
fprintf(outfile,"%s times %d dd 0\n", data[i].name, data[i].size);
}
}
fclose(outfile);
return 0;
}
int Parse() { //Инициализация синтаксического анализатора
int tmp = 0, //Переменная, считающая количество переменных процедуры
tmp1 = 0, //Проверка на наличие имени main
count = 0; //Счетчик
while (curtoken < tokennum) { //Обрабатываем все токены
if (tarr[curtoken].token != PROCEDURE) {
printf("%d: Сначала идет объявление процедуры %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
if (tarr[curtoken].token != IDENT) {
printf("%d: Процедура должна носить имя %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
if (IdentCheck(proch, procnum) != -1) {
printf("%d: Такая процедура уже зарегестрирована %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
if ((proch = realloc(proch, (procnum + 1) * sizeof(datahold))) == 0) { //Записываем в таблицу переменных
printf("Недостаточно памяти"); //новую переменную
return -1;
}
proch[procnum].name = tarr[curtoken].value;
curtoken ++;
fprintf(outfile, ASMPROCSTART, proch[procnum].name);
if (!strcmp(proch[procnum].name, "main")) { //Если имя main, значит tmp = 1
tmp1 = 1;
}
if (tarr[curtoken].token != OPENBRACKET) {
printf("%d: После procedure должна идти открывающая скобка %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
while (1) { //Обрабатываем переменные процедуры
tmp = datanum;
curtoken ++;
if (tarr[curtoken].token == INT) { //Тип переменной
if (Statement(1, 0) == -1) {
return -1;
}
curtoken --;
} else {
break;
} //Записываем несколько переменных
while (tmp != datanum) {
count ++;
fprintf(outfile, ASMVARIABLE, 4 + 4 * (datanum - tmp), data[tmp]);
tmp ++;
}
}
proch[procnum].size = count;
procnum ++;
if (tarr[curtoken].token != CLOSEBRACKET) {
printf("%d: Нет закрывающей скобки %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
curtoken ++;
if (tarr[curtoken].token != START) {
printf("%d: Нет открывающей скобки %s\n", tarr[curtoken].line, tarr[curtoken].value);
return -1;
}
if ((Statement(1, 0) == -1)) { //Обрабатываем тело процедуры
return -1;
}
fprintf(outfile, ASMPROCEND); //Записываем конец процедуры
count = 0;
}
if (tmp1 == 0) {
printf("Ошибка: Главная процедура должна называться main\n");
return -1;
}
DataOut(outfile);
printf("Синтаксический анализ выполнен успешно\n");
return 0;
}
int RunNasmW() { //Запуск насма
int retc;
char command[256];
sprintf(command,"nasmw -f obj %s", outname);
retc = system(command);
return retc;
}
int RunTlink32() { //Запуск компоновщика
int retc;
char command[256],
*tmp;
tmp = malloc(128);
strncpy(tmp, outname, strlen(outname) - 3);
strcat (tmp, "obj");
sprintf(command, "tlink32 %s,,,import32 mylib", tmp);
retc = system(command);
return retc;
}
int OtherOut() { //Запись таблицы символических имен в файл
char tmp[128];
memset(tmp, 0, 128);
strncpy(tmp, outname, strlen(outname) - 3);
strcat (tmp, "er");
if ((outfile = fopen(tmp, "w+")) == NULL) {
printf("Невозможно создать файл для записи таблицы символических имен");
return 0;
}
fprintf(outfile, " Value | Token | Line\n");
for (count = 0; count < tokennum; count++) {
fprintf(outfile, "%10.10s %5.1d %5.1d\n", tarr[count].value, tarr[count].token, tarr[count].line);
}
fprintf(outfile, "\n\n Name | Size\n");
for (count = 0; count < datanum; count++) {
fprintf(outfile, "%10.10s %5.1d\n", data[count].name, data[count].size);
}
fprintf(outfile, "\n\n Name | Size\n");
for (count = 0; count < procnum; count++) {
fprintf(outfile, "%10.10s %5.1d\n", proch[count].name, proch[count].size);
}
fclose(outfile);
return 0;
}
int main(int argc, char* argv[]) { //Основная процедура
char *buffer;
int actlen;
if (argc == 1) {
printf("Введите имя файла\n");
return 0;
}
if ((inpfile = fopen(argv[1], "rb")) == NULL) {
printf("Введите корректное имя файла\n");
return 0;
}
buffer = malloc(FileSize(inpfile) + 1);
strncpy(outname, argv[1], strlen(argv[1])-strlen(strrchr(argv[1], '.')) + 1);
strcat (outname, "asm");
if ((outfile = fopen(outname, "w+")) == NULL) {
printf("Невозможно создать выходной файл");
return 0;
}
memset(buffer, 0, strlen(buffer));
actlen = fread(buffer, 1, FileSize(inpfile), inpfile);
if (actlen == 0) {
printf("Размер файла равен 0");
exit(0);
}
strncpy(buffer, buffer, FileSize(inpfile));
fclose(inpfile);
if (LexAnalyz(buffer) == -1) {
return -1;
}
fprintf(outfile, CodeHeader);
if (Parse() == -1) {
OtherOut();
return -1;
}
RunNasmW();
RunTlink32();
OtherOut();
return 0;
}
файл axtoi.asm
GLOBAL axtoi
SEGMENT code USE32 CLASS=CODE
axtoi:
xor edi,edi
comp: ;compare chars input string with digit
cmp [eax+edi],byte '9'
jg endcomp
cmp [eax+edi],byte '0'
jl endcomp
inc edi
cmp [eax+edi],byte 0
jne comp
endcomp:
mov [tm1],edi ;bring in ebx real quantity digit
xor edi,edi
mov ecx,10
mov [tmp],dword 0
mov ebx,eax
perev: ;procedure of transformation chars in digit
cmp edi,[tm1] ;if edi >= ebx then endpr
jge endpr
xor eax,eax
mov al,[ebx+edi] ;bring from memory digit
sub al,'0'
mov esi,edi ;bring esi position digit in string
inc esi
shift: ;multiplicate digit on 10 in degree here position in string
cmp esi,[tm1] ;if esi >= ebx then ends
jge ends
mul ecx
inc esi
jmp shift
ends:
add eax,[tmp] ;save temporary result
mov [tmp],eax
inc edi
jmp perev
endpr:
mov eax,[tmp] ;output result
ret
SEGMENT data USE32 CLASS=DATA
tmp dd 0
tm1 dd 0
файл itox.asm
GLOBAL itox
EXTERN stdwrite, lfwrite
; GROUP DGROUP code data
SEGMENT code USE32 CLASS=CODE
itox: ;procedure of output digits
pusha
mov edi,lmes
mov edx,mess
call stdwrite
popa
; pusha
mov ebx,10 ;devider
xor esi,esi ;counter
divs:
xor edx,edx ;reset edx
div ebx ;division eax on ebx
add dl,'0'
push edx
inc esi ;increment counter
cmp eax,0 ;if eax<>0 then divs
jne divs
mov [tmp],dword 0
undivs:
pop edx
mov [tmp],dl
mov edx,tmp
mov edi,1
call stdwrite
dec esi
cmp esi,0 ;if esi<>0 then undivs
jne undivs
; popa
mov edi,2
mov edx,mes
call stdwrite
ret
SEGMENT data USE32 CLASS=DATA
tmp dd 0
mess db 'Output number: ',0
lmes equ $-mess
mes db 13,10
файл strexit.asm
GLOBAL stdexit
EXTERN ExitProcess
SEGMENT code USE32 CLASS=CODE
stdexit:
push dword 0
call ExitProcess
ret
файл stdwrite.asm
GLOBAL stdwrite
EXTERN GetStdHandle, WriteFile
SEGMENT code USE32 CLASS=CODE
stdwrite:
push dword -11
call GetStdHandle
push dword 0
push dword actlen
push dword edi
push dword edx
push dword eax
call WriteFile
mov eax,[actlen]
ret
SEGMENT data USE32 CLASS=DATA
actlen db 0
файл stdread.asm
GLOBAL stdread
EXTERN GetStdHandle, ReadFile, axtoi, stdwrite
SEGMENT code USE32 CLASS=CODE
stdread:
; pusha
mov edi,lmes
mov edx,mess
call stdwrite
push dword -10
call GetStdHandle
push dword 0
push dword actlen
push dword 10
push dword string
push dword eax
call ReadFile
mov eax,string
; popa
call axtoi
ret
SEGMENT data USE32 CLASS=DATA
actlen dd 0
string dd 0
mess db 'Please input number: ',0
lmes equ $-mess
Пример работы
Текст программы:
procedure main() { объявление главной процедуры
int a1, a3, i, j; объявление переменных
int a2;
array b[6], a4[10];
i = 5; присваивание переменных
a4[i] = 3 + 1 + 2 * 2 % 7;
a4[i] ++;
other(); вызов процедуры other()
for (i = 0; i<10; i++;) { цикл for
for (j = 0; j<10; j++;) { вложенный цикл for
write j*i; вывод переменной
}
writeln; перевод строки
}
repeat { цикл repeat-until
i++; инкремент переменной
read b[i]; ввод с клавиатуры числа
if (b[i] == 14) { если введеное число равно 14, тогда выводим его и завершаем цикл
write b[i];
break;
} else {
if (b[i] == 20) { если оно равно 10 тогда выводим его и продолжаем цикл без проверки условия
write b[i];
continue;
}
}
write b[i];
} until (b[i] % 10 == 0); проверка условия выхода из цикла
writeln;
i = 1;
writeln;
while (i < 10) { цикл while
i++;
write i;
j = 0;
while (j < 10) {
j++;
write j;
if (j == 5) {
continue;
}
}
writeln;
}
other1(5 + 7, 10, 15, j + 11); вызов процедуры с параметрами
}
procedure other() { объявление процедуры other без параметров
int a;
a = 0;
write a+10;
}
procedure other1(int a10, a11, a12, a14;) { обявление процедуры с параметрами
write a10, a11, a12, a14;
}
Результат на ассемблере:
EXTERN stdexit, stdread, itox, lfwrite
segment .text USE32 CLASS=CODE
..start:
call main
call stdexit
main:
push ebp
mov ebp, esp
push dword 5
pop dword [i+4*0]
push dword 3
push dword 1
pop ebx
pop eax
add eax,ebx
push eax
push dword 2
pop ebx
pop eax
add eax,ebx
push eax
push dword 2
pop ebx
pop eax
mul ebx
push eax
push dword 7
pop ebx
pop eax
xor edx,edx
div ebx
push edx
mov edi, [i+4*0]
pop dword [a4+4*edi]
mov edi,[i+4*0]
push dword [a4+4*edi]
pop ebx
mov eax,1
add eax,ebx
push eax
mov edi, [i+4*0]
pop dword [a4+4*edi]
call other
push dword 0
pop dword [i+4*0]
for_0:
push dword [i+4*0]
push dword 10
pop ebx
pop eax
cmp eax,ebx
jge near endfor_0
push dword [i+4*0]
pop ebx
mov eax,1
add eax,ebx
push eax
pop dword [i+4*0]
push dword 0
pop dword [j+4*0]
for_1:
push dword [j+4*0]
push dword 10
pop ebx
pop eax
cmp eax,ebx
jge near endfor_1
push dword [j+4*0]
pop ebx
mov eax,1
add eax,ebx
push eax
pop dword [j+4*0]
push dword [j+4*0]
push dword [i+4*0]
pop ebx
pop eax
mul ebx
push eax
pop eax
call itox
jmp near for_1
endfor_1:
call lfwrite
jmp near for_0
endfor_0:
repeat_0:
push dword [i+4*0]
pop ebx
mov eax,1
add eax,ebx
push eax
pop dword [i+4*0]
call stdread
mov edi,[i+4*0]
mov [b+4*edi],eax
mov edi,[i+4*0]
push dword [b+4*edi]
push dword 14
pop ebx
pop eax
cmp eax,ebx
jne near endif_0
mov edi,[i+4*0]
push dword [b+4*edi]
pop eax
call itox
jmp near until_0
jmp endelse_0
endif_0:
mov edi,[i+4*0]
push dword [b+4*edi]
push dword 10
pop ebx
pop eax
cmp eax,ebx
jne near endif_1
mov edi,[i+4*0]
push dword [b+4*edi]
pop eax
call itox
jmp near repeat_0
endif_1:
endelse_0:
mov edi,[i+4*0]
push dword [b+4*edi]
pop eax
call itox
mov edi,[i+4*0]
push dword [b+4*edi]
push dword 10
pop ebx
pop eax
xor edx,edx
div ebx
push edx
push dword 0
pop ebx
pop eax
cmp eax,ebx
jne near repeat_0
until_0:
call lfwrite
push dword 1
pop dword [i+4*0]
call lfwrite
while_0:
push dword [i+4*0]
push dword 10
pop ebx
pop eax
cmp eax,ebx
jge near endwhile_0
push dword [i+4*0]
pop ebx
mov eax,1
add eax,ebx
push eax
pop dword [i+4*0]
push dword [i+4*0]
pop eax
call itox
push dword 0
pop dword [j+4*0]
while_1:
push dword [j+4*0]
push dword 10
pop ebx
pop eax
cmp eax,ebx
jge near endwhile_1
push dword [j+4*0]
pop ebx
mov eax,1
add eax,ebx
push eax
pop dword [j+4*0]
push dword [j+4*0]
pop eax
call itox
push dword [j+4*0]
push dword 5
pop ebx
pop eax
cmp eax,ebx
jne near endif_2
jmp near while_1
endif_2:
jmp while_1
endwhile_1:
call lfwrite
jmp while_0
endwhile_0:
push dword 5
push dword 7
pop ebx
pop eax
add eax,ebx
push eax
push dword 10
push dword 15
push dword [j+4*0]
push dword 11
pop ebx
pop eax
add eax,ebx
push eax
call other1
mov esp,ebp
pop ebp
ret
other:
push ebp
mov ebp, esp
push dword 0
pop dword [a+4*0]
push dword [a+4*0]
push dword 10
pop ebx
pop eax
add eax,ebx
push eax
pop eax
call itox
mov esp,ebp
pop ebp
ret
other1:
push ebp
mov ebp, esp
mov eax, [ebp + 20]
mov [a10], eax
mov eax, [ebp + 16]
mov [a11], eax
mov eax, [ebp + 12]
mov [a12], eax
mov eax, [ebp + 8]
mov [a14], eax
push dword [a10+4*0]
pop eax
call itox
push dword [a11+4*0]
pop eax
call itox
push dword [a12+4*0]
pop eax
call itox
push dword [a14+4*0]
pop eax
call itox
mov esp,ebp
pop ebp
ret
segment .data USE32
a1 dd 0
a3 dd 0
i dd 0
j dd 0
a2 dd 0
b times 6 dd 0
a4 times 10 dd 0
a dd 0
a10 dd 0
a11 dd 0
a12 dd 0
a14 dd 0
Список литературы
1. Ахо А., Сети Р., Ульман Д. Компиляторы: принципы, технологии и инструменты. : Пер. с англ. М.: Издательский дом “Вильямс”, 2003. 768 с.: ил