ANTLR
Regole di Lexer in v4
Ricerca…
Regole semplici
Le regole di Lexer definiscono i tipi di token. Il loro nome deve iniziare con una lettera maiuscola per distinguerli dalle regole del parser.
INTEGER: [0-9]+;
IDENTIFIER: [a-zA-Z_] [a-zA-Z_0-9]*;
OPEN_PAREN: '(';
CLOSE_PAREN: ')';
Sintassi di base:
Sintassi | Senso |
---|---|
A | Abbina la regola del lexer o il frammento denominato A |
AB | Abbina A seguito da B |
(A|B) | Abbina A o B |
'text' | Abbina letteralmente "testo" |
A? | Corrisponde A zero o una volta |
A* | Abbina A zero o più volte |
A+ | Partita A una o più volte |
[A-Z0-9] | Abbina un carattere negli intervalli definiti (in questo esempio tra AZ o 0-9) |
'a'..'z' | Sintassi alternativa per un intervallo di caratteri |
~[AZ] | Negazione di un intervallo: corrisponde a qualsiasi singolo carattere non compreso nell'intervallo |
. | Abbina qualsiasi singolo personaggio |
frammenti
I frammenti sono parti riutilizzabili delle regole del lexer che non possono corrispondere da sole: devono essere referenziate da una regola del lexer.
INTEGER: DIGIT+
| '0' [Xx] HEX_DIGIT+
;
fragment DIGIT: [0-9];
fragment HEX_DIGIT: [0-9A-Fa-f];
Regole implicite del lexer
Quando i token come '{'
sono usati in una regola parser , per essi verrà creata una regola implicita per il lexer a meno che non esista una regola esplicita.
In altre parole, se hai una regola lexer:
OPEN_BRACE: '{';
Quindi entrambe le regole del parser sono equivalenti:
parserRule: '{';
parserRule: OPEN_BRACE;
Ma se la regola del lexer OPEN_BRACE
non è definita, verrà creata una regola anonima implicita. In tal caso, la regola implicita verrà definita come se fosse definita prima delle altre regole: avrà una priorità più alta rispetto ad altre regole.
Regole di priorità
Diverse regole di lexer possono corrispondere allo stesso testo di input. In tal caso, il tipo di token sarà scelto come segue:
- Innanzitutto, seleziona la regola lexer che corrisponde all'ingresso più lungo
- Se il testo corrisponde a un token implicitamente definito (come
'{'
), usa la regola implicita - Se diverse regole di lexer corrispondono alla stessa lunghezza di input, scegliere la prima , in base all'ordine di definizione
La seguente grammatica combinata:
grammar LexerPriorityRulesExample;
// Parser rules
randomParserRule: 'foo'; // Implicitly declared token type
// Lexer rules
BAR: 'bar';
IDENTIFIER: [A-Za-z]+;
BAZ: 'baz';
WS: [ \t\r\n]+ -> skip;
Dato il seguente input:
aaa foo bar baz barz
Produrrà la seguente sequenza di token dal lexer:
IDENTIFIER 'foo' BAR IDENTIFIER IDENTIFIER
aaa
è di tipoIDENTIFIER
Solo la regola
IDENTIFIER
può corrispondere a questo token, non vi è alcuna ambiguità.
foo
è di tipo'foo'
La regola parser
randomParserRule
introduce il tipo di token'foo'
implicito, che è prioritario rispetto alla regolaIDENTIFIER
.
bar
è di tipoBAR
Questo testo corrisponde alla regola
BAR
, che è definita prima della regolaIDENTIFIER
e quindi ha la precedenza.
baz
è di tipoIDENTIFIER
Questo testo corrisponde alla regola
BAZ
, ma corrisponde anche alla regolaIDENTIFIER
. Quest'ultimo viene scelto come definito prima dellaBAR
.Data la grammatica,
BAZ
non sarà mai in grado di eguagliare, poiché la regolaIDENTIFIER
copre già tutto ciò cheBAZ
può eguagliare.
barz
è di tipoIDENTIFIER
La regola
BAR
può corrispondere ai primi 3 caratteri di questa stringa (bar
), ma la regolaIDENTIFIER
corrisponderà a 4 caratteri. PoichéIDENTIFIER
corrisponde a una sottostringa più lunga, viene scelto suBAR
.
Come regola generale, le regole specifiche dovrebbero essere definite prima di più regole generiche. Se una regola può solo abbinare un input che è già coperto da una regola precedentemente definita, non verrà mai utilizzato.
Le regole definite in modo implicito come 'foo'
comportano come se fossero state definite prima di tutte le altre regole di lexer.
Comandi Lexer
Una regola lexer può avere comandi associati:
WHITESPACE: [ \r\n] -> skip;
I comandi sono definiti dopo un ->
alla fine della regola.
-
skip
: salta il testo corrispondente, non verrà emesso alcun token -
channel(n)
: emette il token su un altro canale -
type(n)
: cambia il tipo di token emesso -
mode(n)
,pushMode(n)
,popMode
,more
: controlla le modalità lexer
Azioni e predicati semantici
Un'azione lexer è un blocco di codice arbitrario nella lingua di destinazione circondata da {
... }
, che viene eseguita durante la corrispondenza:
IDENTIFIER: [A-Z]+ { log("matched rule"); };
Un predicato semantico è un blocco di codice arbitrario nella lingua di destinazione circondato da {
... }?
, che valuta un valore booleano. Se il valore restituito è falso, la regola del lexer viene saltata.
IDENTIFIER: [A-Z]+ { identifierIsValid() }?;
I predicati semantici dovrebbero essere definiti alla fine della regola quando possibile per motivi di prestazioni.