我正在尝试使用
Ruby中的
Parslet库解析一个简单的缩进敏感语法.
以下是我尝试解析的语法示例:
level0child0 level0child1 level1child0 level1child1 level2child0 level1child2
结果树将如下所示:
[ { :identifier => "level0child0",:children => [] },{ :identifier => "level0child1",:children => [ { :identifier => "level1child0",:children => [] },{ :identifier => "level1child1",:children => [ { :identifier => "level2child0",:children => [] } ] },{ :identifier => "level1child2",] } ]
我现在可以解析嵌套级别0和1个节点的解析器,但无法解析:
require 'parslet' class IndentationSensitiveParser < Parslet::Parser rule(:indent) { str(' ') } rule(:newline) { str("\n") } rule(:identifier) { match['A-Za-z0-9'].repeat.as(:identifier) } rule(:node) { identifier >> newline >> (indent >> identifier >> newline.maybe).repeat.as(:children) } rule(:document) { node.repeat } root :document end require 'ap' require 'pp' begin input = DATA.read puts '','----- input ----------------------------------------------------------------------','' ap input tree = IndentationSensitiveParser.new.parse(input) puts '','----- tree -----------------------------------------------------------------------','' ap tree rescue IndentationSensitiveParser::ParseFailed => failure puts '','----- error ----------------------------------------------------------------------','' puts failure.cause.ascii_tree end __END__ user name age recipe name foo bar
很明显,我需要一个动态计数器,期望3个缩进节点匹配嵌套级别3上的标识符.
如何以这种方式实现使用Parslet的压缩敏感语法解析器?可能吗?
解决方法
有几种方法
>通过将每一行识别为缩进和标识符的集合来解析文档,然后根据缩进数重新构建层次结构.
>使用捕获来存储当前的缩进,并期望下一个节点包含该缩进加更多以作为一个小孩来匹配(我没有像下一个发生在这个方面那么深入)
>规则只是方法.所以你可以定义“节点”作为一种方法,这意味着你可以传递参数! (如下)
这使您可以根据节点(深度1)定义节点(深度).然而,这种方法的问题是node方法与一个字符串不匹配,它会生成一个解析器.所以递归调用永远不会完成.
这就是动态存在的原因.它返回一个解析器,直到它尝试匹配它为止,让您现在可以递归没有问题.
请参阅以下代码:
require 'parslet' class IndentationSensitiveParser < Parslet::Parser def indent(depth) str(' '*depth) end rule(:newline) { str("\n") } rule(:identifier) { match['A-Za-z0-9'].repeat(1).as(:identifier) } def node(depth) indent(depth) >> identifier >> newline.maybe >> (dynamic{|s,c| node(depth+1).repeat(0)}).as(:children) end rule(:document) { node(0).repeat } root :document end
这是我最喜欢的解决方案.