【问题标题】:LOOKAHEADs for the JavaScript/ECMAScript array literal productionJavaScript/ECMAScript 数组字面量生成的 LOOKAHEADs
【发布时间】:2014-11-13 12:16:52
【问题描述】:

我目前使用 JavaCC 实现 JavaScript/ECMAScript 5.1 解析器,但 ArrayLiteral 生产存在问题。

ArrayLiteral :
    [ Elision_opt ]
    [ ElementList ]
    [ ElementList , Elision_opt ]

ElementList :
    Elision_opt AssignmentExpression
    ElementList , Elision_opt AssignmentExpression

Elision :
    ,
    Elision ,

我有三个问题,我会一一问。

这是第二个。


我已将这个产生式简化为以下形式:

ArrayLiteral:
    "[" ("," | AssignmentExpression ",") * AssignmentExpression ? "]"

正确与否请看第一个问题:

How to simplify JavaScript/ECMAScript array literal production?

现在我尝试在JavaCC中实现如下:

void ArrayLiteral() :
{
}
{
    "["
    (
        ","
    |   AssignmentExpression()
        ","
    ) *
    (
        AssignmentExpression()
    ) ?
    "]"
}

JavaCC 抱怨 ,AssignmentExpression(其内容)模棱两可。显然,需要LOOKAHEAD 规范。我花了很多时间试图弄清楚LOOKAHEADs,尝试了不同的东西,比如

  • LOOKAHEAD (AssignmentExpression() ",")(...)*
  • LOOKAHEAD (AssignmentExpression() "]")(...)?

还有其他一些变体,但我无法摆脱 JavaCC 警告。

我不明白为什么这不起作用:

void ArrayLiteral() :
{
}
{
    "["
    (
        LOOKAHEAD ("," | AssignmentExpression() ",")
        ","
    |   AssignmentExpression()
        ","
    ) *
    (
        LOOKAHEAD (AssignmentExpression() "]")
        AssignmentExpression()
    ) ?
    "]"
}

好的,AssignmentExpression() 本身是模棱两可的,但 LOOKAHEADs 中的尾随 ",""]" 应该清楚应该采取哪些选择 - 还是我在这里弄错了?

此产品的正确LOOKAHEAD 规范是什么样的?

更新

不幸的是,这不起作用:

void ArrayLiteral() :
{
}
{
    "["
    (
        ","
    |
        LOOKAHEAD (AssignmentExpression() ",")
        AssignmentExpression()
        ","
    ) *
    (
        AssignmentExpression()
    ) ?
    "]"
}

警告:

Warning: Choice conflict in (...)* construct at line 6, column 5.
         Expansion nested within construct and expansion following construct
         have common prefixes, one of which is: "function"
         Consider using a lookahead of 2 or more for nested expansion.

第 6 行在第一个 LOOKAHEAD 之前是 (。公共前缀"function" 只是AssignmentExpression 的可能开头之一。

【问题讨论】:

    标签: javascript parsing grammar ecmascript-5 javacc


    【解决方案1】:

    JavaCC 产生自顶向下的解析器。我首先要说的是,我不喜欢自上而下的解析器生成器,所以我不是 JavaCC 专家,也没有方便测试。

    (编辑:我认为其他方法会起作用,但后来我意识到我不明白 JavaCC 如何将前瞻附加到实际选择;在( A | B )* C 的情况下,实际上有三个可能的选择:A、B 和 C。我以为它会考虑所有三个,但它可能一次只做两个。所以下面是另一个猜测。)

    话虽如此,我认为以下方法可行,但它涉及解析几乎每个AssignmentExpression() 两次。

    {
        "["
        (
            ","
        |
            AssignmentExpression()
            ","
        ) *
        (
            LOOKAHEAD (AssignmentExpression() "]")
            AssignmentExpression()
        ) ?
        "]"
    }
    

    正如我在the linked question 中指出的,更好的解决方案是以不同的方式重写产生式:

    "[" AssignmentExpression ? ("," AssignmentExpression ?) * "]"
    

    这会导致单标记前瞻语法,因此您不需要LOOKAHEAD 声明来处理它。

    【讨论】:

    • ArrayLiteral -> '[' AssignmentExpression ? (',' AssignmentExpression ?) * ']' - 我正在考虑这个问题,但它使我的代码更加复杂。我实际上使用一种构建器模式构建ArrayLiteral 。所以在',' 这里我不知道是添加省略还是赋值表达式。但可以解决。
    • @lexicore:如果可选的AssignmentExpression 不存在,则您有一个省略号。问题是什么? (换句话说,你不是根据逗号来决定的;你是根据AssignmentExpression的存在与否来决定的)
    • 啊,可能没什么。我想将赋值表达式和省略号都添加到我的数组中。因此,仅查看(...) * 中的',' 我无法决定是否必须在此处添加省略号,我必须知道最后一步是什么。但这实际上是微不足道的,你是对的。我被自己的想法误导了。
    • 我对省略的含义并不完全清楚。但是,我认为@rici 是对的。 "[" {builder.startArrayLiteral();} Item() ("," Item())* "]" {builder.endArrayLiteral();} 并通过 e=AssignmentExp() {builder.addExp(e);} | {builder.addElision();} 定义 Item
    • @rici 请查看我的更新和答案 - LOOKAHEADs,因为您发布的内容不起作用,很遗憾。但是你重写工作得很好!我只需要另一个变量来跟踪最后一个元素是否为AssignmentExpression
    【解决方案2】:

    这是另一种方法。它的优点是无需使用任何语义操作即可识别哪些逗号表示未定义的元素。

    void ArrayLiteral() : {} { "[" MoreArrayLiteral() }
    
    void MoreArrayLiteral() : {} {
        "]"
    |    "," /* undefined item */ MoreArrayLiteral()
    |    AssignmentExpression() ( "]" |  "," MoreArrayLiteral() )
    }
    

    【讨论】:

      【解决方案3】:

      我就是这样解决的(感谢@rici的回答):

      JSArrayLiteral ArrayLiteral() : 
      {
          boolean lastElementWasAssignmentExpression = false;
      }
      {
          "["
          (
              (
                  AssignmentExpression()
                  {
                      // Do something with expression
                      lastElementWasAssignmentExpression = true;
                  }
              ) ?
              (
                  ","
                  {
                      if (!lastElementWasAssignmentExpression)
                      {
                          // Do something with elision
                      }
                  }
                  (
                      AssignmentExpression()
                      {
                          // Do something with expression
                          lastElementWasAssignmentExpression = true;
                      }
                  ) ?
              ) *
          )
          "]"
          {
              // Do something with results
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多