Lex: identificatore vs intero

Sto cercando di creare il mio linguaggio di programmazione semplice. Per questo ho bisogno di inserire alcune regex in Lex. Sto usando il seguente regex per abbinare identificatori e interi.

[a-zA-Z][a-zA-Z0-9]* /* identifier */ return IDENTIFIER; ("+"|"-")?[0-9]+ /* integer */ return INTEGER; 

Ora quando controllo ad esempio un identificatore illegale come:

 0a = 1; 

Lo zero iniziale è riconosciuto come un intero seguito dal ‘a’ riconosciuto come identificatore. Invece di questo voglio che questo token ‘0a’ sia riconosciuto come un personaggio illegale. Come posso includere questa funzionalità? Quale regex devo regolare?

Il modo più semplice per farlo in (F) lex è creare un secondo pattern per l’errore:

 [[:alpha:]][[:alnum:]]* return IDENTIFIER; [+-]?[[:digit:]]+ return INTEGER; [+-]?[[:digit:]]+[[:alpha:]] { fprintf(stderr, "Incorrect integer '%s' in line %d\n", yytext, yylineno); return ERROR; } 

La terza regola corrisponderà a qualsiasi intero con una lettera immediatamente successiva e segnalerà un errore lessicale. ( %option yylineno tu abbia abilitato l’ %option yylineno . In caso contrario, ciò segnalerà sempre l’errore sulla riga 0.)

Un’alternativa potrebbe essere quella di continuare la scansione lessicale. In questo caso, potresti voler ripetere la scansione del carattere alfabetico offensivo. Il modo più semplice per farlo è in Flex è usare l’operatore di contesto finale (idiosincratico) / :

 [[:alpha:]][[:alnum:]]* return IDENTIFIER; [+-]?[[:digit:]]+ return INTEGER; [+-]?[[:digit:]]+/[[:alpha:]] { fprintf(stderr, "Warning: Incorrect integer '%s' in line %d\n", yytext, yylineno); return INTEGER; } 

Ora la terza regola combacerà esattamente con la stessa cosa, ma dopo la sua corrispondenza tornerà alla fine del numero in modo che il successivo lessema inizi con il carattere alfabetico.

Puoi anche farlo con la macro yyless() :

yyless(n) restituisce tutti i primi n caratteri del token corrente allo stream di input …

Quindi puoi usare:

 [[:alpha:]][[:alnum:]]* return IDENTIFIER; [+-]?[[:digit:]]+ return INTEGER; [+-]?[[:digit:]]+[[:alpha:]] { fprintf(stderr, "Warning: Incorrect integer '%s' in line %d\n", yytext, yylineno); yyless(yyleng - 1); return INTEGER; } 

Infine, come sottolinea @CharlieBurns in un commento, puoi semplicemente lasciare che il lexer restituisca due token (un numero e un identificatore) al parser, il quale riconoscerà un errore di syntax se quella sequenza è illegale nella lingua. In molti linguaggi di programmazione, nessun programma grammaticale può contenere un intero immediatamente seguito da un identificatore senza una certa punteggiatura.

Tuttavia, in molte altre lingue, la combinazione è perfettamente ragionevole, in particolare in lingue come Lua dove non c’è un indicatore esplicito di fine istruzione, quindi

  b = 3 a = 4 

è un programma valido composto da due istruzioni di assegnazione. Come altro esempio, la concatenazione di stringhe Awk è rappresentata senza operatore e i numeri sono automaticamente forzati alle stringhe se necessario, quindi

 print 3 a 

stamperà la concatenazione di "3" e il valore di a . Lua insiste su spazi bianchi nell’esempio sopra; Awk no.

Infine, C (++) considera 3a un token singolo, un “numero di pre-elaborazione”. Se il token passa effettivamente attraverso il preprocessore, verrà segnalato un errore, ma il seguente programma non ha errori di syntax:

 #define NOTHING(x) NOTHING(3a) 

Come esempio forse più interessante:

 #define CONCAT2(a,b) a##b #define CONCAT(a,b) CONCAT2(a,b) static const int the_answer = CONCAT(0x, 2a); 

Quindi non esiste “una risposta adatta a tutti”.