使用ANTLR,是否有一种方法可以编写解析器规则,使其可以在不编写Java的情况下以任何顺序表达:x和/或y和/或z。例如,它应该匹配:";x y"yz";,以及";"x y z";但不是";x x y";。我能想到的最好的是下面的规则,但我需要在树上查看";x x y";。
rule: ( x | y | z )* ;
虽然可以做类似于的事情
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;
或者(不那么荒谬(:
rule: x? y? z?
| x? z? y?
| y? x? z?
| y? z? x?
| z? x? y?
| z? y? x?
;
我怀疑您的示例比实际应用程序简单得多,而且这种方法会变得过于乏味(它已经变得荒谬了(。
你也可以使用语义谓词来研究一些东西,但这会将你的语法锁定在特定的目标语言上。(这也会使你的语法复杂化。(
一般来说,我发现ANTLR用户(通常是解析器编写者(经常过于努力地对";所有规则";融入语法。
这似乎很好,但它可能会导致语法的复杂性,并导致";小于最佳";错误消息(因为它们来自解析器(ANTLR(本身(。
我想你会发现,最好保留一个像你现有的规则,它将创建一个ParseTree,准确地表示解释(也称为"解析"(输入的正确方式。然后,您将这样的规则视为语义关注(而不是语法关注(解析器的域(。
这意味着您将编写一个类似于验证侦听器的东西,它将在您的解析树上运行,并且您可以多次检查是否使用了相同的子规则。如果你遇到它,你可以制作一个非常具体的错误消息,对最终用户更有用。
我能想到的最好的是…
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 : [ trn]+ -> 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();
}
}