Prima di tutto, lexer di ANTLR si tokenize l'input da cima a fondo. Quindi i token definiti per primi hanno una precedenza più alta di quelli sotto di esso. E nel caso in cui la regola abbia token sovrapposti, la regola che corrisponde al maggior numero di caratteri avrà la precedenza (corrispondenza avida).
Lo stesso principio vale per le regole del parser. Anche le regole definite per prime verranno confrontate per prime. Per esempio, in regola foo
, sub-regola a
verrà prima provato prima b
:
foo
: a
| b
;
Si noti che nel tuo caso, la regola 2 ° non corrisponde, ma cerca di farlo, e fallisce perché non c'è un'interruzione di riga finale, producendo l'errore:
line 0:-1 mismatched input '<EOF>' expecting NEW_LINE
Quindi, niente è stato abbinato. Ma che è dispari. Perché tu hai impostato il backtrack=true
, dovrebbe almeno fare marcia indietro e partita:
first_rule
("primo token qui")
any_left_over_tokens
("linea-break")
any_left_over_tokens
("Secondo token qui")
se non corrisponde allo first_rule
in primo luogo e nemmeno provare a far corrispondere second_rule
per cominciare.
Una demo rapida durante l'esecuzione manuale dei predicati (e disattivazione di backtrack
nelle opzioni {...} sezione) sarà simile:
grammar T;
options {
output=AST;
//backtrack=true;
memoize=true;
}
rule_list_in_order
: ((first_rule)=> first_rule {System.out.println("first_rule=[" + $first_rule.text + "]");}
| (second_rule)=> second_rule {System.out.println("second_rule=[" + $second_rule.text + "]");}
| any_left_over_tokens {System.out.println("any_left_over_tokens=[" + $any_left_over_tokens.text + "]");}
)+
;
first_rule
: FIRST_TOKEN
;
second_rule
: FIRST_TOKEN NEW_LINE SECOND_TOKEN NEW_LINE
;
any_left_over_tokens
: NEW_LINE
| FIRST_TOKEN
| SECOND_TOKEN
;
FIRST_TOKEN : 'First token here';
SECOND_TOKEN : 'Second token here';
NEW_LINE : ('\r'?'\n');
WS : (' '|'\t'|'\u000C') {$channel=HIDDEN;};
che possono essere testati con la classe:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
String source = "First token here\nSecond token here";
ANTLRStringStream in = new ANTLRStringStream(source);
TLexer lexer = new TLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
TParser parser = new TParser(tokens);
parser.rule_list_in_order();
}
}
che produce l'uscita prevista:
first_rule=[First token here]
any_left_over_tokens=[
]
any_left_over_tokens=[Second token here]
Si noti che non importa se si utilizza:
rule_list_in_order
: ((first_rule)=> first_rule
| (second_rule)=> second_rule
| any_left_over_tokens
)+
;
.210
o
rule_list_in_order
: ((second_rule)=> second_rule // <--+--- swapped
| (first_rule)=> first_rule // <-/
| any_left_over_tokens
)+
;
, sia produrrà i risultati attesi.
Quindi, la mia ipotesi è che potresti aver trovato un bug.
Yout potrebbe provare la mailing list ANTLR, nel caso in cui si desideri una risposta definitiva (Terence Parr frequenta lì più spesso di quanto faccia qui).
Buona fortuna!
PS. Ho provato questo con ANTLR v3.2
Grazie Bart - perspicace come sempre. Solo per riferimento, l'input doveva essere 'Primo token qui \ nSecondo token qui \ n' - l'assenza del secondo \ n era una typeo. Proverò anche la mailing list. –
@Richard, ah, vedo (riguardo all'interruzione della linea). Sì, quindi la seconda regola * è * abbinata. Se la memoria mi serve bene, il 2 ° viene abbinato prima del 1 ° perché hai abilitato l'opzione backtrack facendo in modo che il parser corrisponda il più possibile: facendo corrispondere la prima sotto-regola, e quindi indietro il tracciamento alla seconda sotto-regola e attenersi a quello perché corrisponde di più (ma non ne sono sicuro al 100%: se stai postando alla mailing list, anche a te lo chiedo! :)). E tu sei il benvenuto, naturalmente! –