前言
Ragel 是 Adrian Thurston 提出的狀態機編譯器。
使用Ragel,你可以簡單的製作一個詞法分析的分析器和編譯器。 Ragel還支持多種語言輸出,目前可以生成C / C + + / Objective-C/D/Java/Ruby的源代碼。
狀態機簡單的說是一種狀態轉換圖。 根據輸入值, 狀態機將進到入指定狀態。可以確定狀態轉換最後的輸入,並且可以知道這種輸入是否正確的。
狀態機是的各種用途。
- 作為一個編譯器詞法分析。
- 用來確定數據是否正確的格式。
- 替代正則表達式。
- 用於設計Web應用程序畫面轉換。
- 各種跟狀態變遷有關的設計, 例如網路協定處理, 自動販賣機設計...
Ragel,Ragel用戶指南請點擊這裡
Windows可執行文件請點擊這裡
此外,Ragel,可以產生視化狀態圖,建議您安裝 Graphviz。
Ragel基本使用
要知道如何使用Ragel,讓我們創建第一個簡單的例子。為了描述狀態機,第一次嘗試生成Ragel狀態圖語法的定義。
請參閱list-1。這是一個辨認整數和小數範例。
list-1 ex1.rl:辨認整數和小數的文法
1: %%{ 2: 3: ## 狀態機名稱 4: machine ex1; 5: 6: ## 文法定義 7: main := ('+' | '-')? [0-9]+ ('.' [0-9]+)? ; 8: 9: }%% |
要點如下。
- 第1,9行:Ragel狀態機是指從"%%{"到"}%%"之間。 如果只有一個行,你可以在第一列“%%”之 後寫Ragel敘述句。
- 您需要為狀態機命名。 這個名字,在Ruby和C代碼生成的情況下,成為變量名稱前綴。
- 第7行:從“main:=” 到 “;”是定義文法。在這裡,寫一個模式語法。
- "('+' |'-')?"表示,即使是一個或是沒有的正負符號。
- “[0-9] +”表示數字,最少出現一次, 可以重複出現。
- “('.' [0-9 ]+)?", 小數點, 然後至少一個的數字顯示。 在 “?” 意指整個是可省略的。
- “#”是一個註釋行開頭。
list-2 編譯它,產生狀態圖的圖形文件
### Ragel 編譯 c:> ragel -Vp ex1.rl > ex1.dot ### Graphviz 圖檔產生 (dot.exe 在 Graphviz目錄內) c:> dot -Tpng ex1.dot > ex1.png |
- “○”和“◎”代表狀態。 “◎”是接受狀態,如果輸入值有匹配的語法在結束時的輸入狀態(輸入==接受)的手段。
- 箭頭代表狀態遷移。箭頭上的字符串代表的轉換發生條件。
從這一狀態圖中,可以看到下面。
- 有5個狀態
- 最初狀態是1
- 接受狀態是4 & 5
... 中略 日文基礎太差, 看不懂原文 這是筆記, 不用全部翻譯.....
Ragel允許你為個別Ragel狀態表示式命名。
list-3. ex2.rl
%%{ ## 狀態機名稱 machine ex2; ## 表示式命名 sign = '+' | '-'; d = [0-9]; ## 文法定義 main := sign? d+ ('.' d+)? ; }%% |
當您生成這個定義的狀態轉換圖,跟fig-1是完全一樣的。
Ragel此外,還有下列預定義的別名。 欲了解更多信息參閱Ragel用戶指南,第2.3節的基本機
- any 任意文字符
- ascii ASCII碼, 字符代碼以及介於0和127。
- alpha 字母。 與“[A-Za-z]”相同。
- digit 數字 與 “[0-9]”相同。
- alnum 文數字 與 “[0-9A-Za-z]" 相同。
- xdigit 十六進制字符。與 “[0-9A-Fa-f]" 相同。
- space 空白文字。 與"[\t\v\f\n\r ]"相同。
C代碼生成 (原文是Ruby, 懂C的人還是比較多)
Ragel可自動生成狀態代碼。要做到這一點,寫下面的源代碼。
- %% write data;
- 生成一個狀態機所需的靜態數據。
- %% write init;
- 初始化狀態機必須變數。
- %% write exec;
- 運行狀態機實作。
%%{
## 狀態機名稱
machine ex3;
## 表示式命名
sign = '+' | '-';
## action 定義
##
action on_sign { /* 正負號辨認 */
/* 「fc」是現在scan到的文字
Ragel
*/
sign_char = fc;
#if DEBUG
printf( "*** on_sign: sign_char=%c\n", sign_char);
#endif
}
action on_int { /* 整數辨認 */
ch = fc;
#if DEBUG
printf( "*** on_integer: ch=%c", ch);
#endif
val = val * 10 + (ch - '0');
}
action on_float { /*小數部份 */
ch = fc;
#if DEBUG
printf( "*** on_float: ch=%c", ch);
#endif
e = 0.1 * e;
val += e * (ch - '0');
}
## main 文法定義。
main := sign? @on_sign digit+ @on_int ('.' digit+)? @on_float ;
}%%
#define DEBUG 0
/* 產生必要的狀態機資料 */
%% write data;
float scan( char *input )
{
char *p = input; /* Ragel 的目前文字指標 */
char *pe = input + strlen( p ); /* Ragel 的終點文字指標 */
int cs; /* Ragel 的目前狀態編號 */
int fc; /* Ragel 的目前文字 == *p */
int ch;
float e = 1.0;
float val = 0;
char sign_char = 0;
/* Ragel 初始化 */
%% write init;
/* Ragel 狀態機展開 */
%% write exec;
/* 檢查最終狀態 */
if( cs < ex3_first_final )
{
printf( "** syntax error (p=%p, data[p]='%c')" , p, *p );
}
printf( "cs=%d, fl=%d", cs, ex3_first_final);
/*## return 解析 result */
return( sign_char == '-' ? -val : val);
}
main(int argc, char *argv[])
{
char line[100];
float val;
while( gets(line) )
{
val = scan(line);
printf( "val=%f\nOK.\n", val);
}
}
用Ragel編譯此代碼,生成C源代碼, 用C編譯器產生執行檔, (list-5)。
list-5. 編譯和運行
C:> ragel -C ex3.rl # 從ex3.rl生成 ex3.c C:> wcl386 ex3.c C:> ex3 -123 val=-123 OK. 3.14 val=3.14 OK. 123daa! ** syntax error (p=3, data[p]='d') |
Ragel此外,還有一些變數來指定更先進的和詳細的行動。 欲了解更多信息參閱Ragel用戶指南,第3章用戶操作請點擊這裡。
建立詞法分析器
當您建立一個編譯器詞法分析(掃描)經常使用狀態機來實作。 因此,Ragel提供簡便的詞法分析的功能。
Ragel使用此功能,在定義語法“| *”和“* |”用來描述模式匹配的動作了。 具體來說,像list-6這樣寫。
list-6. Ragel的詞法分析文法
%%{ main := |* pattern1 => { action1 }; pattern2 => { action2 }; pattern3 => { action3 }; *|; }%% |
因為模式匹配, 採用最長匹配方法, 所以需要做匹配失敗時回溯(backtrack). 需要一些變數來記錄這些變化 如下。
- ts
- 匹配起始位置的標記。
- te
- 匹配結束位置的標記。
- act
- 代表最後成功的模式。用於回溯。
list-7. ex4.rl: 字句解析列表7。ex4.rl:詞法分析
%%{ ## 狀態機名稱 machine ex4; ## 定義 sign = '+' | '-'; ## 文法定義 main := |* ## 空白 space+ ; ## do nothing ## 整數 sign? digit+ => { memcpy( tokenstr, ts, te-ts); tokenstr[te-ts]=0; printf( "INT %s ", tokenstr); fbreak; /* 狀態遷移中止 */ }; ## 小數 sign? digit+ '.' digit+ => { memcpy( tokenstr, ts, te-ts); tokenstr[te-ts]=0; printf( "FLOAT %s ", tokenstr); fbreak; /* 狀態遷移中止 */ }; ## 識別子 [a-zA-Z_] [a-zA-Z0-9_]* => { memcpy( tokenstr, ts, te-ts); tokenstr[te-ts]=0; printf( "IDENT %s ", tokenstr); fbreak; /* 狀態遷移中止 */ }; *|; }%% #include |
list-8. 編譯和運行
C:> ragel -C ex4.rl C:> wcl386 ex4.c C:> ex4 123 -3.14 foo ** token=:INT, str="123" ** token=:FLOAT, str="-3.14" ** token=:IDENT, str="foo" OK. func(123) ** token=:IDENT, str="func" ** syntax error (cs=0, p=4, data[p]='(') |
-- 下面整理中--
JSON -- ? 待續