我正在尝试解析 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
;
然后在代码中使用CaseFromToContext
和CaseIsContext
类(必须这样做,以保持编译),但它再次破坏了我项目中的~1000个测试。
然后我想,"嘿,这可能模棱两可!"并把它变成这样:
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。
如果需要额外的上下文,完整的VBAParser.g4语法在github上。
事实证明,重新排序确实有效,但为了防止解析中的歧义,IS whiteSpace comparisonOperator
必须排在第一位:
rangeClause :
(IS whiteSpace?)? comparisonOperator whiteSpace? expression
| selectStartValue whiteSpace TO whiteSpace selectEndValue
| expression
问题在于expression
(以及扩展selectStartValue
和selectEndValue
),它将递归匹配Is =
comparisonOperator comparisonOperator
因为是表达式匹配。 可能有一些工作可以做来防止comparisonOperator comparisonOperator
匹配expression
(它在 VBA AFAIK 中永远不会有效),但上述方法可以作为快速而肮脏的修复。
基本上上述语法所做的只是确保"无效"comparisonOperator comparisonOperator
作为rangeClause
匹配,然后才能匹配为expression
。