编译原理实验七:对完整程序的递归下降语法分析并构造语法树

实验要求

【任务介绍】递归下降的语法分析。

【输入】一个完整的源程序。

【输出】语法树或者错误。

【题目】设计一个程序,输入字符串形式的源程序,输出该程序的语法分析树,有错误时报告错误。要求:

1.源语言及其语法规则:可以参照附录A,也可以自定义。

2.输入为字符串形式的源程序,因此,需要调用前面实验做过的词法分析器,为语法分析器提供单词符号。

3.应该指出错误的具体位置,如:在xx单词之后/之前发现错误,分析中止。

编译环境和语言

编程语言:C++

IDE:vs 2019

实验原理分析

根据附录A提供的文法,结合我自己构造的一些测试数据,在消除左递归之后,得到的文法如下所示:

<Prog> → <Head> <Main>
<Head> → # include < <String> > <Head> | empty
<Main> → <Type> main ( <Type1> ) <Block>
<Type1> → void | empty

<Block> → { <Decls> <STMTS> } 
<Decls> → <Type> <NameList> ; <Decls> | empty 

<NameList> → <Name> <NameList1>
<NameList1> → , <Name> <NameList1> | empty
<Type> → int 
<Name> → id 

<STMTS> → <STMT> <STMTS> | empty 
<STMT> → <Name> = <Expr> ; 
<STMT> → if ( <BOOL> ) <STMT> <STMT1>
<STMT1> → else <STMT> | empty
<STMT> → while ( <BOOL> ) <STMT>
<STMT> → <Block>
<STMT> → return number ;

<BOOL> → <Expr> <RelOp> <Expr>
<RelOp> → < | <= | > | >= | == | !=

<Expr> → <Term> <Expr1> 
<Expr1> → <AddOp> <Term> <Expr1> | empty 
<Term> → <Factor> <Term1> 
<Term1> → <MulOp> <Factor> <Term1> | empty 
<Factor> → id | number | ( <Expr> ) 
<AddOp> → + | - 
<MulOp> → * | / | %

对于上面的文法定义,

因为附录A提供的文法是<Prog> → <Block>,我认为这有一些不适用的地方,比如无法表示宏定义导入库函数,也无法表示函数返回值类型以及函数名,因此我对其进行了一些延伸,我的文法定义<Prog> → <Head> <Main>能够接受宏定义,也能够接受函数返回值类型和函数名,但是只能够接受main()函数,以及int的返回值类型。

并且我对的文法定义中,可以发现可以没有宏定义而直接进入到main()函数部分<Head> → # include < <String> > <Head> | empty

而且我的表达式语句只能够接受赋值语句、if语句(可以用花括号括起来)、if-else语句(可以用花括号括起来)、while语句(可以用花括号括起来)、代码块、return语句,同时return语句只能够接受数字,不能够使用如printf()函数等库函数。

其他和前面实验六的没有太大的区别,可以发现,还是具有比较多的局限性,但是一些关键部分的构造基本都已经覆盖了。

程序关键部分分析

定义

char s[100][100] = { "\0" };  //用来存储初始数据
string str;  //用来存储整合后的数据
int location = 0;  //用来定位算术表达式
bool flag = true;  //用来判断该算术表达式是否合法
string tree_map[100];  //用来存储语法树
const int width = 3;  //设置间隔为3
char token[100] = { "\0" };  //用来暂存单词
string error;  //用来记录错误信息

bool isKey(char* s);
bool isOP(char* s);
bool isDE(char& s);
void pre_process(char* buff, int& in_comment);
bool scanner(int k);

int draw_line(int row, int num);
void string_out(string s, int row, int column, int loc);
int tree_out(string s, int row, int loc);
void printTree(ofstream& fout);
int readToken();
void bindString(int k);
int Prog(int row, int column);
int Head(int row, int column);
int Main(int row, int column);
bool Type1(char* words);
int Block(int row, int column);
int Decls(int row, int column);
int NameList(int row, int column);
int NameList1(int row, int column);
bool Type(char* words);
bool Name(char* words);
int STMTS(int row, int column);
int STMT(int row, int column);
int STMT1(int row, int column);
int BOOL(int row, int column);
bool RelOp(char* words);
int Expr(int row, int column);
int Expr1(int row, int column);
int Term(int row, int column);
int Term1(int row, int column);
int Factor(int row, int column);
bool AddOp(char* words);
bool MulOp(char* words);

关键部分分析

词法分析,直接复制实验六中的两个函数:

void pre_process(char* buff, int& in_comment) {  //预处理
    char data[100] = { '\0' };  //用来存储处理过的数据
    char old_c = '\0';  //用来存储上一个字符
    char cur_c;  //用来存储当前字符
    int i = 0;  //计数器,记录buff
    int j = 0;  //计数器,记录data
    while (i < strlen(buff)) {  //去注释
        cur_c = buff[i++];  //首先将获取的字符存入缓存中
        switch (in_comment) {
        case 0:
            if (cur_c == '\"') {  //进入双引号中
                data[j++] = cur_c;
                in_comment = 3;
            } else if (cur_c == '\'') {  //进入单引号中
                data[j++] = cur_c;
                in_comment = 4;
            } else if (old_c == '/' && cur_c == '*') {  //进入多行注释中
                j--;
                in_comment = 1;
            } else if (old_c == '/' && cur_c == '/') {  //进入单行注释中
                j--;
                in_comment = 2;
            } else {  //其他情况则直接将数据写入data中
                data[j++] = cur_c;
            }
            break;
        case 1:if (old_c == '*' && cur_c == '/') in_comment = 0;  //多行注释结束
            break;
        case 2:if (i == strlen(buff)) in_comment = 0;  //单行注释到这行结束时标志位置为0
            break;
        case 3:
            data[j++] = cur_c;
            if (cur_c == '\"') in_comment = 0;
            break;
        case 4:
            data[j++] = cur_c;
            if (cur_c == '\'') in_comment = 0;
            break;
        }
        old_c = cur_c;  //保留上一个字符
    }

    i = 0;
    int k = 0;
    while (k < j) {  //分隔词
        if (isalpha(data[k]) || data[k] == '_') {  //若为字母或_
            while (!isDE(data[k]) && strchr("+-*/%=^~&|!><?:,", data[k]) == NULL && !isspace(data[k])) {
                buff[i++] = data[k++];
            }buff[i++] = ' ';
        } else if (isdigit(data[k])) {  //若为数字
            while (isdigit(data[k])) {
                buff[i++] = data[k++];
            }buff[i++] = ' ';
        } else if (isspace(data[k])) {
            while (isspace(data[k])) {  //若为空白字符
                k++;
            }
        } else if (isDE(data[k])) {  //若为界符
            buff[i++] = data[k++];
            buff[i++] = ' ';
        } else if (data[k] == '\"') {  //若为双引号
            buff[i++] = data[k++];
            while (data[k] != '\"')  buff[i++] = data[k++];
            buff[i++] = data[k++];
            buff[i++] = ' ';
        } else if (data[k] == '\'') {  //若为单引号
            buff[i++] = data[k++];
            while (data[k] != '\'')  buff[i++] = data[k++];
            buff[i++] = data[k++];
            buff[i++] = ' ';
        } else if (strchr("+-*/%=^~&|!><?:,", data[k]) != NULL) {  //若为运算符,再查看下一个字符,要尽可能多包含一些运算符
            switch (data[k]) {
            case '+':buff[i++] = data[k++];
                if (data[k] == '+' || data[k] == '=') buff[i++] = data[k++];  //为++或+=运算符
                break;
            case '-':buff[i++] = data[k++];
                if (data[k] == '-' || data[k] == '=' || data[k] == '>') buff[i++] = data[k++];  //为--或-=或->运算符
                break;
            case '*':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为*=运算符
                break;
            case '/':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为/=运算符
                break;
            case '%':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为%=运算符
                break;
            case '=':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为==运算符
                break;
            case '^':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为^=运算符
                break;
            case '&':buff[i++] = data[k++];
                if (data[k] == '&' || data[k] == '=') buff[i++] = data[k++];  //为&&或&=运算符
                break;
            case '|':buff[i++] = data[k++];
                if (data[k] == '|' || data[k] == '=') buff[i++] = data[k++];  //为||或|=运算符
                break;
            case '!':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为!=运算符
                break;
            case '>':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为>=运算符
                else if (data[k] == '>') {
                    buff[i++] = data[k++];  //为>>运算符
                    if (data[k] == '=') buff[i++] = data[k++];  //为>>=运算符
                }break;
            case '<':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为<=运算符
                else if (data[k] == '<') {
                    buff[i++] = data[k++];  //为<<运算符
                    if (data[k] == '<') buff[i++] = data[k++];  //为<<=运算符
                }break;
            default:buff[i++] = data[k++];
            }buff[i++] = ' ';
        } else if (data[k] == '#') {
            buff[i++] = data[k++];
            buff[i++] = ' ';
        }
    }
    buff[i] = '\0';  //处理完以后,会在最后留上一个空格
}

bool scanner(int k) {  //词法分析处理
    int in_comment = 0;  //0表示没问题,1表示在多行注释中,2表示在单行注释中,3表示在双引号中,4表示在单引号中
    for (int i = 0; i < k; i++) {
        pre_process(s[i], in_comment);  //首先预处理,去掉注释,词与词之间、词与运算符之间用一个空格隔开
    }
    if (in_comment != 0) return false;  //若标志位不等于0,则说明多行注释不到位,没有结束标志
    else return true;
}

其次是构造语法树的一些相关函数以及对输入数据进行处理的一些函数,还是直接从实验六复制过来的,稍有不同的是bindString()函数,因为这次是读文件的形式,考虑到正常的程序中不会在最后加一个#,因此需要在函数中所有数据整合到一起后我自己人为地在最后加上一个#:

int draw_line(int row, int num) {  //用来画横线,隔开兄弟节点,返回下次开始的起始位置
    tree_map[row].append(num, '-');
    return tree_map[row].size();
}

/**用来输出字符串
* 其中column为该行的起始位置,loc为上一行竖线的位置,
* loc默认为0,表示没有竖线,则此时通过column将该字符串放入到相应位置
* 若不为0,则通过loc对该字符串进行位置的处理
*/
void string_out(string s, int row, int column, int loc = 0) {
    if (loc == 0) {
        if (tree_map[row].size() < column) {  //若不等,则说明中间需要填充空格
            int n = column - tree_map[row].size();
            tree_map[row].append(n, ' ');
        }
        tree_map[row].append(s);
    } else {
        int n1 = s.size() / 2;
        if (loc - n1 <= column) {  //若该节点的长度比父节点长,则还是通过column添加
            if (tree_map[row].size() < column) {  //若不等,则说明中间需要填充空格
                int n = column - tree_map[row].size();
                tree_map[row].append(n, ' ');
            }
            tree_map[row].append(s);
        } else {  //这种情况必须填充空格
            int n = loc - n1 - tree_map[row].size();
            tree_map[row].append(n, ' ');
            tree_map[row].append(s);
        }
    }
}

/**画父子节点之间的竖线,s表示父亲节点的字符,loc表示父亲节点的起始位置
* 返回值用于处理运算符的位置
*/
int tree_out(string s, int row, int column) {
    int n1 = s.size() / 2;
    int n2 = column + n1 - tree_map[row].size();
    tree_map[row].append(n2, ' ');
    tree_map[row] += '|';
    return n1 + column;
}

void printTree(ofstream& fout) {
    for (int i = 0; i < 100; i++) {
        if (!tree_map[i].empty()) {
            //cout << tree_map[i] << endl;
            fout << tree_map[i] << endl;
        } else break;
    }
}

int readToken() {  //用来根据空格从str中取词,并返回该词的长度,以便进行移位操作
    int i = 0;
    for (; str[location + i] != ' '; i++) {
        token[i] = str[location + i];
    }
    token[i] = '\0';
    return i;
}

void bindString(int k) {  //用来将s数组中的内容整合到str中
    for (int i = 0; i <= k; i++) {
        str.append(s[i]);
    }
    str += '#';  //在最后加上一个#作为结束标志
}

然后最关键的部分就是文法的实现,这些文法的实现都是严格根据前面的文法定义构造的,其中因为需要报错并给出错误的位置,所以前面声明了一个string类型的error来存储错误信息,并且报错提示都是指出在当前读到的词token之前缺少期待的词,这里还需要注意的一点是,我在每次递归调用文法函数时,回溯之后都会先判断flag是否为false,if (!flag) return 0;,若为false,则直接返回,这是为了保证错误信息不会被错误地更改:

int Prog(int row, int column) {
    if (flag) {
        string_out("<Prog>", row, column);
        int loc = tree_out("<Prog>", ++row, column);
        int num1 = Head(++row, column);
        if (!flag) return 0;
        column = draw_line(row, num1 + width);
        int num2 = Main(row, column);
        if (!flag) return 0;
        return num1 + num2 + width + 6;
    }
}

int Head(int row, int column) {
    if (flag) {
        string_out("<Head>", row, column);
        int loc = tree_out("<Head>", ++row, column);
        int i = readToken();
        if (strcmp(token, "#") == 0) {
            location = location + i + 1;
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (strcmp(token, "include") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                i = readToken();
                if (strcmp(token, "<") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    column = draw_line(row, width);
                    i = readToken();  //必是<String>
                    location = location + i + 1;
                    string_out("<String>", row, column);
                    loc = tree_out("<String>", row + 1, column);
                    string_out(token, row + 2, column, loc);
                    column = draw_line(row, width);
                    i = readToken();
                    if (strcmp(token, ">") == 0) {
                        location = location + i + 1;
                        string_out(token, row, column);
                        column = draw_line(row, width);
                        int num1 = Head(row, column);
                        if (!flag) return 0;
                        return num1 + width * 5 + 7 + 1 + 8 + 1 + 6;
                    } else {
                        string s = token;
                        error = s + "之前缺少>";
                        flag = false;
                        return 0;
                    }
                } else {
                    string s = token;
                    error = s + "之前缺少<";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少include";
                flag = false;
                return 0;
            }
        } else {
            string_out("empty", ++row, column, loc);
            return 6;
        }
    }
}

int Main(int row, int column) {
    if (flag) {
        string_out("<Main>", row, column);
        int loc = tree_out("<Main>", ++row, column);
        int i = readToken();
        if (Type(token)) {
            location = location + i + 1;
            string_out("<Type>", ++row, column);
            loc = tree_out("<Type>", row + 1, column);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (strcmp(token, "main") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                i = readToken();
                if (strcmp(token, "(") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    column = draw_line(row, width);
                    i = readToken();
                    if (Type1(token)) {  //有void
                        location = location + i + 1;
                        string_out("<Type1>", row, column);
                        loc = tree_out("<Type1>", row + 1, column);
                        string_out(token, row + 2, column, loc);
                        column = draw_line(row, width);
                    } else {  //没有void
                        string_out("<Type1>", row, column);
                        loc = tree_out("<Type1>", row + 1, column);
                        string_out("empty", row + 2, column, loc);
                        column = draw_line(row, width);
                    }
                    i = readToken();
                    if (strcmp(token, ")") == 0) {
                        location = location + i + 1;
                        string_out(token, row, column);
                        column = draw_line(row, width);
                        int num1 = Block(row, column);
                        if (!flag) return 0;
                        return num1 + width * 5 + 4 + 1 + 7 + 1 + 6;
                    } else {
                        string s = token;
                        error = s + "之前缺少)";
                        flag = false;
                        return 0;
                    }
                } else {
                    string s = token;
                    error = s + "之前缺少(";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少main";
                flag = false;
                return 0;
            }
        } else {
            string s = token;
            error = s + "之前缺少int";
            flag = false;
            return 0;
        }
    }
}

bool Type1(char* words) {
    if (strcmp(words, "void") == 0) return true;
    return false;
}

int Block(int row, int column) {
    if (flag) {
        string_out("<Block>", row, column);
        int loc = tree_out("<Block>", ++row, column);
        int i = readToken();
        if (strcmp(token, "{") == 0) {
            location = location + i + 1;
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            int num1 = Decls(row, column);
            if (!flag) return 0;
            column = draw_line(row, num1 + width);
            int num2 = STMTS(row, column);
            if (!flag) return 0;
            column = draw_line(row, num2 + width);
            i = readToken();
            if (strcmp(token, "}") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                return num1 + num2 + width * 3 + 1 + 7;
            } else {
                string s = token;
                error = s + "之前缺少}";
                flag = false;
                return 0;
            }
        } else {
            string s = token;
            error = s + "之前缺少{";
            flag = false;
            return 0;
        }
    }
}

int Decls(int row, int column) {
    if (flag) {
        string_out("<Decls>", row, column);
        int loc = tree_out("<Decls>", ++row, column);
        int i = readToken();
        if (Type(token)) {
            location = location + i + 1;
            string_out("<Type>", ++row, column, loc);
            loc = tree_out("<Type>", row + 1, column);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            int num1 = NameList(row, column);
            if (!flag) return 0;
            column = draw_line(row, num1 + width);
            i = readToken();
            if (strcmp(token, ";") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                int num2 = Decls(row, column);
                if (!flag) return 0;
                return num1 + num2 + width * 3 + 1 + 7;
            } else {
                string s = token;
                error = s + "之前缺少;";
                flag = false;
                return 0;
            }
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 7;
        }
    }
}

int NameList(int row, int column) {
    if (flag) {
        string_out("<NameList>", row, column);
        int loc = tree_out("<NameList>", ++row, column);
        int i = readToken();
        if (Name(token)) {
            location = location + i + 1;
            string_out("<Name>", ++row, column, loc);
            loc = tree_out("<Name>", row + 1, column + 2);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            int num1 = NameList1(row, column);
            if (!flag) return 0;
            return num1 + width + 10;
        } else {
            string s = token;
            error = s + "之前缺少id";
            flag = false;
            return 0;
        }
    }
}

int NameList1(int row, int column) {
    if (flag) {
        string_out("<NameList1>", row, column);
        int loc = tree_out("<NameList1>", ++row, column);
        int i = readToken();
        if (strcmp(token, ",") == 0) {
            location = location + i + 1;
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (Name(token)) {
                location = location + i + 1;
                string_out("<Name>", ++row, column);
                tree_out("<Name>", row + 1, column);
                string_out(token, row + 2, column);
                column = draw_line(row, width);
                int num1 = NameList1(row, column);
                if (!flag) return 0;
                return num1 + 6 + width * 2 + 11;
            } else {
                string s = token;
                error = s + "之前缺少id";
                flag = false;
                return 0;
            }
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 11;
        }
    }
}

bool Type(char* words) {
    if (strcmp(words, "int") == 0) return true;
    else return false;
}

bool Name(char* words) {
    if (!isOP(words) && !isKey(words) && !isDE(words[0]) && !isdigit(words[0]) && words[0] != '\'' && words[0] != '\"') {
        if (words[0] == '_' || isalpha(words[0])) return true;
    }
    return false;
}

int STMTS(int row, int column) {
    if (flag) {
        string_out("<STMTS>", row, column);
        int loc = tree_out("<STMTS>", ++row, column);
        int i = readToken();
        if (Name(token) || strcmp(token, "if") == 0 || strcmp(token, "while") == 0 || 
            strcmp(token, "return") == 0 || strcmp(token, "{") == 0) {
            int num1 = STMT(++row, column);
            if (!flag) return 0;
            column = draw_line(row, num1 + width);
            int num2 = STMTS(row, column);
            if (!flag) return 0;
            return num1 + num2 + 7;
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 7;
        }
    }
}

int STMT(int row, int column) {
    if (flag) {
        string_out("<STMT>", row, column);
        int loc = tree_out("<STMT>", ++row, column);
        int i = readToken();
        location = location + i + 1;
        if (Name(token)) {  //id
            string_out("<Name>", ++row, column, loc);
            tree_out("<Name>", row + 1, column);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (strcmp(token, "=") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                int num1 = Expr(row, column);
                if (!flag) return 0;
                column = draw_line(row, num1 + width);
                i = readToken();
                if (strcmp(token, ";") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    return num1 + width * 3 + 2 + 6;
                } else {
                    string s = token;
                    error = s + "之前缺少;";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少=";
                flag = false;
                return 0;
            }
        } else if (strcmp(token, "if") == 0) {  //if
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (strcmp(token, "(") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                int num1 = BOOL(row, column);
                if (!flag) return 0;
                column = draw_line(row, num1 + width);
                i = readToken();
                if (strcmp(token, ")") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    column = draw_line(row, width);
                    int num2 = STMT(row, column);
                    if (!flag) return 0;
                    column = draw_line(row, num2 + width);
                    int num3 = STMT1(row, column);
                    if (!flag) return 0;
                    return num1 + num2 + num3 + width * 5 + 2 + 6;
                } else {
                    string s = token;
                    error = s + "之前缺少)";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少(";
                flag = false;
                return 0;
            }
        } else if (strcmp(token, "while") == 0) {  //while
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (strcmp(token, "(") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                int num1 = BOOL(row, column);
                if (!flag) return 0;
                column = draw_line(row, num1 + width);
                i = readToken();
                if (strcmp(token, ")") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    column = draw_line(row, width);
                    int num2 = STMT(row, column);
                    if (!flag) return 0;
                    return num1 + num2 + width * 4 + 2 + 6;
                } else {
                    string s = token;
                    error = s + "之前缺少)";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少(";
                flag = false;
                return 0;
            }
        } else if (strcmp(token, "{") == 0) {  //<Block>
            int num1 = Block(++row, column);
            if (!flag) return 0;
            return num1 + 6;
        } else if (strcmp(token, "return") == 0) {  //return
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (isdigit(token[0])) {
                int length = strlen(token);
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                i = readToken();
                if (strcmp(token, ";") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    return width * 3 + length + 1 + 6;
                } else {
                    string s = token;
                    error = s + "之前缺少;";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少数字";
                flag = false;
                return 0;
            }
        } else {
            string s = token;
            error = s + "之前缺少id";
            flag = false;
            return 0;
        }
    }
}

int STMT1(int row, int column) {
    if (flag) {
        string_out("<STMT1>", row, column);
        int loc = tree_out("<STMT1>", ++row, column);
        int i = readToken();
        if (strcmp(token, "else") == 0) {
            location = location + i + 1;
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            int num1 = STMT(row, column);
            if (!flag) return 0;
            return num1 + width + 7;
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 7;
        }
    }
}

int BOOL(int row, int column) {
    if (flag) {
        string_out("<BOOL>", row, column);
        tree_out("<BOOL>", ++row, column);
        int num1 = Expr(++row, column);
        if (!flag) return 0;
        column = draw_line(row, num1 + width);
        int i = readToken();
        if (RelOp(token)) {  //若是关系运算符
            location = location + i + 1;
            string_out("<RelOp>", row, column);
            int loc = tree_out("<RelOp>", row + 1, column);
            string_out(token, row + 2, loc);
            column = draw_line(row, width);
            int num2 = Expr(row, column);
            if (!flag) return 0;
            return num1 + num2 + width * 2 + 6;
        } else {
            string s = token;
            error = s + "之前缺少关系运算符";
            flag = false;
            return 0;
        }
    }
}

bool RelOp(char* words) {
    if (strcmp(words, "<") == 0 || strcmp(words, "<=") == 0 || strcmp(words, ">") == 0 || 
        strcmp(words, ">=") == 0 || strcmp(words, "==") == 0 || strcmp(words, "!=") == 0) {
        return true;
    }return false;
}

int Expr(int row, int column) {
    if (flag) {
        string_out("<Expr>", row, column);
        tree_out("<Expr>", ++row, column);
        int num1 = Term(++row, column);
        if (!flag) return 0;
        column = draw_line(row, num1 + width);
        int num2 = Expr1(row, column);
        if (!flag) return 0;
        return num1 + num2 + width + 6;
    }
}

int Expr1(int row, int column) {
    if (flag) {
        string_out("<Expr1>", row, column);
        int loc = tree_out("<Expr1>", ++row, column);
        int i = readToken();
        if (AddOp(token)) {  //若字符为+或-
            location = location + i + 1;
            string_out("<AddOp>", ++row, column, loc);
            loc = tree_out("<AddOp>", row + 1, column);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            int num1 = Term(row, column);
            if (!flag) return 0;
            column = draw_line(row, num1 + width);
            int num2 = Expr1(row, column);
            if (!flag) return 0;
            return num1 + num2 + width * 2 + 7;
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 7;
        }
    }
}

int Term(int row, int column) {
    if (flag) {
        string_out("<Term>", row, column);
        tree_out("<Term>", ++row, column);
        int num1 = Factor(++row, column);
        if (!flag) return 0;
        column = draw_line(row, num1 + width);
        int num2 = Term1(row, column);
        if (!flag) return 0;
        return num1 + num2 + width + 6;
    }
}

int Term1(int row, int column) {
    if (flag) {
        string_out("<Term1>", row, column);
        int loc = tree_out("<Term1>", ++row, column);
        int i = readToken();
        if (MulOp(token)) {  //若字符为*或/
            location = location + i + 1;
            string_out("<MulOp>", ++row, column, loc);
            loc = tree_out("<MulOp>", row + 1, column);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            int num1 = Factor(row, column);
            if (!flag) return 0;
            column = draw_line(row, num1 + width);
            int num2 = Term1(row, column);
            if (!flag) return 0;
            return num1 + num2 + width * 2 + 7;
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 7;
        }
    }
}

int Factor(int row, int column) {
    if (flag) {
        string_out("<Factor>", row, column);
        int loc = tree_out("<Factor>", ++row, column);
        int i = readToken();
        location = location + i + 1;
        if (Name(token)) {
            string_out(token, ++row, column, loc);
            return 8;
        } else if (isdigit(token[0])) {
            string_out(token, ++row, column, loc);
            return 8;
        } else if (strcmp(token, "(") == 0) {
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            int num1 = Expr(row, column);
            if (!flag) return 0;
            i = readToken();
            if (strcmp(token, ")") == 0) {
                location = location + i + 1;
                column = draw_line(row, num1 + width);
                string_out(token, row, column);
                return num1 + width * 2 + 8;
            } else {  //若一直没有),则说明该算术表达式错误
                string s = token;
                error = s + "之前缺少)";
                flag = false;
                return 0;
            }
        } else {
            string s = token;
            error = s + "之前缺少id";
            flag = false;
            return 0;
        }
    }
}

bool AddOp(char* words) {
    if (strcmp(words, "+") == 0 || strcmp(words, "-") == 0)return true;
    return false;
}

bool MulOp(char* words) {
    if (strcmp(words, "*") == 0 || strcmp(words, "/") == 0 || strcmp(words, "%") == 0)return true;
    return false;
}

最后便是在main()函数中启动,这里我会先将读入的数据一行一行的输出,并在整合这些数据之后再次输出,以便后续的验证以及纠错,同时若发现在语法分析的过程中出现错误,则会提示语法分析未通过,并给出相应的错误提示,而若是预处理出现错误,则直接提示预处理未通过:

int main() {
    int k = 0;
    string filename;
    cout << "请输入要处理的文件名:";
    cin >> filename;
    ifstream fin(filename);
    if (!fin.is_open()) {
        cout << "文件不存在!" << endl;
        return 0;
    }
    cout << filename << "中的内容如下:" << endl;
    k = getData(fin);
    if (scanner(k)) {  //先进行词法分析
        bindString(k);  //将多行输入数据整合到string类型的str中
        cout << "合并到一个字符串之后内容如下:" << endl << str << endl;
        Prog(0, 0);  //进入语法分析
        if (str[location] == '#') {
            cout << "Correct!" << endl;
            cout << "接下来输出语法树!" << endl;
            ofstream fout("output.txt");
            printTree(fout);
            fout.close();
            cout << "输出成功!请查收output.txt文件!" << endl;
        } else {
            cout << "Error!语法分析未通过!" << endl;
            cout << error << endl;  //将错误信息输出
        }
    } else {
        cout << "Error!预处理未通过!" << endl;
    }
    return 0;
}

程序测试

1.输入数据:

#include<iostream>
int main(){
	int x;
	x = 12;
	if(x == 1) x = 2;
}

运行结果如下:

在这里插入图片描述
在这里插入图片描述

不知道是不是因为列数太长的原因,在sublime中会有一部分(右下角,也就是递归下降的深处)奇迹般地消失了(但是这里我用命令行和记事本都输出过,都能够显示出来),因此没办法,只能用Windows自带的记事本将就一下了,可以看到,其中很多地方并没有对齐,但还是能够大概看出这是一棵语法树。

2.输入数据:

#include<iostream>
int main(){
	int x;
	x = 12;
	if x == 1 x = 2;
}

运行结果如下:

在这里插入图片描述

可以看到,输入数据中的if语句没有(),语法分析未通过,报错信息为x之前缺少(。

3.输入数据:

#include<iostream>
int main(){
	while(x == 1)
		x = 2;
	return 0;
}

运行结果如下:

在这里插入图片描述
在这里插入图片描述

4.输入数据:

#include<iostream>
main(){
	while(x == 1)
		x = 2;
	return 0;
}

运行结果如下:

在这里插入图片描述

可以看到,因为main()函数缺少返回类型int,因此语法分析未通过,报错信息提示main之前缺少int。

5.输入数据:

#include<iostream>
int a(){
	while(x == 1)
		x = 2;
	return 0;
}

运行结果如下:

在这里插入图片描述

可以看到,因为我的文法定义中规定了函数开头int之后必须跟main,而这个数据中int后跟的是a,因此语法分析未通过,报错信息提示a之前缺少main。

总结

本次实验由于是要对整个程序进行语法分析,而整个程序涉及到的文法会非常庞大,要完整实现全部文法的工作量还是比较巨大的,因此我结合了一些我自己构造的测试数据,以及我认为的一些比较重要的文法,构造出了新的文法定义,当然我的文法定义还是会有些不足,比如只能有一个函数,函数名只能为main,不能够使用如printf()这些库函数等,因为考虑到要实现这些会让程序更加复杂,当然,更关键的是时间上快要来不及了,而且其实我觉得很多文法的实现都是大同小异的,并没有什么太大的区别,因此只是完成一些主要的文法,保证自己确实掌握了这个知识点即可。

完整代码

#include<iostream>
#include<fstream>
#include<string>
#include<cctype>
using namespace std;

char s[100][100] = { "\0" };  //用来存储初始数据
string str;  //用来存储整合后的数据
int location = 0;  //用来定位算术表达式
bool flag = true;  //用来判断该算术表达式是否合法
string tree_map[100];  //用来存储语法树
const int width = 3;  //设置间隔为3
char token[100] = { "\0" };  //用来暂存单词
string error;  //用来记录错误信息

bool isKey(char* s);
bool isOP(char* s);
bool isDE(char& s);
void pre_process(char* buff, int& in_comment);
bool scanner(int k);

char keywords[34][20] = {  //关键字,包括main在内共有34个
    "auto", "short", "int", "long", "float", "double", "char", "struct",
    "union", "enum", "typedef", "const", "unsigned", "signed", "extern",
    "register", "static", "volatile", "void", "if", "else", "switch",
    "case", "for", "do", "while", "goto", "continue", "break", "default",
    "sizeof", "return", "main", "include"
};
char operators[38][10] = {  //运算符,共38个
    "+", "-", "*", "/", "%", "++", "--", "==", "!=", ">", ">=", "<", "<=",
    "&&", "||", "!", "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", "&=",
    "^=", "|=", "&", "|", "^", "~", "<<", ">>", "?", ":", ",", ".", "->"
};
char delimiters[7] = { '(', ')', '[', ']', '{', '}' , ';' };  //分隔符,共7个

bool isKey(char* s) {  //用来判断字符串是否为关键字,是则返回true,否则返回false
    for (int i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
        if (strcmp(s, keywords[i]) == 0) return true;
    }return false;
}

bool isOP(char* s) {  //用来判断字符串是否为运算符,是则返回true,否则返回false
    for (int i = 0; i < sizeof(operators) / sizeof(operators[0]); i++) {
        if (strcmp(s, operators[i]) == 0) return true;
    }return false;
}

bool isDE(char& s) {  //用来判断字符是否为分隔符,是则返回true,否则返回false
    if (strchr(delimiters, s) != NULL) return true;
    return false;
}

void pre_process(char* buff, int& in_comment) {  //预处理
    char data[100] = { '\0' };  //用来存储处理过的数据
    char old_c = '\0';  //用来存储上一个字符
    char cur_c;  //用来存储当前字符
    int i = 0;  //计数器,记录buff
    int j = 0;  //计数器,记录data
    while (i < strlen(buff)) {  //去注释
        cur_c = buff[i++];  //首先将获取的字符存入缓存中
        switch (in_comment) {
        case 0:
            if (cur_c == '\"') {  //进入双引号中
                data[j++] = cur_c;
                in_comment = 3;
            } else if (cur_c == '\'') {  //进入单引号中
                data[j++] = cur_c;
                in_comment = 4;
            } else if (old_c == '/' && cur_c == '*') {  //进入多行注释中
                j--;
                in_comment = 1;
            } else if (old_c == '/' && cur_c == '/') {  //进入单行注释中
                j--;
                in_comment = 2;
            } else {  //其他情况则直接将数据写入data中
                data[j++] = cur_c;
            }
            break;
        case 1:if (old_c == '*' && cur_c == '/') in_comment = 0;  //多行注释结束
            break;
        case 2:if (i == strlen(buff)) in_comment = 0;  //单行注释到这行结束时标志位置为0
            break;
        case 3:
            data[j++] = cur_c;
            if (cur_c == '\"') in_comment = 0;
            break;
        case 4:
            data[j++] = cur_c;
            if (cur_c == '\'') in_comment = 0;
            break;
        }
        old_c = cur_c;  //保留上一个字符
    }

    i = 0;
    int k = 0;
    while (k < j) {  //分隔词
        if (isalpha(data[k]) || data[k] == '_') {  //若为字母或_
            while (!isDE(data[k]) && strchr("+-*/%=^~&|!><?:,", data[k]) == NULL && !isspace(data[k])) {
                buff[i++] = data[k++];
            }buff[i++] = ' ';
        } else if (isdigit(data[k])) {  //若为数字
            while (isdigit(data[k])) {
                buff[i++] = data[k++];
            }buff[i++] = ' ';
        } else if (isspace(data[k])) {
            while (isspace(data[k])) {  //若为空白字符
                k++;
            }
        } else if (isDE(data[k])) {  //若为界符
            buff[i++] = data[k++];
            buff[i++] = ' ';
        } else if (data[k] == '\"') {  //若为双引号
            buff[i++] = data[k++];
            while (data[k] != '\"')  buff[i++] = data[k++];
            buff[i++] = data[k++];
            buff[i++] = ' ';
        } else if (data[k] == '\'') {  //若为单引号
            buff[i++] = data[k++];
            while (data[k] != '\'')  buff[i++] = data[k++];
            buff[i++] = data[k++];
            buff[i++] = ' ';
        } else if (strchr("+-*/%=^~&|!><?:,", data[k]) != NULL) {  //若为运算符,再查看下一个字符,要尽可能多包含一些运算符
            switch (data[k]) {
            case '+':buff[i++] = data[k++];
                if (data[k] == '+' || data[k] == '=') buff[i++] = data[k++];  //为++或+=运算符
                break;
            case '-':buff[i++] = data[k++];
                if (data[k] == '-' || data[k] == '=' || data[k] == '>') buff[i++] = data[k++];  //为--或-=或->运算符
                break;
            case '*':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为*=运算符
                break;
            case '/':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为/=运算符
                break;
            case '%':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为%=运算符
                break;
            case '=':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为==运算符
                break;
            case '^':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为^=运算符
                break;
            case '&':buff[i++] = data[k++];
                if (data[k] == '&' || data[k] == '=') buff[i++] = data[k++];  //为&&或&=运算符
                break;
            case '|':buff[i++] = data[k++];
                if (data[k] == '|' || data[k] == '=') buff[i++] = data[k++];  //为||或|=运算符
                break;
            case '!':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为!=运算符
                break;
            case '>':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为>=运算符
                else if (data[k] == '>') {
                    buff[i++] = data[k++];  //为>>运算符
                    if (data[k] == '=') buff[i++] = data[k++];  //为>>=运算符
                }break;
            case '<':buff[i++] = data[k++];
                if (data[k] == '=') buff[i++] = data[k++];  //为<=运算符
                else if (data[k] == '<') {
                    buff[i++] = data[k++];  //为<<运算符
                    if (data[k] == '<') buff[i++] = data[k++];  //为<<=运算符
                }break;
            default:buff[i++] = data[k++];
            }buff[i++] = ' ';
        } else if (data[k] == '#') {
            buff[i++] = data[k++];
            buff[i++] = ' ';
        }
    }
    buff[i] = '\0';  //处理完以后,会在最后留上一个空格
}

bool scanner(int k) {  //词法分析处理
    int in_comment = 0;  //0表示没问题,1表示在多行注释中,2表示在单行注释中,3表示在双引号中,4表示在单引号中
    for (int i = 0; i < k; i++) {
        pre_process(s[i], in_comment);  //首先预处理,去掉注释,词与词之间、词与运算符之间用一个空格隔开
    }
    if (in_comment != 0) return false;  //若标志位不等于0,则说明多行注释不到位,没有结束标志
    else return true;
}

int draw_line(int row, int num);
void string_out(string s, int row, int column, int loc);
int tree_out(string s, int row, int loc);
void printTree(ofstream& fout);
int readToken();
void bindString(int k);
int Prog(int row, int column);
int Head(int row, int column);
int Main(int row, int column);
bool Type1(char* words);
int Block(int row, int column);
int Decls(int row, int column);
int NameList(int row, int column);
int NameList1(int row, int column);
bool Type(char* words);
bool Name(char* words);
int STMTS(int row, int column);
int STMT(int row, int column);
int STMT1(int row, int column);
int BOOL(int row, int column);
bool RelOp(char* words);
int Expr(int row, int column);
int Expr1(int row, int column);
int Term(int row, int column);
int Term1(int row, int column);
int Factor(int row, int column);
bool AddOp(char* words);
bool MulOp(char* words);

int draw_line(int row, int num) {  //用来画横线,隔开兄弟节点,返回下次开始的起始位置
    tree_map[row].append(num, '-');
    return tree_map[row].size();
}

/**用来输出字符串
* 其中column为该行的起始位置,loc为上一行竖线的位置,
* loc默认为0,表示没有竖线,则此时通过column将该字符串放入到相应位置
* 若不为0,则通过loc对该字符串进行位置的处理
*/
void string_out(string s, int row, int column, int loc = 0) {
    if (loc == 0) {
        if (tree_map[row].size() < column) {  //若不等,则说明中间需要填充空格
            int n = column - tree_map[row].size();
            tree_map[row].append(n, ' ');
        }
        tree_map[row].append(s);
    } else {
        int n1 = s.size() / 2;
        if (loc - n1 <= column) {  //若该节点的长度比父节点长,则还是通过column添加
            if (tree_map[row].size() < column) {  //若不等,则说明中间需要填充空格
                int n = column - tree_map[row].size();
                tree_map[row].append(n, ' ');
            }
            tree_map[row].append(s);
        } else {  //这种情况必须填充空格
            int n = loc - n1 - tree_map[row].size();
            tree_map[row].append(n, ' ');
            tree_map[row].append(s);
        }
    }
}

/**画父子节点之间的竖线,s表示父亲节点的字符,loc表示父亲节点的起始位置
* 返回值用于处理运算符的位置
*/
int tree_out(string s, int row, int column) {
    int n1 = s.size() / 2;
    int n2 = column + n1 - tree_map[row].size();
    tree_map[row].append(n2, ' ');
    tree_map[row] += '|';
    return n1 + column;
}

void printTree(ofstream& fout) {
    for (int i = 0; i < 100; i++) {
        if (!tree_map[i].empty()) {
            //cout << tree_map[i] << endl;
            fout << tree_map[i] << endl;
        } else break;
    }
}

int readToken() {  //用来根据空格从str中取词,并返回该词的长度,以便进行移位操作
    int i = 0;
    for (; str[location + i] != ' '; i++) {
        token[i] = str[location + i];
    }
    token[i] = '\0';
    return i;
}

void bindString(int k) {  //用来将s数组中的内容整合到str中
    for (int i = 0; i <= k; i++) {
        str.append(s[i]);
    }
    str += '#';  //在最后加上一个#作为结束标志
}

int Prog(int row, int column) {
    if (flag) {
        string_out("<Prog>", row, column);
        int loc = tree_out("<Prog>", ++row, column);
        int num1 = Head(++row, column);
        if (!flag) return 0;
        column = draw_line(row, num1 + width);
        int num2 = Main(row, column);
        if (!flag) return 0;
        return num1 + num2 + width + 6;
    }
}

int Head(int row, int column) {
    if (flag) {
        string_out("<Head>", row, column);
        int loc = tree_out("<Head>", ++row, column);
        int i = readToken();
        if (strcmp(token, "#") == 0) {
            location = location + i + 1;
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (strcmp(token, "include") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                i = readToken();
                if (strcmp(token, "<") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    column = draw_line(row, width);
                    i = readToken();  //必是<String>
                    location = location + i + 1;
                    string_out("<String>", row, column);
                    loc = tree_out("<String>", row + 1, column);
                    string_out(token, row + 2, column, loc);
                    column = draw_line(row, width);
                    i = readToken();
                    if (strcmp(token, ">") == 0) {
                        location = location + i + 1;
                        string_out(token, row, column);
                        column = draw_line(row, width);
                        int num1 = Head(row, column);
                        if (!flag) return 0;
                        return num1 + width * 5 + 7 + 1 + 8 + 1 + 6;
                    } else {
                        string s = token;
                        error = s + "之前缺少>";
                        flag = false;
                        return 0;
                    }
                } else {
                    string s = token;
                    error = s + "之前缺少<";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少include";
                flag = false;
                return 0;
            }
        } else {
            string_out("empty", ++row, column, loc);
            return 6;
        }
    }
}

int Main(int row, int column) {
    if (flag) {
        string_out("<Main>", row, column);
        int loc = tree_out("<Main>", ++row, column);
        int i = readToken();
        if (Type(token)) {
            location = location + i + 1;
            string_out("<Type>", ++row, column);
            loc = tree_out("<Type>", row + 1, column);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (strcmp(token, "main") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                i = readToken();
                if (strcmp(token, "(") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    column = draw_line(row, width);
                    i = readToken();
                    if (Type1(token)) {  //有void
                        location = location + i + 1;
                        string_out("<Type1>", row, column);
                        loc = tree_out("<Type1>", row + 1, column);
                        string_out(token, row + 2, column, loc);
                        column = draw_line(row, width);
                    } else {  //没有void
                        string_out("<Type1>", row, column);
                        loc = tree_out("<Type1>", row + 1, column);
                        string_out("empty", row + 2, column, loc);
                        column = draw_line(row, width);
                    }
                    i = readToken();
                    if (strcmp(token, ")") == 0) {
                        location = location + i + 1;
                        string_out(token, row, column);
                        column = draw_line(row, width);
                        int num1 = Block(row, column);
                        if (!flag) return 0;
                        return num1 + width * 5 + 4 + 1 + 7 + 1 + 6;
                    } else {
                        string s = token;
                        error = s + "之前缺少)";
                        flag = false;
                        return 0;
                    }
                } else {
                    string s = token;
                    error = s + "之前缺少(";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少main";
                flag = false;
                return 0;
            }
        } else {
            string s = token;
            error = s + "之前缺少int";
            flag = false;
            return 0;
        }
    }
}

bool Type1(char* words) {
    if (strcmp(words, "void") == 0) return true;
    return false;
}

int Block(int row, int column) {
    if (flag) {
        string_out("<Block>", row, column);
        int loc = tree_out("<Block>", ++row, column);
        int i = readToken();
        if (strcmp(token, "{") == 0) {
            location = location + i + 1;
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            int num1 = Decls(row, column);
            if (!flag) return 0;
            column = draw_line(row, num1 + width);
            int num2 = STMTS(row, column);
            if (!flag) return 0;
            column = draw_line(row, num2 + width);
            i = readToken();
            if (strcmp(token, "}") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                return num1 + num2 + width * 3 + 1 + 7;
            } else {
                string s = token;
                error = s + "之前缺少}";
                flag = false;
                return 0;
            }
        } else {
            string s = token;
            error = s + "之前缺少{";
            flag = false;
            return 0;
        }
    }
}

int Decls(int row, int column) {
    if (flag) {
        string_out("<Decls>", row, column);
        int loc = tree_out("<Decls>", ++row, column);
        int i = readToken();
        if (Type(token)) {
            location = location + i + 1;
            string_out("<Type>", ++row, column, loc);
            loc = tree_out("<Type>", row + 1, column);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            int num1 = NameList(row, column);
            if (!flag) return 0;
            column = draw_line(row, num1 + width);
            i = readToken();
            if (strcmp(token, ";") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                int num2 = Decls(row, column);
                if (!flag) return 0;
                return num1 + num2 + width * 3 + 1 + 7;
            } else {
                string s = token;
                error = s + "之前缺少;";
                flag = false;
                return 0;
            }
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 7;
        }
    }
}

int NameList(int row, int column) {
    if (flag) {
        string_out("<NameList>", row, column);
        int loc = tree_out("<NameList>", ++row, column);
        int i = readToken();
        if (Name(token)) {
            location = location + i + 1;
            string_out("<Name>", ++row, column, loc);
            loc = tree_out("<Name>", row + 1, column + 2);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            int num1 = NameList1(row, column);
            if (!flag) return 0;
            return num1 + width + 10;
        } else {
            string s = token;
            error = s + "之前缺少id";
            flag = false;
            return 0;
        }
    }
}

int NameList1(int row, int column) {
    if (flag) {
        string_out("<NameList1>", row, column);
        int loc = tree_out("<NameList1>", ++row, column);
        int i = readToken();
        if (strcmp(token, ",") == 0) {
            location = location + i + 1;
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (Name(token)) {
                location = location + i + 1;
                string_out("<Name>", ++row, column);
                tree_out("<Name>", row + 1, column);
                string_out(token, row + 2, column);
                column = draw_line(row, width);
                int num1 = NameList1(row, column);
                if (!flag) return 0;
                return num1 + 6 + width * 2 + 11;
            } else {
                string s = token;
                error = s + "之前缺少id";
                flag = false;
                return 0;
            }
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 11;
        }
    }
}

bool Type(char* words) {
    if (strcmp(words, "int") == 0) return true;
    else return false;
}

bool Name(char* words) {
    if (!isOP(words) && !isKey(words) && !isDE(words[0]) && !isdigit(words[0]) && words[0] != '\'' && words[0] != '\"') {
        if (words[0] == '_' || isalpha(words[0])) return true;
    }
    return false;
}

int STMTS(int row, int column) {
    if (flag) {
        string_out("<STMTS>", row, column);
        int loc = tree_out("<STMTS>", ++row, column);
        int i = readToken();
        if (Name(token) || strcmp(token, "if") == 0 || strcmp(token, "while") == 0 || 
            strcmp(token, "return") == 0 || strcmp(token, "{") == 0) {
            int num1 = STMT(++row, column);
            if (!flag) return 0;
            column = draw_line(row, num1 + width);
            int num2 = STMTS(row, column);
            if (!flag) return 0;
            return num1 + num2 + 7;
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 7;
        }
    }
}

int STMT(int row, int column) {
    if (flag) {
        string_out("<STMT>", row, column);
        int loc = tree_out("<STMT>", ++row, column);
        int i = readToken();
        location = location + i + 1;
        if (Name(token)) {  //id
            string_out("<Name>", ++row, column, loc);
            tree_out("<Name>", row + 1, column);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (strcmp(token, "=") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                int num1 = Expr(row, column);
                if (!flag) return 0;
                column = draw_line(row, num1 + width);
                i = readToken();
                if (strcmp(token, ";") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    return num1 + width * 3 + 2 + 6;
                } else {
                    string s = token;
                    error = s + "之前缺少;";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少=";
                flag = false;
                return 0;
            }
        } else if (strcmp(token, "if") == 0) {  //if
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (strcmp(token, "(") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                int num1 = BOOL(row, column);
                if (!flag) return 0;
                column = draw_line(row, num1 + width);
                i = readToken();
                if (strcmp(token, ")") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    column = draw_line(row, width);
                    int num2 = STMT(row, column);
                    if (!flag) return 0;
                    column = draw_line(row, num2 + width);
                    int num3 = STMT1(row, column);
                    if (!flag) return 0;
                    return num1 + num2 + num3 + width * 5 + 2 + 6;
                } else {
                    string s = token;
                    error = s + "之前缺少)";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少(";
                flag = false;
                return 0;
            }
        } else if (strcmp(token, "while") == 0) {  //while
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (strcmp(token, "(") == 0) {
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                int num1 = BOOL(row, column);
                if (!flag) return 0;
                column = draw_line(row, num1 + width);
                i = readToken();
                if (strcmp(token, ")") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    column = draw_line(row, width);
                    int num2 = STMT(row, column);
                    if (!flag) return 0;
                    return num1 + num2 + width * 4 + 2 + 6;
                } else {
                    string s = token;
                    error = s + "之前缺少)";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少(";
                flag = false;
                return 0;
            }
        } else if (strcmp(token, "{") == 0) {  //<Block>
            int num1 = Block(++row, column);
            if (!flag) return 0;
            return num1 + 6;
        } else if (strcmp(token, "return") == 0) {  //return
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            i = readToken();
            if (isdigit(token[0])) {
                int length = strlen(token);
                location = location + i + 1;
                string_out(token, row, column);
                column = draw_line(row, width);
                i = readToken();
                if (strcmp(token, ";") == 0) {
                    location = location + i + 1;
                    string_out(token, row, column);
                    return width * 3 + length + 1 + 6;
                } else {
                    string s = token;
                    error = s + "之前缺少;";
                    flag = false;
                    return 0;
                }
            } else {
                string s = token;
                error = s + "之前缺少数字";
                flag = false;
                return 0;
            }
        } else {
            string s = token;
            error = s + "之前缺少id";
            flag = false;
            return 0;
        }
    }
}

int STMT1(int row, int column) {
    if (flag) {
        string_out("<STMT1>", row, column);
        int loc = tree_out("<STMT1>", ++row, column);
        int i = readToken();
        if (strcmp(token, "else") == 0) {
            location = location + i + 1;
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            int num1 = STMT(row, column);
            if (!flag) return 0;
            return num1 + width + 7;
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 7;
        }
    }
}

int BOOL(int row, int column) {
    if (flag) {
        string_out("<BOOL>", row, column);
        tree_out("<BOOL>", ++row, column);
        int num1 = Expr(++row, column);
        if (!flag) return 0;
        column = draw_line(row, num1 + width);
        int i = readToken();
        if (RelOp(token)) {  //若是关系运算符
            location = location + i + 1;
            string_out("<RelOp>", row, column);
            int loc = tree_out("<RelOp>", row + 1, column);
            string_out(token, row + 2, loc);
            column = draw_line(row, width);
            int num2 = Expr(row, column);
            if (!flag) return 0;
            return num1 + num2 + width * 2 + 6;
        } else {
            string s = token;
            error = s + "之前缺少关系运算符";
            flag = false;
            return 0;
        }
    }
}

bool RelOp(char* words) {
    if (strcmp(words, "<") == 0 || strcmp(words, "<=") == 0 || strcmp(words, ">") == 0 || 
        strcmp(words, ">=") == 0 || strcmp(words, "==") == 0 || strcmp(words, "!=") == 0) {
        return true;
    }return false;
}

int Expr(int row, int column) {
    if (flag) {
        string_out("<Expr>", row, column);
        tree_out("<Expr>", ++row, column);
        int num1 = Term(++row, column);
        if (!flag) return 0;
        column = draw_line(row, num1 + width);
        int num2 = Expr1(row, column);
        if (!flag) return 0;
        return num1 + num2 + width + 6;
    }
}

int Expr1(int row, int column) {
    if (flag) {
        string_out("<Expr1>", row, column);
        int loc = tree_out("<Expr1>", ++row, column);
        int i = readToken();
        if (AddOp(token)) {  //若字符为+或-
            location = location + i + 1;
            string_out("<AddOp>", ++row, column, loc);
            loc = tree_out("<AddOp>", row + 1, column);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            int num1 = Term(row, column);
            if (!flag) return 0;
            column = draw_line(row, num1 + width);
            int num2 = Expr1(row, column);
            if (!flag) return 0;
            return num1 + num2 + width * 2 + 7;
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 7;
        }
    }
}

int Term(int row, int column) {
    if (flag) {
        string_out("<Term>", row, column);
        tree_out("<Term>", ++row, column);
        int num1 = Factor(++row, column);
        if (!flag) return 0;
        column = draw_line(row, num1 + width);
        int num2 = Term1(row, column);
        if (!flag) return 0;
        return num1 + num2 + width + 6;
    }
}

int Term1(int row, int column) {
    if (flag) {
        string_out("<Term1>", row, column);
        int loc = tree_out("<Term1>", ++row, column);
        int i = readToken();
        if (MulOp(token)) {  //若字符为*或/
            location = location + i + 1;
            string_out("<MulOp>", ++row, column, loc);
            loc = tree_out("<MulOp>", row + 1, column);
            string_out(token, row + 2, column, loc);
            column = draw_line(row, width);
            int num1 = Factor(row, column);
            if (!flag) return 0;
            column = draw_line(row, num1 + width);
            int num2 = Term1(row, column);
            if (!flag) return 0;
            return num1 + num2 + width * 2 + 7;
        } else {  //否则输出为empty
            string_out("empty", ++row, column, loc);
            return 7;
        }
    }
}

int Factor(int row, int column) {
    if (flag) {
        string_out("<Factor>", row, column);
        int loc = tree_out("<Factor>", ++row, column);
        int i = readToken();
        location = location + i + 1;
        if (Name(token)) {
            string_out(token, ++row, column, loc);
            return 8;
        } else if (isdigit(token[0])) {
            string_out(token, ++row, column, loc);
            return 8;
        } else if (strcmp(token, "(") == 0) {
            string_out(token, ++row, column, loc);
            column = draw_line(row, width);
            int num1 = Expr(row, column);
            if (!flag) return 0;
            i = readToken();
            if (strcmp(token, ")") == 0) {
                location = location + i + 1;
                column = draw_line(row, num1 + width);
                string_out(token, row, column);
                return num1 + width * 2 + 8;
            } else {  //若一直没有),则说明该算术表达式错误
                string s = token;
                error = s + "之前缺少)";
                flag = false;
                return 0;
            }
        } else {
            string s = token;
            error = s + "之前缺少id";
            flag = false;
            return 0;
        }
    }
}

bool AddOp(char* words) {
    if (strcmp(words, "+") == 0 || strcmp(words, "-") == 0)return true;
    return false;
}

bool MulOp(char* words) {
    if (strcmp(words, "*") == 0 || strcmp(words, "/") == 0 || strcmp(words, "%") == 0)return true;
    return false;
}

int getData(ifstream& fin) {  //读文件
    int i = 0;
    while (!fin.eof() && i < 100) {
        fin.getline(s[i++], 100);
        cout << s[i - 1] << endl;
    }
    return i;
}

int main() {
    int k = 0;
    string filename;
    cout << "请输入要处理的文件名:";
    cin >> filename;
    ifstream fin(filename);
    if (!fin.is_open()) {
        cout << "文件不存在!" << endl;
        return 0;
    }
    cout << filename << "中的内容如下:" << endl;
    k = getData(fin);
    if (scanner(k)) {  //先进行词法分析
        bindString(k);  //将多行输入数据整合到string类型的str中
        cout << "合并到一个字符串之后内容如下:" << endl << str << endl;
        Prog(0, 0);  //进入语法分析
        if (str[location] == '#') {
            cout << "Correct!" << endl;
            cout << "接下来输出语法树!" << endl;
            ofstream fout("output.txt");
            printTree(fout);
            fout.close();
            cout << "输出成功!请查收output.txt文件!" << endl;
        } else {
            cout << "Error!语法分析未通过!" << endl;
            cout << error << endl;  //将错误信息输出
        }
    } else {
        cout << "Error!预处理未通过!" << endl;
    }
    return 0;
}
  • 9
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花无凋零之时

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值