【问题标题】:Getting date parts from a simple treetop parser: wrong argument type Class (expected Module)从简单的树顶解析器获取日期部分:错误的参数类型类(预期模块)
【发布时间】:2015-07-14 13:02:50
【问题描述】:

对于以下树顶语法,在解析 '3/14/01' 时(通过 irb 中的 t = Parser.parse('3/14/01')),我收到“TypeError: wrong argument type Class (expected Module)”。

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

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

对于 Ruby(或树顶)如何实例化这些类或 AST 生成,我显然不理解会发生什么的事情。您能否解释或指出一些可以帮助我理解为什么 &lt;MonthLiteral&gt;&lt;DayLiteral&gt; 无法生成对象的内容?

第二, 这可能是一座太远的桥梁,但我真正想要的是获得一个具有三个属性的DateMDY对象——月、日和年——这样我就可以很容易地从一个DateMDY 中的方法 to_time,但现在我只满足于将组成部分作为对象生成。

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

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

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

谢谢! 理查德

【问题讨论】:

    标签: ruby-on-rails ruby parsing treetop


    【解决方案1】:

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

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

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

    你可以通过在你的顶级规则中添加一个块来做到这一点,就像这样(假设你有一个适当的 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添加单独的结果方法更简洁;这只是介绍如何添加这些方法。

    【讨论】:

    • 谢谢你,克利福德希思。您对 YearLiteral 的解释是有道理的。关于你对 DateMDY 的第二个问题的回答,理论上,我同意你的方法,但我很难看到如何实现这一点。我可能只是没有找到正确的例子。我可以看更多“在野外”的例子,但有没有你推荐的公开例子?我确信我只是对它的工作原理有一个错误的假设,只需要那个时刻来弄清楚如何按照你的建议去做。谢谢!
    • 这个例子有帮助,谢谢。在我有机会尝试之后,我会尝试一下,并将你的标记为答案。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-13
    • 2019-08-28
    • 2018-08-11
    相关资源
    最近更新 更多