📄 symtax.cpp
字号:
#include "StdAfx.h"
#include ".\symtax.h"
#include <string.h>
#include "pl0.h"
#include "errors.h"
#include "wording.h"
#include "form.h"
#include "code.h"
CSymtax::CSymtax(CPl0 *p)
{
pl=p;
//确立声明开始、表达式开始和项开始符号集合
declbegsys+=constsym;
declbegsys+=varsym;
declbegsys+=procsym;
statbegsys+=beginsym;
statbegsys+=callsym;
statbegsys+=ifsym;
statbegsys+=whilesym;
statbegsys+=repeatsym;
facbegsys+=ident;
facbegsys+=number;
facbegsys+=lparen;
}
void CSymtax::Analysis()
{
getsym(); //首次调用词法分析子程序,获取源程序的第一个词(token)
lev=-1;
CSymset s;
s = declbegsys;
s += statbegsys;
s += period;
block(s);
}
void CSymtax::test(CSymset s1,CSymset s2,int n)
{
/*参数:s1:当语法分析进入或退出某一语法单元时当前单词符合应属于的集合
s2:在某一出错状态下,可恢复语法分析正常工作的补充单词集合
n:出错信息编号,当当前符号不属于合法的s1集合时发出的出错信息 */
if (!s1.InSet(sym)) //如果当前符号不在应属于的集合中
{
pl->errors->Add(n); //发出n号错误
s1+=s2; //把s2集合补充进s1集合
while (!s1.InSet(sym))
getsym(); //通过循环找到下一个合法的符号,以恢复语法分析工作
}
}
void CSymtax::block(CSymset fsys)
{
lev++;
int tx0,cx0; //tx0:记录本层开始时符号表位置;cx0:记录本层开始时代码段分配位置
CSymset s;
CErrors *err = pl->errors;
CCode *cd = pl->code; //dm
CForm *fm = pl->form; //bg
dx[lev]=3; //地址指示器给出每层局部量当前已分配到的相对位置。
//每一层最开始的位置有三个空间用于存放静态链SL、动态链DL和返回地址RA
tx0 = fm->TableIndex(); //初始符号表指针指向当前层的符号在符号表中的开始位置
fm->table[tx0].adr = cd->cx; //符号表当前位置记下当前层代码的开始位置
cd->Gen(jmp,0,0); //产生一行跳转指令,跳转位置暂时未知填0
if (lev>MAX_LEVEL)
err->Add(32); //如果当前过程嵌套层数大于最大允许的套层数,发送32号错误
do{
if (sym==constsym) //如果当前token是const保留字,开始进行常量声明
{
getsym(); //获取下一个token,正常应为用作常量名的标识符
do{
constdeclaration();
while (sym==comma) //如果遇到了逗号则反复声明下一个常量
{
getsym(); //获取下一个token,这里正好应该是标识符
constdeclaration();
}
if (sym==semicolon) //如果常量声明结束,应遇到分号
getsym();
else
err->Add(5); //如果常量声明语句结束后没有遇到分号则发出5号错误
}while (sym==ident); //反复进行常量声明,如果遇到非标识符,则常量声明结束
}
if (sym==varsym)
{ //如果当前token是var保留字,开始进行变量声明,与常量声明类似
getsym();
do{
vardeclaration();
while (sym==comma)
{
getsym();
vardeclaration();
}
if (sym==semicolon)
getsym();
else
err->Add(5);
}while (sym==ident);
}
while (sym==procsym) //循环声明各子过程
{
getsym(); //获取下一个token,此处正常应为作为过程名的标识符
if (sym==ident)
{
fm->Enter(procedure); //把这个过程登录到名字表中
getsym();
}
else
err->Add(4);
if (sym==semicolon) //如果当前token为分号
getsym();
else
err->Add(5);
s = fsys;
s += semicolon;
block(s); //递归调用语法分析过程,当前层次加一,同时传递表头索引、合法单词符
if (sym==semicolon)
{ //递归返回后当前token应为递归调用时的最后一个end后的分号
getsym();
s=statbegsys;
s+=ident;
s+=procsym;
test(s,fsys,6); //检查当前token是否合法,不合法则用fsys恢复语法分析同时抛6号错
}
else
err->Add(5); //如果过程声明后的符号不是分号,抛出5号错误
}
s = statbegsys;
s += ident;
test(s,declbegsys,7); //查当前状态是否合法,不合法则用声明开始符号作出错恢复、抛7号错
}while (declbegsys.InSet(sym)); //直到声明性的源程序分析完毕,继续向下执行,分析主程序
cd->code[fm->table[tx0].adr].a = cd->cx; //把前面生成的跳转语句的跳转位置改成当前位置
fm->table[tx0].adr = cd->cx; //地址为当前代码分配地址
fm->table[tx0].size = dx[lev]; //长度为当前数据代分配位置
cx0 = cd->cx; //记下当前代码分配位置
cd->Gen(tint,0,dx[lev]); //生成分配空间指令,分配dx个空间
s=fsys;
s+=semicolon;
s+=endsym;
statement(s); //处理当前遇到的语句或语句块
cd->Gen(opr,0,0); //生成从子程序返回操作指令
test(fsys,CSymset(),8); //用fsys检查当前状态是否合法,不合法则抛8号错
lev--;
}
//常量声明处理过程constdeclaration
void CSymtax::constdeclaration()
{
CErrors *err = pl->errors;
CForm *fm = pl->form;
if ( sym == ident ) //常量声明过程开始遇到的第一个符号必然应为标识符
{
getsym();
if (sym==eql || sym==becomes) //如果是等号或赋值号
{
if (sym==becomes) //如果是赋值号(常量生明中应该是等号)
err->Add(1);
getsym();
if (sym==number) //如果的确是数字
{
fm->Enter(constant); //把这个常量登陆到符号表
getsym();
}
else
err->Add(2); //如果等号后接的不是数字,抛出2号错误
}
else
err->Add(3); // 如果常量标识符后接的不是等号或赋值号,抛出3号错误
}
else
err->Add(4); //如果常量声明过程遇到的第一个符号不为标识符,抛出4号错误
}
//变量声明过程vardeclaration
void CSymtax::vardeclaration()
{
CErrors *err = pl->errors;
CCode *cd = pl->code;
CForm *fm = pl->form;
if (sym==ident) //变量声明过程开始遇到的第一个符号必然应为标识符
{
fm->Enter(variable);
dx[lev]++;
getsym();
}
else
err->Add(4); //如果变量声明过程遇到的第一个符号不是标识符,抛出4号错误
}
//因子处理过程factor
//参数说明:fsys: 如果出错可用来恢复语法分析的符号集合
void CSymtax::factor(CSymset fsys)
{
int i;
CErrors *err = pl->errors;
table_type *table = pl->form->table;
CCode *cd = pl->code;
test(facbegsys,fsys,24); //开始因子处理前,先检查当前token是否在facbegsys集合中。
//如果不是合法的token,抛24号错误,并通过fsys集恢复使语法处理可以继续进行
while (facbegsys.InSet(sym)) //循环处理因子
{
switch (sym)
{
case ident: //如果遇到的是标识符
i = pl->form->Position(pl->wording->id); //查符号表,找到当前标识符在符号表中的位置
if (i==0)
err->Add(11); //如果查符号表返回为0,表示没有找到标识符
else
{
switch (table[i].kind)
{
case constant: //如果这个标识符对应的是常量,值为val,生成lit指令,把val放到栈顶
cd->Gen(lit,0,table[i].val);
break;
case variable: //如果标识符是变量名,生成lod指令
cd->Gen(lod,lev-table[i].level,table[i].adr);
//把位于距离当前层level的层的偏移地址为adr的变量放到栈顶
break;
case procedure:
err->Add(21); //如果在因子处理中遇到的标识符是过程名,出错了,抛21号错
break;
}
}
getsym();
break;
case number: //如果因子处理时遇到数字
if ( pl->wording->num > MAX_NUMBER )
{
err->Add(31); //数字超界
pl->wording->num = 0;
}
cd->Gen(lit,0,pl->wording->num);//生成lit指令,把这个数值字面常量放到栈顶
getsym();
break;
case lparen: //如果遇到的是左括号
getsym();
expression( fsys += rparen ); //递归调用expression子程序分析一个子表达式
if (sym == rparen)
getsym(); //子表达式分析完后,应遇到右括号
else
err->Add(22);
break;
}
test(fsys,facbegsys,23); //一个因子处理完毕,遇到的token应在fsys集合中
//如果不是,抛23号错,并找到下一个因子的开始,使语法分析可以继续运行下去
}
}
//项处理过程term
void CSymtax::term(CSymset fsys)
{
symbol mulop;
CSymset symset;
symset+=fsys;
symset+=times;
symset+=slash;
factor(symset); //每一个项都应该由因子开始,因此调用factor子程序分析因子
while ( sym==times||sym==slash ) //一个因子后应当遇到乘号或除号
{
mulop = sym;
getsym();
factor(symset); //运算符后应是一个因子,故调factor子程序分析因子
if (mulop == times)
pl->code->Gen(opr,0,4); //生成乘法指令
else
pl->code->Gen(opr,0,5); //不是乘号一定是除号,生成除法指令
}
}
//表达式处理过程expression
void CSymtax::expression(CSymset fsys)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -