Rya*_*wis 10 ruby parsing indentation parslet
我试图使用Ruby中的Parslet库解析一个简单的缩进敏感语法.
以下是我尝试解析的语法示例:
level0child0
level0child1
level1child0
level1child1
level2child0
level1child2
Run Code Online (Sandbox Code Playgroud)
生成的树看起来像这样:
[
{
:identifier => "level0child0",
:children => []
},
{
:identifier => "level0child1",
:children => [
{
:identifier => "level1child0",
:children => []
},
{
:identifier => "level1child1",
:children => [
{
:identifier => "level2child0",
:children => []
}
]
},
{
:identifier => "level1child2",
:children => []
},
]
}
]
Run Code Online (Sandbox Code Playgroud)
我现在的解析器可以解析嵌套级别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
Run Code Online (Sandbox Code Playgroud)
很明显,我需要一个动态计数器,它需要3个缩进节点匹配嵌套级别3上的标识符.
如何以这种方式使用Parslet实现缩进敏感的语法分析器?可能吗?
Nig*_*rne 14
有几种方法.
通过将每一行识别为缩进和标识符的集合来解析文档,然后应用转换以基于缩进的数量重构层次结构.
使用捕获来存储当前缩进并期望下一个节点包含该缩进加上更多以匹配作为子项(我没有深入研究这种方法,因为下一个发生在我身上)
规则只是方法.所以你可以将'node'定义为一个方法,这意味着你可以传递参数!(如下)
这让你定义node(depth)来讲node(depth+1).但是,这种方法的问题是该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
Run Code Online (Sandbox Code Playgroud)
这是我最喜欢的解决方案.