Come dovrebbe un lexer trattare con istruzioni multilinea (ad esempio definizioni di funzioni, istruzioni Control-Flow)?

5

tl; dr-ers:

Come fa normalmente un lexer a occuparsi di affermazioni non in linea. affermazioni che non terminano con un delimitatore di istruzioni specificato. Come le dichiarazioni del flusso di controllo?

Credo di avere una conoscenza abbastanza buona dell'analisi lessicale e posso andare avanti nella mia ricerca per comprendere i lexer / parser. Tuttavia, capisco in che modo i lexer si occupano delle 1 istruzioni multilinea.

Dopo aver letto un articolo di Wikipedia sulla Confronto della sintassi dei linguaggi di programmazione , La cosa che tutte le lingue hanno in comune, è che hanno un delimitatore di dichiarazione molto particolare. Alcuni usano il punto e virgola ( ; ) come delimitatori di istruzioni, altri usano lo spazio bianco ( \ws ) e alcuni periodi usati ( . ).

Utilizzando questo metodo, non riesco a vedere come quei linguaggi di programmazione sono in grado di avere funzioni \ class \ control flow, che si estendono su più righe. Correggimi se sbaglio, ma sono abbastanza sicuro che la maggior parte dei linguaggi di programmazione popolari (Python, Java, C, C ++, C #, Javascript, ecc.) Non usano i loro delimitatori di istruzioni alla fine delle funzioni , o classi (so che le classi in C ++ usano il punto e virgola alla fine, ma questo è oltre il punto), o il flusso di controllo.

Questo significa che: A: i Lexer creano un'eccezione speciale per le istruzioni che si estendono su più righe. O B: il lexer tratta le istruzioni multilinea come un'istruzione regolare, ed è compito del parser dare un senso a queste.

Ad esempio, prendi questo pseudo programma in C ++:

int exampleVar; //<-- inline statement. Delimited with a semicolon

void exampleFunc() { //<-- multi-line statement. This statement is the start of a block.
    // do things
} //<-- this is where the statement that was started above, should end?

Chiaramente, è facile vedere dove si dovrebbe terminare la prima dichiarazione. Lo finisci al punto e virgola. Ma come viene gestita la seconda affermazione? La dichiarazione si estende per includere tutto fino alla parentesi graffa di chiusura?

O potrei semplicemente essere fuorviato nel mio modo di pensare. Potrebbe darsi che il lexer non abbia assolutamente nulla a che fare con le istruzioni multilinea 1 . È questo il lavoro del parser? Cioè, è compito del parser dare un senso alle 1 istruzioni multilinea?

Per quanto possibile, la mia domanda è: come (se dovrebbero, in primo luogo), un lexer dovrebbe trattare le affermazioni che non sono in linea e non possono essere semplicemente terminate come se lo fossero. Come fa normalmente un lexer a occuparsi di affermazioni non in linea. affermazioni che non terminano con un delimitatore di istruzioni specificato. Come le dichiarazioni del flusso di controllo?

1 Per essere chiari, non intendo affermazioni su più righe, nel senso di continuazione della linea. Li intendo nel senso di affermazioni che partono da un blocco. Come una dichiarazione di funzione. Quando si definisce una funzione, è necessario conoscere anche le istruzioni che seguono la definizione della funzione fino a un determinato delimitatore di blocchi. Quindi non puoi semplicemente terminare l'affermazione dopo la definizione.

    
posta Christian Dean 28.09.2016 - 04:03
fonte

1 risposta

7

Come hai già congetturato, questo non è il lavoro del lexer. Non si scambia in termini di dichiarazioni, dichiarazioni e definizioni ma in entità molto più di basso livello chiamate token .

Ad esempio, per la seguente funzione C,

static int
sum_plus_42(const int a, const int b)
{
  int result = a + b + 42;
  return result;
}

il lexer produrrebbe la seguente sequenza di token.

  • parola chiave static
  • parola chiave int
  • identificatore sum_plus_42
  • parentesi aperta
  • parola chiave const
  • parola chiave int
  • identificatore a
  • virgola
  • parola chiave const
  • parola chiave int
  • identificatore b
  • parentesi chiusa
  • parentesi graffa
  • parola chiave int
  • identificatore result
  • operatore =
  • identificatore a
  • operatore +
  • identificatore b
  • operatore +
  • intero letterale 42
  • virgola
  • parola chiave return
  • identificatore result
  • virgola
  • parentesi chiusa

Se nel codice ci fosse un errore di sintassi (come una parentesi non corrispondente), il lexer lo avrebbe comunque felicemente reso noto. Tuttavia, rileva un errore lessicale come caratteri non validi in un valore letterale numerico, ad esempio 123wrong456 .

Dopo che il codice sorgente è stato tokenizzato, il parser costruisce l'albero della sintassi dalla sequenza di token.

Entrambe le analisi lessicali e sintattiche sono guidate da una specifica di una grammatica formale e non vi è alcuna ragione teorica per cui queste non possano essere unite in una sola. In pratica, tuttavia, rende il codice più pulito e meglio strutturato per separare i due passaggi. La grammatica per l'analisi lessicale è solitamente molto più semplice e regolare mentre la grammatica utilizzata per descrivere la sintassi della lingua è context-free . In pratica, le grammatiche spesso non sono pulite e hanno un involucro più o meno speciale al di fuori delle regole formali delle grammatiche regolari e prive di contesto.

Nel caso in cui questo non sia chiaro, i simboli terminali usati nella grammatica per l'analisi sintattica sono i token prodotti dal lexer, che sono non terminale simboli della sua stessa grammatica.

    
risposta data 28.09.2016 - 04:31
fonte

Leggi altre domande sui tag