1

对于以下树顶语法,在解析“3/14/01”(通过t = Parser.parse('3/14/01')irb)时,我得到一个“TypeError:错误的参数类型类(预期的模块)”。

grammar SimpleDate

  rule dateMDY
      whitespace? month_part ( '/' / '-') day_part ( ('/' / '-') year_part)? whitespace?  <DateMDY>
  end

  rule month_part
    ( ( '1' [0-2] ) / ( '0'? [1-9] ) )  <MonthLiteral>
  end

  rule day_part
    ( ( [12] [0-9] ) / ( '3' [0-1] ) / ( '0'? [1-9] ) ) <DayLiteral>
  end

  rule year_part
    ( ( '1' '9' ) / ( '2' [01] ) )? [0-9] [0-9]   <YearLiteral>   # 1900 through 2199 (4 digit)
  end

  rule whitespace
    [\s]+
  end

end

首先,如果我注释掉<MonthLiteral><DayLiteral>类引用,一切都很好。注释掉<DateMDY>,但保留那些 Literal 对象,也会发出错误。注释掉<YearLiteral>似乎无关紧要(不管它会起作用还是不起作用)——这似乎表明因为前两个是非终端的,我不能为它们生成元素。

很明显,对于 Ruby(或树顶)如何实例化这些类或关于 AST 生成可以解释会发生什么,我并不欣赏。你能解释或指出一些可以帮助我理解为什么<MonthLiteral><DayLiteral>不能生成对象的东西吗?

其次,这可能是一座过分的桥梁,但我真正喜欢的是获得一个DateMDY具有三个属性的对象——月、日和年——这样我就可以很容易地从中Time的方法生成一个 Ruby 对象,但是现在我会满足于将组成部分作为对象来生产。to_timeDateMDY

所以我尝试<DateMDY>作为对象离开并注释掉对<MonthLiteral>,<DayLiteral>和. 的引用<YearLiteral>。我看到从.parse(t在我的原始示例中) 返回的结果 AST 对象有两个公共方法——但是当我调用这些方法时:day_part:month_part这些方法似乎是 nil (比如puts t.day_part) 并且没有:year_part方法,所以这似乎没有帮助我出去。

是否有可能以某种方式DateMDY最终访问其组成部分?

仅供参考,Parser我使用的代码本身是树顶教程中非常标准的东西node_extensions.rb,定义对象类的代码也很简单,但如果您需要查看这些代码,我也可以发布它们。

谢谢!理查德

4

1 回答 1

4

错误消息准确地告诉您您做错了什么。只有有限的一组地方可以通过这种方式使用 Class。如果允许,Class 必须是 SyntaxNode 的子类。然而,通常你应该使用一个模块,它被扩展()到由内部规则创建的 SyntaxNode 中。YearLiteral 的不同之处在于它不像 Month 和 Day 文字那样包装带括号的序列。这个带括号的序列返回一个现有的 SyntaxNode,它不能用另一个类扩展(),只能用一个模块,所以你得到了 TypeError。

至于您的第二个问题,您想要的 DateMDY 对象几乎肯定不应该是 SyntaxNode - 因为所有 SyntaxNodes 都保留对其所有子 SyntaxNodes 和输入字符串的引用 - 这是我们正在谈论的解析器内部结构。您真的想向外界公开解析器内部的一些内容吗?

相反,您应该安排在解析完成后访问适当的语法节点,方法是调用一个函数,该函数返回使用这些解析器对象标识和保存的子字符串构造的域对象类型。最好添加这些函数以从最顶层的规则向下遍历,而不是尝试“从外部”遍历解析树。

你可以通过在你的顶级规则中添加一个块来做到这一点,就像这样(假设你有一个适当的 DateMDY 类)。当你有一个成功的解析树时,通过调用“tree.result”来获取你的 DateMDY:

rule dateMDY
  whitespace? month_part ( '/' / '-') day_part y:( ('/' / '-') year_part)? whitespace?
  {
    def result
      DateMDY.new(y.empty? ? nil : y.year_part.text_value.to_i,
        month_part.text_value.to_i,
        day_part.text_value.to_i)
    end
  }
end

当然,为 year_part、month_part 和 day_part 添加单独的结果方法更简洁;这只是如何添加这些方法的介绍。

于 2015-05-05T11:55:01.523 回答