以下是语法的起点:
%%{
machine xo;
char = "x" | "o";
group = "(" char* ")";
main := group;
}%%
例如,它处理(xxxx(oo)()xx)
。如何扩展它以允许嵌套组;例如(xxxx(o(x)o)()xx
?
我知道递归通常不受一台Ragel机器的支持。所以这不起作用:
group = "(" ( char | group )* ")";
来自Ragel State Machine Compiler User Guide (PDF) :(为加重而添加的粗体文字):
“一般来说,Ragel无法处理递归结构,因为语法被解释为常规语言。但是,根据需要解析的内容,有时使用手动编码技术实现递归部分 这通常适用于递归结构简单易于识别的情况,例如括号中的平衡。“
“解析递归结构的一种方法是使用递增和递减计数器的操作,或以其他方式识别递归结构的入口和退出结构,然后使用fcall和fret跳转到适当的机器定义。或者,可以使用语义条件测试计数器变量。
“更传统的方法是在输入递归结构时调用单独的解析函数(以主机语言表示),然后在识别结束时返回。”
从a mailing list discussion on nested brackets开始,提到了相同的三种方法:
使用prepush和postpop指定可增长的堆栈,然后使用fcall 和烦恼。
计算然后在行动或条件中进行验证。
- 醇>
在递归时调用新的解析函数(使用宿主语言) 输入结构。
你能指点我的一个例子 - 最好是用我上面的例子 - 在Ruby中吗?谢谢!
答案 0 :(得分:9)
使用fcall / fret的一般模式如下:
balanced = [^(){}\[\]] |
'(' @{ fcall balancedTokensParen; } |
'[' @{ fcall balancedTokensBracket; } |
'{' @{ fcall balancedTokensBrace; };
balancedTokensParen := balanced* ')' @{ fret; };
balancedTokensBracket := balanced* ']' @{ fret; };
balancedTokensBrace := balanced* '}' @{ fret; };
所以你的案子可以作为
处理 char = [xo];
group = '(' @{ fcall group_rest; };
group_rest := (char|group)* ')' @{ fret; };
main := group;
词法分析器函数应包含stack
数组,您必须手动检查top
以确保没有未关闭'(':
stack = []
%% write init;
%% write exec;
if top > 0
cs = %%{ write error; }%%
end
答案 1 :(得分:1)
我也一直在寻找关于Ragel问题的日子!
对于传递递归[嵌套括号]等需求,Ragel没有很好的文档记录。
我在Google搜索5天后找到的唯一示例代码是:
https://bitbucket.org/mitsuhiko/arana-main/src/289ad1a6f083/arana/lexnparse.rl
看看Ragel堆栈的需求以及fgoto()[或fcall()],fret()以及其他代码管理需要做的Ragel代码,我(就像其他许多人一样)思考Ragel并不是满足这些需求的简单工具。否则将有多个(1)工作示例可用。
答案 2 :(得分:1)
粗略地说,如果你试图匹配parens,解决方案将涉及以下内容:
open_paren = '(' @{ @paren_count += 1}
close_paren = (')' when @paren_count > 0) @{ @parent_count -= 1}
查看“语义条件用户指南”末尾的部分。
顺便说一句:Ragel是一个非常强大的工具,您必须了解实际使用的基础。使用Ragel的第一步是阅读用户指南并理解它 - 虽然还有一些你不确定的部分,但Ragel使用起来会非常令人沮丧。