在Ruby中使用Parslet的压缩敏感解析器?

前端之家收集整理的这篇文章主要介绍了在Ruby中使用Parslet的压缩敏感解析器?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试使用 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

这是我最喜欢的解决方案.

猜你在找的Ruby相关文章