使用有限狀態自動機實現C語言的宣告解析器
摘要:在很多的遊戲程式設計中,我們使用了有限狀態自動機作為模型。有限狀態自動機作為變成模型,具有通用性好,方便理解的特點。本文主要結合前一個系列的兩篇文章(1)C語言宣告解析器的實現和(2)用C語言實現有限狀態自動機來說明如何用有限狀態自動機模型實現一個C語言的宣告解析器。
一、狀態機的設計
形式定義
· 定義:有限狀態自動機(FA—finite automaton)是一個五元組:
– M=(Q, Σ, δ, q0, F)
· 其中,
– Q——狀態的非空有窮集合。∀q∈Q,q稱為M的一個狀態,我們常常用列舉型別來實現,定義在machine結構體之外。
– Σ——輸入字母表。我們常常用列舉型別來實現,定義在machine結構體之外。
– δ——狀態轉移函式,有時又叫作狀態轉換函式或者移動函式,δ:Q×Σ→Q,δ(q,a)=p。可以用一個陣列來實現,大概是一個statenum*inputnum的陣列,元素是trasition,包含動作函式action和下一個狀態。放在machine結構之內。
– q0——M的開始狀態,也可叫作初始狀態或啟動狀態。q0∈Q。
– F——M的終止狀態集合。F被Q包含。任給q∈F,q稱為M的終止狀態。
在我們進行程式實現的時候,一個statemachine的定義大概是這樣的:
typedef struct
{
state current;
trasition trasitions[STATENUM][CONDITIONS];
} StateMachine, * pStateMachine;
其中,附屬的其他定義還有其他的相關。
我們在解析一個C語言宣告的時候,大致過程是這樣的:
1)讀取宣告到第一個變數名稱識別符號name,此為狀態S0
2)讀取name後面的下一個識別符號,確定型別並採取相應的動作:
“[”:這是一個陣列,我們要處理這個陣列,然後進入狀態S1
“(”:這是一個函式,我們要處理這個函式,然後進入狀態S2
“)”:它的前面是一個指標,我們要處理這個指標,然後進入狀態S3
“/0”:宣告讀取完畢,可以結束了,進入狀態S4
”other“:出錯
這個狀態機如下:
注意:S1,S2,S3,S4的狀態圖沒有從圖中體現出來,實際上,S1~S4的出邊和S0是幾乎一樣的,所不同的就是S1沒有經過function到達S2的出邊,因為陣列的元素不能是函式(可以是函式指標)。這一部分可以參考前面的C語言宣告規則。
S0:讀完宣告到第一個變數識別符號
S1:讀完一個陣列
S2:讀完後面的一個函式
S3:讀完前面的一個指標
S4:解析完畢
二、資料結構的設計
我們僅僅談與自動機相關的部分,其他的部分可以在 另外一篇文章 一個C語言宣告解析器的設計與實現裡找到。
typedef int (* actiontype)( );//函式指標,是一個動作
typedef struct{
state next;
actiontype action;
}trasition, *ptrasition;//轉移動作:下一個狀態+動作
typedef struct
{
state current;
int statenum;
int conditionnum;
trasition trasitions[STATENUM][CONDITIONS];
} StateMachine, * pStateMachine;//自動機:關鍵是 當前狀態+轉移函式集合
int initMachine(pStateMachine mymachine)//初始化狀態機,設定初始狀態和轉移函式
state step(pStateMachine machine, condition mycondition)//狀態機的運轉函式
三、詳細程式碼
#include<stdio.h>
#include<ctype.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#define MAXTOKENLEN 64
#define MAXTOKENS 100
#define MAXDECLLEN 200
typedef int state;
typedef char condition;
#define STATENUM 5
#define STATE0 0
#define STATE1 1
#define STATE2 2
#define STATE3 3
#define STATE4 4
#define STATETRAP 6
#define CONDITIONS 5
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);
/*
* 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(){
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--;
}
}
typedef int (* actiontype)( );
typedef struct{
state next;
actiontype action;
}trasition, *ptrasition;
typedef struct
{
state current;
int statenum;
int conditionnum;
trasition trasitions[STATENUM][CONDITIONS];
} StateMachine, * pStateMachine;
int initMachine(pStateMachine mymachine){
trasition array={
STATE1,deal_with_array
},
fun={
STATE2,deal_with_function
},
pointer={
STATE3,deal_with_pointer
},
left={
STATE4,move_left
},
acerr={
STATE4,parse_error
},
end={
STATE4,parse_error/*it will no be execed*/
};
trasition mytrasitions[STATENUM][CONDITIONS]={
/*[, (, ), \0,other*/
/*s0*/ array,fun,pointer,left,acerr,
/*s1*/ array,fun,pointer,left,acerr,
/*s2*/ array,fun,pointer,left,acerr,
/*s3*/ array,fun,pointer,left,acerr,
/*s4*/ end, end, end ,end , end
};
mymachine->statenum=STATENUM;
mymachine->conditionnum=CONDITIONS;
mymachine->current=STATE0;
int i,j;
for (i = 0; i < STATENUM ; ++i)
{
for (j = 0; j < CONDITIONS; ++j)
{
mymachine->trasitions[i][j]=mytrasitions[i][j];
}
}
}
state step(pStateMachine machine, condition mycondition)
{
int index;
switch (mycondition){
case '[': index=0;break;
case '(': index=1;break;
case ')': index=2;break;
case '\0': index=3;break;
default: index=4;
}
trasition t = machine->trasitions[machine->current][index];
(*(t.action))( );
if(machine->current==1&&index==1){
printf("\n\n\nerror:the element of array should not be function\n");
parse_error();
}
if(machine->current==2&&index==1){
printf("\n\n\nerror:the returning type of a funciton should not be function\n");
parse_error();
}
if(machine->current==2&&index==0){
printf("\n\n\nerror:the returning type of a funciton should not be array\n");
parse_error();
}
machine->current = t.next;
//printf("the current state is %d\n",t->next );
return machine->current;
}
int main(int argc, char *argv[])
{
StateMachine mymachine;
init_parse_decl();
read_to_first_identifier();
initMachine(&mymachine);
char mycon;
while(mymachine.current!=STATE4){
gettoken();
mycon=this.type;
step(&mymachine,mycon);
}
printf("\n");
return 0;
}
說明:注意line339~line350,這一部分邏輯是新加上去的,因為“函式的返回值不能是函式”等三條規則,所以有了這一部分if語句。這樣,這個宣告解析器在邏輯上就更完備了。
相關文章
- 用C語言實現有限狀態自動機FSMC語言
- 用C語言實現有限狀態機--讀《C專家程式設計》C語言程式設計
- 一個有限狀態機的C++實現C++
- 一個C語言宣告解析器的設計與實現C語言
- 使用有限狀態機原理實現英文分詞分詞
- 有限狀態機(FSM)的使用
- Unity/C# 有限狀態機UnityC#
- 從Promise的實現來看有限狀態機Promise
- [python]有限狀態機(FSM)簡單實現Python
- PHP 有限狀態機使用說明PHP
- C 語言實現使用動態陣列實現迴圈佇列陣列佇列
- 在 .NET 中使用有限狀態機實現工作流建模 - Lloyd
- Unity 中用有限狀態機來實現一個 AIUnityAI
- 前端狀態管理與有限狀態機前端
- JavaScript與有限狀態機JavaScript
- 發現C語言遞迴深度有限制C語言遞迴
- 分析C語言的宣告(2)C語言
- Unity——有限狀態機FSM修改Unity
- 實戰併發-使用分散式快取和有限狀態機分散式快取
- Go 語言實現解析器翻譯Go
- 作業系統:程式狀態轉換模擬,C語言實現作業系統C語言
- 23種設計模式 之 State模式(狀態模式)[C語言實現]設計模式C語言
- C 語言實現使用靜態陣列實現迴圈佇列陣列佇列
- 使用 C 語言實現一個虛擬機器虛擬機
- c語言的定義與宣告C語言
- 探索FSM (有限狀態機)應用
- 從React Redux的實際業務場景來看有限狀態機ReactRedux
- 狀態模式(c++實現)模式C++
- C語言動態呼叫庫(轉)C語言
- C語言動態走迷宮C語言
- FSM狀態機及C#反射實現邏輯C#反射
- 「譯」有限狀態機在 CSS 動畫中的應用CSS動畫
- go語言 變數的宣告與使用Go變數
- Go中的有限狀態機FSM的詳細介紹Go
- C 語言隨機數生成器的實現分析隨機
- 使用列舉實現狀態機來優雅你的狀態變更邏輯
- realloc 實現隨使用者輸入自動擴充套件陣列長度(C語言)套件陣列C語言
- C語言(動態記憶體分配)C語言記憶體