一個C語言宣告解析器的設計與實現
概論:C語言的宣告解析往往復雜多樣,使得初學者不知所云,例如double (*(*(*fp3)())[10])() 定義的是什麼?int *a[10]和int (*a)[10]有什麼區別?C語言的宣告解析遵循什麼規則?本文主要為你介紹C語言宣告解析的規則,然後為你介紹編譯器的一個與之相關的部分——宣告解析器的設計與實現。
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]的陣列
int (*fun())():函式的返回值可以是一個函式指標
int (*foo())[]:函式的返回值可以是一個指向陣列的指標
int (*foo[])():陣列中可以是函式指標
int foo[][]:陣列裡可以有陣列
你不可以這樣做:
foo()():函式的返回值不能是一個函式
foo()[]:函式的返回值不能是一個陣列
foo[]():陣列裡面不能是函式
也就是說:函式和函式不相容,函式與陣列互不相容。
設計方案:主要資料結構是一個棧,我們從左向右讀取,直到遇到識別符號為止。然後繼續向右讀取一個標記,如果遇到括號就向左讀取和處理。為了簡化程式設計,說明原理,我們這裡約定函式不含有引數。另外,我們只解析正確的宣告,對錯誤宣告的出錯處理不是很完善。
主要的資料結構:
struct token{char type;
struct token stack[MAXTOKENS];
//正在處理的那個標識
struct token this;
功能程式:
//字串分類
classify_string()
檢視this,通過string成員,確定type成員的值:“type”,qualifier或者是identifier
gettoken():取標記
讀取下一個標記到this.string
如果是數字和字母組合,利用clasify_string分類;
如果是一個單字元,this的type值定為該字元;注意使用nul結尾
解析程式:
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()
main
init_parse_decl
read_to_first_identifier
deal_with_declarator
本文來源:一個C語言宣告解析器的設計與實現
一、一些複雜宣告例項
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 200
struct 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 token
struct 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 pointer
notice: 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 );
#endif
return 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 type
we just go to right
1) "(":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_declaration
4) 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--;
}
}
相關文章
- 使用有限狀態自動機實現C語言的宣告解析器C語言
- c語言的定義與宣告C語言
- [譯] 用javascript實現一門程式語言-寫一個解析器JavaScript
- 如何設計和實現一個C庫 -- 《C語言介面與實現:建立可重用軟體的技術 》書評C語言
- 實現一個自己的語法解析器與執行引擎
- C語言-超市倉庫管理系統的設計與實現C語言
- 分析C語言的宣告(2)C語言
- Go 語言實現解析器翻譯Go
- C語言如何實現泛型程式設計?C語言泛型程式設計
- 《Effective C++》閱讀總結(四): 設計、宣告與實現C++
- MySQL的C語言程式設計(一)MySqlC語言程式設計
- C語言實現一個簡易的Hash table(7)C語言
- 一個關於pynoi遊戲的C語言程式設計遊戲C語言程式設計
- c語言程式設計--實驗報告一C語言程式設計
- # c語言程式設計——實驗報告一C語言程式設計
- c語言程式設計——實驗報告一C語言程式設計
- JavaScript實現一個簡單的Markdown語法解析器JavaScript
- 使用 C 語言實現一個虛擬機器虛擬機
- JavaScript函數語言程式設計之pointfree與宣告式程式設計JavaScript函數程式設計
- C語言實現的一個簡單的猜數小遊戲C語言遊戲
- C 語言宣告與定義不一致導致的問題
- C語言預設引數值的實現C語言
- C語言異常與斷言介面的實現C語言
- c語言 - 模仿qsort的功能實現一個通用的氣泡排序C語言排序
- go語言 變數的宣告與使用Go變數
- C語言開發入門與程式設計實踐pdfC語言程式設計
- 執行緒池的原理與C語言實現執行緒C語言
- C語言程式設計C語言程式設計
- 使用C語言程式設計的7個步驟C語言程式設計
- Effective C++ 4.設計與宣告C++
- 使用Xcode實現第一個C語言程式——Hello worldXCodeC語言
- 用JavaScript實現一門程式語言 3-2 (解析器之InputStream)JavaScript
- DDD的函數語言程式設計實現函數程式設計
- 用C語言實現有限狀態機--讀《C專家程式設計》C語言程式設計
- 奇怪的C語言——C51程式設計C語言注意事項C語言程式設計
- C語言-字串函式的實現(一)之strlenC語言字串函式
- 用JavaScript實現一門程式語言 3-1 (解析器之抽象語法樹)JavaScript抽象語法樹
- 《C語言程式設計:問題與求解方法》——2.2節C語言歷史概述C語言程式設計