ANTLR4:匹配所有输入替代exaclty一次(ANTLR4: Matching all inpu

2019-07-20 02:02发布

我怎样才能让一个规则,任何顺序匹配所有的替代品只有一次,在ANTLR?

rule: ('example\r\n' | 'example2\r\n') nextRule

我想“示例”和“示例2”,以移动到下一个规则之前只有一次匹配。

应该匹配的输入:

example
example2

要么

example2
example

但不投入:

example
example
example2

Answer 1:

我怎样才能让一个规则,任何顺序匹配所有的替代品只有一次,在ANTLR?

“所有的替代品只有一次”很简单rule: altA altB altC ... 。 这是比较容易的部分。 问rule来接受所有的替代altAaltBaltC 任何安排等手段容纳每一个安排。 这是很容易手工处理为小数字( 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。

Modifiers.g

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; 

ModifiersTest.java

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)


Answer 2:

随着ANTLR 4,我真的喜欢使用类似于以下,其中输入预期正好包含各1个ab ,和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 ...
}


文章来源: ANTLR4: Matching all input alternatives exaclty once
标签: antlr4