我怎样才能让一个规则,任何顺序匹配所有的替代品只有一次,在ANTLR?
即
rule: ('example\r\n' | 'example2\r\n') nextRule
我想“示例”和“示例2”,以移动到下一个规则之前只有一次匹配。
应该匹配的输入:
example
example2
要么
example2
example
但不投入:
example
example
example2
我怎样才能让一个规则,任何顺序匹配所有的替代品只有一次,在ANTLR?
即
rule: ('example\r\n' | 'example2\r\n') nextRule
我想“示例”和“示例2”,以移动到下一个规则之前只有一次匹配。
应该匹配的输入:
example
example2
要么
example2
example
但不投入:
example
example
example2
我怎样才能让一个规则,任何顺序匹配所有的替代品只有一次,在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
包含3个值。 规则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
,在任何数量和任何顺序。 调用代码执行而不择手段的测试中,它选择,记录,它希望任何错误。
下面是拉在一起我提到的三个选项,给什么我谈论的一个清晰的概念的uberexample。
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;
}
}
输入:OK
public static final x
排列输出
(done)
谓词输出
(done)
监听输出
(No duplicate modifiers detected)
(No missing modifiers detected)
(done)
输入:OK
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)
输入:缺少与重复修饰
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)
随着ANTLR 4,我真的喜欢使用类似于以下,其中输入预期正好包含各1个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 ...
}