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 tipoIDENTIFIERSolo la regola
IDENTIFIERpuò corrispondere a questo token, non vi è alcuna ambiguità.
fooè di tipo'foo'La regola parser
randomParserRuleintroduce il tipo di token'foo'implicito, che è prioritario rispetto alla regolaIDENTIFIER.
barè di tipoBARQuesto testo corrisponde alla regola
BAR, che è definita prima della regolaIDENTIFIERe quindi ha la precedenza.
bazè di tipoIDENTIFIERQuesto testo corrisponde alla regola
BAZ, ma corrisponde anche alla regolaIDENTIFIER. Quest'ultimo viene scelto come definito prima dellaBAR.Data la grammatica,
BAZnon sarà mai in grado di eguagliare, poiché la regolaIDENTIFIERcopre già tutto ciò cheBAZpuò eguagliare.
barzè di tipoIDENTIFIERLa regola
BARpuò corrispondere ai primi 3 caratteri di questa stringa (bar), ma la regolaIDENTIFIERcorrisponderà a 4 caratteri. PoichéIDENTIFIERcorrisponde 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.