gpt4 book ai didi

ruby - 在 Ruby 中使用 Parslet 的缩进敏感解析器?

转载 作者:数据小太阳 更新时间:2023-10-29 06:46:17 24 4
gpt4 key购买 nike

我正在尝试使用 Parslet 解析简单的缩进敏感语法Ruby 中的库。

以下是我尝试解析的语法示例:

level0child0
level0child1
level1child0
level1child1
level2child0
level1child2

生成的树看起来像这样:

[
{
:identifier => "level0child0",
:children => []
},
{
:identifier => "level0child1",
:children => [
{
:identifier => "level1child0",
:children => []
},
{
:identifier => "level1child1",
:children => [
{
:identifier => "level2child0",
:children => []
}
]
},
{
:identifier => "level1child2",
:children => []
},
]
}
]

我现在拥有的解析器可以解析嵌套级别 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. 通过将每一行识别为缩进和标识符的集合来解析文档,然后应用转换以根据缩进数重建层次结构。

  2. 使用捕获来存储当前缩进并期望下一个节点包含该缩进加上更多以作为子节点匹配(我没有深入研究这种方法,因为我想到了下一个)

  3. 规则只是方法。所以你可以将'node'定义为一个方法,这意味着你可以传递参数! (如下)

这让您可以根据 node(depth+1) 定义 node(depth)。然而,这种方法的问题是 node 方法不匹配字符串,它生成一个解析器。所以递归调用永远不会完成。

这就是 dynamic 存在的原因。它返回一个解析器,该解析器直到它尝试匹配它时才被解析,让您现在可以毫无问题地进行递归。

请看下面的代码:

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 - 在 Ruby 中使用 Parslet 的缩进敏感解析器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16504958/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com