我正在尝试解析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;
Run Code Online (Sandbox Code Playgroud)
问题是expressionin rangeClause优先,并使这个:
Run Code Online (Sandbox Code Playgroud)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
Run Code Online (Sandbox Code Playgroud)
我尝试重新排序替代方案,以便expression分支具有较低的优先级,如下所示:
rangeClause :
selectStartValue whiteSpace TO whiteSpace selectEndValue
| (IS whiteSpace?)? comparisonOperator whiteSpace? expression
| expression
;
Run Code Online (Sandbox Code Playgroud)
但是这打破了整个语法的各种方式(在我的项目中打破了~1000次测试),所以我尝试将其rangeClause更改为此(删除了可选的令牌,因为Is没有=实际上是非法的VBA代码):
rangeClause :
expression (whiteSpace TO whiteSpace expression)? #caseFromTo
| (IS whiteSpace comparisonOperator whiteSpace)? expression #caseIs
;
Run Code Online (Sandbox Code Playgroud)
然后在代码中使用CaseFromToContext和CaseIsContext编写类(必须,以保持编译),但它又在我的项目中破坏了~1000次测试.
然后我想,"嘿,这可能是模棱两可的!" 把它变成了这个:
rangeClause :
expression whiteSpace TO whiteSpace expression #caseFromTo
| IS whiteSpace comparisonOperator whiteSpace expression #caseIs
| expression #caseExpr
;
Run Code Online (Sandbox Code Playgroud)
......但没有运气,同样的结果.
如何rangeClause理解这种烦人的Case Is = foobar语法?我正在使用ANTLR 4.3,但我们计划很快升级到ANTLR 4.6.
如果需要其他上下文,则完整的VBAParser.g4语法在github上.
事实证明,重新排序确实有效,但为了避免解析中的歧义,必须IS whiteSpace comparisonOperator首先:
rangeClause :
(IS whiteSpace?)? comparisonOperator whiteSpace? expression
| selectStartValue whiteSpace TO whiteSpace selectEndValue
| expression
Run Code Online (Sandbox Code Playgroud)
问题在于 with expression(并且扩展为selectStartValueand selectEndValue) 它将递归匹配,Is =因为comparisonOperator comparisonOperator是表达式匹配。可能可以做一些工作来防止 comparisonOperator comparisonOperator匹配expression(据我所知,它在 VBA 中永远无效),但上面的方法可以作为一种快速而肮脏的修复。
基本上,上述语法所做的就是确保“无效”在可以comparisonOperator comparisonOperator匹配为rangeClause 之前先匹配为 a expression。