词法分析器(C语言版)

词法分析器:

有限状态机的理论并不难,但是如果把状态机理论转换成代码,这个就需要思考了


数据结构设计:

char charList[_CHARLIST_SIZE][15] = {0};
char charList_nu[_CHARLIST_SIZE] = {0};
char charList_index = 0;

char numList[_NUMLIST_SIZE][15] = {0};
char numList_nu[_CHARLIST_SIZE] = {0};
char numList_index = 0;

char delimilterList[_DELIMILTER_SIZE][15] = {0};
char delimilterList_nu[_DELIMILTER_SIZE] = {0};
char delimilterList_index = 0;


由于最后输出需要看到非保留字和数字本身,以及出现的行号,所以这里用三个变量来记录,其中*_index 表示的数组的长度。

 

保留字:

char reserveList[_RESERVE_NUM][15] = {
	"void", "int", "char", "float", "double",
	"while", "auto", "break", "case", "const",
	"continue", "default", "do", "else", "enum",
	"extern", "for", "goto", "if", "long",
	"return", "short", "signed", "sizeof", "static",
	"struct", "switch", "typedef", "union", "unsigned",
	"volatile",  "redister"
};


Google后所查询到的保留字,这里我没有发现main,由此可见main 应该是预处理的时候给处理掉了。

状态机:

由于书上的内容不全,因此在编写的时候在判断 isLetter 加入了’_’,判断数字的时候加入了+- 号的判断,以及双引号的判断。

 

待编译内容

void _fun() {

}

int main() {
    int a = -111;
    int b = +1;
    printf("%d, a);
    return 0;
}


可以看到这个代码是正确的代码。

运行后的结果,可以看到字符和常量已经分别出来了,这里我就不截图了,太麻烦。

 

在处理双引号的时候,当匹配到第一个引号时,即是状态机的初态,遇到下一个引号即到达终态,但是如果是错误代码(如下图),即无法到达终态,怎么办?

对于这种情况我没有单独处理,输出结果如下:


我们看到这里有一个总行数total nu 为7是错误的。因为我们在处理引号的初态时,始终无法匹配到第二个引号,自然也就始终把 /n 当成是引号中的内容,在以引号为初态的状态机中跳转。

关于回退指针,由于我没有一次读取到一个字符数组中,所以用了一个内置函数 ungetc,能将字符放回到流中

由于要以文本输出,就用freopen,然后fprintf即可,如果用fputc的话只能单个的输入。

 

对于数字的处理(下图为样例):


由此可见第一个是正确的输入,后两个均为不可识别的部分。下图为我识别出的部分


原本我想的是这样的识别肯定是错误的,因为它把不应该识别的部分也识别出来了,也就是说这里存在语法错误。但是后来一想,这是对的,因为只要存在不识别的部分,就直接报错。我们只需要关注需要识别的部分即可。这里可以很清楚的看到我把-3.12e+1.11识别出来了。

如果要让line 2和line 3不显示东西的话,需要考虑的问题就是当终态出现的时候,比如识别+1e-e时,先识别 +1e-,然后当识别e 的时候,这里出现了错误,无法到达终态。然后用ungetc回退,之后再次读入e,这里就当成了一个字符,即最后在line 2显示出了e ,这就是原因。Line3也是。但是如我之前所说,只要出现识别不了的东西,直接报错即可。我这里通过返回error,并且这个error是用lastRetval全局变量来标识的,这和windows编程里的getLastError有着一样的意思。


由于是现学现写,不免有部分是错误的。还望各位大神指点,而且目前对语法分析,和语义分析还不清楚。

 

代码:


#include <stdio.h>
#include <iostream>
#include <cstring>

#define NUMERROR -1

#define _RESERVE_NUM 32
#define _DELIMILTER_NUM 8
#define _DELIMILTER_SIZE 100
#define _CHARLIST_SIZE 100
#define _NUMLIST_SIZE 100
#define _TOKEN_SIZE 100
#define COL 1000

#define LT 1
#define LE 2
#define EQ 3

using namespace std;

FILE* fp;
int lastRetval = 0;

char charList[_CHARLIST_SIZE][15] = {0};
char charList_nu[_CHARLIST_SIZE] = {0};
char charList_index = 0;

char numList[_NUMLIST_SIZE][15] = {0};
char numList_nu[_CHARLIST_SIZE] = {0};
char numList_index = 0;

char delimilterList[_DELIMILTER_SIZE][15] = {0};
char delimilterList_nu[_DELIMILTER_SIZE] = {0};
char delimilterList_index = 0;

char reserveList[_RESERVE_NUM][15] = {
	"void", "int", "char", "float", "double",
	"while", "auto", "break", "case", "const",
	"continue", "default", "do", "else", "enum",
	"extern", "for", "goto", "if", "long",
	"return", "short", "signed", "sizeof", "static",
	"struct", "switch", "typedef", "union", "unsigned",
	"volatile",  "redister"
};

char delimilter[_DELIMILTER_NUM][5] = {
	"+", "-", "*", "/", "<", ";", "<=", "=="		// six plus two
};

void nu_print(int nu) {
	int i, cindex, nindex, flag, hasPrint;
	cindex = nindex = 0;
	printf("\n======================== each line to see =========================\n");
	for(i = 0; i < nu; i++) {
		hasPrint = 0;
		for( ; cindex <= charList_index || nindex <= numList_index; ) {
			flag = 0;
			if(charList_nu[cindex] == i+1) {
				if(0 == hasPrint) {
					printf("\nline %d\n", i+1);
					hasPrint = 1;
				}
				printf("    %s ", charList[cindex]);
				++cindex;
				flag = 1;
			}
			if(numList_nu[nindex] == i+1) {
				if(0 == hasPrint) {
					printf("\nline %d\n", i+1);
					hasPrint = 1;
				}
				printf("    %s ", numList[nindex]);
				++nindex;
				flag = 1;
			}
			if(0 == flag) break;
		}
	}
}

void _print(int nu) {
	int i, j;
	printf("\n============== char of list ================\n");
	for(i = 0; i < charList_index; i++) {
		printf("%s  nu: %d\n", charList[i], charList_nu[i]);
		fprintf(fp,"%s  nu: %d\n", charList[i], charList_nu[i]);
	}
	printf("\n============== const number of list ================\n");
	for(i = 0; i < numList_index; i++) {
		printf("%s  nu: %d\n", numList[i], numList_nu[i]);
		fprintf(fp, "%s  nu:%d\n", numList[i], numList_nu[i]);
	}
	printf("\ntotal nu: %d\n", nu);
}

bool isLetter(char a){
	if((a <= 'Z' && a >= 'A') || (a <= 'z' && a >= 'a') || '_' == a) {
		return true;
	}
	else return false;
}

bool isDigit(char a) {
	if(a <= '9' && a >= '1') {
		return true;
	}
	else return false;
}

void concatenation(char token[_TOKEN_SIZE], char str) {
	int len = strlen(token);
	token[len] = str;
}

int reserve(char token[_TOKEN_SIZE]) {
	int i, j;

	for(i = 0; i < _RESERVE_NUM; i++) {
		if(!strcmp(&reserveList[i][0], token)) {
			return 1;
		}
	}
	for(i = 0; i < _DELIMILTER_NUM; i++) {
		if(!strcmp(&delimilter[i][0], token)) {
			return 2;
		}
	}
	return 0;
}

int buildCharList(char token[_TOKEN_SIZE]) {
	strcpy(&charList[charList_index][0], token);
	++charList_index;

	return charList_index-1;
}

int buildNumList(char token[_TOKEN_SIZE]) {
	strcpy(&numList[numList_index][0], token);
	++numList_index;

	return numList_index-1;
}

int analysisCode(char str, int& nu) {
	int num;
	char token[_TOKEN_SIZE];
	memset(token, 0, sizeof(token));
	
	if('\n' == str) {
		++nu;
		return '\n';
	}
	else if(isLetter(str)) {
		while(isLetter(str) || isDigit(str)) {
			concatenation(token, str);
			str = getchar();
		}
		ungetc(str, stdin);
		int type = reserve(token);
		if(0 == type) {
			num = buildCharList(token);
			charList_nu[num] = nu;
		}
		memset(token, 0, sizeof(token));
		return num;
	}
	else if(isDigit(str) || '+' == str || '-' == str) {
		if(NUMERROR == lastRetval) {
			return NUMERROR;
		}
		int dotFlag, eFlag, numFlag, fFlag;
		int eNum, dotNum, fNum;

		dotFlag = eFlag = 0;
		numFlag = fFlag = 1;
		eNum = dotNum = fNum = 0;
		while(isDigit(str) || 'e' == str || '.' == str || '+' == str || '-' == str) {
			if('e' == str) {
				if(0 == eFlag || 1 == eNum) {
					ungetc(str, stdin);
					return NUMERROR;
				}
				dotFlag = 0;
				eFlag = 0;
				numFlag = 1;
				fFlag = 1;
				++eNum;
				dotNum = 0;
				fNum = 0;
			}
			else if('+' == str || '-' == str) {
				if(0 == fFlag || 1 == fNum) {
					ungetc(str, stdin);
					return NUMERROR;
				}
				dotFlag = 0;
				eFlag = 0;
				numFlag = 1;
				fFlag = 0;
				++fNum;
			}
			else if('.' == str) {
				if(0 == dotFlag || 1 == dotNum) {
					ungetc(str, stdin);
					return NUMERROR;
				}
				dotFlag = 0;
				eFlag = 0;
				numFlag = 1;
				fFlag = 0;
				++dotNum;
			}
			else if(isDigit(str)) {
				dotFlag = 1;
				eFlag = 1;
				numFlag = 1;
				fFlag = 0;
			}
			concatenation(token, str);
			str = getchar();
		}
		ungetc(str, stdin);
		num = buildNumList(token);
		numList_nu[num] = nu;
		memset(token, 0, sizeof(token));
		return num;
	}
	else if('"' == str) {
		int flag = 0;
		while(0 == flag) {
			concatenation(token, str);
			str = getchar();
			if('"' == str) {
				flag = 1;
			}

		}
		concatenation(token, str);
	}
	else {
		for(int i = 0; i < 6; i++) {
			if(delimilter[i][0] == str) {
				return str;
			}
		}
		if('<' == str) {
			str = getchar();
			if('=' == str) {
				return LE;
			}
			ungetc(str, stdin);
			return LT;
		}
		if('=' == str) {
			str = getchar();
			if('=' == str) {
				return EQ;
			}
			ungetc(str, stdin);
			return '=';
		}
	}
//	return NUMERROR;
}

int main() {
	freopen("t1.txt", "rw", stdin);
	fp = fopen("D://file.txt", "w");

	char str;
	char token[_TOKEN_SIZE];
	int nu = 1;
	memset(token, 0, sizeof(token));
	while(scanf("%c", &str) != EOF) {
		lastRetval = analysisCode(str, nu);
	}
	
	_print(nu-1);

	nu_print(nu-1);

	fclose(stdin);
	return 0;
}




好的,我可以为您解答关于编译原理词法分析器的问题。首先,词法分析器是编译器中的一个重要组成部分,它的作用是将源代码转换为词法单元(Token)序列。在C语言中,我们可以使用有限自动机(DFA)来实现词法分析器。 以下是一个简单的C语言词法分析器示例: ``` #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #define MAX_LENGTH 100 typedef enum { ID, NUM, PLUS, MINUS, TIMES, DIVIDE, ASSIGN, SEMI, LPAREN, RPAREN, ERROR } TokenType; typedef struct { TokenType type; char value[MAX_LENGTH]; } Token; Token scan() { Token token; char ch = getchar(); while (isspace(ch)) { ch = getchar(); } if (isalpha(ch)) { int i = 0; while (isalnum(ch)) { token.value[i++] = ch; ch = getchar(); } token.value[i] = '\0'; ungetc(ch, stdin); token.type = ID; } else if (isdigit(ch)) { int i = 0; while (isdigit(ch)) { token.value[i++] = ch; ch = getchar(); } token.value[i] = '\0'; ungetc(ch, stdin); token.type = NUM; } else if (ch == '+') { token.type = PLUS; } else if (ch == '-') { token.type = MINUS; } else if (ch == '*') { token.type = TIMES; } else if (ch == '/') { token.type = DIVIDE; } else if (ch == '=') { token.type = ASSIGN; } else if (ch == ';') { token.type = SEMI; } else if (ch == '(') { token.type = LPAREN; } else if (ch == ')') { token.type = RPAREN; } else { token.type = ERROR; } return token; } int main() { Token token; while ((token = scan()).type != ERROR) { printf("%s: %s\n", token.value, token.type); } return 0; } ``` 在这个例子中,我们定义了一个`Token`结构体,包含了`Token`的类型和值。然后通过`scan()`函数实现了词法分析,根据不同的字符类型返回不同的`Token`。最后在`main()`函数中,循环调用`scan()`函数,输出每个`Token`的类型和值。 需要注意的是,这个例子只是一个简单的示例,实际上词法分析器实现要更加复杂,需要考虑到关键字、运算符、注释等情况。不过通过这个例子,您可以了解到词法分析器的基本原理,以及如何使用C语言实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值