我有一个ANTLR4监听器,它处理标准和格式良好的语法,但是我正在努力解决如何处理非标准实现。尽管所有变体都没有问题地通过词法分析器,但是解析阶段更加棘手。
执行此操作的传统方式类似于
// Header of document
variant = STANDARD;
if (header.indexOf("microsoft") != -1) {
variant = MICROSOFT;
} else if (header.indexOf("google") != -1) {
variant = GOOGLE;
}
...
// Parsing a particular element
if (variant.equals(MICROSOFT)) {
// Microsoft-specific stuff
} else if (variant.equals(GOOGLE)) {
// Google-specific stuff
} else {
// Standard stuff
}
但这很快变得无法维持。显而易见的解决方案是为标准实现提供ParseTreeListener
,然后为每个变体创建子类,但在我开始解析之前,我不知道它是哪个变体。
那么我怎样才能通过解析从一个侦听器切换到另一个侦听器,或者一旦我知道我正在处理哪个变体,就用新的侦听器重启解析?
答案 0 :(得分:2)
如果经常出现这些变体,您可能需要考虑使用谓词(以下伪语法中的{...}?
构造)嵌入自定义代码来处理上下文敏感的解析:
rule
: { boolean-expression-a }? a-alternative
| { boolean-expression-b }? b-alternative
| /* fall through */ not-a-or-b-alternative
;
假设您要解析包含chunk
的文件。 chunk
由header
和data
行组成。在header
中,您可以设置您的变体。普通变体的data
包含3个NUMBER
,Google的变体包含2个NUMBER
,Microsoft的变体包含单个NUMBER
。此类文件的示例如下所示:
header: none
data: 1 2 3
header: google
data: 4 5
header: microsoft
data: 6
这是一个能够解析它的上下文敏感的ANTLR v4语法的演示:
grammar T;
@parser::members {
enum Variant {
GOOGLE,
MICROSOFT,
OTHER;
public static Variant tryValueOf(String name) {
try {
return Variant.valueOf(name.toUpperCase());
}
catch(Exception e) {
return OTHER;
}
}
}
private Variant variant = Variant.OTHER;
}
parse
: chunk+ EOF
;
chunk
: header data
;
header
: K_HEADER COLON NAME {variant = Variant.tryValueOf($NAME.text);}
;
data
: {variant == Variant.MICROSOFT}? K_DATA COLON NUMBER #MicrosoftData
| {variant == Variant.GOOGLE}? K_DATA COLON NUMBER NUMBER #GoogleData
| K_DATA COLON NUMBER NUMBER NUMBER #OtherData
;
K_DATA : 'data';
K_HEADER : 'header';
NAME : [a-zA-Z]+;
NUMBER : [0-9]+;
COLON : ':';
SPACE : [ \t\r\n] -> skip;
导致以下解析: