PL0 虚拟机

发布于:2025-03-05 ⋅ 阅读:(17) ⋅ 点赞:(0)

 

input.txt 文件内容

INT  0  3

LIT  0  2

LIT  0  3

OPR  0  2

OPR  0 14

OPR  0  0

 


#include <stdio.h>
#include <string.h>

#define stacksize 512

enum cmd {
//	lit = 0,
//	opr = 1,
//	lod = 2,
//	sto = 3,
//	cal = 4,
//	inte = 5,
	cmp=6,
//	jmp = 7,
//	jpc = 8,
//	error = 9
	
	// 重新按照字符顺序排列,这样序号和后面的字符对应,就可以根据操作码查字符是否翻译正确而不是偏移
	cal=0,
	inte=1,
	jmp=2,
	jpc=3,
	lit=4,
	lod=5,
	opr=6,
	sto=7,
	error=8
};

// 338 词法分析使用
char word[20][10];
enum cmd wsym[10];

void init() {
	/*设置保留字名字,按照字母顺序,便于折半查找*/
//	大小写也不相同
//	strcpy(&(word[0][0]), "cal");
	strcpy(&(word[1][0]),"cmp");
//	strcpy(&(word[2 - 1][0]), "inte");
//	strcpy(&(word[3 - 1][0]), "jmp");
//	strcpy(&(word[4 - 1][0]), "jpc");
//	strcpy(&(word[5 - 1][0]), "lit");
//	strcpy(&(word[6 - 1][0]), "lod");
//	strcpy(&(word[7 - 1][0]), "opr");
//	strcpy(&(word[8 - 1][0]), "sto");


	strcpy(&(word[0][0]), "CAL");
//	strcpy(&(word[1][0]),"cmp");
	strcpy(&(word[2 - 1][0]), "INT");		//INTE INT
	strcpy(&(word[3 - 1][0]), "JMP");
	strcpy(&(word[4 - 1][0]), "JPC");
	strcpy(&(word[5 - 1][0]), "LIT");
	strcpy(&(word[6 - 1][0]), "LOD");
	strcpy(&(word[7 - 1][0]), "OPR");
	strcpy(&(word[8 - 1][0]), "STO");


	// 二分查找后 字符与指令对应,字符折半,翻译区间折半对应

	wsym[0] = cal;
//	wsym[1]=cmp;
	wsym[2 - 1] = inte;
	wsym[3 - 1] = jmp;
	wsym[4 - 1] = jpc;
	wsym[5 - 1] = lit;
	wsym[6 - 1] = lod;
	wsym[7 - 1] = opr;
	wsym[8 - 1] = sto;

//	strcpy(&(word[9][0]),"if");
//	strcpy(&(word[10][0]),"int");
//	strcpy(&(word[11][0]),"odd");
//	strcpy(&(word[12][0]),"procedure");
//	strcpy(&(word[13][0]),"read");
//	strcpy(&(word[14][0]),"then");
//	strcpy(&(word[15][0]),"to"); //新增to
//	strcpy(&(word[16][0]),"var");
//	strcpy(&(word[17][0]),"while");
//	strcpy(&(word[18][0]),"write");
}

//*x:instruction.f; //操作码
//*y:instruction.l; //跳转指令需要的层次差
//*z:instruction.a; //地址或操作数
struct instruction {
//	int f;					// opr 之类开头字母
	cmd f;
	int l;					// 第几号
	int a;					// 参数
};

struct instruction code[1000];
FILE* fa2;								// 输出结果

cmd getoption(char* id) {
	int i, j, k;
	int norw = 8; // 9 不行 // 8个
//	char* id=new char[20];
	cmd sym;							// 翻译的指令
//	k=0;
	i = 0;
	j = norw - 1;
	do {
		k = (i + j) / 2;
		if (strcmp(id, word[k]) <= 0) {
			j = k - 1;
//			printf("字符小于列表\n");
//			printf("%s\n",id);
		}
		if (strcmp(id, word[k]) >= 0) {
			i = k + 1;
		}
//		if (strcmp(id, word[k]) == 0) {
//			break;
//		}
//		printf("%s\n",word[k]);
//		printf("%d\n",k);
	} while (i <= j);

	printf("匹配字位置 %d\n", k);

	if (i - 1 > j) {
		sym = wsym[k]; //保留字
		//printf("识别出保留字\n");
		return sym;
	} else {
//		sym=ident; //标识符
		//printf("识别出标识符\n");
		sym = error;
		return sym;
	}
}

// 每一个开头字符
int transop(char* cmdstr, instruction* code) {
	char* p = cmdstr;
	char op[100];
	int cnt = 0;
	while ((*p) != ' ') {
		op[cnt] = *p;
		cnt++;
		p++;
	}
	op[cnt] = '\0';
//	printf("%s", op);

	enum cmd opv2 ;
	opv2 = getoption(op);
	if (opv2 == error) {
		printf("操作码查询失败\n");
		printf("无效命令 --%s--\n", op);
	} else {
		printf("翻译参数\n");
		code->f = opv2;
		printf("op = %d\n",opv2);
		printf("%s\n", cmdstr);
		printf("%s\n",word[opv2]);
		// 字符串转数字
		// 发现两个空格
//		p++;
//		p++;

		while (*p == ' ') {
			p++;
		}

		int num = 0;
		while ((*p) != ' ') {
//			printf("kkk %c\n", *p);
			num = num * 10 + *p - '0';
			p++;
		}
		code->l = num;
		printf("%d\n", num);
//
//		while (*p == ' ') {
//			p++;
//		}
//
//		int lnumv2[20];
//		int iv2 = 0;
//		while ((*p) != ' '&&(*p)!='\0'&&(*p)!='\n') {
//			lnum[iv2] = *p - '0';
//			printf("end2=%c\n",*p);
//			iv2++;
//			p++;
//		}
//		int numv2 = 0;
//
//		while (iv2 > 0) {
//			iv2--;
//			numv2 = numv2 * 10 + lnumv2[iv2];
//		}
//		printf("a=%d\n", numv2);
//		code->a = numv2;

		while (*p == ' ') {
			p++;
		}
		
		int numv2 = 0;
		while ((*p) != ' ' && (*p) != '\0' && (*p) != '\n') {
//		while ((*p) != ' ' && (*p) != '\0') {
//			if(*p=='\n'){
//				printf("发现回车\n");
//				*p='\0';
//				break;
//			}
			numv2 = numv2 * 10 + (*p) - '0';
			printf("a=%c\n", *p);
			p++;
		}
//		numv2= numv2/10-('\n'-'0');
		
		printf("numv2=%d\n",numv2);
		code->a = numv2;

	}
	return 1;

}





/**
 * 通过目标层次callee与当前层次caller层差来获声明callee的最近活动记录基址
 * @param L 目标层次与当前层次的层次差
 * @param stack 运行栈
 * @param base 当前活动记录基址
 * @return 目标活动记录的基址
 */

int base(int L, int* stack, int base) {
	int pointer = base;
	while (L > 0) {
		pointer = stack[pointer];
		L--;
	}

	return pointer;
}



/*
* PL0 解释程序
* 来源:清华大学,王生原等,《编译原理》附录A
**/

void interpret() {
	int p, b, t;    /* 指令指针,指令基址,栈顶指针 */
	struct instruction i;   /* 存放当前指令 */
	int s[stacksize];   /* 栈 */

	for(int n=0;n<stacksize;n++){
		s[n]=0;
	}
	fa2=fopen("output.txt","w");		// 发现命令中有文件输出,所以之前需要开启文件
	printf("start pl0\n");
	t = 0;
	b = 0;
	p = 0;
	s[0] = s[1] = s[2] = 0;
	do {
		printf("%d,%d,%d,%d\n", p, code[p].f, code[p].l, code[p].a);
		for(int k=10;k>0;k--){
			printf("%d\n",s[k]);
		}
		i = code[p];    /* 读当前指令 */
		p++;
		switch (i.f) {
			case lit:   /* 将a的值取到栈顶 */
				s[t] = i.a;
				t++;
				break;
			case opr:   /* 数学、逻辑运算 */
				switch (i.a) {
					case 0:
					printf("最后退出 opr 0 0 0指令\n");
						t = b;
						p = s[t + 2];
						b = s[t + 1];
						break;
					case 1:
						s[t - 1] = -s[t - 1];
						break;
					case 2:
						t--;
						s[t - 1] = s[t - 1] + s[t];
						break;
					case 3:
						t--;
						s[t - 1] = s[t - 1] - s[t];
						break;
					case 4:
						t--;
						s[t - 1] = s[t - 1] * s[t];
						break;
					case 5:
						t--;
						s[t - 1] = s[t - 1] / s[t];
						break;
					case 6:
						s[t - 1] = s[t - 1] % 2;
						break;
					case 8:
						t--;
						s[t - 1] = (s[t - 1] == s[t]);
						break;
					case 9:
						t--;
						s[t - 1] = (s[t - 1] != s[t]);
						break;
					case 10:
						t--;
						s[t - 1] = (s[t - 1] < s[t]);
						break;
					case 11:
						t--;
						s[t - 1] = (s[t - 1] >= s[t]);
						break;
					case 12:
						t--;
						s[t - 1] = (s[t - 1] > s[t]);
						break;
					case 13:
						t--;
						s[t - 1] = (s[t - 1] <= s[t]);
						break;
					case 14:
						printf("%d", s[t - 1]);
						fprintf(fa2, "%d", s[t - 1]);
						t--;
						break;
					case 15:
						printf("\n");
						fprintf(fa2, "\n");
						break;
					case 16:
						printf("?");
						fprintf(fa2, "?");
						scanf("%d", &(s[t]));
						fprintf(fa2, "%d\n", s[t]);
						t++;
						break;
				}
				break;
			case lod:   /* 取相对当前过程的数据基地址为a的内存的值到栈顶 */
				s[t] = s[base(i.l, s, b) + i.a];
				t++;
				break;
			case sto:   /* 栈顶的值存到相对当前过程的数据基地址为a的内存 */
				t--;
				s[base(i.l, s, b) + i.a] = s[t];
				break;
			case cal:   /* 调用子过程 */
				s[t] = base(i.l, s, b); /* 将父过程基地址入栈 */
				s[t + 1] = b; /* 将本过程基地址入栈,此两项用于base函数 */
				s[t + 2] = p; /* 将当前指令指针入栈 */
				b = t;  /* 改变基地址指针值为新过程的基地址 */
				p = i.a;    /* 跳转 */
				break;
			case inte:  /* 分配内存 */
				t += i.a;
				break;
			case jmp:   /* 直接跳转 */
				p = i.a;
				break;
			case jpc:   /* 条件跳转 */
				t--;
				if (s[t] == 0) {
					p = i.a;
				}
				break;
		}
	} while (p != 0);

//	fa2 = fopen("output.txt", "w");
//	for (int i = 0; i < t; i++) {
//		printf("%d\n", t);
//		fprintf(fa2, "%d\n", s[t]);
//	}
	fclose(fa2);
}



int main() {
	init();

	FILE* fp;
	fp = fopen("input.txt", "r");


	char* str = new char[200];
	char** cmd = new char*[200];
	for (int i = 0; i < 100; i++) {
		cmd[i] = new char[200];
	}
	int cnt = 0;
	while (fgets(str, 200, fp) != NULL) {

		if (strcmp(str, "\n") == 0) {
//			printf("发现空\n");
//			printf("--%s--\n", str);
		} else if (str[0] == '\n') {

		} else {
			strcpy(cmd[cnt], str);	// 存储命令
			printf("--%s--\n", str);
//			printf("%d\n",strlen(str));
			cnt++;
		}

	}


	for (int i = 0; i < cnt; i++) {
		if (cmd[i][0] != '\0')
			transop(cmd[i], &code[i]);
	}


	for (int i = 0; i < 10; i++) {
		printf("%d,%d,%d\n", code[i].f, code[i].l, code[i].a);
	}

//	fa2 = fopen("output.txt", "w");

//	fprintf(s[t],fa2);
	interpret();

//	fclose(fa2);
	fclose(fp);


	return 0;
}