间接方式是指单独设置一个字符串数组来存放所有的标识符,并在符号表的名字栏中设置两项内容:一是指针,用来指向标识符在数组中的起始位置;二是一整数值,用来表示该标识符的长度。
另一种组织方式是按标识符的种属,如简单变量、数组、过程等分别建立不同的符号表,如简单变量名表、数组名表、过程名表等。例如,下面的函数: int f(int a,int b) { int c; if(a>b) c=1; else c=0; return c; } 常用符号表结构 1.线性符号表
符号表中最简单且最容易实现的数据结构是线性表,它是按程序中标识符出现的先后次序建立的符号表,编译程序不做任何整理次序的工作。
在扫描源程序时,根据各标识符在程序说明部分出现的先后顺序将标识符及其有关信息填入符号表中。当编译过程中需要查找符号表中的某个标识符时,只能采用线性查找方法,即从符号表的第一项开始直到表尾一项一项地顺序查找。这种查找方法的缺点是效率较低。 2.有序符号表
为了提高查表速度,可以在造表的同时把各标识符按照一定的顺序进行排列。显然,这样的符号表是有序的对于有序符号表,一般采用折半查找法进行查表,即首先从表的中项开始比较,如果未找到则将查找范围缩小一半,然后继续查找,直到找到或查找失败为止。使用这种折半查找法对一个含有N项的符号表来说,查找其中的一项最多只需做1+lbN次比较。虽然这种查找法的效率有所提高,但是对于一个边填写边引用的动态查找符号表来说,每填进一项就引起表中内容重新排序,这无疑会增加时间的开销。
对于动态查找的符号表,可以采用二叉树结构来组织有序符号表。对二叉树中的任一结点P来说,它的左子树上的任何结点都大于结点P的值,而右子树上任何结点都小于结点P的值,这样一棵二叉树实际上是二叉排序树。每当向符号表中填入一项时,总是将其作为二叉排序树的叶结点插入到合适位置。查找的过程也是这样,
首先将给定值K与二叉排序树根结点值比较,若相等则查找成功;若不等,则当
根结点值小于K时到根的左子树去继续查找,否则到根的右子树去继续查找。在随机情况下,二叉排序树的平均查找长度为1+4lbN。较之折半查找法,虽然二叉排序树的查找速度略有降低,但它大大减少了生成有序符号表的时间,是一种较好的有序符号表结构。 3.散列符号表
散列符号表是大多数编译程序采用的一种符号表。符号表采用散列技术,相对来讲具有较高的运行效率,特别适用于边填写边引用的动态查找符号表。 散列符号表又称哈希符号表,其关键在于引进了一种函数——哈希函数,并将程序中出现的标识符通过哈希函数进行映射,得到的函数值作为该标识符在符号表中的位置。
哈希函数(Hash)一般具有如下性质:
(1) 函数值只依赖于对应的标识符; (2) 函数的计算简单且高效;
(3) 函数值能比较均匀地分布在一定范围内。 错误处理
1 语法错误的校正
对源程序错误的处理通常有两类不同方法:一类是对错误进行适当的修补;另一类是跳过有错误的那个语法成分。第二类方法又称为错误的局部化法。 2 语义错误的校正 (1).遏止错误株连信息
错误株连,是指当源程序出现一个错误时,此错误将导致发生其它错误,而后者可能并不是一个真正的错误。
为了遏止这种株连信息,一种简单的办法是在源程序中用一个“正确”的标识符去替换出错的标识符,同时把新标识符登入符号表中并尽可能填入各种属性。 (2).遏止重复出错信息
防止诸如此类的重复出错信息比较容易。当在源程序中发现使用了一个未经说明的标识符时,就将它登入符号表中,并根据上下文填写所查出的一些属性;再建立另一张表,其中各个登记项有相应标识符的各种错误用法,在遇到一个错用的标识符时就顺序检查这张表。如果以前曾按同样方式使用过该标识符,就不再输出出错信息;否则,除输出出错信息外,还要将本次错用的情况登入该表。
编译原理实验程序的开发
编译原理是计算机类专业特别是计算机软件专业的一门重要专业课。设置
该课程的目的在于系统地向学生讲述编译系统的结构、工作流程及编译程序各组成部分的设计原理和实现技术,使学生通过学习既掌握编译理论和方法方面的基本知识,也具有设计、实现、分析和维护编译程序等方面的初步能力。编译原理是一门理论性和实践性都比较强的课程,使读者学习起来普遍感到内容抽象、不容易理解。在教学过程中发现有很多学生对课程不理解,为了辅助教学,帮助学生更好的学习编译原理在开发了课件后又开了几个经典的编译原理实验程序。进行上机实验的目的是使学生通过完成上机实验题目加深对课堂教学内容的理解。同时培养学生实际动手能力。
1词法分析程序的开发
实验内容:
用C语言编写程序,输入B类单词的源程序,利用词法分析将其转换为单词,
二元式形式输出。 实验目的:
深刻理解词法分析的含义,握计算机内部程序的编译过程。 实现过程:
本次实验所用的语言为标准C,以下同。本功能实现的主函数为getToken函数。通过从文件中读取字符到缓冲区中并由C语言字符的状态转换图流程判断返回一个字符(Token)。分析出来的Token主要分为关键字,专用符号,标记符号。 主体结构的说明:
在这里说明部分告诉我们使用的LETTER,DIGIT, IDENT(标识符,通常定义为字母开头的字母数字串)和STR(字符串常量,通常定义为双引号括起来的一串字符)是什么意思.这部分也可以包含一些初始化代码.例如用#include来使用标准的头文件和前向说明(forward ,references).这些代码应该再标记\和\之间;规则部分>可以包括任何你想用来分析的代码;我们这里包括了忽略所有注释中字符的功能,传送ID名称和字符串常量内容到主调函数和main函数的功能. 实现原理:
程序中先判断这个句语句中每个单元为关键字、常数、运算符、界、对 不同的单词符号给出不同编码形式的编码,用以区分之。 实验代码: #include
char token[10],a[30]; int letter(chars) {
if(s>=97&&s<=122) return(1); else return(0); }
int digit(chars) {
if(s>=48&&s<=57) return(1) else return(0); }
void get(); {
i++; c=a[i]; int lookup() {
if(!strcmp(token,”while”)) return(1); else if(!strcmp(token,”if”)) return(2); else if(!strcmp(token,”else”)) return(3); else if(!strcmp(token,”switch”)) return(4); else if(!strcmp(token,”case”)) return(5); else return(6); }
void main() {
printf(“input your source program:\\n”);
i=0; do {i=i+1;
scanf(“%c”,&a[i]); }
wile(a[i]!=“#”); getchar(): for(k=1;k<10;k++) token[k]=” “; i=0;j=0; get();
while(c!=”#”) {
switch(c); {
case ‘a’; case ‘b’; case ‘c’; case ‘d’; case ‘e’; case ‘f’; case ‘g’; case ‘h’;
case ‘i’; case ‘j’; case ‘k’’ case ‘l’; case ‘m’; case ‘n’; case ‘o’; case ‘p’; case ‘q’; case ‘r’; case ‘s’;