Ragel是個有限狀態機編譯器,它將基於正則表達式的狀態機編譯成傳統語言(C,C++,D,Java,Ruby等)的解析器。
用Ragel可以很方便且很容易的寫出各種FSM,也經常用作語法檢測器。
一個用C語言實現的例子:
#include <stdio.h> #include <string.h> %%{ machine foo; #FSM 名稱 #定義動作 action res_true { res=1; } action res_false { res=0; } action res_err { res=-1; } #FSM 起點 main := ( 'true'0 @res_true | 'false'0 @res_false | any @res_err); #寫入 FSM 數據 write data; }%% int GetRes(char *pbuf) { int res; char *p=pbuf; //初始化 p 指向需要做 FSM 處理的數組起始地址 char *pe=p+strlen(pbuf)+1; //初始化 pe 指向 p 的結束地址 int cs; // cs 用來保存 FSM 運行中狀態 //寫入初始化代碼 %%write init; //寫入執行代碼 %%write exec; return res; } int main() { int cs; char buf[256]; while (scanf("%s",buf)) { printf("res=%d\n",GetRes(buf)); } return 0; }
編譯
上面的代碼還不能直接用gcc編譯,需要先用ragel編譯成C語言代碼,再用gcc編譯成可執行程序。
ragel -o main.c main.rl gcc -o test main.c
上面例子實現的是把字符串"true" "false"轉換成C語言1 0的形式,如果既不是"true"也不是"false"則結果為-1。
執行結果
輸入
true false truefalse
輸出
res=1 res=0 res=-1
基本語法
多行的FSM定義以 %%{ 開始 %%} 結束。單行的FSM定義在行首以 %% 開始。
machine foo; 狀態機的名稱。
action 定義匹配動作,動作內寫入匹配后所要執行的代碼。
上面代碼有3個動作,分別是 res_true, res_false, res_err 用來得出結果 。
main := 正則表達式; 表示FSM起始點,匹配先從這里開始。
上面代碼中( 'true'0 @res_true | 'false'0 @res_false | any @res_err)表示
(如果成功匹配 "true\0" 執行動作res_true) 或則 (如果成功匹配 "false\0" 執行動作res_false) 或則 (如果成功匹配 任意字符 執行動作res_err )
any是Ragel 的關鍵字,類似的還有
| 關鍵字 | 描述 |
| any | 所有字符. |
| ascii | ascii字符.0~127 |
| extend | ascii擴展的字符.有符號-128~127或無符號0~255 |
| alpha | 字母.[a~z A~Z] |
| digit | 數字.[0~9] |
| alnum | 字母和數字.[a~z A~Z 0~9] |
| lower | 小寫字母.[a~z] |
| upper | 大寫字母.[A~Z] |
| xdigit | 16進制數字.[0~9 a~f A~F] |
| cntrl | 控制字符.0~31 |
| graph | 可視字符.[!-~] |
| 可打印字符.[ -~] | |
| punct | 非字母數字可視字符.[!-/:-@[-‘{-~] |
| space | 空白字符.[\t\v\f\n\r ] |
| zlen | 空字符串."" |
| empty | 空集.^any |
%%write data; 寫入FSM運行中需要的狀態數據,可以放在任何地方,但必須要在 %%write exec 之上。
%%write init; 寫入FSM的初始化代碼,放在函數之內,需要先定義 int cs。
%%write exec; 寫入FSM的執行代碼,放在函數之內,需要先定義 char *p 和 char *pe。
結束語
雖然使用Ragel很輕松的解決平常我們 if else if 功能,而且效率也不錯,但是會使生成的源代碼和程序體積變大(19K生成30M源碼),所以使用前還是需要考慮考慮。
更詳細的使用說明可以在Ragel State Machine Compiler下載使用手冊。
