在 Ruby Treetop 中使用 elements.map 时,如何处理 0 或多个语句中没有非终端节点?

How do you handle no nonterminal node in a 0 or more statement when using elements.map in Ruby Treetop?

我正在尝试创建一个映射其所有非终端节点的自定义语法节点 class。问题是其中一个节点不一定必须存在,这在自定义语法节点 class 中使用 elements.map 时会产生问题,因为语法节点树会创建 SyntaxNode: "" for相反,我还没有为它创建 class。

grammar Foo
  rule textStructure
    beginDoc twoOrMoreNewLines (block twoOrMoreNewLines)* endDoc <Structure>
  end

  rule beginDoc
    'begin document' <BeginLine>
  end

  rule twoOrMoreNewLines
    "\n" 2.. <NewLine>
  end

  rule block
    !beginDoc information:((!"\n" .)+) <BlockLine>
  end

  rule endDoc
    'end document' <EndLine>
  end
end

# On a different file


module Foo
  class Structure < Treetop::Runtime::SyntaxNode
    def to_array
      return self.elements.map {|x| x.to_array}
    end
  end

  class BeginLine < Treetop::Runtime::SyntaxNode
    def to_array
      return self.text_value
    end
  end

  class NewLine < Treetop::Runtime::SyntaxNode
    def to_array
      return self.text_value
    end
  end

  class BlockLine < Treetop::Runtime::SyntaxNode
    def to_array
      return self.information.text_value
    end
  end

  class EndLine < Treetop::Runtime::SyntaxNode
    def to_array
      return self.text_value
    end
  end
end

例如,如果我尝试解析:"begin document\n\nend document"。然后我希望这是一个输出:["begin document"、“\n\n”、"end document"],但我收到错误消息:block in to_array': undefined methodto_array'对于 SyntaxNode offset=16, "":Treetop::Runtime::SyntaxNode (NoMethodError).

所以我做了一些进一步的调查,发现语法节点树确实在 offset=16 处包含一个 SyntaxNode "",我认为这是由于 (block twoOrMoreNewLines)* 不存在。

我该如何处理这个问题?有没有办法避免创建 SyntaxNode ""?

偏移量 16 处的 SyntaxNode 包含一个空子数组,用于迭代 sub-rule。 Packrat 解析算法需要它才能工作。您不应该只在任意 SyntaxNode 上调用 to_array,而应该对其进行特殊处理。最好的方法是给它打上标签,然后在迭代它的元素之前询问标签是否为空:

rule textStructure
  beginDoc twoOrMoreNewLines nested:(block twoOrMoreNewLines)* endDoc <Structure>
end

...

class Structure < Treetop::Runtime::SyntaxNode
  def to_array
    return nested.empty? ? [] : nested.elements.map {|x| x.to_array}
  end
end

或类似的东西。