概述
前文:
最近课程实验有点多且难,上一次编译原理做完词法分析器后,这一次终于把语法分析器给撸出来了。通过这一次的实验发现,词法分析器做的有很多不严谨的地方,因为词法分析器不应该是一个独立的,而是需要和语法分析器甚至下面的语义相关联,所以在一开始我们就应该对其有一定的规划,但是刚开始的时候没有这个意识,在网上看了相关代码之后只是觉得符合要求就提交上去了。于是觉得应该对词法分析器加以改进,故重新撸了一篇词法分析器的博文。这一次是语法分析器,先来看看我们老师的要求吧。
题目要求
可以看到这里要求递归下降分析,这个就不多说了。还有一个拓展要求,要求错误恢复和提示,这个依据恐慌模式来做,不懂?可以看一下这个视频。
代码分析:
一下代码是我昨晚一点多撸完的,感觉太臃肿了,应该分成项目来做,至少也得多个头文件,但是由于时间太赶,先原文贴出。
代码有点偏长,九百多行,其中大部分是词法分析器的,所以最好看一下我的上一篇关于词法分析器的代码。
#include<stdio.h>
#include<conio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
int wordanalysis();
void Irparser();
void yucu();
void statement();
void expression();
void term();
void factor();
void One();
void Two();
void Three();
void is_equal();
void not_equal();
void is_four_cal();
void not_four_cal();
void is_colon();
void not_right();
void not_biaozhifu();
using namespace std;
//BEGIN
//
//
//a = 10;
//b = a + 10;
//e = a + b
//end#
//保留字的种别码。从1开始到25
//出现过的标识符的种别码设为26
//未出现过的标识符种别码为52,将该标识符保存到标识符表中
//数字种别码设为27
// 等号种别码为28
// <= 种别码为29.
// <> 种别码为30。
// < 种别码为31
// >= 种别码为32
// > 种别码为33.
// + 种别码为34
// - 种别码为35
// * 种别码为36
// / 种别码 53
// ;种别码为38
// = 种别码为28
//1到51为正常的。空格为100, 换行符为101, #号为102,错误为404
//case '(': syn = 39;
//case ')': syn = 40;
int i, row = 0, line = 0; //line用来保存标识符的个数。row来保存数字个数。i保存读到哪个字符了
char test[1000]; //这是读取文件的内容的。
int number[100]; //常数表,有个疑问,数字需要常数来保存吗?
char mark[100][5]; //用来存放标识符的。
int count = 1; //记录出错的行号
int syn;
int wu;
int final_flag = 0;
//关键字:
char pro[100][100] = { "PROGRAM", "BEGIN", "END", "VAR", "INTEGER", "WHILE",
"IF", "THEN", "ELSE", "DO", "PROCEDURE" ,"char",
"int","if","else","var" ,"return","break",
"do","while","for","double","float","short", "main" };
//关键字:从1开始到25.
void Irparser() {
printf("语法分析n");
i = 0;
count = 1;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
if (syn == 2) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
yucu();
//yucu()函数结束之后,把空格和换行过滤掉
if (syn == 3) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
if (syn == 102 && final_flag == 0) {
printf("成功");
}
else {
printf("这是一串失败的语句");
}
}
else {
printf("缺少END");
}
}
else {
printf("开头缺少BEGINn");
final_flag = 1;
// 看这个单词,如果是BEGIN的下一个找文件内容下一个单词与他匹配,否则就还是从BEGIN开始,单词从下一个找
//BEGIN的下一个是END或者标识符
if (syn == 3 || syn == 26 || syn == 52) {
if (syn == 3) {
One();
}
else if (syn == 26 || syn == 52) {
Two();
}
}
else {
Three();
}
}
}
//当当前这一位是BEGIN的FOLLOW集END时
void One() {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
if (syn == 102 && final_flag == 0) {
printf("成功");
}
else {
printf("这是一串失败的语句");
}
}
// 当当前这一位是BEGIN的follow集标识符时。
void Two() {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
yucu();
//yucu()函数结束之后,把空格和换行过滤掉
if (syn == 3) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
if (syn == 102 && final_flag == 0) {
printf("成功");
}
else {
printf("这是一串失败的语句");
}
}
else {
printf("缺少END");
}
}
//这个是当前单词不是BEGIN的FOLLOW集,所以选择下一个单词。
void Three() {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
if (syn == 2) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
yucu();
//yucu()函数结束之后,把空格和换行过滤掉
if (syn == 3) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
if (syn == 102 && final_flag == 0) {
printf("成功");
}
else {
printf("这是一串失败的语句");
}
}
else {
printf("缺少END");
}
}
else {
printf("第%d行缺少BEGINn", count);
final_flag = 1;
// 看这个单词,如果是BEGIN的下一个找文件内容下一个单词与他匹配,否则就还是从BEGIN开始,单词从下一个找
//BEGIN的下一个是END或者标识符
if (syn == 3 || syn == 26 || syn == 52) {
if (syn == 3) {
One();
}
else if (syn == 26 || syn == 52) {
Two();
}
}
else {
Three();
}
}
}
void yucu() {
statement();
//如果是;,则不断循环下去
while (syn == 38) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
statement();
}
}
void statement() {
//如果是标识符
if (syn == 26 || syn == 52) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
//如果标识符后面为=
if (syn == 28) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
expression();
}
else {
printf("第%d行等号赋值错误n", count);
final_flag = 1;
//当跟=不匹配时,我们知道=的FOLLOW集为标识符(这里的标识符为26
//出现过的标识符的种别码设为26
//未出现过的标识符种别码为52,
if (syn == 26) {
expression();
}
else {
not_biaozhifu();
}
}
}
else {
printf("第%d行标识符有错误n", count);
final_flag = 1;
//这里出现不是标识符的情况,那么如果这个单词是标识符的FOLLOW集,即
//=,所以看这个单词是=吗,要是不是的话,那就直接下一个单词匹配是否是标识符
if (syn == 28) {
is_equal();
}
else {
not_equal();
}
}
}
void not_biaozhifu() {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
if (syn == 28) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
expression();
}
else {
printf("第%d行等号赋值错误n", count);
final_flag = 1;
//当跟=不匹配时,我们知道=的FOLLOW集为标识符(这里的标识符为26
//出现过的标识符的种别码设为26
//未出现过的标识符种别码为52,
if (syn == 26) {
expression();
}
else {
not_biaozhifu();
}
}
}
void is_equal() {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
expression();
}
void not_equal() {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
if (syn == 26 || syn == 52) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
//如果标识符后面为=
if (syn == 28) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
expression();
}
else {
printf("第%d行等号赋值错误n", count);
final_flag = 1;
//当跟=不匹配时,我们知道=的FOLLOW集为标识符(这里的标识符为26
//出现过的标识符的种别码设为26
//未出现过的标识符种别码为52,
if (syn == 26) {
expression();
}
else {
not_biaozhifu();
}
}
}
else {
printf("第%d行标识符有错误n", count);
final_flag = 1;
//这里出现不是标识符的情况,那么如果这个单词是标识符的FOLLOW集,即
//=,所以看这个单词是=吗,要是不是的话,那就直接下一个单词匹配是否是标识符
if (syn == 28) {
is_equal();
}
else {
not_equal();
}
}
}
void expression() {
term();
//当时加或者减的时候
while (syn == 34 || syn == 35) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
term();
}
}
void term() {
factor();
// 当为/或者*的时候
while (syn == 53 || syn == 36) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
factor();
}
}
void factor() {
// 后面是数字或者出现过的标识符
if (syn == 26 || syn == 27) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
}
// 如果是(
else if (syn == 39) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
expression();
// 如果是)的话
if (syn == 40) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
}
else {
printf("第%d行有错误, 缺少)n", count);
final_flag = 1;
//如果不是的话,那么)的FOLLOW集应该是加减乘除或者;或者END
if (syn == 34 || syn == 35 || syn == 36 || syn == 53 || syn == 38 || syn == 3) {
if (syn == 34 || syn == 35 || syn == 36 || syn == 53) {
is_four_cal();
}
// 如果是;38
if (syn == 38) {
is_colon();
}
// 如果是END
if (syn == 3) {
One();
}
}
else {
// 代表没有)的FOLLOW集
not_right();
}
}
}
else {
printf("第%d行输出表达式有错误n", count);
final_flag = 1;
//这里我们看到标识符或者数字下一个即FOLLOW集应该是加减乘除或者;或者END
if (syn == 34 || syn == 35 || syn == 36 || syn == 53 || syn == 38 || syn == 3) {
if (syn == 34 || syn == 35 || syn == 36 || syn == 53) {
is_four_cal();
}
// 如果是;38
if (syn == 38) {
is_colon();
}
// 如果是END
if (syn == 3) {
One();
}
}
else {
not_four_cal();
}
}
}
void not_right() {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
// 如果是)的话
if (syn == 40) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
}
else {
printf("第%d行有错误, 缺少)n", count);
final_flag = 1;
//如果不是的话,那么)的FOLLOW集应该是加减乘除或者;或者END
if (syn == 34 || syn == 35 || syn == 36 || syn == 53 || syn == 38 || syn == 3) {
if (syn == 34 || syn == 35 || syn == 36 || syn == 53) {
is_four_cal();
}
// 如果是;38
if (syn == 38) {
is_colon();
}
// 如果是END
if (syn == 3) {
One();
}
}
else {
// 代表没有)的FOLLOW集
not_right();
}
}
}
void is_colon() {
while (syn == 38) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
statement();
}
}
void not_four_cal() {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
// 后面是数字或者出现过的标识符
if (syn == 26 || syn == 27) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
}
// 如果是(
else if (syn == 39) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
expression();
// 如果是)的话
if (syn == 40) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
}
else {
printf("第%d行有错误, 缺少)n", count);
final_flag = 1;
//如果不是的话,那么)的FOLLOW集应该是加减乘除或者;或者END
if (syn == 34 || syn == 35 || syn == 36 || syn == 53 || syn == 38 || syn == 3) {
if (syn == 34 || syn == 35 || syn == 36 || syn == 53) {
is_four_cal();
}
// 如果是;38
if (syn == 38) {
is_colon();
}
// 如果是END
if (syn == 3) {
One();
}
}
else {
// 代表没有)的FOLLOW集
not_right();
}
}
}
else {
printf("输出表达式有错误n");
final_flag = 1;
//这里我们看到标识符或者数字下一个即FOLLOW集应该是加减乘除或者;或者END
if (syn == 34 || syn == 35 || syn == 36 || syn == 53 || syn == 38 || syn == 3) {
if (syn == 34 || syn == 35 || syn == 36 || syn == 53) {
is_four_cal();
}
// 如果是;38
if (syn == 38) {
is_colon();
}
// 如果是END
if (syn == 3) {
One();
}
}
else {
not_four_cal();
}
}
}
void is_four_cal() {
//当时加或者减的时候
while (syn == 34 || syn == 35) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
term();
}
// 当为/或者*的时候
while (syn == 53 || syn == 36) {
i++;
wu = wordanalysis();
while (wu == 100 || wu == 101) {
i++;
wu = wordanalysis();
}
factor();
}
}
//过滤
void guolv() {
char temp[1000];
int flag = 0;
int j = 0;
while (test[flag] != '#') {
//单行注释//
if (test[flag] == '/' && test[flag + 1] == '/') {
while (test[flag] != 'n') {
flag++;
}
}
else if (test[flag] == '/' && test[flag + 1] == '*') {
//多行注释/**/
flag = flag + 2;
while (test[flag] != '*' && test[flag + 1] != '/') {
flag++;
}
flag = flag + 2;
}
temp[j] = test[flag];
flag++;
j++;
}
temp[j] = '#';
strcpy(test, temp);
}
//词法分析
int wordanalysis()
{
//标识符和保留字
//当第一个字符是字母的时候
if ((test[i] >= 'A'&&test[i] <= 'Z') || (test[i] >= 'a'&&test[i] <= 'z'))
{
char word[10];
//保留字的种别码。从1开始到25
int n = 0;
word[n++] = test[i++];
while ((test[i] >= 'A'&&test[i] <= 'Z') || (test[i] >= '0' && test[i] <= '9') || (test[i] >= 'a'&&test[i] <= 'z'))
{
word[n++] = test[i++];
}
word[n] = '