📄 pl0.cpp
字号:
{
printf("文件 %s 不能打开.\n", s);
exit(1);
}
phi = createset(SYM_NULL);
relset = createset(SYM_EQU, SYM_NEQ, SYM_LSS, SYM_LEQ, SYM_GTR, SYM_GEQ, SYM_NULL);
declbegsys = createset(SYM_CONST, SYM_VAR, SYM_PROCEDURE, SYM_NULL);
statbegsys = createset(SYM_BEGIN, SYM_CALL, SYM_IF, SYM_WHILE, SYM_READ, SYM_WRITE, SYM_FOR, SYM_REPEAT, SYM_NULL);
facbegsys = createset(SYM_IDENTIFIER, SYM_NUMBER, SYM_LPAREN, SYM_NULL);
err = cc = cx = ll = 0;
ch = ' ';
getsym(); //词法分析,读取一个单词
set1 = createset(SYM_PERIOD, SYM_NULL);
set2 = uniteset(declbegsys, statbegsys);
set0 = uniteset(set1, set2);
block(set0);
destroyset(set1);//释放set1空间
destroyset(set2);//释放set2空间
destroyset(set0);//释放set0空间
destroyset(phi);//释放phi空间
destroyset(relset);//释放relset空间
destroyset(declbegsys);//释放declbegsys空间
destroyset(statbegsys);//释放statbegsys空间
destroyset(facbegsys);//释放空facbegsys间
if (sym != SYM_PERIOD) //主程序分析结束,应遇到表明程序结束的句号
error(9); // 应为'.' .
if (err == 0) //如果出错次数为0,可以开始解释执行编译产生的代码
interpret(); //开始解释执行类PCODE代码
else
printf("有 %d 个错误在此 PL/0 程序里.\n", err);
fclose(infile); //关闭源程序文件
fclose(outfile);
system("pause");
return 1;
}
/*************************************************************************/
void error(int n) //出错处理,打印出错位置和错误编码
{
int i;
printf("*****");
for (i=1;i<=cc-1;i++)
printf(" ");
fprintf(outfile, " ");
fprintf(outfile, "^\n");//在屏幕cc-1位置显示^与出错代码提示,由于cc是行缓冲区指针,所以^所指位置即为出错位置
printf("^\n"); //在文件cc-1位置输出^与出错代码提示
fprintf(outfile, "Error %3d: %s\n", n, err_msg[n]);
printf("Error %3d: %s\n", n, err_msg[n]);
err++;
}
/**********************************/
/*词法分析程序getsym将完成下列任务:
*1.滤空格
*2.识别保留字
*3.识别标识符
*4.拼数
*5.拼复合词
*6.输出源程序
由于一个单词往往是由一个或几个字符组成的,
所以在记法分析过程中又定义了一个取字符
过程getch,由词法分析需要取字符时调用。
***********************************/
void getsym(void)
{
int i, k;
char a[MAXIDLEN + 1];
while (ch == ' '|| ch == '\t')//读出一个有效的字符
getch();
if (isalpha(ch))//如果读出的字符是一个字母,说明是保留字或标识符
{
k = 0; //标识符缓冲区指针置0
do
{
if (k < MAXIDLEN) //如果标识符长度没有超过最大标识符长度(如果超过,就取前面一部分,把多余的报出弃)
a[k++] = ch;
getch(); //读下一个字符
}while (isalpha(ch) || isdigit(ch)); //直到读出的不是字母或数字
a[k] = 0;
strcpy(id, a); //最后读出标识符等于a
word[0] = id;
i = NORW; //i指向最后一个保留字
while (strcmp(id, word[i--]));
if (++i)
sym = wsym[i];
else
sym = SYM_IDENTIFIER;
}
else
if (isdigit(ch))
{
k = num = 0; //数字位数k和数字num置为0
sym = SYM_NUMBER; //置sym为SYM_NUMBER,表示这一次读到的是数字
do //这个循环依次从源文件中读出字符,组成数字
{
num = num * 10 + ch - '0';
k++; //数字位数加一
getch();
}while (isdigit(ch)); //直到读出的字符不是数字为止
if (k > MAXNUMLEN) //如果组成的数字位数大于最大允许的数字位数
error(30); // 发出30号错
}
else
//对赋值号的处理
if (ch == ':') //如果读出的不字母也不是数字而是冒号
{
getch(); //再读一个字符
if (ch=='=') //如果读到的是等号就可以与冒号构成赋值符号
{
sym = SYM_BECOMES; // sym的类型设为赋值号SYM_BECOMES
getch(); //再读出下一个字
}
else
sym = SYM_NULL; // 如果不是读到等号,那单独的一个冒号就什么也不是
}
else
if (ch == '<') //如果读到小于号
{
getch(); //再读一个字符
if (ch == '=') //如果读到等号
{
sym = SYM_LEQ; // 购成一个小于等于号
getch(); //读一个字符
}
else //如果小于号后不是跟的等号
if (ch == '>')
{
sym = SYM_NEQ;// <>
getch();
}
else
sym = SYM_LSS; //那就是一个单独的小于号
}
else //如果读到不是字母也不是数字也不是冒号也不是小于号
if (ch == '>') //如果读到大于号
{
getch();//再读一个字符
if (ch == '=') //如果读到等号
{
sym = SYM_GEQ; //购成一个大于等于号
getch(); //读一个字符
}
else //如果大于号后不是跟的等号
sym = SYM_GTR; // 那就是一个单独的大于号
}
else // 如果读到不是字母也不是数字也不是冒号也不是小于号也不是大于号
{
i = NSYM;
csym[0] = ch;
while (csym[i--] != ch);
if (++i)
{
sym = ssym[i]; //直接成符号表中查到它的类型,赋给sym
getch(); //读下一个字符
}
else
{
printf("程序源代码没有完成。\n");
fprintf(outfile, "程序源代码没有完成。\n");
exit(1);
}
}
}
/*************************************************************************/
void getch(void) //读取字符
{
if (cc == ll) //如果行缓冲区指针指向行缓冲区最后一个字符就从文件读一行到行缓冲区
{
if (feof(infile)) //如果到达文件尾
{
printf("\n程序代码未完成!\n");
fclose(infile);
exit(1);
}
ll = cc = 0; //行缓冲区ll长度置0,行缓冲区指针cc置行首
printf("%4d ", cx);
fprintf(outfile, "%4d ", cx); //输出cx值,宽度为4
while ( (!feof(infile))&& ((ch = getc(infile)) != '\n')) //当未到行末时,从文件读入一个字符到 ch
{
ll=ll+1;
fprintf(outfile, "%c", ch); //把ch输出到文件
printf("%c", ch); //在屏幕输出ch
line[ll] = ch; //行缓冲区ll长度加一,把读到的字符存入行缓冲区相应的位置
}
printf("\n");
fprintf(outfile, "\n");
ll=ll+1;
line[ll] = ' '; // 行缓冲区长度加一,用于容纳即将读入的回车符CR
}
cc=cc+1;
ch = line[cc]; //行缓冲区指针cc加一,指向即将读到的字符;读出字符,放入全局变量ch
}
/*******************************************************
×生成Pcode指令,送入目标程序区,供后面的解释器解释执行。
*******************************************************/
void gen(int x, int y, int z)//x要生成的一行代码的助记符,y、z是代码的两个操作数
{
if (cx > CXMAX)//如果cx>cxmax表示当前生成的代码行号大于允许的最大代码行数
{
fprintf(outfile, "程序代码过长!\n");
printf("程序代码过长!\n");
fclose(infile);
exit(1);
}
//把代码写入目标代码数组的当前cx所指位置
code[cx].f = x;
code[cx].l = y;
code[cx].a = z; //移动cx指针指向下一个空位
cx=cx+1;
}
/*************************************************************************/
void listcode(int from, int to) //列出当前一层类PCODE目标代码过程listcode
{
int i;
printf("类Pcode指令代码:\n");
for (i = from; i < to; i++)
{
printf("%4d %s\t%d\t%d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);
fprintf(outfile, "%4d %s\t%d\t%d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);
}
}
/*******************************************************************
* 测试当前单词符号是否合法
* s1:当语法分析进入或退出某一语法单元时当前单词符合应属于的集合
* s2:在某一出错状态下,可恢复语法分析正常工作的补充单词集合
********************************************************************/
void test(symset s1, symset s2, int n)
{
symset s;
if (! inset(sym, s1))//如果当前符号不在s1中
{
showset(s1);
showset(s2);
printf("sym=%d, id=%s\n", sym, id);
error(n);//发出n号错误
s = uniteset(s1, s2);// 把s2集合补充进s1集合
while(! inset(sym, s))//通过循环找到下一个合法的符号,以恢复语法分析工作
getsym();
destroyset(s);
}
}
/*************************************************************************/
void enter(int kind) //登录符号表过程,kind欲登录到符号表的符号类型
{
//int i;
mask *mk;
if ( position(id)> 0 )
error(26);
tx++;//符号表指针指向一个新的空位
strcpy(table[tx].name, id);//开始登录,name是符号的名字,对于标识符,这里就是标识符的名字
table[tx].kind = kind;
switch (kind)//根据不同的类型进行不同的操作
{
case ID_CONSTANT://如果是常量名
if (num > MAXADDRESS)//在常量的数值大于允许的最大值的情况下
{
error(30);
num = 0; //实际登录的数字以0代替
}
table[tx].value = num; //如是合法的数值,就登录到符号表
break;
case ID_VARIABLE: //如果是变量名
mk = (mask*) &table[tx];
mk->level = level;//记下它所属的层次号
mk->address = dx++;//记下它在当前层中的偏移量,偏移量自增一,为下一次做好准备
break;
case ID_PROCEDURE: //如果要登录的是过程名
mk = (mask*) &table[tx];
mk->level = level;//记录下这个过程所在层次
break;
}
}
/*************************************************************************/
int position(char* id) // 在符号表中查找指定符号所在位置的函数
{
int i;
strcpy(table[0].name, id); //先把id放入符号表0号位置
i = tx ;//从符号表中当前位置也即最后一个符号开始找
while (strcmp(table[i].name, id) != 0) //如果当前的符号与要找的不一致,找前面一个
i--;
return i; //返回找到的位置号,如果没找到则返回0
}
/*************************************************************************/
void block(symset fsys)//分程序分析处理,fsys用于出错恢复的单词集合
{
int cx0; //记录本层开始时代码段分配位置
mask* mk;
int block_dx;//数据段内存分配指针,指向下一个被分配空间在数据段中的偏移位置
int savedTx;//记录本层开始时符号表位置
symset set1, set;
dx = 3;//地址指示器给出每层局部量当前已分配到的相对位置。
block_dx = dx;
mk = (mask*) &table[tx];
mk->address = cx; //符号表当前位置记下当前层代码的开始位置
gen(JMP, 0, 0); //产生一行跳转指令,跳转位置暂时未知填0
if (level > MAXLEVEL) //如果当前过程嵌套层数大于最大允许的套层数
error(32);
do
{//开始循环处理源程序中所有的声明部分
if (sym == SYM_CONST) //如果当前token是const保留字,开始进行常量声明
{
getsym(); //取下一个token,正常应为用作常量名的标识符
do
{ //反复进行常量声明
constdeclaration(); //声明以当前token为标识符的常量
while (sym == SYM_COMMA) //如果遇到了逗号则反复声明下一个常量
{
getsym(); //获取下一个token,这里正好应该是标识符
constdeclaration(); //声明以当前token为标识符的常量
}
if (sym == SYM_SEMICOLON)//如果常量声明结束,应遇到分号
getsym(); //获取下一个token,为下一轮循环做好准备
else
error(5); //如果常量声明语句结束后没有遇到分号则发出5号错误
}while (sym == SYM_IDENTIFIER); //如果遇到非标识符,则常量声明结束
}
if (sym == SYM_VAR) //如果当前token是var保留字,开始进行变量声明,与常量声明类似
{
getsym(); // 获取下一个token,此处正常应为用作变量名的一个标识符
do
{ //反复进行变量声明
vardeclaration(); //以当前token为标识符声明一个变量
while (sym == SYM_COMMA)//如果遇到了逗号则反复声明下一个变量
{
getsym(); //获取下一个token,这里正好应该是标识符
vardeclaration(); //声明以当前token为标识符的变量
}
if (sym == SYM_SEMICOLON) //如果变量声明结束,应遇到分号
getsym(); //获取下一个token,为下一轮循环做好准备
else
error(5); //如果变量声明语句结束后没有遇到分号则发出5号错误
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -