Я бы просто сопоставил любой a
, b
или c
один или несколько раз:
rule
: ( a | b | c )+
;
, а затем, после анализа, обходит дерево разбора и проверяет, совпадают ли a
, b
и c
все ровно один раз.
Но да, это возможно в самой грамматике, используя предикаты, где это необходимо.
Демонстрация:
grammar Permutation;
parse
: permutation[5] {System.out.println("parsed: " + $permutation.text);} EOF
;
permutation[final int n]
@init{
java.util.Set set = new java.util.HashSet();
int counter = n;
}
: (
{counter > 0}?=> token // keep matching a `token` as long as `counter > 0`
{ //
set.add($token.text); // add the contents of `token` to `set`
counter--; // decrease `counter`
} //
)+
{set.size() == n}? // if `set.size() != n`, an exception is thrown
;
token
: A
| B
| C
| D
| E
;
A : 'A';
B : 'B';
C : 'C';
D : 'D';
E : 'E';
Space : ' ' {skip();};
Приведенная выше демонстрационная грамматика использует 2 различных типа предикатов: 1) a стробированный семантический предикат i , чтобы убедиться, что правило permutation
не соответствует no больше, чем параметр final int n
токенов и 2) a проверки семантического предиката i , чтобы гарантировать, что set
содержит ровно final int n
элементов для убедитесь, что это правильная перестановка из 5 токенов.
Более подробную информацию о семантических предикатах можно найти здесь: Что такое «семантический предикат» в ANTLR?
Вы можете проверить грамматику с помощью следующего класса:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
PermutationLexer lexer = new PermutationLexer(new ANTLRStringStream(args[0]));
PermutationParser parser = new PermutationParser(new CommonTokenStream(lexer));
parser.parse();
}
}
java -cp antlr-3.3.jar org.antlr.Tool Permutation.g
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main "A B C D E"
parsed: ABCDE
java -cp .:antlr-3.3.jar Main "B D C E A"
parsed: BDCEA
java -cp .:antlr-3.3.jar Main "A B C D B"
line 1:9 rule permutation failed predicate: {set.size() == n}?
parsed: null