2

我已经简化了用 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。

我遵循这里解释的策略,但没有成功:我的 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]

我错过了什么?

4

1 回答 1

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 方法返回最后评估的表达式,即数组元素的 each 方法。实际上,您要返回的是根节点,因此该行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

或者您可以将 parse 方法更改为以下内容:

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 通常会保留很多您实际上不需要的信息,但是您可能会丢失,例如,使用其标识符(自定义标签)引用已解析表达式的可能性或非终端符号的自动定义的元素访问器方法 )(这是一个网络存档链接)。

此外,在某些情况下,仅仅因为它的类名是“Treetop::Runtime::SyntaxNode”而清理节点是不正确的,因为在某些情况下,您必须使用模块而不是类来扩展节点,并且,在这种情况下,节点类名仍将是“Treetop::Runtime::SyntaxNode”,但节点将从树中清除,您将失去混合模块功能。

让我知道我是否清楚(不幸的是,文档站点似乎已关闭,它有很多有用的示例我想向您展示,并且由于已经有一段时间了,我不玩语法我真的不记住它)。

于 2014-06-15T15:27:04.273 回答