如何正确解析 VB Case 语句?

How to correctly parse a VB Case statement?

我正在尝试解析 VBA 代码,规范的 5.4.2.10 部分定义了 Select Case 语句,我们定义如下:

// 5.4.2.10 Select Case Statement
selectCaseStmt :
    SELECT whiteSpace? CASE whiteSpace? selectExpression endOfStatement
    caseClause*
    caseElseClause?
    END_SELECT
;
selectExpression : expression;
caseClause :
    CASE whiteSpace rangeClause (whiteSpace? COMMA whiteSpace? rangeClause)* endOfStatement block
;
caseElseClause : CASE whiteSpace? ELSE endOfStatement block;
rangeClause :
    expression
    | selectStartValue whiteSpace TO whiteSpace selectEndValue   
    | (IS whiteSpace?)? comparisonOperator whiteSpace? expression
;
selectStartValue : expression;
selectEndValue : expression;

问题是 rangeClause 中的 expression 优先,因此:

Select Case foo
    Case Is = 42
        Exit Sub
End Select

...最终被拾取并视为 {undeclared-variable} {EQ} {literal},这是一个问题,因为 Is 应该是词法分析器标记,而不是比较表达式的 LHS:

expression whiteSpace? (EQ | NEQ | LT | GT | LEQ | GEQ | LIKE | IS) whiteSpace? expression    # relationalOp

我尝试重新排序备选方案,以便 expression 分支具有较低的优先级,如下所示:

rangeClause :
    selectStartValue whiteSpace TO whiteSpace selectEndValue   
    | (IS whiteSpace?)? comparisonOperator whiteSpace? expression
    | expression
;

但这以各种方式破坏了整个语法(在我的项目中破坏了约 1000 个测试),所以我尝试将 rangeClause 更改为此(删除了可选标记,因为 Is没有 = 实际上是非法的 VBA 代码):

rangeClause :
      expression (whiteSpace TO whiteSpace expression)?                 #caseFromTo
    | (IS whiteSpace comparisonOperator whiteSpace)? expression         #caseIs
;

然后在代码中使用 CaseFromToContextCaseIsContext 类(必须继续编译),但它再次破坏了我项目中的 ~1000 次测试。

然后我想,"hey that's potentially ambiguous!"然后把它变成这样:

rangeClause :
      expression whiteSpace TO whiteSpace expression                    #caseFromTo
    | IS whiteSpace comparisonOperator whiteSpace expression            #caseIs
    | expression                                                        #caseExpr
;

...但运气不好,结果相同。

如何让 rangeClause 理解这种烦人的 Case Is = foobar 语法?我正在使用 ANTLR 4.3,但我们计划很快升级到 ANTLR 4.6。

如果需要额外的上下文,the complete VBAParser.g4 grammar is on github

事实证明 re-ordering 确实有效,但为了避免解析中的歧义,IS whiteSpace comparisonOperator 必须排在第一位:

rangeClause :
    (IS whiteSpace?)? comparisonOperator whiteSpace? expression
    | selectStartValue whiteSpace TO whiteSpace selectEndValue 
    | expression

问题在于 expression(以及扩展 selectStartValueselectEndValue),它将递归匹配 Is =,因为 comparisonOperator comparisonOperator 是表达式匹配。可能有一些工作可以完成 prevent comparisonOperator comparisonOperator 匹配 expression (它在 VBA AFAIK 中永远无效),但上面的工作是快速而肮脏的修复。

基本上上面所有语法所做的就是确保 "invalid" comparisonOperator comparisonOperator 匹配为 rangeClause before 它可以匹配为expression.