我试图将语法从v3转换为v4,但遇到了一些问题。
在v3中,我有这样的规则:
dataspec[DataLayout layout] returns [DataExtractor extractor]
@init {
DataExtractorBuilder builder = new DataExtractorBuilder(layout);
}
@after {
extractor = builder.create();
}
: first=expr { builder.addAll(first); } (COMMA next=expr { builder.addAll(next); })*
;
expr returns [List<ValueExtractor> ext]
...
然而,由于v4中的规则返回了这些自定义上下文对象,而不是我明确告诉它们返回的对象,所以一切都一团糟。v4的方法是什么?
这里有多种情况:
- 访问传入变量(
layout
) - 访问当前规则的返回值(
extractor
) - 访问局部变量(
first
、next
)
传入变量和当前规则的返回值
访问传入变量或当前规则的返回值时,只需在规则定义中给定的名称前面加上$
即可。
layout
变为$layout
extractor
变为$extractor
局部变量
显然,需要做的是引用变量的成员,该成员是根据返回值的规则的returns
子句命名的。
例如,first
正在捕获来自expr
规则的结果,expr
将其返回值命名为ext
,这意味着:
first
变为$first.ext
next
变为$next.ext
何时使用$
表格
与v3中可以将某些变量引用为常规java字段不同,在所有情况下,包括在操作中、在@init
和@after
块中,以及将变量传递给其他规则时,都需要使用$
形式。
其他陷阱
如果您在本地变量中捕获可选标记,那么现在您正在引用该变量的属性,可能会遇到空指针异常。
single_lname returns [String s]
: p=LNAME_PREFIX? r=NAME { $p.text + toNameCase($r.text); }
;
您需要检查$p
是否为null,但大多数情况下这会导致"缺少属性访问"错误。ANTLR4会产生一个特殊的异常,以便您可以对此进行检查,仅在if
条件中使用时适用(例如,将其重构为使用三元运算符仍然会导致错误)。
single_lname returns [String s]
: p=LNAME_PREFIX? r=NAME {
if ($p == null) {
$s = toNameCase($r.text);
} else {
$s = $p.text + toNameCase($r.text);
}
}
;
更新后的规则
把所有这些放在一起,dataspec
规则变成:
dataspec[DataLayout layout] returns [DataExtractor extractor]
@init {
DataExtractorBuilder builder = new DataExtractorBuilder($layout);
}
@after {
$extractor = builder.create();
}
: first=expr { builder.addAll($first.ext); }
(COMMA next=expr { builder.addAll($next.ext); })*
;