您的位置:首页 > 其它

ZZZ语言的语义分析,第一步,解析Specifier(上)

2013-06-06 22:47 302 查看
1 语义分析的任务

  我们的任务是检查我们的程序语言ZZZ的语义错误以及为后续的中间代码生成创造便利条件。其中很重要的内容是有关类型的检查。

首先,我们就得知道每个变量的类型,

  要进行语义分析,首先就要知道每个变量的类型。我们采取解析语法树的办法,把遇到的变量和它的类型存入符号表,之后每次遇

到一个变量,都查看符号表,看变量在其作用域内的声明或定义是否存在,其类型是什么,之后才能进一步判断语义是否正确。

2 解析Specifier

  ZZZ的语言支持的类型包括基本类型(double,int,bool),数组,结构体和它们的指针,加上我们的模板类(Template,模板类

只支持结构体)。要得到每个变量的类型,就必须解析语法树的Specifier节点。

2.1 表示类型的数据结构

  首先,定义类型的数据结构。我在参考的实验攻略的基础上定义的表示类型的结构体如下:

//basic's value
#define BASIC_INT 0
#define BASIC_DOUBLE 1
#define BASIC_BOOL 2

enum kind_t{basic,array,structure,T};//T is a template mark,it will be replaced
// by basic or structure later

typedef struct Type_* Type;
typedef struct FieldList_* FieldList;

struct Type_{
char *name;
enum kind_t kind;
int isTempDec;//Is it a Template structure
int isPoint;//Is it a point type
union{
int basic;
struct {Type elem;int size;} array;
FieldList structure;//if (kind == structure && u.structure == NULL),it is the same with the struct who does have members
}u;

};

struct FieldList_{
char *name;
Type type;
FieldList next;
};


  对上面的Type_的定义,有几点需要注意。

1) 其中kind表示这个类型是基本类型(basic),还是数组(array)还是结构体(structure),

  它们分别对应联合体u的三个成员。

2 ) basic具体是什么,就用3个宏定义表示;

3) array是一个Type类型的链表,从头开始记录每一个维度的size,n维数组的前n-1个Type结点的kind都是array,最后一个结点的Type表示元素的类型。

4) 注意,kind_t.T表示的是当前类型就是泛型T,还没有具体类型。它只会出现在泛型结构体和泛型函数的参数,返回类型和局部变量中。此时

  联合体u中没有对应的值;

5) isTempDec表示的是当前结构体是否为一个泛型结构体,当它为true时,此时它的kind一定为kind_t.structure(因为只有结构体类型有泛型,不要和kind.T混淆)。

该结构体成员依旧由FieldList structure链表连起来。只不过其成员有可能会有kind_t.T的类型。

2.2 解析Specifier

  分析每个变量的类型,当然是根据语法树的结构,也就是对应的产生式进行解析。以下是Specifier的产生式:

Specifier          :    TYPE
|    StructSpecifier Temp
|    StructSpecifier
|    POINT StructSpecifier Temp
|    POINT StructSpecifier
|    T
|    POINT T
;

StructSpecifier     :    STRUCT OptTag LC DefList RC
|    STRUCT Tag
;

OptTag              :    ID
|     /*empty*/
;

Tag                 :     ID
;

BASICTYPE           :        INT
|        DOUBLE
|        BOOL

TYPE                :        BASICTYPE
|        POINT BASICTYPE
;


  产生式的左边的非终结符就是语法树的父结点,右边每个终结符或非终结符都是子结点。

  通过遍历语法树,我们每次碰到一个Specifier结点,就以此为参数,调用Type parse_specifier(node,isTempDec)函数来解析,函数返回指就是一个Type_的指针,

指向的是一个已经创建好了的Type_结点。

  解析函数中,isTempDec被当作参数是因为,TempDec是与Specifier同层的结点,产生式如下:(其中,终结符只有INT DOUBLE BOOL POINT T TEMPLATE ID)

ExtDef                 :  TempDec    Specifier ExtDecList SEMI
|  TempDec    Specifier SEMI
  |    TempDec    Specifier FunDec CompSt
    ;

TempDec                :    TEMPLATE
  |   /*empty*/
  ;


读到这里,对于解析specifier的困难之处,读者乍一看可能看不出来.

其中的难点其实在structSpecifier:

StructSpecifier		:	STRUCT OptTag LC DefList RC


这个产生式中的DefList我还没列出来,DefList,顾名思义,就是定义列表.这里表示的就是结构体的成员定义.

其产生式如下:

DefList				     :	Def 	DefList
|	/*empty*/
;

Def					:	Specifier	DecList SEMI
;

DecList				     :	Dec
|	Dec COMMA DecList
;

Dec					:	VarDec
|	VarDec	ASSIGN	Exp
;

VarDec	 			     : 	ID
|	VarDec LB LINTEGER RB
;


DefList是一个递归的定义,表示可以有无穷多个Def,Def就是一个或者一组同类型变量定义,举个例子:

struct Spec0{
int a;
double d1,d2;
};


这里int a和double d1,d2;分别是2个Def.这里的主要任务有:

1) 对DecList的解析,要递归的生成一个链表,表中所有的类型都相同(都为Specifier,递归调用parse_specifier(),返回值被当作参数传入parse_decList()).最后

 返回FieldList的表头,连接到先前生成的成员链表的尾部;变量名 都必须不同;//查错点1

2 )在解析VarDec的时候,生成的可能是数组,需要递归地连接数组的每一个维度;

3 )结构体中变量不能初始化,(我们没有static机制),所以解析Dec的时候能够查VarDec ASSIGN Exp的语义错;//查错点2

4 )每次返回一个解析完Def后的成员链表,都必须检查这个表与之前的整个结构体成员链表是否有重名的变量,如果有就报错;//查错点3

2.3 有关结构体和指针和泛型的事宜

这部分内容将在下一个博客中讨论。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: