我简化了在Treetop中表达的语法,我试图使用自定义节点将解析器的输出过滤为AST。
grammar Elem
rule top
lpar 'top' space
args_:(lpar 'args' space ((ident / number) space?)* rpar) space?
rpar <Top>
end
rule ident
[a-zA-Z] [a-zA-Z0-9_]* <Ident>
end
rule number
[0-9]+ <Number>
end
rule space
[\s]+
end
rule lpar
space? '(' space?
end
rule rpar
space? ')' space?
end
end
基本上,它可以解析以下示例:
(top (args foo bar 42))
自定义节点全部继承 Treetop :: Runtime :: SyntaxNode
现在,我需要将Treetop生成的解析树过滤成AST。
我遵循解释here的策略,没有成功:我的AST只是空的......
我的编译器驱动程序如下:
require 'treetop'
require 'pp'
require_relative 'elem'
require_relative 'node_extension'
class ElemCompiler
def initialize
@parser=ElemParser.new
end
def compile filename
puts "==> compiling #{filename}"
@ast=parse(filename)
puts "==> AST in memory. Good."
end
def parse filename
pp tree=@parser.parse(IO.read(filename))
pp clean(tree)
end
private
def clean(root_node)
return if(root_node.elements.nil?)
pp root_node.elements.collect {|node| node.class.name =="Treetop::Runtime::SyntaxNode" }
pp root_node.elements.delete_if{|node| node.class.name == "Treetop::Runtime::SyntaxNode" }
root_node.elements.each {|node| clean(node) }
end
end
compiler=ElemCompiler.new.parse ARGV[0]
我错过了什么?
答案 0 :(得分:3)
您的代码确实正确解析了提供的表达式。
但是,在clean方法中有一个小错误:
def clean(root_node)
return if(root_node.elements.nil?)
pp root_node.elements.collect {|node| node.class.name =="Treetop::Runtime::SyntaxNode" }
pp root_node.elements.delete_if{|node| node.class.name == "Treetop::Runtime::SyntaxNode" }
root_node.elements.each {|node| clean(node) }
end
clean方法返回最后一个求值表达式,它是数组元素的每个方法。实际上,您希望返回的是根节点,因此行pp clean(tree)
实际上会打印生成的干净树,而不是每个表达式的结果。
您可以通过两种方式解决,一种是将root_node添加为返回表达式:
def clean(root_node)
(...)
pp root_node.elements.delete_if{|node| node.class.name == "Treetop::Runtime::SyntaxNode" }
root_node # here
end
或者您可以将解析方法更改为以下内容:
def parse filename
pp tree = @parser.parse(IO.read(filename))
clean(tree) # we clean the tree
pp tree # since tree is an object, side-effects will persist here
end
但是,我不建议清理树。这样做我有一些非常糟糕的经历。确实,你得到一个更清晰的结构,你可以更好地理解,因为Treetop经常保存你实际上不需要的大量信息,但是你可能会失去,例如,有可能引用带有标识符的解析表达式(自定义标签)或Automatically-Defined Element Accessor Methods表示非终端符号)(这是一个webarchive链接)。
此外,在某些情况下,仅仅因为它的类名是“Treetop :: Runtime :: SyntaxNode”而清理节点根本就不正确,因为在某些情况下你必须使用模块而不是类来扩展你的节点,在这种情况下,节点类名仍将是“Treetop :: Runtime :: SyntaxNode”,但该节点将从树中清除,您将失去混合模块功能。
如果我很清楚,请告诉我(不幸的是文档网站似乎已经关闭,它有很多有用的例子我想告诉你,因为我已经有一段时间不玩语法我不喜欢真的记得它。)