这次课程实验要求实现一个SLR(1)语法分析器,所以自己抽空用c++写了一个,当然我也没用什么c++特性,所以当做c程序来运行应该也差不多,顶多改改头文件和比较string的函数,废话不多说,就直接进入正题吧。
首先来看下项目目录,这里面有一部分是词法分析,但词法分析比较简单,这里就不讲了,说到底就是字符串的解析。
这里面着重讲几个地方:
1、分析表单元没什么好讲的,其实就两个部分:op是操作(s是状态跳转;r是用第几个产生式进行归约;acc是接收;*是出错;gt是非终结符的状态转换),direct是方向(s和gt时是跳转到的状态,r时是归约用第几个产生式)
2、collection其实也没什么好说的,follow集和first集都是用的这个结构体,vts是集合对应的终结符id(下标),top是vts的数组尾。
3、item是项目,简单来说就是 E->A.B 这样的,pId是对应产生式的id(下标),idx是 . 的前一个符号的下标,也就是在分析栈中最后一个元素的下标,假设 E->AB 是第3号产生式,那 E->A.B 对应的 item 是 pId=3,idx=0。需要注意的是 E->.AB 对应的idx是-1。
4、production是产生式,leftPart是产生式左部,rightPart是产生式右部的符号数组,rightPartLength是对应符号数组的数组尾。如S->AB,leftPart是S,rightPart[0]是A,rightPart[1]是B,rightPartLength是1(尾部下标)。
5、state是状态,就是状态机的每个状态。id暂时没有用到,是状态的序号。item是s状态对应的项目集,top是items的尾部下标。
6、symbol和word是词法分析用到的,这里我把词法和语法分析解耦了,所以这里用不到,没什么好讲的。
话不多说,直接放:
#define MAX 10000
// 产生式
Production productions[100]; // 产生式集合
int productionTop = 0; // 产生式集合最大下标
// 非终结符
string vtn[100]; // 非终结符集合
int vtnTop = 0; // 非终结符集合最大下标
// 终结符
string terminal[100]; // 非终结符集合
int terminalTop = 0;
// 状态
State CC[100]; // 项目集规范族
int CCTop = 0; // 最大下标
State firstState; // 初始状态
// 分析表
AnalysisCell actionTable[100][100]; // action 表
AnalysisCell gotoTable[100][100]; // goto 表
// FIRST 集
Collection FIRST[100]; // 非终结符下标对应的 first 集
int firstTop = 0; // 数组尾
// FOLLOW 集
Collection FOLLOW[100]; // 非终结符下标对应的 follow 集
int followTop = 0; // 数组尾
先看看函数头文件:
函数非常多,但大多都是功能性的,所以这里简单介绍下重要函数和流程。
分析的第一步首先要构建项目集规范族(状态机可以看成),我们主要通过CLOSURE、GOTO和getCanonicalCollection三个函数来构建。
这个函数用来计算一个状态(也可以看成一组项目集)的闭包,这里不讲过多的原理,需要自己学习,简单举个例子,假设有产生式A->B和B->b(大写是非终结符,小写是终结符),则A->.B的闭包是A->.B和B->.b 。
// 获取闭包
State CLOSURE(State target){
State newState;
newState.items = copyItems(target);
newState.top = target.top;
//newState.id = target.id + 1;
// 判断是否停止
Item *itemStack = copyItems(target); // 待处理item栈
int stackTop = target.top; // 栈顶
while(stackTop != 0){
// 出栈
stackTop--;
int idx = itemStack[stackTop].idx;
Production tempPro = productions[itemStack[stackTop].pId];
// 判断该item是否还能扩展
if(idx < tempPro.rightPartLength){
string tempVT = tempPro.rightPart[idx + 1]; // 点右边的第一个字符
if(isNonTerminal(tempVT)){ // 如果是非终结符
int* pId = existLeftPro(tempVT);
for(int i = 0; i < productionTop; i++){
int id = *(pId + i);
if(id != -1 && !itemRepeat(newState, id, -1)){
// 添加新的item,点标在最左边
newState.items[newState.top].pId = id;
newState.items[newState.top].idx = -1;
// 入栈
itemStack[stackTop++] = newState.items[newState.top];
newState.top++;
}
}
}
}
}
GOTO其实很简单,就是给定一个状态和下一个要识别的符号,移动 . 并计算新的闭包。举个简单的例子,一个状态s的项目集是S->.B与S->A.CD,如果GOTO(s, C),得到的结果就是 S->AC.D 的闭包。
// GOTO
State GOTO(State state, string x){
State j;
j.top = 0;
for(int i = 0; i < state.top; i++){
int pId = state.items[i].pId;
int idx = state.items[i].idx;
if(productions[pId].rightPart[idx + 1].compare(x) == 0){ // 如果下一个符号是目标符号
j.items[j.top].idx = idx + 1;
j.items[j.top].pId = pId;
j.top++;
}
}
return CLOSURE(j);
}
这个其实没啥好说的,就是一直找,看有没有新的状态。
// 获取项目集规范族
void getCanonicalCollection(){
firstState.items[0].pId = 0;
firstState.items[0].idx = -1;
firstState.top = 1;
CC[CCTop++] = CLOSURE(firstState);
State stateStack[MAX];
stateStack[0] = CC[CCTop - 1];
int stackTop = 1;
while(stackTop != 0){
// 出栈
stackTop--;
State topState = stateStack[stackTop];
// 对非终结符
for(int i = 0; i < vtnTop; i++){
State temp = GOTO(topState, vtn[i]);
if(temp.top !=0 && !checkItemInCC(temp)){ // 一个状态不能为空,如当GOTO(i, "#")时
stateStack[stackTop++] = temp;
CC[CCTop++] = temp;
}
}
// 对终结符
for(int i = 0; i < terminalTop; i++){
State temp = GOTO(topState, terminal[i]);
if(temp.top !=0 && !checkItemInCC(temp)){
stateStack[stackTop++] = temp;
CC[CCTop++] = temp;
}
}
}
}
获取完项目集规范族之后就要构建分析表了,由于我们要用SLR分析,所以要先求FIRST和FOLLOW集。因为可能会出现归约-移进冲突,所以不能像LR一样能归约就归约,可以归约的时候,要看看句柄后面一个符号是否可能存在于归约后非终结符的后面。建议大家了解下这块,这里简单讲一下。假设现在的句型是 xxxxb.a
,存在产生式A->b
,而A
的FOLLOW集中不存在a
,这个时候,我们就不能用A->b
进行归约。如果进行归约,当前句型会变成 xxxxA.a
,但a
不在A
的FOLLOW集中,所以在当前文法的任意一个句型中,a
不可能出现在A
的后面,所以句型 xxxxA.a
是不符合语法规范的,因此要规避这种归约。
这里用到了几个函数,没什么好说的,直接给代码:
// 检查follow或first集中是否有一个字符
bool checkCollection(Collection cl, int vId){
for(int i = 0; i < cl.top; i++){
if(cl.vts[i] == vId){
return true;
}
}
return false;
}
// 将一个集合加到另一个中,参数follow是指是否加在follow集中
void addCollection(int id, Collection target, bool follow){
// 加到follow集
if(follow){
for(int i = 0; i < target.top; i++){
if(!checkCollection(FOLLOW[id], target.vts[i])){
FOLLOW[id].vts[FOLLOW[id].top++] = target.vts[i];
}
}
} else {
// 加到first集
for(int i = 0; i < target.top; i++){
if(!checkCollection(FIRST[id], target.vts[i])){
FIRST[id].vts[FIRST[id].top++] = target.vts[i];
}
}
}
}
// 获取并产生某一非终结符的FIRST集
Collection getFIRST(int vId){
// 如果该first集已经有了,就直接返回
if(FIRST[vId].top != 0){
return FIRST[vId];
}
// 计算其first集
for(int i = 0; i < productionTop; i++){
// 如果产生式的左部是v
if(productions[i].leftPart.compare(vtn[vId]) == 0){
// 如果产生式右部的第一个符号是自己,要跳过不然就是将FIRST[X]放到FIRST[X]中,会死循环
if(productions[i].rightPart[0].compare(vtn[vId]) == 0){
continue;
}
// 如果产生式右部第一个符号是终结符,就把该终结符加进去
int id = getTerminalId(productions[i].rightPart[0]);
if(id != -1){
if(!checkCollection(FIRST[vId], id)){
FIRST[vId].vts[FIRST[vId].top++] = id;
}
} else {
id = getVtnId(productions[i].rightPart[0]);
if(id == -1){
cout << "cant find symbol [" << productions[i].rightPart[0] << "]" << endl;
return FIRST[vId];
}
// 产生式右部第一个符号是非终结符,把它的first集加入
addCollection(vId, getFIRST(id), false);
}
}
}
return FIRST[vId];
}
// 初始化FIRST集
void initFIRST(){
// 为每个非终结符计算first集
for(int i = 0; i < vtnTop; i++){
getFIRST(i);
}
}
这里用到了几个函数,也没什么好说的,直接给代码:
// 获取follow集
Collection getFOLLOW(int vId){
// 如果该follow集已经有了,就直接返回
if(FOLLOW[vId].top != 0){
return FOLLOW[vId];
}
if(vId == 0){
// 扩广文法新加的产生式的右部自带 #,如 S' -> S 中的 S
FOLLOW[0].vts[FOLLOW[0].top++] = getTerminalId("#");
}
// 构建follow集
for(int i = 1; i < productionTop; i++){
for(int j = 0; j <= productions[i].rightPartLength; j++){
if(vtn[vId].compare(productions[i].rightPart[j]) == 0){
int id = -1;
// 如果该非终结符在产生式右部最后
if(j == productions[i].rightPartLength){
// 右部最后一个符号不能是自己,不然会死循环
if(vtn[vId].compare(productions[i].leftPart) == 0){
continue;
}
id = getVtnId(productions[i].leftPart);
if(id == -1){
cout << "unknown non-terminal v: [" << productions[i].leftPart << "]" << endl;
return FOLLOW[vId];
}
// 把产生式左部非终结符的follow集加入
addCollection(vId, getFOLLOW(id), true);
} else if((id = getTerminalId(productions[i].rightPart[j + 1])) != -1) {
// 如果该非终结符的下一个符号是终结符,直接加到follow集中
//cout << productions[i].rightPart[j + 1] << endl;
if(!checkCollection(FOLLOW[vId], id)){
FOLLOW[vId].vts[FOLLOW[vId].top++] = id;
}
} else if((id = getVtnId(productions[i].rightPart[j + 1])) != -1){
// 如果该非终结符下一个符号是另一个非终结符,就把该非终结符的follow集加入
//cout << productions[i].rightPart[j + 1] << endl;
addCollection(vId, getFIRST(id), true);
}
}
}
}
return FOLLOW[vId];
}
// 初始化FOLLOW集
void initFOLLOW(){
// 为每个非终结符计算follow集
for(int i = 0; i < vtnTop; i++){
getFOLLOW(i);
}
}
但在这里我想着重说一下FIRST和FOLLOW集的求解问题。大家细心的话可以看到,我的FIRST和FOLLOW集是全局变量,getFIRST()和getFOLLOW()函数除了返回对应集合外,如果对应的集合没有被创建,还会创建,举个例子,我求A的FOLLOW集时有B->XXXA的产生式,所以B的FOLLOW集也要加入A的FOLLOW集中,所以要调用getFOLLOW(B),但此时会直接将全局变量中B的FOLLOW集也给构建好,这样不会出现重复构建一个FOLLOW集的情况(getFOLLOW()函数要是一个FOLLOW集已经被创建好,就直接返回,否则创建并返回),就类似动态规划和分治法的关系,可以一定程度上提高效率,是一种多目标(求A的FOLLOW集可能顺便也求了其他的FOLLOW集),也可以说是雨露均沾的做法。
但相应的,这种做法也可能会出现一定的问题,现在我们不考虑文法的规范性,只从代码层面分析,假设我们在求A的FOLLOW集时要求B的FOLLOW,但求B的FOLLOW集时也要求A的FOLLOW集,如:A->XXXB,B->XXXA。不难发现,这样会导致无限循环(可以说是循环依赖问题)。所以为了解决这个问题,提高代码的可用性,要牺牲一定的性能。
这里我们聚焦于单个目标,不雨露均沾,修改getFOLLOW()函数的具体内容,如果一个FOLLOW集已被创建,直接返回,要是没有,创建并返回,但不会存到全局变量FOLLOW[]里。而加个createFOLLOW()函数才会创建FOLLOW集并存到全局变量FOLLOW[]里。当然还会有些细节的差距。这里我们加入一个类似方法栈的数组 FSTACK[],记录求FOLLOW集的递归方法链,举个例子,假设我们求A的FOLLOW集要用到B的FOLLOW集,求B的FOLLOW集要用到C的FOLLOW集,求C的FOLLOW集要用到D的FOLLOW集,假设我们求A的FOLLOW集,递归求到D的FOLLOW集时,FSTACK[]的内容便是[A, B, C],如果求D的FOLLOW集的时候还要用到ABC(FSTACK[]中记录的非终结符)的FOLLOW集,直接跳过,因为我们只是单纯想求A的FOLLOW集(聚焦于单个目标,所有的操作只为一个目标服务),反正就算我们在求D的FOLLOW集时不求ABC的FOLLOW集,最后ABC的FOLLOW集也会被加到A的FOLLOW集中(因为调用的顺序是createFOLLOW(A)->getFOLLOW(B)->getFOLLOW(C)->getFOLLOW(D)),就没必要再多此一举了,同时可以避免出现死循环的问题。
可见getFOLLOW()只是为createFOLLOW()服务的,不过这样就会造成多次求同一个FOLLOW集的问题了。
求FIRST集的时候出现这种问题也可以用这个方法来解决。
但刚刚我们只是在代码的层面上讨论,而在文法的层面上,会不会出现这种循环依赖的情况,就要你们自己考虑了(这里我不是很清楚,但印象中不会出现这种情况)。
如果真的出现了这种情况,就请大家自己修改下代码吧,我就先不改了,思路我已经说的比较明白了,当然大家可能会有更好的解决办法,可以在评论区分享一下。
之后开始构建分析表,先是初始化,将每个表单元都设成op是*(异常),direct是-1。
// 初始化分析表
void initAnalysisTable(){
// goto 表
for(int i = 0; i < CCTop; i++){
for(int j = 0; j < vtnTop; j++){
gotoTable[i][j].direct = -1;
gotoTable[i][j].op = "*";
}
}
// action 表
for(int i = 0; i < CCTop; i++){
for(int j = 0; j < terminalTop; j++){
actionTable[i][j].direct = -1;
actionTable[i][j].op = "*";
}
}
}
然后开始构建:
// 构造分析表
void developAnalysisTable(){
// 遍历状态
for(int i = 0; i < CCTop; i++){
// 当前状态
State tempState = CC[i];
// 遍历状态中的项目
for(int j = 0; j < tempState.top; j++){
// 当前项目
Item tempItem = tempState.items[j];
// action 与 goto
if(tempItem.idx < productions[tempItem.pId].rightPartLength){
string tempEle = productions[tempItem.pId].rightPart[tempItem.idx + 1];
// 终结符
int eleId = getTerminalId(tempEle);
// 如果不是终结符,填goto表
if(eleId == -1){
eleId = getVtnId(tempEle);
if(eleId == -1){
printf("cant get element id!!!!!!!!!!!\n");
return;
}
// 获取下一个状态
State nextState = GOTO(tempState, tempEle);
// 获取状态id
int nextStateId = getStateId(nextState);
if(nextStateId == -1){
printf("cant get state id!!!!!!!!!!!\n");
return;
}
// 填表
gotoTable[i][eleId].direct = nextStateId;
gotoTable[i][eleId].op = "gt";
} else {
// action 表
// 获取下一个状态
State nextState = GOTO(tempState, tempEle);
// 获取状态id
int nextStateId = getStateId(nextState);
if(nextStateId == -1){
printf("cant get state id!!!!!!!!!!!\n");
return;
}
// 填表
actionTable[i][eleId].direct = nextStateId;
actionTable[i][eleId].op = "s";
}
} else {
// acc
if(tempItem.pId == 0 && tempItem.idx == 0){
actionTable[i][getTerminalId("#")].op = "acc";
} else {
// 归约
for(int m = 0; m < terminalTop; m++){
//
// 这里是是否是SLR的关键,SLR需要判断下一个非终结符是否在该变量的follow集中,LR就不需要,直接true就行
//
if(inFOLLOW(productions[tempItem.pId].leftPart, m)){
//if(true){
actionTable[i][getTerminalId(terminal[m])].op = "r";
actionTable[i][getTerminalId(terminal[m])].direct = tempItem.pId;
}
}
}
}
}
}
}
最后就是语法分析了,没啥好说的,直接上代码:
// 语法分析
void analysis(WORD target[], int wordNum){
WORD symbolStack[MAX]; // 符号栈
int symbolStackTop = 0; // 符号栈顶
int stateStack[MAX]; // 状态栈
int stateStackTop = 0; // 状态栈顶
// 最后是#
target[wordNum].key = "#";
target[wordNum].code = getTerminalId("#");
wordNum++;
// 将状态0和#入栈
symbolStack[symbolStackTop].code = START_END;
symbolStack[symbolStackTop].key = "#";
symbolStackTop++;
stateStack[stateStackTop++] = 0;
// 开始归约
int wait = 0;
cout << "------------------状态分析-------------------" << endl;
printStateString(stateStack, stateStackTop);
printSymbolString(symbolStack, symbolStackTop, target, wait, wordNum);
cout << "---------------------------------------------" << endl;
while(true){
// 获取要读取的符号
WORD tempWord = target[wait++];
int id = getTerminalId(tempWord.key);
int currentState = stateStack[stateStackTop - 1];
// 判断是不是终结符
if(id == -1){ // 是变量
id = getVtnId(tempWord.key);
if(id == -1){
cout << "cant recognize the symbol [" << tempWord.key << "]" << endl;
}
AnalysisCell cell = gotoTable[currentState][id];
if(cell.op.compare("*") == 0){
cout << "analysis error in goto table (" << currentState << "," << id << ")" << endl;
return;
}
cout << "goto(" << currentState << ", " << tempWord.key << ") = " << cell.direct << endl;
// 将状态压入栈
stateStack[stateStackTop++] = cell.direct;
// 将符号压入栈
symbolStack[symbolStackTop++] = tempWord;
} else { // 终结符
AnalysisCell cell = actionTable[currentState][id];
if(cell.op.compare("acc") == 0){
symbolStack[symbolStackTop].key = "#";
symbolStack[symbolStackTop].code = getTerminalId("#");
symbolStackTop++;
cout << "---------------------------------------------" << endl;
printStateString(stateStack, stateStackTop);
printSymbolString(symbolStack, symbolStackTop, target, wait, wordNum);
cout << "---------------------------------------------" << endl;
cout << "analysis finish!" << endl;
return;
}
if(cell.op.compare("*") == 0){
cout << "analysis error in action table (" << currentState << "," << id << ")" << endl;
return;
}
// 继续移进
if(cell.op.compare("s") == 0){
cout << "action(" << currentState << ", " << tempWord.key << ") = " << cell.op << to_string(cell.direct) << endl;
stateStack[stateStackTop++] = cell.direct;
symbolStack[symbolStackTop++] = tempWord;
}
// 归约
if(cell.op.compare("r") == 0){
// 获取产生式
Production tempProduction = productions[cell.direct];
cout << "action(" << currentState << ", " << tempWord.key << ") = " << cell.op << to_string(cell.direct) << endl;
cout << "use production: {" << tempProduction.leftPart << " -> ";
for(int k = 0; k <= tempProduction.rightPartLength; k++){
cout << tempProduction.rightPart[k];
}
cout << "} to reduce" << endl;
// 符号栈和状态栈弹出产生式右部长度个元素
stateStackTop = stateStackTop - tempProduction.rightPartLength - 1;
symbolStackTop = symbolStackTop - tempProduction.rightPartLength - 1;
// 将待识别的符号改成产生式左部
wait = wait - 2;
target[wait].key = tempProduction.leftPart;
target[wait].code = getVtnId(tempProduction.leftPart);
}
}
// 打印分析过程
printStateString(stateStack, stateStackTop);
printSymbolString(symbolStack, symbolStackTop, target, wait, wordNum);
cout << "---------------------------------------------" << endl;
}
}
首先看函数grammaticalAnalysis,自己用的时候要设置产生式、非终结符和终结符(因为和词法分析解耦了,所以非终结符和终结符也要自己输入):
// 分析,target是待处理符号串,wordNum是target的符号个数
void grammaticalAnalysis(WORD* target, int wordNum){
// 设定产生式
productions[productionTop].leftPart = "S'";
productions[productionTop].rightPart[0] = {"S"};
productions[productionTop].rightPartLength = 0;
productionTop++;
productions[productionTop].leftPart = "S";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"W"};
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"C"};
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"E"};
productionTop++;
productions[productionTop].leftPart = "W";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"if"};
productionTop++;
productions[productionTop].leftPart = "S";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"E"};
productionTop++;
productions[productionTop].leftPart = "W";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"while"};
productionTop++;
productions[productionTop].leftPart = "E";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"id"};
productionTop++;
productions[productionTop].leftPart = "E";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"num"};
productionTop++;
productions[productionTop].leftPart = "S";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"E"};
productionTop++;
productions[productionTop].leftPart = "C";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"E"};
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"U"};
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"E"};
productionTop++;
productions[productionTop].leftPart = "U";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"=="};
productionTop++;
productions[productionTop].leftPart = "U";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"<"};
productionTop++;
productions[productionTop].leftPart = "U";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {">"};
productionTop++;
productions[productionTop].leftPart = "E";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"E"};
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"O"};
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"E"};
productionTop++;
productions[productionTop].leftPart = "O";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"="};
productionTop++;
productions[productionTop].leftPart = "O";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"+"};
productionTop++;
productions[productionTop].leftPart = "O";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"-"};
productionTop++;
productions[productionTop].leftPart = "O";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"*"};
productionTop++;
productions[productionTop].leftPart = "O";
productions[productionTop].rightPartLength = -1;
productions[productionTop].rightPart[++productions[productionTop].rightPartLength] = {"/"};
productionTop++;
cout << "----------------产生式-----------------" << endl;
for(int i = 0; i < productionTop; i++){
cout << i << ". " << productions[i].leftPart << " -> ";
for(int j = 0; j <= productions[i].rightPartLength; j++){
cout << productions[i].rightPart[j];
}
cout << endl;
}
// 设定非终结符
//
// 第一个非终结符一定要是扩广文法加的那个产生式的右部
// 比如 S' -> S 的 S
//
vtn[vtnTop++] = "S";
vtn[vtnTop++] = "C";
vtn[vtnTop++] = "W";
vtn[vtnTop++] = "O";
vtn[vtnTop++] = "U";
vtn[vtnTop++] = "E";
//vtn[vtnTop++] = "S'";
// 设定终结符
terminal[terminalTop++] = "id";
terminal[terminalTop++] = "num";
terminal[terminalTop++] = "==";
terminal[terminalTop++] = ">";
terminal[terminalTop++] = "<";
terminal[terminalTop++] = "=";
terminal[terminalTop++] = "+";
terminal[terminalTop++] = "-";
terminal[terminalTop++] = "*";
terminal[terminalTop++] = "/";
terminal[terminalTop++] = "if";
terminal[terminalTop++] = "while";
terminal[terminalTop++] = "#";
// 生成first集
initFIRST();
printFIRST();
// 生成follow集
initFOLLOW();
printFOLLOW();
// 获取规范族
getCanonicalCollection();
// 初始化分析表
initAnalysisTable();
// 构造分析表
developAnalysisTable();
cout << "---------------分析表------------------" << endl;
printAnalysisTable();
// 分析
analysis(target, wordNum);
}
然后在看main函数,很好理解,就是获取符号串,然后分析:
#include
#include "LexicalAnalysis.h"
#include "GrammaticalAnalysis.h"
using namespace std;
int main()
{
cout << "-------------------- 词法分析 ----------------------------" << endl;
char test[] = "while a<3 a=a+1#";
scan(test);
printToken();
cout << "-------------------- 语法分析 ----------------------------" << endl;
grammaticalAnalysis(getToken(), getTop());
return 0;
}
我觉得我的这篇应该算是比较全面的了,希望大家不能只关注我的代码,更多的是学习知识,融入自己的思想,毕竟我的也可能有很多错误。代码我会上传到空间里。
然后就是,如果你的文法比较复杂,建议修改下各个地方的内存分配,不然可能会不够用,然后溢出,特别是State的items那里,也坑了我一把。
虽然这次我是用c++写的,但实际上我并不擅长c/c++,这次虽然使用了c++,但语法应该大部分都是c的,有很多申请内存分配或是处理可变长度数组的地方处理的不是很好。程序没有过多测试,但应该问题不大,如果遇到什么问题,可以自己尝试改一下,或是放到评论区一起看下。希望大家能有所收获,谢谢大家的观看!
附码云:SLR(1)语法分析器