使用c/c++实现SLR(1)语法分析器

使用c/c++实现SLR1语法分析器

  • 一、前言
  • 二、具体实现
    • 1、结构体介绍
      • analysis_table_cell.h
      • collection.h
      • item.h
      • prodection_rule.h
      • state.h
      • symbol.h和word.h
    • 2、重要结构介绍
    • 3、重要函数介绍
      • CLOSURE()
      • GOTO()
      • getCanonicalCollection()
      • FIRST
      • FOLLOW
      • 构建分析表
      • 语法分析
  • 三、用法
  • 四、测试结果
  • 五、后记

一、前言

  这次课程实验要求实现一个SLR(1)语法分析器,所以自己抽空用c++写了一个,当然我也没用什么c++特性,所以当做c程序来运行应该也差不多,顶多改改头文件和比较string的函数,废话不多说,就直接进入正题吧。

二、具体实现

  首先来看下项目目录,这里面有一部分是词法分析,但词法分析比较简单,这里就不讲了,说到底就是字符串的解析。
使用c/c++实现SLR(1)语法分析器_第1张图片

1、结构体介绍

  这里面着重讲几个地方:

analysis_table_cell.h

1、分析表单元没什么好讲的,其实就两个部分:op是操作(s是状态跳转;r是用第几个产生式进行归约;acc是接收;*是出错;gt是非终结符的状态转换),direct是方向(s和gt时是跳转到的状态,r时是归约用第几个产生式)
使用c/c++实现SLR(1)语法分析器_第2张图片

collection.h

2、collection其实也没什么好说的,follow集和first集都是用的这个结构体,vts是集合对应的终结符id(下标),top是vts的数组尾。
使用c/c++实现SLR(1)语法分析器_第3张图片

item.h

3、item是项目,简单来说就是 E->A.B 这样的,pId是对应产生式的id(下标),idx是 . 的前一个符号的下标,也就是在分析栈中最后一个元素的下标,假设 E->AB 是第3号产生式,那 E->A.B 对应的 item 是 pId=3,idx=0。需要注意的是 E->.AB 对应的idx是-1。
使用c/c++实现SLR(1)语法分析器_第4张图片

prodection_rule.h

4、production是产生式,leftPart是产生式左部,rightPart是产生式右部的符号数组,rightPartLength是对应符号数组的数组尾。如S->AB,leftPart是S,rightPart[0]是A,rightPart[1]是B,rightPartLength是1(尾部下标)。
使用c/c++实现SLR(1)语法分析器_第5张图片

state.h

5、state是状态,就是状态机的每个状态。id暂时没有用到,是状态的序号。item是s状态对应的项目集,top是items的尾部下标。
使用c/c++实现SLR(1)语法分析器_第6张图片

symbol.h和word.h

6、symbol和word是词法分析用到的,这里我把词法和语法分析解耦了,所以这里用不到,没什么好讲的。
使用c/c++实现SLR(1)语法分析器_第7张图片
使用c/c++实现SLR(1)语法分析器_第8张图片

2、重要结构介绍

  话不多说,直接放:

#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;  // 数组尾

3、重要函数介绍

  先看看函数头文件:
使用c/c++实现SLR(1)语法分析器_第9张图片
  函数非常多,但大多都是功能性的,所以这里简单介绍下重要函数和流程。
  分析的第一步首先要构建项目集规范族(状态机可以看成),我们主要通过CLOSURE、GOTO和getCanonicalCollection三个函数来构建。

CLOSURE()

  这个函数用来计算一个状态(也可以看成一组项目集)的闭包,这里不讲过多的原理,需要自己学习,简单举个例子,假设有产生式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()

  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);
}

getCanonicalCollection()

  这个其实没啥好说的,就是一直找,看有没有新的状态。

// 获取项目集规范族
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是不符合语法规范的,因此要规避这种归约。

FIRST

  这里用到了几个函数,没什么好说的,直接给代码:

// 检查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

  这里用到了几个函数,也没什么好说的,直接给代码:

// 获取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;
}

四、测试结果

  这个大家自己看吧,没啥难度。
使用c/c++实现SLR(1)语法分析器_第10张图片

五、后记

  我觉得我的这篇应该算是比较全面的了,希望大家不能只关注我的代码,更多的是学习知识,融入自己的思想,毕竟我的也可能有很多错误。代码我会上传到空间里。
  然后就是,如果你的文法比较复杂,建议修改下各个地方的内存分配,不然可能会不够用,然后溢出,特别是State的items那里,也坑了我一把。
  虽然这次我是用c++写的,但实际上我并不擅长c/c++,这次虽然使用了c++,但语法应该大部分都是c的,有很多申请内存分配或是处理可变长度数组的地方处理的不是很好。程序没有过多测试,但应该问题不大,如果遇到什么问题,可以自己尝试改一下,或是放到评论区一起看下。希望大家能有所收获,谢谢大家的观看!
  附码云:SLR(1)语法分析器

你可能感兴趣的:(编译原理,语法分析,SLR,编译器,c++,c语言)