要匹配的ANTLR解析器规则:x和/或y和/或z的任意顺序

要匹配的ANTLR解析器规则:x和/或y和/或z的任意顺序,antlr,antlr4,Antlr,Antlr4,使用ANTLR,有没有一种方法可以编写解析器规则,这样它就可以在不编写Java的情况下以任意顺序表示:x和/或y和/或z。例如,它应该匹配:“xy”、“yz”和“xyz”,而不是“xxy”。我能想到的最好的办法是下面的规则,但是我需要在树行者中检查“x x y” 虽然你可以做一些类似于: rule: x | y | z | x y | y x | x z | z x | y z | z y | x y z | x z

使用ANTLR,有没有一种方法可以编写解析器规则,这样它就可以在不编写Java的情况下以任意顺序表示:x和/或y和/或z。例如,它应该匹配:“xy”、“yz”和“xyz”,而不是“xxy”。我能想到的最好的办法是下面的规则,但是我需要在树行者中检查“x x y”

虽然你可以做一些类似于:

rule: x
    | y
    | z
    | x y
    | y x
    | x z
    | z x
    | y z
    | z y
    | x y z
    | x z y
    | y x z
    | y z x
    | z x y
    | z y x;
或者(稍微不那么荒谬):

我怀疑您的示例比实际应用程序简单得多,而且这种方法会变得太单调乏味(它已经变得荒谬了)

您也可以使用语义谓词研究某些内容,但这会将语法锁定到特定的目标语言。(这也会使你的语法变得复杂。)

总的来说,我发现ANTLR用户(一般来说是解析器编写者)经常试图将“所有规则”编码到语法中

这似乎很好,但它会导致语法的复杂性,并导致“不太理想”的错误消息(因为它们来自解析器(ANTLR)本身)

我认为您会发现,最好保留像您这样的规则,它将创建一个ParseTree,准确地表示正确的解释(也称为“parse”)输入的方法。然后,你认为这样的规则是一个语义关注点(而不是语法关注点(语法分析器的域)。


这意味着您将编写类似于验证侦听器的东西,该侦听器将针对您的解析树运行,并且您可以多次检查同一子规则的使用情况。如果遇到它,您可以编写一条对最终用户更有用的非常具体的错误消息。

我能想到的最好方法是

grammar Sandbox;

@members {
    boolean a, b, c;
}

start: ( 'test' test )+ EOF ;

test:
    {a=b=c=true;}   // Reset
    (   {a}? a {a=false;}
    |   {b}? b {b=false;}
    |   {c}? c {c=false;}
    )* ;

a: 'a';
b: 'b';
c: 'c';

WS : [ \t\r\n]+ -> skip ;
还有试驾

package sandbox;

import org.antlr.v4.runtime.*;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    private Main() {
        System.out.println("Should be OK...");
        test("test a b c test c test c b a test c");
        System.out.println("Should fail...");
        test("test c a a");
    }

    private void test(String toTest) {
        final CharStream cs = CharStreams.fromString(toTest);
        final SandboxLexer lexer = new SandboxLexer(cs);
        final CommonTokenStream tokens = new CommonTokenStream(lexer);
        final SandboxParser parser = new SandboxParser(tokens);
        parser.start();
    }
}

Thx Mike。遗憾的是,你是对的,有三个例子我想分别对3、4和7个变量使用“技巧”,所以组合方法不太管用!我本来计划在解析器之外处理这个问题,但结果也很困难-我有5个侦听器(!)目前,我希望尽可能多地返回到解析器中。也许现在有人会有一个想法让我不知所措,但我建议有多个侦听器并没有那么糟糕,更像是“ANTLR方式”。我总是发现我需要一个验证侦听器来进行语义错误检查。如果现有侦听器中有一个正在进行验证,我会将其添加到其中。根据您的用例,我不知道5个侦听器是“坏事”,特别是如果它有助于代码的清晰性,从而实现关注点的分离。(TBC)我发现,有时我会故意将某些规则排除在语法之外,只要语法可以给我一个正确解释输入的ParseTree。然后,我就可以提供比仅查看语法规则就可以预期ANTLR生成的更好的错误消息。(另一个技巧是为已知的问题结构实际设置规则,以便您能够识别它们并给出更有意义的错误消息。)我已经在下面添加了一个我能想到的最佳答案,它看起来比我想象的要好。我可以在不同的规则中重用布尔值,我不打算在Java中使用它……这就是我提到的语义谓词解决方案。(并且没有费心写:)).而且,是的,它不像我想象的那么丑陋
grammar Sandbox;

@members {
    boolean a, b, c;
}

start: ( 'test' test )+ EOF ;

test:
    {a=b=c=true;}   // Reset
    (   {a}? a {a=false;}
    |   {b}? b {b=false;}
    |   {c}? c {c=false;}
    )* ;

a: 'a';
b: 'b';
c: 'c';

WS : [ \t\r\n]+ -> skip ;
package sandbox;

import org.antlr.v4.runtime.*;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    private Main() {
        System.out.println("Should be OK...");
        test("test a b c test c test c b a test c");
        System.out.println("Should fail...");
        test("test c a a");
    }

    private void test(String toTest) {
        final CharStream cs = CharStreams.fromString(toTest);
        final SandboxLexer lexer = new SandboxLexer(cs);
        final CommonTokenStream tokens = new CommonTokenStream(lexer);
        final SandboxParser parser = new SandboxParser(tokens);
        parser.start();
    }
}