读书人

一个C语言宣言解析器的设计与实现

发布时间: 2013-10-10 14:14:51 作者: rapoo

一个C语言声明解析器的设计与实现
概论:C语言的声明解析往往复杂多样,使得初学者不知所云,例如double (*(*(*fp3)())[10])() 定义的是什么?int *a[10]和int (*a)[10]有什么区别?C语言的声明解析遵循什么规则?本文主要为你介绍C语言声明解析的规则,然后为你介绍编译器的一个与之相关的部分——声明解析器的设计与实现。

本文来源:一个C语言声明解析器的设计与实现


一、一些复杂声明实例


int *a[10]
int (*a)[10]
int (*(*func)[5][6])[7][8];
int (*(*func[7][8][9])(int*))[5];
看看这些声明自己能否很好的解析:
1)a是一个数组,数组元素是一个指向int的指针
2)a是一个指针,指向具有10个int的数组
3)func是一个指针,它指向一个5X6的数组,数组元素是指针,这些指针指向7X8的int数组
4)func是数组,数组元素是一个指针,这个指针指向一个行参为(int *)的函数指针,这个指针指向int[5]的数组


二、复杂声明解析规则

关于C语言的声明解析规则,可以参考这里:


需要说明的是,合法的声明中存在限制条件,你可以这样做:
int (*fun())():函数的返回值可以是一个函数指针
int (*foo())[]:函数的返回值可以是一个指向数组的指针
int (*foo[])():数组中可以是函数指针
int foo[][]:数组里可以有数组
你不可以这样做:
foo()():函数的返回值不能是一个函数
foo()[]:函数的返回值不能是一个数组
foo[]():数组里面不能是函数
也就是说:函数和函数不兼容,函数与数组互不兼容。


三、一个C语言声明解析器的设计与实现

设计方案:主要数据结构是一个栈,我们从左向右读取,直到遇到标识符为止。然后继续向右读取一个标记,如果遇到括号就向左读取和处理。为了简化编程,说明原理,我们这里约定函数不含有参数。另外,我们只解析正确的声明,对错误声明的出错处理不是很完善。


主要的数据结构:
struct token{char type;

char string[MAXTOKENLEN];};


//第一个标识之前的所有标识
struct token stack[MAXTOKENS];


//正在处理的那个标识
struct token this;


功能程序:
//字符串分类
classify_string()
查看this,通过string成员,确定type成员的值:“type”,qualifier或者是identifier
gettoken():取标记
读取下一个标记到this.string
如果是数字和字母组合,利用clasify_string分类;
如果是一个单字符,this的type值定为该字符;注意使用nul结尾

read_to_first_identifier():

读到第一个标识符号

gettoken()

入栈,直到遇到第一个标识符



解析程序:
deal_with_function():
当读取到)以后,打印“函数返回”
deal_with_array():
读取到】,然后打印size
deal_with_pointer():
解析*和左侧的(
go_left():
不断解析栈顶,直到它变成空
deal_with_declaration()
如果遇到最后声明结尾(gettoken==0),go_left
switch this.type ‘[’ :deal_with_array()
'(': deal with funciton()
‘)’:deal with pointer()

deal with declaration()//递归


主程序:
main
init_parse_decl
read_to_first_identifier
deal_with_declarator


具体的代码:

#include<stdio.h>#include<ctype.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#define MAXTOKENLEN 64#define MAXTOKENS 100#define MAXDECLLEN 200struct token{  char type;  char string[MAXTOKENLEN];};struct decl{  char string[MAXDECLLEN];  int current_location;};struct decl mydecl;int top=-1;struct token this;//the current tokenstruct token stack[MAXTOKENS];//the tokens before the first identifer#define IDENTIFIER 'I'#define QUALIFIER 'Q'#define TYPE 'T'#define pop stack[top--]#define push(s) stack[++top]=s//#define debug char classify_string(const char *string);int main(int argc, char *argv[]){  init_parse_decl();  read_to_first_identifier();  deal_with_declaration();  printf("\n");    return 0;}/* * init the value of mydecl*/int init_parse_decl(){  if( fgets(mydecl.string,MAXDECLLEN,stdin) == NULL){    perror("can not read from the stdin");  }  if(strlen(mydecl.string)==MAXDECLLEN-1)    printf("you input is too long, you may get an error!");  mydecl.current_location=0;  #ifdef debug  printf("init:we get last char of mydecl:%c,%d\n",mydecl.string[strlen(mydecl.string)],mydecl.string[strlen(mydecl.string)]);  #endif}/*  get a token from the current string,value the "this" and move the pointernotice: we may get a '\0' at the end of the string  */int gettoken(){  char *ch_pointer=this.string;  //leave out the blank  while(mydecl.string[mydecl.current_location]==' '){    mydecl.current_location++;  }  *ch_pointer=mydecl.string[mydecl.current_location];  if(isalnum(*ch_pointer)){  while(isalnum(*ch_pointer)){  mydecl.current_location++;  ch_pointer++;  *ch_pointer=mydecl.string[mydecl.current_location];}*ch_pointer='\0';this.type=classify_string(this.string );//indentifier,qualifier,type#ifdef debug printf("we get token:%s ",this.string );#endifreturn 1;  }  //else:(, ), [, ]   //  printf("current location is:%d\n",mydecl.current_location); // printf("(%d)",mydecl.string[mydecl.current_location]);  switch(*ch_pointer){    case '*':    case '(':    case ')':    case '[':    case ']':      this.type=*ch_pointer;      this.string[1]='\0';      mydecl.current_location++;      #ifdef debug       printf("we get token:%s ",this.string );      #endif      break;    case '\n':    case '\0':this.type='\0';      // printf("we come to the end\n");      strcpy(this.string,"then end");      return 0;   default :      printf("we can not read this indentifier %d\n",*ch_pointer);      parse_error();  }  return 1;}char classify_string(const char *string){  if(isspace(*string))    return '\0';  if(!strcmp(string,"const"))    return QUALIFIER;  if(!strcmp(string,"volatile"))    return QUALIFIER;    if(!strcmp(string,"void"))     return TYPE;  if(!strcmp(string,"char"))     return TYPE;  if(!strcmp(string,"signed"))     return TYPE;  if(!strcmp(string,"unsigned"))     return TYPE;  if(!strcmp(string,"short"))     return TYPE;  if(!strcmp(string,"int"))     return TYPE;  if(!strcmp(string,"long"))     return TYPE;  if(!strcmp(string,"float"))     return TYPE;  if(!strcmp(string,"struct"))     return TYPE;  if(!strcmp(string,"union"))     return TYPE;  if(!strcmp(string,"enum"))     return TYPE;  return IDENTIFIER ;}/*follow the dec string until the first identifier,push token to a stack*/int read_to_first_identifier(const char *mydecl){  if(gettoken()==0){    printf("readtofirst:missing the identifier,please put in your string...\n");    return 0;  }  //the token is a struct with member type and string  while(this.type!=IDENTIFIER)  {    push(this);//a stack with element of token    if(gettoken()==0){      printf("readtofirst:missing the identifier\n");      return 0;    }  }  printf("%s is ", this.string);    return 1;}/*deal with the next token in diff ways according to the token typewe just go to right1) "(":deal in a function way,deal_with_declaration,2) "[":deal in a array way,deal_with_declaration,3) ")":deal with a pointer way,deal_with_declaration4) NULL:move_left,*/int  deal_with_declaration(){  if(gettoken()==0){    move_left();    return 1;   }    //  switch(this.type){    case '(': deal_with_function();break;    case ')': deal_with_pointer();break;    case '[': deal_with_array();break;    default : printf("there should not be a %c after the identifier\n",this.type );parse_error();  }  deal_with_declaration();}/*the current token is [,the content in the '[]' may be digtal,al,and other*/int deal_with_array(){  printf("array of ");  while(this.type!=']'){    gettoken();//we may get an "]" or digital    if(isdigit(this.string[0])){      printf("%d  ", atoi(this.string));      //the next token must be ]      gettoken();      if(this.type!=']'){printf("the array miss the ']'before %s",this.string);parse_error();      }    }  }  return 0;}int parse_error(){printf("press any key to exit\n");getchar();exit(0);}/*the current token is ')' */int deal_with_pointer(){  while(stack[top].type=='*'){    printf("pointer pointing to \n");    pop;  }  if(stack[top].type!='('){    printf(" missing an '(' after %s\n",stack[top].string );    parse_error();  }  else{    pop;    return 1;  }}/*the current token is '('*/int deal_with_function(){  while(this.type!=')'){    gettoken();    if(this.type=='\0'){      printf(" missing an ')' before %s",this.string);      parse_error();    }  }  printf(" function returning ");  return 1;}int move_left(){  while(top>=0){    switch (stack[top].type){      case '*': printf(" pointer pointing to ");break;      case  QUALIFIER: printf(" %s ",stack[top].string );break;      case  TYPE: printf(" %s ",stack[top].string );break;      default :printf("there is someting wrong about %s",stack[top].string);parse_error();break;    }    top--;  }}


外部参考:[1]《C专家编程》第三章


读书人网 >C语言

热点推荐