15.6 Система-виртуальная машина + транслятор
15.6.1 Эволюция программных систем
Как правило, сложная программная система (ПС) претерпевает несколько
стадий своего развития:
1) первоначальная версия;
2) конфигурационная версия;
3) инструментальная версия.
Первоначальная версия системы - это жесткая программа, которая ориентирована на выполнение конкретных функций, при этом модификация функций не
предполагается.
Конфигурационная версия предполагает существование специального файла конфигураций, в котором могут быть записаны различные варианты функций
и параметров ПС. Как правило, в этом файле записывается последовательность
строк, следующей структуры:
< параметр > = <
значение >, где <
параметр > некоторый параметр ПС или название функции;
< значение > - это числовая константа или строка символов, означающая
некоторый вариант функции или параметра.
При таком подходе возможно подстраивать ПС под конкретные нужды потребителей, изменяя значения в конфигурационном файле.
Инструментальная версия ПС является дальнейшим развитием конфигурационной ПС, и предполагает наличие некоторого языка, на котором описывается
вариант реализации ПС. Этот язык является предметно ориентированным и, в зависимости от особенностей предметной области, может быть декларативным или
процедурным. Теперь использование ПС состоит в два этапа: создание конкретной
версии ПС и выполнение данной конкретной ПС. Таким образом, инструментальную ПС можно разделить на две части: системная часть, отвечающую за реализацию языка и проблемную часть, в которой содержатся программы реализующие
алгоритмы обработки в данной предметной области.
Системная часть производит анализ программы (описания конкретной варианта ПС) и генерацию конкретного варианта ПС. Если предметный язык процедурного типа, то создается транслятор, если декларативного - то генератор. Проблемная часть может быть также реализована несколькими способами: библиотека подпрограмм, интерпретатор некоторого внутреннего кода. Библиотека подпрограмм
создается в тех случаях, когда состав функций данной предметной области может
расширяться. Тогда с внесением новой подпрограммы меняется и системная часть.
В настоящее время сложные программные системы строятся по принципу «транслятор - виртуальная машина». Например, так реализована система программирования
для интернета - Java[].
15.6.2 Описание языка
Синтаксис языка описывается следующей грамматикой:
<Программа> => program <операторы программы>
<Операторы программы> => <Оператор> < Операторы программы >
<Операторы программы>=> endprog
<Оператор>=> if <Выражение> then <Операторы> endif
<Оператор>=> if <Выражение> then <Операторы> else <Операторы> endif
<Оператор>=> cycle <Выражение> <Операторы> endcycle
<Оператор>=> <Идентификатор>:=<Выражение>
<Оператор>=> output <Список выражений>
<Oператор>=> input < Идентификатор >
<Оператор>=> ;
<Операторы> => <Оператор> < Операторы>
Комментарий в программе оформляется следующим образом: пишется два
подряд символа ’-’ и далее до конца строки все символы воспринимаются как
комментарий. Например,
– Это комментарий к программе;
– увеличить счетчик
Переменные в программе не объявляются, тип автоматически определяется
в зависимости от значения, которое будет присвоено. Типы данных следующие:
целый, вещественный, строка символов, точка. Более подробно смотри реализацию.
Примеры программ
Пример №1. Нахождение суммы чисел натурального ряда. Первые две строки комментарий к программе. Затем устанавливаются значения двух переменных
i- текущее число натурального ряда и s - сумма. Затем организуется цикл подсчета
суммы. В цикле выводится значение текущего натурального числа. По окончанию
цикла выводится значение суммы.
// this program count sum
// 15.01.04
program
i:=1; s:=0;
cycle(i<5)
s:=s+i;
i:=i+1;
output i;
endcycle
output s;
endprog
Пример №2. Нахождение значения n! (факториал).
program
// factorial(10)
i:=1; s:=1;
cycle(i<10)
s:=s*i; i:=i+1;
output i;
endcycle
output s;
endprog
15.6.3 Описание языка
Синтаксис языка описывается следующей грамматикой:
<Программа> => program <операторы программы>
<Операторы программы> => <Оператор> < Операторы программы >
<Операторы программы>=> endprog
<Оператор>=> if <Выражение> then <Операторы> endif
<Оператор>=> if <Выражение> then <Операторы> else <Операторы> endif
<Оператор>=> cycle <Выражение> <Операторы> endcycle
<Оператор>=> <Идентификатор>:=<Выражение>
<Оператор>=> output <Список выражений>
<Oператор>=> input < Идентификатор >
<Оператор>=> ;
<Операторы> => <Оператор> < Операторы>
Комментарий в программе оформляется следующим образом: пишется два
подряд символа ’-’ и далее до конца строки все символы воспринимаются как
комментарий. Например,
– Это комментарий к программе;
– увеличить счетчик
Переменные в программе не объявляются, тип автоматически определяется
в зависимости от значения, которое будет присвоено. Типы данных следующие:
целый, вещественный, строка символов, точка. Более подробно смотри реализацию.
Примеры программ
Пример №1. Нахождение суммы чисел натурального ряда. Первые две строки комментарий к программе. Затем устанавливаются значения двух переменных
i- текущее число натурального ряда и s - сумма. Затем организуется цикл подсчета
суммы. В цикле выводится значение текущего натурального числа. По окончанию
цикла выводится значение суммы.
--this program count sum
--15.01.04
program
i:=1; s:=0;
cycle(i<5)
s:=s+i;
i:=i+1;
output i;
endcycle
output s;
endprog
Пример №2. Нахождение значения n! (факториал).
program
--factorial(10)
i:=1; s:=1;
cycle(i<10)
s:=s*i; i:=i+1;
output i;
endcycle
output s;
endprog
15.6.4 Стековая виртуальная машина
Стековая виртуальная машина является удобным
инструментом в сочетании с транслятором, основанном на методе
рекурсивного спуска. В стековой ВМ операнды операции берутся и з
стека и результаты операций также заносятся в стек (см. раздел
использование стека для вычисления арифметических выражений). Стек
используется для хранения промежуточных результатов. Сами объекты
хранятся в специальной памяти и есть команду Push и Pull - которые
вставляют в стек объект из памяти и выталкивают объект из стека и
запоминают его адресу в памяти.
Ниже приведено описание заголовочного файла с описанием
параметров стековой виртуальной машины.
| Таблица 15.6: Команды виртуальной машины |
| #define | IN | 1 | //ввод числа |
| #define | OUT | 2 | //вывод числа |
| #define | ADD | 3 | //сложить |
| #define | SUB | 4 | //вычесть |
| #define | MULT | 5 | //умножить |
| #define | DIV | 6 | //разделить |
| #define | JUMP | 7 | //безусловный переход |
| #define | GRT | 8 | //проверка на больше |
| #define | LST | 9 | //проверка на меньше |
| #define | BEQ | 10 | //перейти если флаг установлен |
| #define | STOP | 11 | //останов |
| #define | EQU | 12 | //проверка на равно |
| #define | MOV | 13 | //переслать данные |
| #define | ILOAD | 14 | //косвенная загрузка данных |
| #define | ISAVE | 15 | //косвенное соранение данных |
| #define | INC | 16 | //увеличить на единицу |
| #define | DEC | 17 | //уменьшить на единицу |
| #define | PUSH | 18 | //втолкнуть в стек |
| #define | PULL | 19 | //вытащить из стека |
| #define | TOP | 20 | //взять вершину стека |
| #define | BNE | 21 | //не равно |
Параметры
размера памяти под объекты и стека #define
SIZEMEMORY 256 #define SIZESTACK
256
Ключ
установки
отладочного
режима #define
DEBUG_SVM
Описание типа объекта #define
INT 1 //целый
#define DOUBLE 2 //вещественный
#define STRING
3 //строка #define POINT 4
//точка
Описание структуры точки typedef
struct PointTag { int x; int y; } Point;
Описание
объекта typedef struct ObjectTag {
unsigned
char type; //тип хранимого объекта union { //объединение различных типов
int int_number; //целое число
double
float_number; //вещественное
число
char string[20]; //строка
Point
p; //структура ТО }
m; } Object; //это все может храниться
в стеке
Стек для хранения объектов Object
Stack[SIZESTACK]; int Topp;
//указатель стека Object
Mem[SIZEMEMORY]; //память под
данные
Функция заносит значение объекта в
стек. int
Push(Object *obj){ if(Topp<SIZESTACK){
Stack[Topp++]=*obj;
return 1; //нормальное завершение
} return -1; //стек переполнен }
Функция
выталкивает значение объекта из стека int
Pull(Object *obj) { //забрать
из
стека if(Topp>0){
*obj=Stack[--Topp];
return
1; //нормальное
завершение
} return -1; //стек пуст }
Функция взятие
значения из вершины стека int
Top(Object *obj) { if(Topp>0){
*obj=Stack[Topp-1];
return 1; //нормальное завершение
} return -1; //стек пуст }
Функция проверки
отношения a > b . Отношение проверяется в зависимости от типа.
Если отношение выполняется, то результат равен единице.
В противном случае
0. int Grt(Object *op1, Object
*op2){ char buf[20]; if(op1->type==INT &&
op2->type==INT ){
if(op1->m.int_number >
op2->m.int_number) return
1;
else return 0;
} else
if(op1->type==DOUBLE
&& op2->type==INT ){
if(op1->m.float_number
> op2->m.int_number) return
1;
else return 0;
}
else
if(op1->type==INT
&& op2->type==DOUBLE {
if(op1->m.int_number>op2->m.float_number)
return 1;
else return 0;
}
else
if(op1->type==DOUBLE
&& op2->type==DOUBLE ){
if(op1->m.float_number>op2->m.float_number)
return 1;
else return 0;
}
else
if(op1->type==STRING
&& op2->type==STRING ){
if(strcmp(op1->m.string,op2->m.string)>0)
return 1;
else return 0;
}
else return 0; //можно
дальше
продолжить }
Функция
проверки
отношения
a < b . Отношение
проверяется в зависимости от типа. Если отношение выполняется, то
результат равен единице. В противном случае
0. int Lst(Object *op1, Object
*op2){ char buf[20]; if(op1->type==INT &&
op2->type==INT ){
if(op1->m.int_number <
op2->m.int_number) return
1;
else return 0;
} else
if(op1->type==DOUBLE
&& op2->type==INT ){
if(op1->m.float_number
< op2->m.int_number) return
1;
else return 0;
}
else
if(op1->type==INT
&& op2->type==DOUBLE ){
if(op1->m.int_number <
op2->m.float_number) return
1;
else return 0;
}
else
if(op1->type==DOUBLE
&& op2->type==DOUBLE ){
if(op1->m.float_number
< op2->m.float_number) return
1;
else return 0;
}
else
if(op1->type==STRING
&& op2->type==STRING ){
if(strcmp(op1->m.string,op2->m.string)<0)
return 1;
else return 0;
}
else return 0; //можно
дальше
продолжить } //равно
a==b int
Equ(Object *op1, Object *op2){ char buf[20]; if(op1->type==INT &&
op2->type==INT ){
if(op1->m.int_number ==
op2->m.int_number) return
1;
else return 0;
} else
if(op1->type==DOUBLE
&& op2->type==INT ){
if(op1->m.float_number ==
op2->m.int_number) return
1;
else return 0;
}
else
if(op1->type==INT
&& op2->type==DOUBLE ){
if(op1->m.int_number ==
op2->m.float_number)
return 1;
else return 0;
}
else
if(op1->type==DOUBLE
&& op2->type==DOUBLE ){
if(op1->m.float_number ==
op2->m.float_number) return
1;
else return 0;
}
else
if(op1->type==STRING
&& op2->type==STRING ){
if(strcmp(op1->m.string,op2->m.string)==0)
return 1;
else return 0;
}
else return 0;//можно
дальше
продолжить } //функция
сложения
с
учетом
типа
операнда void
Add(Object *op1, Object *op2, Object *res){ char buf[20]; if(op1->type==INT &&
op2->type==INT ){
res->type=INT;
res->m.int_number=op1->m.int_number+op2->m.int_number;
//int=int+int
} else
if(op1->type==DOUBLE
&& op2->type==INT ){
res->type=DOUBLE;
res->m.float_number=op1->m.float_number+op2->m.int_number
}
else
if(op1->type==INT
&& op2->type==DOUBLE ){
res->type=DOUBLE;
res->m.float_number=op1->m.int_number+op2->m.float_number
}
else
if(op1->type==DOUBLE
&& op2->type==DOUBLE ){
res->type=DOUBLE;
res->m.float_number=op1->m.float_number+op2->m.float_numb
}
else
if(op1->type==STRING
&& op2->type==STRING ){
res->type=STRING;
strcpy(res->m.string,op1->m.string);
strcat(res->m.string,op2->m.string);
}
else
if(op1->type==STRING
&& op2->type==INT ){
res->type=STRING;
strcpy(res->m.string,op1->m.string);
itoa(op2->m.int_number,buf,10);
strcat(res->m.string,buf);
}
else
if(op1->type==STRING
&& op2->type==DOUBLE ){
res->type=STRING;
strcpy(res->m.string,op1->m.string);
sprintf(buf,"%f",op2->m.float_number);
strcat(res->m.string,buf);
}
else ; //можно
дальше
продолжить } //функция
вычитания
с
учетом
типа
операнда void
Sub(Object *op1, Object *op2, Object *res){ if(op1->type==INT &&
op2->type==INT ){
res->type=INT;
res->m.int_number=op1->m.int_number-op2->m.int_number;
//int=int-int
} else
if(op1->type==DOUBLE
&& op2->type==INT ){
res->type=DOUBLE;
res->m.float_number=op1->m.float_number-op2->m.int_number
}
else
if(op1->type==INT
&& op2->type==DOUBLE ){
res->type=DOUBLE;
res->m.float_number=op1->m.int_number-op2->m.float_number
}
else
if(op1->type==DOUBLE
&& op2->type==DOUBLE ){
res->type=DOUBLE;
res->m.float_number=op1->m.float_number-op2->m.float_numb
}
else ;//можно
дальше
продолжить } //функция
умножения
с
учетом
типа
операнда void
Mult(Object *op1, Object *op2, Object *res){ if(op1->type==INT &&
op2->type==INT ){
res->type=INT;
res->m.int_number=op1->m.int_number*op2->m.int_number;
//int=int*int
} else
if(op1->type==DOUBLE
&& op2->type==INT ){
res->type=DOUBLE;
res->m.float_number=op1->m.float_number*op2->m.int_number
}
else
if(op1->type==INT
&& op2->type==DOUBLE ){
res->type=DOUBLE;
res->m.float_number=op1->m.int_number*op2->m.float_number
}
else
if(op1->type==DOUBLE
&& op2->type==DOUBLE ){
res->type=DOUBLE;
res->m.float_number=op1->m.float_number*op2->m.float_numb
}
else ;//можно
дальше
продолжить }
void Div(Object *op1, Object *op2, Object
*res){ if(op1->type==INT &&
op2->type==INT ){
res->type=INT;
res->m.int_number=op1->m.int_number/op2->m.int_number;
//int=int/int
} else
if(op1->type==DOUBLE
&& op2->type==INT )
res->type=DOUBLE;
res->m.float_number=op1->m.float_number/op2->m.int_number
}
else
if(op1->type==INT
&& op2->type==DOUBLE ){
res->type=DOUBLE;
res->m.float_number=op1->m.int_number/op2->m.float_number
}
else
if(op1->type==DOUBLE
&& op2->type==DOUBLE ){
res->type=DOUBLE;
res->m.float_number=op1->m.float_number/op2->m.float_numb
}
else ; //можно
дальше
продолжить } //печать
первых
10 ячеек
памяти void
PrintMemory(){
printf("dump memory ------------------\n"); for(int
i=0; i<10; i++)
PrintObjectAddr(i,&Mem[i]); } //ввод
объекта void
InputObject(Object *res){ int ch; char buf[40]; while(1){
printf("Input type (int-’1’, float-’2’, string-’3’,
point-’4’)\n");
printf("type>");
ch=getch();
printf("%c\n",ch);
ch-=’0’;
if(ch>0&&ch<5)
res->type=ch;
else {
puts("Error type\n");
continue;
}
switch(ch){
case 1:
printf("int>");
gets(buf);
res->m.int_number=atoi(buf);
return;
case 2:
printf("float>");
gets(buf);
res->m.float_number=atof(buf);
return;
case 3:
printf("string>");
gets(buf);
strcpy(res->m.string,buf);
return;
case 4:
printf("point>");
gets(buf);
return;
}
} } //печать
объекта void
OutputObject(Object *res){ switch(res->type){
case 1:
printf("out->int>");
printf("%d\n",res->m.int_number);
return;
case 2:
printf("out->float>");
printf("%g\n",res->m.float_number);
return ;
case 3:
printf("out->string>");
printf("%s\n",res->m.string);
return;
case 4:
puts("out->point>");
return;
}
} //печать
объекта
для
дампа void
PrintObjectAddr(int Addr,Object
*res){ switch(res->type){
case 1: printf("%03d
(int)%d\n",Addr,res->m.int_number);
return;
case 2: printf("%03d
(float)%g\n",Addr,res->m.float_numbe
return ;
case 3: printf("%03d
(string)%20s\n",Addr,res->m.string);
return;
case 4:
puts("point>");
return;
} } //вывод
команды
на
терминал int
PrintCommand(unsigned char *prog){ int Addr,Addr2,Addr3; int pc=0; switch (*prog){
case IN:
Addr=prog[pc+1]; //ввод
числа
printf("IN %3d\n",Addr);
pc+=2;
break;
case OUT: //вывод
числа
Addr=prog[pc+1];
printf("OUT %3d\n",Addr);
pc+=2;
break;
case PUSH:
Addr=prog[pc+1];
printf("PUSH %3d\n",Addr);
pc+=2;
break;
case PULL:
Addr=prog[pc+1];
printf("PULL %3d\n",Addr);
pc+=2;
break;
case TOP:
Addr=prog[pc+1];
printf("TOP %3d\n",Addr);
pc+=2;
break;
case ADD: //сложить
два
числа
printf("ADD\n");
pc++;
break;
case SUB://вычитание
printf("SUB\n");
pc++;
break;
case MULT://умножение
printf("MULT\n");
pc++;
break;
case DIV: //деление
printf("DIV\n");
pc++;
break;
case JUMP://безусловный
переход
Addr=prog[pc+1]; //ввод
числа
printf("JUMP %3d\n",Addr);
pc+=2;
break;
case EQU: //проверка
на
равно
printf("EQU\n");
pc++;
break; case GRT: //проверка
a>b
printf("GRT \n");
pc++;
break;
case LST://проверка
a<b
printf("LST \n");
pc++;
break;
case BEQ://условный
переход
Addr=prog[pc+1];
printf("BEQ %3d\n",Addr);
pc+=2;
break;
case BNE://условный
переход
Addr=prog[pc+1];
printf("BNE %3d\n",Addr);
pc+=2;
break;
case INC://Инкримент
Addr=prog[pc+1];
printf("INC %3d\n",Addr);
pc+=2;
break;
case DEC: //Декримент
Addr=prog[pc+1];
printf("DEC %3d\n",Addr);
pc+=2;
break;
case MOV: //переслать
данные
Addr=prog[pc+1];
Addr2=prog[pc+2];
printf("MOV %3d, %3d\n",Addr,Addr2);
pc+=3;
break;
case ILOAD: //косвенная
загрузка
данных
Addr=prog[pc+1];
Addr2=prog[pc+2];
Addr3=prog[pc+3];
printf("ILOAD %3d, %3d, %3d\n",Addr,Addr2,Addr3);
pc+=4;
break;
case ISAVE: //косвенное
сохрание
данных
Addr=prog[pc+1];
Addr2=prog[pc+2];
Addr3=prog[pc+3];
printf("ILOAD %3d,%3d, %3d\n",Addr,Addr2,Addr3);
pc+=4;
break;
case STOP: printf("STOP\n");
return 0;
default: printf("Error
command\n");
return -1;
} return pc; } //программа
моделирования
стековой
виртуальной
машины int
StackVirtualMachine(unsigned char *prog,Object Mem[]) { int com, //код
команды
Addr,Addr2,Addr3, //вспомогательные
ячейки
для
хранения
адресов pc=0;
//счетчик
команд
(program counter)
Object obj1,obj2,obj3,res; while(1) //цикл
выполнения
программы
{
#ifdef DEBUG_SVM //для
отладки
PrintMemory();
PrintCommand(&prog[pc]);
if(getch()==27) exit(0);
//если
нажата
ESC завершить
программу
#endif
com=prog[pc]; //выделяем
код
операции
switch
(com)
{
case
IN:
Addr=prog[pc+1];
InputObject(&Mem[Addr]);
pc=pc+2;
//смещаем счетчик команд
break;
case
OUT:
//вывод числа
Addr=prog[pc+1];
OutputObject(&Mem[Addr]);
pc=pc+2;
//смещаем счетчик команд
break;
case
PUSH:
Addr=prog[pc+1];
Push(&Mem[Addr]);
pc=pc+2;
break;
case
PULL:
Addr=prog[pc+1];
Pull(&Mem[Addr]);
pc=pc+2;
break;
case
TOP:
Addr=prog[pc+1];
Top(&Mem[Addr]);
pc=pc+2;
break;
case
ADD:
//сложить два числа
Pull(&obj1);
Pull(&obj2);
Add(&obj1,&obj2,&res);
Push(&res);
pc=pc+1;
break;
case
SUB:
//вычитание
Pull(&obj1);
Pull(&obj2);
Sub(&obj1,&obj2,&res);
Push(&res);
pc=pc+1;
break;
case
MULT:
//умножение
Pull(&obj1);
Pull(&obj2);
Mult(&obj1,&obj2,&res);
Push(&res);
pc=pc+1;
break;
case
DIV:
//деление
Pull(&obj2);
Pull(&obj1);
Div(&obj1,&obj2,&res);
//res=ob1/ob2;
Push(&res);
pc=pc+1;
break;
case
JUMP:
//безусловный переход
pc=prog[pc+1];
break;
case
EQU:
//проверка на равно
Pull(&obj1);
Pull(&obj2);
res.type=INT;
res.m.int_number=Equ(&obj1,&obj2);
Push(&res);
pc=pc+2;
break;
case
GRT:
//проверка a
> b
Pull(&obj2);
Pull(&obj1);
res.type=INT;
res.m.int_number=Grt(&obj1,&obj2)
pc++;
break;
case
LST:
//проверка a
< b
Pull(&obj2);
Pull(&obj1);
res.type=INT;
res.m.int_number=Lst(&obj1,&obj2);
Push(&res);
pc++;
break;
case BEQ: //условный
переход
если
истина
Addr=prog[pc+1];
Pull(&res);
if(res.m.int_number)
pc=Addr;
else pc=pc+2;
break;
case BNE: //условный
переход
если
ложь
Addr=prog[pc+1];
Pull(&res);
if(!res.m.int_number)
pc=Addr;
else pc=pc+2;
break;
case INC: //Инкримент
Addr=prog[pc+1];
Mem[Addr].m.int_number++;
pc=pc+2;
break;
case DEC: //Декримент
Addr=prog[pc+1];
Mem[Addr].m.int_number++;
pc=pc+2;
break;
case MOV: //переслать
данные
Addr=prog[pc+1];
Addr2=prog[pc+2];
Mem[Addr]=Mem[Addr2];
pc=pc+3;
break;
case ILOAD: //косвенная
загрузка
данных
Addr=prog[pc+1];
Addr2=prog[pc+2];
Addr3=prog[pc+3];
Mem[Addr]=Mem[Addr2+Mem[Addr3].m.int_number];
pc=pc+4;
break;
case ISAVE: //косвенное
сохрание
данных
Addr=prog[pc+1];
Addr2=prog[pc+2];
Addr3=prog[pc+3];
Mem[Addr2+Mem[Addr3].m.int_number]=Mem[Addr];
pc=pc+4;
break;
case STOP: return 1;
default: printf("Error
command\n");
return -1;
}
} } #define X
0 #define Y 1 #define Z 2 unsigned char prog[]={ IN, X, IN,
Y, PUSH, Y, PUSH, X, DIV, //x/y PULL, Z, OUT,
Z, STOP }; void main()
{
StackVirtualMachine(prog,Mem); //выполение
первой
программы
SetMemory();
StackVirtualMachine(prog2,Mem); //выполение второй программы
getch();
}
15.6.5 Транслятор для
стековой виртуальной машины#include
<stdio.h> #include
<string.h> #include
<math.h> #include
<ctype.h> #include
<conio.h> #include
"stack_vm.h"
extern Object
Mem[]; //внешняя
ссылка
на
пямять
по
объекты #define
SIZEPROGRAM 500 //размер
программы #define
SIZETABLVAR 30 //размер
таблицы
символов
struct
//описание
таблицы
символов { int length; //длина
имени char Name[10]; //имя
переменной }TablVar[SIZETABLVAR];
void SkipBlanks(); void ExpressAdd(void); void ExpressTest(); void Statement();
int TopVar; //номер
свобоной
ячейки
таблицы char
*ch; //указатель
для
разбора
вводимой
строки char
in_line[800]; //вводимая
строка
(буфер
для
хранения
исходного
текста)
unsigned
char program[SIZEPROGRAM]; //буфер
для
хранения
выходного
кода
программы int
CurPos; //текщая
позиция
для
записи
кода
void
OutCode(unsigned char Code) //записать
байта
в
выходной
код
программы { if(CurPos<SIZEPROGRAM)
program[CurPos++]=Code; }
int GetCurrentPosition() { return CurPos; } //взять
текущую
позицию void
SetBrench(int pos, int addr) //установть
значение
операнда {
program[pos]=addr; } //Печать
ошибки void
Error(char *soo) {
printf("%s\n",soo); } //
функция
поиска
имени
в
таблице
символов int
FindNameVar(char *var, int *num) { int i; int len;
len=strlen(var); for(i=0; i<SIZETABLVAR; i++)
{
*num=i;
if(TablVar[i].length==0)
return -1;
if(TablVar[i].length==len)
if(strcmp(TablVar[i].Name,var)==0) return i;
} return -1; } // функция
добавления
нового
имени
в
таблицу
символов int
AddNameVar(char *var) { int num; int len; char *ptr; if(FindNameVar(var,&num)==-1)
{
if(TopVar<SIZETABLVAR)
{
num=TopVar;
len=strlen(var);
TablVar[TopVar].length=len;
strncpy(TablVar[TopVar].Name,var,9);
TopVar++;
}
else Error("Переполнена
таблица
символов");
} return num; } //функция
взять
имя
во
входной
строке int
GetName(char *name, int max) //здесь
используются
функции
ctype.h { int i;
SkipBlanks(); //пропустить
пробелы if(isalpha(*ch)) *name++=*ch++; //первый
символ
должен
быть
буквой else { *name=0; return 0;} for(i=1; i<max; i++) //взять
остальные
символы
(буква
или
цифра)
{
if(isalnum(*ch))
*name++=*ch++;
else
{
*name=0; return 1; //имя
нормальное
}
}
*name=0;
Error("Большое
имя"); return 0; } // функция
пропуска
служебных
символов void
SkipBlanks() { while(1)
{
switch (*ch)
{
case 8: //табуляция
case 26: //конец
файла
case 10: //возврат
каретки
case 13: //перевод
строки
case ’ ’: //пробел
ch++; break;
default:
return;
}
} } // проверка
вхождения
строки
str во
входной
строке int
match(char *str) { char *chv;
SkipBlanks(); //пропустить
служебные
символы
и
пробелы
chv=ch; while(*str)
{
if((*str++)!=(*chv++)) return 0; //если
не
совпала
строка
то
ноль
}
ch=chv; //если
совпала,
то
двигаем
указатель return 1; } // проверка
вхождения
строки
без
сдвига
указателя
ch int
wait(char *str) { char *chv;
SkipBlanks();
chv=ch; while(*str)
{
if((*str++)!=(*chv++)) return 0;
} return 1; } //функция
проверки
конца
строки int
eof() {
SkipBlanks(); if(*ch==’\0’) return 1; else return 0; } //функция
пропуска
одного
символа void
skipchar() { if(*ch!=’\0’) ch++;
} // функция
взятия
числа
во
входной
строки
и
преобразования
к
double double
getfloat(char *num, int max) //используются
функции
<ctype.h> { int i=0, j;
SkipBlanks(); //пропустить
пробелы if(*ch==’-’|| *ch==’+’) //взять
знак
num[i++]=*ch++;
SkipBlanks(); //пропусить
пробелы for( ; i<max; i++) //взять
целую
часть
числа
{
if(isdigit(*ch))
num[i]=*ch++;
else break;
}
j=i; if(*ch==’.’) //взять
дробную
часть
числа
{
num[i]=*ch++;
for(j=i+1; j<max;
j++)
{
if(isdigit(*ch))
num[j]=*ch++;
else break;
}
}
num[j]=0; return atof(num); } // функция
разбора
первичного
выражения void
ExpressFunc(void) { int num; char name[30]; double f; int max=29; if(match("(")) //вычисление
выражения
в
скобках
{
ExpressTest();
if(match(")")==0)
Error("Missmatch )");
} else //вычисление
функций
{ //if(match("sin")) TranSin(); //else // if(match("cos")) TranCos(); //else // if(match("atan"))
TranAtan(); //else // if(match("log")) TranLog(); //else // if(match("abs")) TranAbs(); //else
{
if(GetName(name,max)==1)//взятие
значения
переменной
{
num=AddNameVar(name); //найти
переменную
в
таблице
OutCode(PUSH); //сгенерировать
команду
Push <Addr>
OutCode(num);
}
else //взять
значение
константы
f=getfloat(name,max);
if(name[0]==’\0’) {
skipchar(); return; }
num=AddNameVar(name); //найти
или
записать
константу
Mem[num].type=DOUBLE; //занести
значение
в
память
Mem[num].m.float_number=f;
OutCode(PUSH); //сгенерировать
команду
Push <Addr>
OutCode(num);
}
}
} } // Функция
разбора
выражение
для
умножения
и
деления void
ExpressMult(void) {
ExpressFunc(); //вычислить
первичное while(1)
{
if(match("*")) //если
знак
умножения
{
ExpressFunc(); //вычислить
второе
первичное
OutCode(MULT); //сгенерировать
команду
умножить
}
else
if(match("/")) //если
знак
деления
{
ExpressFunc(); //вычислить
второе
первичное
OutCode(DIV); //сгенерировать
команду
разделить
}
else break;
} } // функция
разбора
выражения
для
сложения
и
вычитания void
ExpressAdd(void) {
ExpressMult(); //вычислить
значение
первого
операнда while(1)
{
if(match("+")) //если
знак
плюс
{
ExpressMult(); //вычислить
значение
для
второго
опреанда
OutCode(ADD);
}
else
if(match("-")) //если
это
минус
{
ExpressMult(); //взять
значение
второго
операнда
OutCode(SUB);
}
else break;
} } // функция
разбора
логических
выражений void
ExpressTest(void) {
ExpressAdd(); //вычислить
значение
первого
операнда while(1)
{
if(match(">")) //если
знак
больше
{
ExpressAdd(); //вычислить
значение
для
второго
опреанда
OutCode(GRT);
}
else
if(match("<")) //если
это
меньше
{
ExpressAdd(); //взять
значение
второго
операнда
OutCode(LST);
}
else
if(match("=")) //если
равно
{
ExpressAdd(); //взять
значение
второго
операнда
OutCode(EQU);
}
else break;
} } // функция
разьора
оператора
присваивания void
AssignOp(void) { char name[10]; int max=9; int num; if(GetName(name,max)==1) //взять
имя
переменной
{
num=AddNameVar(name); //записать
имя
в
таблицу
} else
{
printf("uncorect symbol %c\n",*ch);
skipchar();
return;
} if(match(":=")) //имеется
операция
присваивания
{
ExpressTest(); //разбрать
выражение
OutCode(PULL); //сгнерировать
команду
PULL
OutCode(num);
} } //функция
разбора
условного
оператора
if<>then<>else<>endif void
trans_if() { int pos_else; int pos_end;
ExpressTest(); //разбор
условия
в
стеке
результат
сравнения
OutCode(BNE); //если
ложь
переход
на
else или
на
endif
pos_else=GetCurrentPosition(); //запомнить
позицию
для
будущего
заполения
OutCode(0); //выделить
место
под
будущее
значение if(match("then")){
do //разбор
операторов
в
блоке
then
{
Statement();
}
while(!wait("else")&&!wait("endif")&&!eof());
//ждать
пока
OutCode(JUMP); //переход
на
конец
цикла
pos_end=GetCurrentPosition(); //запомнить
позицию
для
будущего
заполения
OutCode(0); //выделить
место
под
будущее
значение
адреса
перехода
} else Error("absent then"); //сообщение
об
ошибке
if(match("else")) //разбор
блока
else
{
SetBrench(pos_else,GetCurrentPosition());
//установить
адрес
перехода
на
else блок
do //разбор
операторов
else блока
{
Statement();
}
while(!wait("endif")&&!eof());
//ждать
конца
endif или
буфера
SetBrench(pos_end,GetCurrentPosition()); //устанвить
переход
на
конец
оператора
if
}
else //если
else блока
нет
SetBrench(pos_else,GetCurrentPosition());
//устанвить
переход
на
конец
оператора
if
if(match("endif")) } //функция
разбора
операторы
цикла
cycle<условие>
<тело>
endcycle void
trans_while() { int
pos_beg=GetCurrentPosition(); int pos_end;
ExpressTest(); //разбор
условия,
в
стеке
результат
сравнения
OutCode(BNE); //если
ложь
переход
pos_end=GetCurrentPosition();
OutCode(0); //заполнить do
{
Statement(); //разбор
операторов
тела
цикла
} while(!wait("endcycle")&&!eof());
//ожидаем
конца
цикла if(match("endcycle"))
{
OutCode(JUMP); //переход
в
начало
цикла
OutCode(pos_beg);
SetBrench(pos_end,GetCurrentPosition());
//выход
за
цикл
} else Error("Missmatch
endcycle"); } //функция
рабора
оператора
ввода
input <список
переменных> void
trans_input() { char name[20]; int max=19, num; do
{
if(GetName(name,max)==1)
//взятие
значения
переменной
{
num=AddNameVar(name); //найти
переменную
в
таблице
OutCode(IN);
OutCode(num);
}
}
while(match(",")); //продолжить
если
есть
запятая } //функция
разбора
оператора
вывода void
trans_output() //output
<список
переменных> { char name[20]; int max=19, num; do{
if(GetName(name,max)==1)
//взятие
значения
переменной
{
num=AddNameVar(name); //найти
переменную
в
таблице
OutCode(OUT);
OutCode(num);
}
} while(match(",")); //продолжить
если
есть
запятая } //функция
разбора
одного
из
операторов void
Statement() { char *beg=ch; if(match("cycle")) trans_while(); //разбор
оператора
цикла else if(match("if")) trans_if(); //разбор
условногооператора else if(match("input")) trans_input(); //разбор
оператора
ввода else if(match("output")) trans_output(); //разбор
оператора
вывода else if(match(";")); //пустой
оператор else
AssignOp(); //разбор
оператора
присваивания for(;beg!=ch; beg++) printf("%c",*beg);
printf("\n"); } //функция
пропуска
комментариев void
SkipCommentary() { while(match("--")) while(*ch!=’\n’&&!eof())
skipchar(); } //функция
разбора
текста
программы
вместе
с
крмментариями void
trans_program() {
SkipCommentary(); //пропуск
комментариев
в
начале
программы if(match("program"))
{
do
{
SkipCommentary(); //пропуск
коментариев
в
теле
программы
Statement();
}
while((!wait("endprog"))&&(!eof()));
} if(match("endprog")) OutCode(STOP); //генерация
команды
останова else Error("Missmatch
endprog\n");
SkipCommentary(); //пропуск
комментариев
в
конце
программы } // Функция
вывода
таблицы
символов PrintTablVar() { int i; for(i=0; i<TopVar; i++)
{
printf("%2d%10s\n",i,TablVar[i].Name);
} } //функция
дезассемлирования
программного
кода
виртуальной
машины void
Disassembler(unsigned char *program) { int next,pc=0;
printf("Disassembler for stack vm (ver 1.00)\n");
printf("---------------------\n"); do
{
printf("%03d ",pc); //вывод
адреса
команды
next=PrintCommand(&program[pc]); //вывод
команды
pc+=next;
} while(next!=0&&next!=-1);
printf("--------- end -----------\n"); } //примеры
программ char
example1[]= "program input x; x:=10/x; output x; endpro //программа
вычисления
суммы=(1+2+3+4+5) char
example2[]= "\ –this programm count sum \n\]–15.01.04\n\ program \
i:=1; s:=0; \ cycle(i<5) \ s:=s+i; i:=i+1; \ output i;\ endcycle
\ output s;\ endprog"; //программа
вычисления
факториала char
example3[]= "program \ –factorial(10)\n\ i:=1; s:=1;\ cycle(i<10)
\ s:=s*i; i:=i+1;\ output i;\ endcycle\ output s;\ endprog";
void main() {
TopVar=0; //инициализация
стека
и
таблицы
переменных
printf("trans>"); //вывод
подсказки
gets(in_line); //ввод
строки
символов
strcpy(in_line,"program x:=10/x if(c>20) then c:=5
else
c:=6 endif
endprog");
strcpy(in_line,"program x:=10/x cycle(c>20)
c:=c+1
endcycle endprog");
strcpy(in_line,example2);
ch=&in_line[0]; //инициализация
указателя
разбора
printf("%s\n",in_line);
trans_program();
Disassembler(program);
PrintTablVar();
StackVirtualMachine(program,Mem);
getch(); }
|