如何在ANTLR中制定规则以任何顺序匹配其所有替代品?
即
rule: ('example\r\n' | 'example2\r\n') nextRule
我想'示例'和' example2'在进入下一个规则之前只匹配一次。
应匹配以下输入:
example
example2
或
example2
example
但不是输入:
example
example
example2
答案 0 :(得分:15)
如何在ANTLR中制定规则以任何顺序匹配其所有替代品?
“所有替代品只有一次”只是rule: altA altB altC ...
。这很容易。要求rule
接受任何安排中的所有替代altA
,altB
,altC
等,这意味着适应每个安排。这很容易手动处理小数字(rule: (altA altB | altB altA);
)。但是我没有自动快捷方式来自动处理所有排列。
以下是一些方法,如果没有内置方式,并假设您无法放松您的要求。警告:我不知道你问题的全部范围;我不懂你的语法;我不知道你为什么想要你所要求的;我不知道你喜欢什么类型的解决方案,除了你可能比任何这些选项更容易。
首先,您可以咬住子弹并自己生成所有匹配的排列,可以手动或通过运行排列生成器。那么ANTLR会以它理解的方式拥有你想要的东西。它原始但有效:它是直接的ANTLR语法,因此没有涉及外部代码,因为下面的选项。
例如,假设您有一个规则field
来处理类似"public static final x"
的输入,所有三个修饰符都是预期的,但没有特定的顺序。排列看起来像这样:
field : modifiers ID EOF;
modifiers
: PUBLIC STATIC FINAL //ABC
| PUBLIC FINAL STATIC //ACB
| STATIC PUBLIC FINAL //BAC
| STATIC FINAL PUBLIC //BCA
| FINAL PUBLIC STATIC //CAB
| FINAL STATIC PUBLIC //CBA
;
这就是结束。没有外部代码,没有谓词,没有任何内容。
其次,您可以在语法中使用语义谓词,以确保提供和匹配所有匹配而无需重复。编写谓词本身有多种方法,但它归结为跟踪已经进行的匹配(以防止重复),然后测试规则是否匹配了它所期望的所有部分。这是一个基本的例子,遵循与前一个相同的要求:
field
locals [java.util.HashSet<String> names = new java.util.HashSet<String>();]
: modifiers ID EOF;
modifiers
//Ensure that the full number of modifiers have been provided
: {$field::names.size() < 3}? modifier modifiers
| {$field::names.size() == 3}? //match nothing once we have (any) three modifiers
;
modifier
//Ensure that no duplicates have been provided
: {!$field::names.contains("public")}? PUBLIC {$field::names.add("public");}
| {!$field::names.contains("static")}? STATIC {$field::names.add("static");}
| {!$field::names.contains("final")}? FINAL {$field::names.add("final");}
;
此处规则field
跟踪局部变量names
中的修饰符名称。规则modifiers
会调用规则modifier
,直到names
包含三个值。规则modifier
匹配names
中没有相应密钥的任何名称。请注意,这些值会手动添加到names
。它们可以是任意值,只要modifier
的替代值为它匹配的令牌的两边添加相同的值。
我的实现有点粗糙,因为修饰符最终嵌套在生成的解析树中(因为modifiers
包含一个modifier
和一个modifiers
,其中包含一个modifier
和一个modifiers
那个...),但我希望你明白这个想法。
第三,你可以单独留下可怜的解析器并测试调用代码的完整性。这可以在使用解析器侦听器进行解析期间完成,也可以在使用解析器生成的ParserRuleContext
对象进行解析后完成。这将问题分成两部分:让解析器解决“任何顺序的任何X,Y,Z”,并让调用代码解决“所有,只有X,Y,Z。”
以下是使用侦听器方法的示例:
//partial grammar
field : modifier* ID EOF; //accept any modifiers in any order
modifier
: PUBLIC
| STATIC
| FINAL
;
//snippet of calling code
//initialize lexer and parser
parser.addParseListener(new MyGrammarBaseListener() {
@Override
public void exitField(FieldContext ctx) {
// The field rule has finished. Let's verify that no modifiers
// were duplicated.
//TODO check for duplicates, ensure all modifiers are specified.
//TODO log errors accordingly.
}
});
//Call the parser.
parser.field();
语法保持清洁。修饰符可以在ID
之前任意数量和任意顺序任意出现在输入中。调用代码通过它选择的任何方式执行测试,记录它想要的任何错误。
这是一个将我提到的三个选项结合在一起的示例,以便更清楚地了解我在说什么。
grammar Modifiers;
//Hard-coded version : all the permutations are specified //
permutationField : permutationModifiers ID EOF;
permutationModifiers
: PUBLIC STATIC FINAL //ABC
| PUBLIC FINAL STATIC //ACB
| STATIC PUBLIC FINAL //BAC
| STATIC FINAL PUBLIC //BCA
| FINAL PUBLIC STATIC //CAB
| FINAL STATIC PUBLIC //CBA
;
// Predicate version : use semantic predicates to prevent duplicates and ensure all the modifiers are provided //
predicateField
locals [java.util.HashSet<String> names = new java.util.HashSet<String>();]
: predicateModifiers ID EOF;
predicateModifiers
//Ensure that the full number of modifiers have been provided
: {$predicateField::names.size() < 3}? predicateModifier predicateModifiers
| {$predicateField::names.size() == 3}? //match nothing once we have (any) three modifiers
;
predicateModifier
//Ensure that no duplicates have been provided
: {!$predicateField::names.contains("public")}? PUBLIC {$predicateField::names.add("public");}
| {!$predicateField::names.contains("static")}? STATIC {$predicateField::names.add("static");}
| {!$predicateField::names.contains("final")}? FINAL {$predicateField::names.add("final");}
;
//Listener version : test everything when the parser calls the listener //
listenerField : listenerModifier* ID EOF;
listenerModifier
: PUBLIC
| STATIC
| FINAL
;
PUBLIC : 'public';
STATIC : 'static';
FINAL : 'final';
FOO : 'foo';
ID : [a-zA-Z]+;
WS : [ \r\n\t]+ -> skip;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.misc.Nullable;
public class ModifiersTest {
public static void main(String[] args) {
run("public static final x", "ok");
run("final static public x", "ok");
run("static public static final x", "too many modifiers");
run("static x", "missing modifiers");
run("final final x", "missing & duplicated modifiers");
}
private static void run(String code, String title) {
System.out.printf("%n---%n**Input : %s**%n%n\t%s%n%n", title, code);
System.out.println("**Permutation Output**\n");
runPermutationTest(code);
System.out.println();
System.out.println("**Predicate Output**\n");
runPredicateTest(code);
System.out.println();
System.out.println("**Listener Output**\n");
runListenerTest(code);
System.out.println();
}
private static void runPermutationTest(String code) {
ModifiersParser parser = createParser(code);
parser.permutationField();
System.out.println("\t(done)");
}
private static void runPredicateTest(String code) {
ModifiersParser parser = createParser(code);
parser.predicateField();
System.out.println("\t(done)");
}
private static void runListenerTest(String code) {
ModifiersParser parser = createParser(code);
parser.addParseListener(new ModifiersBaseListener() {
@Override
public void exitListenerField(ModifiersParser.ListenerFieldContext ctx) {
// The field rule has finished. Let's verify that no modifiers
// were duplicated.
HashSet<String> uniqueNames = new HashSet<String>();
ArrayList<String> allNames = new ArrayList<String>();
HashSet<String> expectedNames = new HashSet<String>();
expectedNames.add("public");
expectedNames.add("static");
expectedNames.add("final");
if (ctx.listenerModifier() != null && !ctx.listenerModifier().isEmpty()) {
List<ModifiersParser.ListenerModifierContext> modifiers = ctx.listenerModifier();
// Collect all the modifier names in a set.
for (ModifiersParser.ListenerModifierContext modifier : modifiers) {
uniqueNames.add(modifier.getText());
allNames.add(modifier.getText());
}
}
// Is the number of unique modifiers less than the number of
// all given modifiers? If so, then there must be duplicates.
if (uniqueNames.size() < allNames.size()) {
ArrayList<String> names = new ArrayList<String>(allNames);
for (String name : uniqueNames){
names.remove(name);
}
System.out.println("\tDetected duplicate modifiers : " + names);
} else {
System.out.println("\t(No duplicate modifiers detected)");
}
//Are we missing any expected modifiers?
if (!uniqueNames.containsAll(expectedNames)) {
ArrayList<String> names = new ArrayList<String>(expectedNames);
names.removeAll(uniqueNames);
System.out.println("\tDetected missing modifiers : " + names);
} else {
System.out.println("\t(No missing modifiers detected)");
}
}
});
parser.listenerField();
System.out.println("\t(done)");
}
private static ModifiersParser createParser(String code) {
ANTLRInputStream input = new ANTLRInputStream(code);
ModifiersLexer lexer = new ModifiersLexer(input);
ModifiersParser parser = new ModifiersParser(new CommonTokenStream(lexer));
BaseErrorListener errorListener = createErrorListener();
lexer.addErrorListener(errorListener);
parser.addErrorListener(errorListener);
return parser;
}
private static BaseErrorListener createErrorListener() {
BaseErrorListener errorListener = new BaseErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, @Nullable Object offendingSymbol, int line,
int charPositionInLine, String msg, @Nullable RecognitionException e) {
//Print the syntax error
System.out.printf("\t%s at (%d, %d)%n", msg, line, charPositionInLine);
}
};
return errorListener;
}
}
输入:确定
public static final x
排列输出
(done)
谓词输出
(done)
听众输出
(No duplicate modifiers detected)
(No missing modifiers detected)
(done)
输入:确定
final static public x
排列输出
(done)
谓词输出
(done)
听众输出
(No duplicate modifiers detected)
(No missing modifiers detected)
(done)
输入:修改器太多
static public static final x
排列输出
extraneous input 'static' expecting 'final' at (1, 14)
(done)
谓词输出
no viable alternative at input 'static' at (1, 14)
(done)
听众输出
Detected duplicate modifiers : [static]
(No missing modifiers detected)
(done)
输入:缺少修饰符
static x
排列输出
no viable alternative at input 'staticx' at (1, 7)
(done)
谓词输出
no viable alternative at input 'x' at (1, 7)
(done)
听众输出
(No duplicate modifiers detected)
Detected missing modifiers : [final, public]
(done)
输入:缺失&amp;重复的修饰符
final final x
排列输出
no viable alternative at input 'finalfinal' at (1, 6)
(done)
谓词输出
no viable alternative at input 'final' at (1, 6)
(done)
听众输出
Detected duplicate modifiers : [final]
Detected missing modifiers : [static, public]
(done)
答案 1 :(得分:12)
使用ANTLR 4,我实际上更喜欢使用类似下面的内容,其中输入应该包含a
,b
和c
中的每一个。
items : (a | b | c)*;
然后在监听器中,我将使用如下代码:
@Override
public void enterItems(ItemsContext ctx) {
if (ctx.a().size() != 1) {
// report error
} else if (ctx.b().size() != 1) {
// report error
} else ...
}