📄 pl0.cpp
字号:
}while (sym == SYM_IDENTIFIER); //如果遇到非标识符,则变量声明结束
block_dx = dx;
}
while (sym == SYM_PROCEDURE) //循环声明各子过程
{
getsym(); //获取下一个token,此处正常应为作为过程名的标识符
if (sym == SYM_IDENTIFIER) //如果token确为标识符
{
enter(ID_PROCEDURE); //把这个过程登录到名字表中
getsym(); //获取下一个token,正常情况应为分号
}
else
error(4);
if (sym == SYM_SEMICOLON) //如果当前token为分号
getsym(); // 获取下一个token,准备进行语法分析的递归调用
else
error(5);
level++;
savedTx = tx;
set1 = createset(SYM_SEMICOLON, SYM_NULL);
set = uniteset(set1, fsys);
block(set); //递归调用语法分析过程,当前层次加一,同时传递表头索引、合法单词符
destroyset(set1);
destroyset(set);
tx = savedTx;
level--;
if (sym == SYM_SEMICOLON) //递归返回后当前token应为递归调用时的最后一个end后的分号
{
getsym(); //获取下一个token
set1 = createset(SYM_IDENTIFIER, SYM_PROCEDURE, SYM_NULL);
set = uniteset(statbegsys, set1);
test(set, fsys, 6); //检查当前token是否合法,不合法则用fsys恢复语法分析同时报出6号错误
destroyset(set1);
destroyset(set);
}
else
error(5); //如果过程声明后的符号不是分号,报出出5号错误
}
set1 = createset(SYM_IDENTIFIER, SYM_NULL);
set = uniteset(statbegsys, set1);
test(set, declbegsys, 7); //检查当前状态是否合法,不合法则用声明开始符罕ǔ鲎鞒龃砘指础报出?号错
destroyset(set1);
destroyset(set);
}while (inset(sym, declbegsys)); //直到声明性的源程序分析完毕,继续向下执行,分析主程序
code[mk->address].a = cx; //把前面生成的跳转语句的跳转位置改成当前位置
mk->address = cx; //地址为当前代码分配地址
cx0 = cx; //记下当前代码分配位置
gen(INT, 0, block_dx); //生成分配空间指令,分配dx个空间
set1 = createset(SYM_SEMICOLON, SYM_END, SYM_NULL);
set = uniteset(set1, fsys);
statement(set); //处理当前遇到的语句或语句块
destroyset(set1);
destroyset(set);
gen(OPR, 0, 0); //生成从子程序返回操作指令
test(fsys, phi, 8); // 用fsys检查当前状态是否合法,不合法则报出8号错
listcode(cx0, cx); //列出本层的类PCODE代码
}
/*************************************************************************/
void constdeclaration() //常量声明处理过程
{
if (sym == SYM_IDENTIFIER) //常量声明过程开始遇到的第一个符号必然应为标识符
{
getsym(); //获取下一个token
if (sym == SYM_EQU || sym == SYM_BECOMES) //如果是等号或赋值号
{
if (sym == SYM_BECOMES)//如果是赋值号(常量生明中应该是等号)
error(1); //这里其实自动进行了错误纠正使编译继续进行,把赋值号当作等号处理
getsym(); //获取下一个token,等号或赋值号后应接上数字
if (sym == SYM_NUMBER) //如果的确是数字
{
enter(ID_CONSTANT); //把这个常量登陆到符号表
getsym(); //获取下一个token,为后面作准备
}
else
error(2); // 如果等号后接的不是数字,报出出2号错误
}
else
error(3); // 如果常量标识符后接的不是等号或赋值号,报出出3号错误
}
else
error(4); // 如果常量声明过程遇到的第一个符号不为标识符,报出出4号错误
}
/*************************************************************************/
void vardeclaration(void) //变量说明处理
{
if (sym == SYM_IDENTIFIER)//变量声明过程开始遇到的第一个符号必然应为标识符
{
enter(ID_VARIABLE); //将标识符登陆到符号表中
getsym();//获取下一个token,为后面作准备
}
else
error(4); // 如果变量声明过程遇到的第一个符号不是标识符,报出4号错误
}
/*************************************************************************/
void statement(symset fsys) //语句部分分析处理,fsys如果出错可用来恢复语法分析的符号集合
{
int i, cx1, cx2, q;
char ida[MAXIDLEN + 1];
symset set1, set;
mask* mk;
if (sym == SYM_IDENTIFIER)
{
if (! (i = position(id))) //在符号表中没有查到该标识符所在位置
error(11);
else
if (table[i].kind != ID_VARIABLE) //如果在符号表中找到该标识符,但该标识符不是变量名
{
error(12);
i = 0; //i置0作为错误标志
}
getsym(); //获得下一个token,正常应为赋值号
if (sym == SYM_BECOMES) //如果的确为赋值号
getsym(); //获取下一个token,正常应为一个表达式
else
error(13);
expression(fsys); //处理表达式
mk = (mask*) &table[i];
if (i) //如果不曾出错,i将不为0,i所指为当前语名左部标识符在符号表中的位置
gen(STO, level - mk->level, mk->address);//产生一行把表达式值写往指定内存的sto目标代码
}
else
if (sym == SYM_CALL) //如果是call语句
{
getsym(); //获取token,应是过程名型标识符
if (sym != SYM_IDENTIFIER) //如果call后跟的不是标识符
error(14);
else//从符号表中找出相应的标识符
{
if (! (i = position(id)))
error(11);
else //如果找到标识符位于符号表第i位置
if (table[i].kind == ID_PROCEDURE) //如果这个标识符为一个过程名
{
mk = (mask*) &table[i];
gen(CAL, level - mk->level, mk->address); //生成cal目标代码,呼叫这个过程
}
else
error(15); // 如果call后跟的不是过程名,报出出15号错误
getsym(); //获取下一token,为后面作准备
}
}
else
if (sym == SYM_IF) //如果是if语句
{
getsym(); //获取一token应是一个逻辑表达式
set1 = createset(SYM_THEN, SYM_DO, SYM_NULL); //对逻辑表达式进行分析计算,出错恢复集中加入then和do语句
set = uniteset(set1, fsys);
condition(set);
destroyset(set1);
destroyset(set);
if (sym == SYM_THEN) //表达式后应遇到then语句
getsym(); //获取then后的token,应是一语句
else
error(16); // 如果if后没有then,报出出16号错误
cx1 = cx; //记下当前代码分配指针位置,这是then的开始位置
gen(JPC, 0, 0); //生成条件跳转指令,跳转位置暂时填0,分析完语句后再填写
set1 = createset(SYM_ELSE, SYM_NULL);
set = uniteset(set1, fsys);
statement(set);
destroyset(set1);
destroyset(set);
if (sym != SYM_ELSE) //表达式后遇到不是else语句
code[cx1].a = cx;//把then填0的跳转位置改成当前位置,完成if语句的处理
else
{//表达式后遇到else语句
getsym();
cx2=cx;//记下当前代码分配位置,这是else的开始位置
gen(JMP,0,0);//产生一行跳转指令,跳转位置暂时未知填0
statement(fsys);
code[cx1].a = cx2 + 1;//把then语句填0的跳转位置改成当前位置,完成if语句的处理
code[cx2].a = cx;//把else填0的跳转位置改成当前位置,完成else语句的处理
}
}
else
if (sym == SYM_BEGIN) //如果遇到begin
{
getsym(); //获取下一个token
set1 = createset(SYM_SEMICOLON, SYM_END, SYM_NULL);
set = uniteset(set1, fsys);
statement(set); //对begin与end之间的语句进行分析处理
while (sym == SYM_SEMICOLON || inset(sym, statbegsys))
{ //如果分析完一句后遇到分号或语句开始符循环分析下一句语句
if (sym == SYM_SEMICOLON) //如果语句是分号(可能是空语句)
getsym(); //获取下一token继续分析
else
error(10); //如果语句与语句间没有分号,出10号错
statement(set); //分析一个语句
}
destroyset(set1);
destroyset(set);
if (sym == SYM_END)//如果语句全分析完了,应该遇到end
getsym(); // 的确是end,读下一token
else
error(17);
}
else
if (sym == SYM_WHILE)//如果遇到while语句
{
cx1 = cx; //记下当前代码分配位置,这是while循环的开始位置
getsym(); //获取下一token,应为一逻辑表达式
set1 = createset(SYM_DO, SYM_NULL);
set = uniteset(set1, fsys);
condition(set); //对这个逻辑表达式进行分析计算
destroyset(set1);
destroyset(set);
cx2 = cx; //记下当前代码分配位置,这是while的do中的语句的开始位置
gen(JPC, 0, 0); // 生成条件跳转指令,跳转位置暂时填0
if (sym == SYM_DO) //逻辑表达式后应为do语句
getsym(); //获取下一token
else
error(18); //if后缺少then,报出出18号错误
statement(fsys); //分析do后的语句块
gen(JMP, 0, cx1); //循环跳转到cx1位置,即再次进行逻辑判断
code[cx2].a = cx; //把刚才填0的跳转位置改成当前位置,完成while语句的处理
}
else
if(sym == SYM_READ) // 如果不是赋值语句,而是遇到了read语句
{
getsym();//获得下一token,正常情况下应为左括号
if(sym != SYM_LPAREN) //如果read语句后跟的不是左括号
error(40);
else //循环得到read语句括号中的参数表,依次产生相应的“从键盘读入”目标代码
{
do
{
getsym(); //获得一个token,正常应是一个变量名
if (sym == SYM_IDENTIFIER) //如果确为一个标识符
{
if (! (i = position(id))) //查符号表,找到它所在位置给i,找不到时i会为0
error(11);
else
if( table[i].kind!=ID_VARIABLE) //判别类型是否为标识符
{
error(12);
i=0;
}
else
{
mk = (mask*) &table[i];
gen(RED, level - mk->level, mk->address); //生成red指令,把读入的值存入指定变量所在的空间
}
}
else
error(4);
getsym();
}while(sym==SYM_COMMA);//不断生成代码直到read语句的参数表中的变量遍历完为止
if(sym!=SYM_RPAREN)
error(22);
getsym(); //如果read语句正常结束,得到下一个token,一般为分号或end
}
}
else
if(sym == SYM_WRITE)// 如果不是赋值语句,而是遇到了write语句
{
getsym(); //获取下一token,应为左括号
if(sym != SYM_LPAREN)//如果write语句后跟的不是左括号
error(40);
else
{
do
{ //依次获取括号中的每一个值,进行输出
getsym();
set1 =createset(SYM_RPAREN, SYM_COMMA, SYM_NULL);
set=uniteset(set1,fsys);
expression(set);
gen(WRT,0,0);//生成wrt指令,把读入的值存入指定变量所在的空间
}while(sym==SYM_COMMA);
if(sym!=SYM_RPAREN)//如果不是右括号
error(22);
getsym();//正常情况下要获取下一个token,为后面准备好
}
}
else
if(sym == SYM_FOR)//for循环语句
{
getsym();
if(sym != SYM_IDENTIFIER)
error(25);
strcpy(ida, id); //用另一个ida记录当前读到的IDENTIFIER
set1 = createset(SYM_TO, SYM_DOWNTO, SYM_NULL);
set = uniteset(set1, fsys);
statement(set);
destroyset(set1);
destroyset(set);
if((sym != SYM_TO) && (sym != SYM_DOWNTO))
error(26);
q=sym; //记录下当前所读到的字符
cx1=cx;//记下当前代码分配位置,这是to/downto语句的开始位置
gen(JMP,0,0);//产生一行跳转指令,跳转位置暂时未知填0
getsym();
set1 =createset(SYM_DO,SYM_BY,SYM_NULL);
set=uniteset(set1,fsys);
expression(set);
if((sym != SYM_DO) && (sym != SYM_BY))
error(27);
strcpy(id, ida);//将ida记录下的IDENTIFIER拷贝给id
i=position(id);
if(i==0)
error(11);
else
if( table[i].kind!=ID_VARIABLE)//判别类型是否为变量
{
error(12);
i=0;
}
else
{
mk = (mask*) &table[i];
gen(LOD, level - mk->level, mk->address);//生成lod指令,把读入的值存入指定变量所在的空间
if(sym == SYM_BY)
{ //对by后的表达式进行判断
getsym();
//如果前一个字符为downto,则by后的sym应为SYM_MINUS,否则出错
//如果前一个字符为to,则by后的sym应为SYM_PLUS,否则出错
if(( sym==SYM_MINUS) || ( sym == SYM_PLUS))
{
set1 =createset(SYM_DO, SYM_NULL);
set=uniteset(set1,fsys);
expression(set);
if(sym != SYM_DO)
error(28);
}
else
{
if(q==SYM_DOWNTO)
error(35);
else
if(q==SYM_TO)
error(36);
}
}
else //当没有by时,执行默认处理指令
{
gen(LIT,0,1);
//downto默认执行1号操作
if(q==SYM_DOWNTO)
gen(OPR,0,1);
}
gen(OPR,0,2);
mk = (mask*) &table[i];
gen(STO,level - mk->level, mk->address);//生成sto指令,把读入的值存入指定变量所在的空间
gen(LOD,level - mk->level, mk->address);//生成lod指令,把读入的值存入指定变量所在的空间
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -