第四章 C编译器的实现
C编译器从整体上被划分为4个阶段:词法分析、语法分析、语义分析、代码生成,这4个阶段分别用不同的程序模块来实现(如表3-1)。一个C源程序经过C编译器的编译之后,生成三地址的四元表达式目标代码,在整个编译过程中,这4个阶段分别承担了相应的翻译任务。
4.1 词法分析阶段
4.1.1 概述
编译器的词法分析阶段可将源程序读作有序字符文件并将其扫描分解为若干个记号(token)。记号与自然语言中的单词类似:每一个记号都是表示源程序中信息单元的字符序列。典型的有:关键字(keyword),例如if和while,它们是字母的固定串,在该语言中具有特定的含义;标识符(identifier)是由用户定义的串,它们通常由字母和数字组成并由一个字母开头,例如变量名函数名等;算符符(operation symbol)在语言中是作为语法上的运算符号使用的。例如,+,-,*,/,(,),和++, --, <=;界符在语言中作为语法上的分界;数字,广义上就是源程序中的字面值。
如前所述,词法分析程序的输入是源程序字符串,输出是与源程序等价的符号序列。作为词法分析程序的符号可以有各种不同的内部表现形式,原则是不同的符号能彼此区别开且有唯一的表示。
为了便于编译程序的进一步加工(语法分析),内部表示的符号都按属性字形式,因此,词法分析程序的输出即是属性字序列,采用二元式来表示一个单词符号的内部形式。
符号类别 符号值 表4-1 词法Token字 这五类单词将用不同的方法处理。关键字、算符与界符将直接形成Token字;标识符将插入符号表后形成Token字,数字将插入常数表后形成Token字。
在实际做词法分析时,考虑了所有的C语言现象,使得每一个C语言程序都可以被此法分析切割为单词并且赋值上属性。
一般情况下,可以通过两种途径来设计词法分析程序。一种是用手工方式构造,另一种是所谓词法分析程序的自动生成。为了实现简单,本实验采取手工构造方式。 4.1.2 C词法分析程序的实现
C语言的记号分为5种类型:标示符,关键字,数字,算符和界限符。关键字一共
14
有8个,它们的含义类似于标准C语言中的相应关键字。特殊符号共有12种:分别是4种基本的整数运算符号,5种比较符号(等号、小于号、小于等于、大于、大于等于),以及左括号、右括号、分号、赋值符号。其中,除了比较等号、小于等于符号和大于等于是两个字符的长度之外,其余均为单个字符。“其他”记号就是数和标识符了,数是一个或多个数字的序列,而标识符又是一个或多个字母的序列。所有这些记号归纳如下表4-2:
关键字(8个) 界限符(12个) 其它 Int、if、while、double、return、void、continue、break +、-、*、/、==、= 、<、<=、>、>=、( 、) 、; 数 (1个或更多的数字序列) 标识符 (1个或更多的字母序列) 表4-2C语言的记号
除了上表的记号之外,C语言的源程序还要遵循以下的词法规则:代码应是自由书写格式;空白符由空格、制表位和新行组成。
根据对语言中各类单词某种描述的定义,用手工方式构造词法分析程序。
词法分析程序的主要任务就是扫描程序、识别单词、转换并输出属性字。下面给出单独成趟词法分析控制流程图。
开 始
输入C源文件 是否文件结尾N Y 进入主控模块
Y 选择识别的单词 形成Token字
停 止 15
图4-1 单独成趟的词法分析控制流程图
词法分析主要包含在Scanner.h和Scanner.cpp文件中,其核心函数是scan( ),它每次从第一个非空格或换行符开始识别,直到遇到空格或者换行符,遇到单行注释就跳过本行从“//”开始的以后字符识别,遇到多行注释就跳过直到遇到“*/”为止(不支持注释嵌套)。每次返回一个识别的单词,遇到不识别的单词就报错,输出不识别的单词和该单词的行数。
扫描程序在识别出每个记号的同时,还会计算出该记号的特性(比如串值)这五类单词将用不同的方法处理。关键字、算符与界符将直接形成Token字;标识符将插入符号表后形成Token字,数字将插入常数表后形成Token字。
Token是本实验的所有类的基类,有2个成员变量:Tag,Value。Value是文法中非终结符,而Tag是Value对应的唯一标记。 4.1.3 关键字与标识符的识别
C对于关键字的识别,是通过先将它们看作是标识符,然后再调用Token类的get_int()方法,根据返回值Tag是否22(标示符的标志)为来判断是标示符还是关键字。 4.1.4 词法识别具体实现
//分析一个单词,并返回单词的Token字
Token Scanner::scan() {
int i=0; string s;
static char prev_c = 0;
char line[Token::NAME_SIZE] = {0}; prev_c = c; while (true) { if (fin.get(c)==NULL) //到文件结尾 return (Token()); if (c=='\\n') //遇到换行符 linenum++; //行数+1 else if (c==' ' || c=='\\t') //遇到空格或者制表符 continue; //跳过,继续读 else //遇到普通字符,跳出循环 break; }
16
//遇到注释的情况
while (c=='/' && (fin.peek()=='/' || fin.peek()=='*')) { //处理注释 } //end while
if (isalpha(c)) //字母 return (alpha_process()); else if (isdigit(c)) //数字 return (digit_process()); else //其他 { switch(c) //对字符进行分析 { case '+': {...} case '-': {...} case '*': {...} case '/': {...} case '=': {...} case '<': {...} case '>': {...} case '!': {...} case '&': {...} case '|': {...} case '~': {...} case '(': return (Token(get_int(\ case ')': return (Token(get_int(\ case '[': return (Token(get_int(\ case ']': return (Token(get_int(\ case '{': return (Token(get_int(\ case '}': return (Token(get_int(\
17
}
case '\ return (string_process()); case ';': return (Token(get_int(\ case ',': return (Token(get_int(\ case '.': return (Token(get_int(\ case ':': return (Token(get_int(\ case '\\'': {...} case '#': {...} default: std::cout << \ << \ line[0] = c; line[1] = '\\0'; return (Token(get_int(\ } }
return Token();
4.2 语法分析阶段
4.2.1 概述
语法分析是编译过程的核心,分析的任务是根据语法规则分析源程序的语法结构,并在分析过程中,对源程序进行语法检查,如果语法没有错误,则给出正确的语法结构,为语义分析和代码生成做准备。
目前语法分析方法有多种多样,大致分为自顶而下和自底而上两大类。自顶而下又分为LL(1)分析方法和递归下降分析方法。自底而上又分为简单优先文法、算符优先文法、LR(K)分析方法。下面主要介绍自底而上的LR(K)分析方法。
自底向上分析法,也称移进-归约分析法。它的实现思想是对输入符号串自左向右进行扫描,并将输入符逐个移入一个后进先出栈中,边移入边分析,一旦栈顶符号串形成某个句型的句柄时,(该句柄对应某产生式的右部),就用该产生式的左部非终结符代替相应右部的文法符号串,这称为移步归约。重复这一过程直到归约到栈中只剩文法的开始符号时则为分析成功,也就确认输入串是文法的句子。否则,分析失败,表示输入符号串不是文法的一个句子,其中必定存在语法错误。
LR(K)分析方法是1965年由D.Knuth先生提出的一种自底而上的语法分析方法。自底而上的分析方法就是移进-规约的过程。LR(K)分析法能根据分析栈的当前内容以及向前看输入串的K个字符来决定分析动作移进还是规约。
LR(K)分析方法适用范围较广,分析速度较快,并且能准确及时地发现语法错误。由于LR(K)分析方法对文法限制很少,因而大多数能用上下文无关文法描述的程序设计语言都可用LR分析法进行有效分析,而且LR分析效率不亚于自顶向下分析法好、
18
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库C语言编译器设计与实现毕业论文设计(5)在线全文阅读。
相关推荐: