简单的Ragel示例平衡括号?

时间:2012-08-18 03:50:54

标签: ruby ragel

以下是语法的起点:

%%{
  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开始,提到了相同的三种方法:

  
      
  1. 使用prepush和postpop指定可增长的堆栈,然后使用fcall   和烦恼。

  2.   
  3. 计算然后在行动或条件中进行验证。

  4.   
  5. 在递归时调用新的解析函数(使用宿主语言)   输入结构。

  6.   

你能指点我的一个例子 - 最好是用我上面的例子 - 在Ruby中吗?谢谢!

3 个答案:

答案 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使用起来会非常令人沮丧。