编译器-词法分析

总结
  • 词法分析就是对字符串进行处理,然后输出对应的属性字为语法分析做准备;
  • 如果对某个单词进行分析后,不属于任何属性字,那么也要按照错误处理
  • 因此词法分析需要使用到许多字符串处理函数
  • 语法分析就是根据给出的第一个属性字,然后预测接下来的属性字,然后读取下一个属性字,进行比较,如果和预测相比错误,那么就是语法错误
词法分析
  • 汇编器要做的所有事情并不是在一次同时完成的
  • 语言处理器通常是分为不同的阶段,而每个阶段都关注小的,相当简单的任务
  • 这些阶段放在一起构成了一个管道,在它的不同阶段源文件都会向他的目标形式前进一步
  • 一般而言,翻译任何语言的第一个阶段是词法分析,词法分析是把源文件分解成组成它的词
  • 在分离和提取单词之后,词法分析器的真正工作是把单词流转变成属性字流(Token stram)
  • 把单词流转换成属性字流的过程叫做属性字识别,因此词法分析器也叫做属性字识别器
//字符串流
Mov Sum, X; 执行加法运算
//单词流
MOV SUM, X
//属性字流
TOKEN_TYPE_INSTR
TOKEN_TYPE_IDENT
TOKEN_TYPE_COMMA
TOKEN_TYPE_IDENT
语法分析
  • 在管道中语法分析器紧跟在词法分析器和属性字识别器后,并且有一个非常重要的任务
  • 给定一个属性字流,当把它们作为整体单元时,语法分析器负责把它们拼凑在一起
  • 对于函数声明的基本语法分析过程
Token CurrToken = GetNextToken();//从属性字流中读取下一个属性字
if (CurrToken == TOKEN_TYPE_FUNC) //是否是一个函数声明的开始
{
	if (GetNextToken() == TOKEN_TYPE_IDENT)
	{
		string FuncName = GetCurrLexeme(); //当前的单词是函数名 所以保存他
		if (GetNextToken() == TOKEN_TYPE_OPEN_BRACKET) {
			//正确的属性字流
		}
	}
}
  • 在对一个指令进行了语法分析之后,就可以使用指令查找标来验证它的操作数并把它转换成机器码
字符串处理库
  • 空白符 空白符可以存在于任何一个字符串中,它通常被简单地定义为不可见的字符比如说空格,制表符,和和换行符
  • 区分空白符是否包含换行符是很重要的。对于语句可以跨越多行的C语言中,换行没有意义,空白可以包含空白符
  • 字符串与字符可以通过许多方法进行分组和分类,例如如果一个字符串的每个字符都独立满足数字字符条件,那么这个字符串就可以被看作一个数字字符串
  • 你经常需要验证各种各样的字符串类型,范围从标识符到浮点数再到单个的字符,比如说大括号或双引号
  • 这也是决定单词相应属性字时的公用功能,字符串处理函数库应该包含字符串分类函数的扩展集
字符串分类函数
  • 汇编器完成的时候,你会发现最频繁需要的就是字符串分类函数
  • 一般来说,当你按照你的方式处理源代码的时候,你需要了解所给的字符是否是下面几种中的一种
  1. 数字字符
  2. 合法标识符中的字符
  3. 空白符 (空格或制表符)
  4. 分隔符(用来分隔元素的符号,如括号,逗号等)
#define TRUE 1;
#define FALSE 0;

//判定一个字符是否是数字字符
int IsCharNumeric(char cChar) {
	if (cChar >= '0' && cChar <= '9')
	{
		return TRUE;
	}
	else { return FALSE; }
}

//判定一个字符是否是空白符
int IsCharWhitespace(char cChar) {
	if (cChar == ' ' || cChar == "\t")
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

//判定一个字符是否是有效标识符的部分
int IsCharIdent(char cChar) {
	if ((cChar >= '0' && cChar <= '9') ||
		(cChar >= 'A' && cChar <= 'Z') ||
		(cChar >= 'a' && cChar <= 'z') ||
		cChar == '_')
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

int IsCharDelimiter(char cChar) {

	if (cChar == ';' || cChar == ',' || cChar == '"' || cChar == '[' || cChar == ']' || cChar == '{' || cChar == '}' || IsCharWhitespace(cChar))
	{
		return TRUE;
	}
	else {
		return FALSE;
	}

}

int IsStringInt(char* pstrString) {

	if (!pstrString) return FALSE;
	if (strlen(pstrString) == 0) return FALSE;
	unsigned int iCurrCharIndex;

	if (!IsCharNumeric(pstrString[0]) && !pstrString[0] == '-') return FALSE;
	
	for (iCurrCharIndex = 1; iCurrCharIndex < strlen(pstrString); ++iCurrCharIndex)
	{
		if (!IsCharNumeric(pstrString[iCurrCharIndex]))
		{
			return FALSE;
		}
	}
		
	return TRUE;
}

int IsStringFloat(char* pstrString) {
	if (!pstrString) return FALSE;
	if (strlen(pstrString) == 0) return FALSE;

	unsigned int iCurrCharIndex;

	for (iCurrCharIndex = 0; iCurrCharIndex < strlen(pstrString); iCurrCharIndex++)
	{
		if (!IsCharNumeric(pstrString[iCurrCharIndex])&&!(pstrString[iCurrCharIndex]=='.')&&!(pstrString[iCurrCharIndex]=='-'))
		{
			return FALSE;
		}
	}

	//小数点
	int iRadixPointFound = FALSE;
	for (iCurrCharIndex = 0; iCurrCharIndex < strlen(pstrString); iCurrCharIndex++)
	{
		if (pstrString[iCurrCharIndex]=='.')
		{
			if (iRadixPointFound) { return FALSE; }
			else
			{
				iRadixPointFound = TRUE;
			}
		}
	}

	for (iCurrCharIndex = 1; iCurrCharIndex < strlen(pstrString); iCurrCharIndex++)
	{
		if (pstrString[iCurrCharIndex]=='-')
		{
			return FALSE;
		}
	}

	if (iRadixPointFound) {
		return TRUE;
	}
	else {
		return FALSE;
	}

}

int IsStringWhitespace(char* pstrString) {
	if (!pstrString) return FALSE;
	if (strlen(pstrString) == 0) return TRUE;


	for (unsigned int iCurrCharIndex = 0; iCurrCharIndex < strlen(pstrString); iCurrCharIndex++)
	{
		if (!IsCharWhitespace( pstrString[iCurrCharIndex]))
		{
			return FALSE;
		}
	}

	return TRUE;
}

int IsStringIdent(char* pstrString) {
	if (!pstrString) return FALSE;
	if (strlen(pstrString) == 0) return FALSE;

	if (pstrString[0] >= '0' && pstrString[0] <= '9') return FALSE;

	for (unsigned int iCurrCharIndex = 0; iCurrCharIndex < strlen(pstrString); iCurrCharIndex++)
	{
		if (!IsCharIdent(pstrString[iCurrCharIndex]))
		{
			return FALSE;
		}
	}
	return TRUE;
}
汇编程序
  • 汇编程序主要是由管理各种脚本所定义元素如变量,函数和标签的表组成
  • 由于各个脚本之间的这些元素的数量都各不相同,对于他们当中的大部分表,可以使用链表来增长到需要的大小
  • 我决定在内存中缓存所有的东西,这样会使处理过程更快速
  • 缓冲将要输出的汇编指令流,
词法分析器的接口
  • int GetNextToken()
  • 返回当前节点的属性字并把当前节点向后移动一个节点,还要填入g_Lexer结构来反映所有的当前属性字的信息
  • 当我们对单词进行分析的时候,因为分隔符本身也是属性字的一种,所以对于Index0,当遍历到其不是空白符或者制表符的时候就停止,然后让Index1等于Index0,再让Index1进行累加遍历,如果此时Index1遇到分隔符就停止,这样就可以分离出来分隔符。但是其长度为0,所以要让index1加一。
  • 不过此时会有一个bug," " "
  • char * GetCurrLexeme()
  • 返回一个字符指针,它指向包含当前单词的字符串,什么是g_Tokenizer
  • GetLookAheadChar()
  • 向前查看,查看位于当前属性字之后的那个属性字的处理过程。虽然他的确读取了字符,但是它并没有把属性字流的当前指针向后移动。
  • 如果当前读取内容不足以让你正确决定余下的属性字应该是什么,在这些情况下使用向前查看Var MyVar Var MyVar[256] //不确定性
  • void SkipToNextLine()
  • 仅仅想忽略掉一整行的属性字,源代码在内部存储的时候被看作一系列单独的行,这个函数就是增加当前行的计数并且重置属性字识别器的位置
  • ResetLexer()
  • 重置一切,词法分析器要对源代码执行两次遍历,每次执行之前都需要重置,这个函数只会被使用两次
错误处理
  • 错误处理主要包括三个方面,检测,重新同步和消息输出
  • 检测是判断错误是什么时候发生的,它是什么类型的错误
  • 重新同步是使语法分析器回滚的处理过程,让程序可以标记多重错误
  • 错误消息必须输出到屏幕或是某种类型的日志文件
语法分析
  • 重点在于识别初始的属性字,并且根据那个初始的属性字是如何适合语言的规则的,
  • 来预知它后面应该跟随什么属性字
  • 根据这些初始的属性字,你可以判断你正在处理什么种类的代码
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值