总结词法分析就是对字符串进行处理然后输出对应的属性字为语法分析做准备如果对某个单词进行分析后不属于任何属性字那么也要按照错误处理因此词法分析需要使用到许多字符串处理函数语法分析就是根据给出的第一个属性字然后预测接下来的属性字然后读取下一个属性字进行比较如果和预测相比错误那么就是语法错误词法分析汇编器要做的所有事情并不是在一次同时完成的语言处理器通常是分为不同的阶段而每个阶段都关注小的相当简单的任务这些阶段放在一起构成了一个管道在它的不同阶段源文件都会向他的目标形式前进一步一般而言翻译任何语言的第一个阶段是词法分析词法分析是把源文件分解成组成它的词在分离和提取单词之后词法分析器的真正工作是把单词流转变成属性字流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语言中换行没有意义空白可以包含空白符字符串与字符可以通过许多方法进行分组和分类例如如果一个字符串的每个字符都独立满足数字字符条件那么这个字符串就可以被看作一个数字字符串你经常需要验证各种各样的字符串类型范围从标识符到浮点数再到单个的字符比如说大括号或双引号这也是决定单词相应属性字时的公用功能字符串处理函数库应该包含字符串分类函数的扩展集字符串分类函数汇编器完成的时候你会发现最频繁需要的就是字符串分类函数一般来说当你按照你的方式处理源代码的时候你需要了解所给的字符是否是下面几种中的一种数字字符合法标识符中的字符空白符 空格或制表符分隔符用来分隔元素的符号如括号逗号等#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_TokenizerGetLookAheadChar()向前查看查看位于当前属性字之后的那个属性字的处理过程。虽然他的确读取了字符但是它并没有把属性字流的当前指针向后移动。如果当前读取内容不足以让你正确决定余下的属性字应该是什么在这些情况下使用向前查看Var MyVar Var MyVar[256] //不确定性void SkipToNextLine()仅仅想忽略掉一整行的属性字源代码在内部存储的时候被看作一系列单独的行这个函数就是增加当前行的计数并且重置属性字识别器的位置ResetLexer()重置一切词法分析器要对源代码执行两次遍历每次执行之前都需要重置这个函数只会被使用两次错误处理错误处理主要包括三个方面检测重新同步和消息输出检测是判断错误是什么时候发生的它是什么类型的错误重新同步是使语法分析器回滚的处理过程让程序可以标记多重错误错误消息必须输出到屏幕或是某种类型的日志文件语法分析重点在于识别初始的属性字并且根据那个初始的属性字是如何适合语言的规则的来预知它后面应该跟随什么属性字根据这些初始的属性字你可以判断你正在处理什么种类的代码