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 | 非字母数字可视字符.[!-/:[email protected][-‘{-~] |
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下载使用手册。