RICERCA

Il traduttore è ... Tipi di traduttori. Converti e traduci il programma

I programmi, come le persone, richiedono un traduttore o un traduttore per tradurre da una lingua all'altra.

Concetti di base

Il programma è linguisticorappresentazione dei calcoli: i → P → P (i). L'interprete è un programma che viene inviato al programma P e alcuni dati di input x. Esegue P su x: I (P, x) = P (x). Il fatto che ci sia un solo traduttore in grado di eseguire tutti i programmi possibili (che possono essere rappresentati nel sistema formale) è una scoperta molto profonda e significativa di Turing.

Il processore è l'interprete del programma sulinguaggio macchina. Di solito è troppo costoso scrivere interpreti per i linguaggi di alto livello, quindi sono tradotti in una forma che è più facile da interpretare.

Alcuni tipi di traduttori hanno nomi molto strani:

  • Assembler traduce i programmi in assembler in linguaggio macchina.
  • Il compilatore traduce da un linguaggio di alto livello a un linguaggio di livello inferiore.

Un traduttore è un programma che accettaCome input il programma in una lingua S e distribuisce il programma nel linguaggio T in modo che entrambi abbiano la stessa semantica: P → X → Q. Cioè, ∀x. P (x) = Q (x).

traduttore è

Se trasmetti l'intero programma in qualcosainterpretato, questo è chiamato compilazione prima dell'esecuzione, o compilazione AOT. I compilatori AOT possono essere utilizzati in sequenza, l'ultimo dei quali è spesso un assemblatore, ad esempio:

Codice sorgente → Compilatore (compilatore) → Codice assemblatore → Assemblatore (traduttore) → Codice macchina → CPU (interprete).

Compilazione operativa o dinamicasi verifica se una parte del programma viene tradotta quando vengono eseguite altre parti compilate. I traduttori JIT ricordano ciò che hanno già fatto, in modo da non ripetere più volte il codice sorgente. Possono persino produrre compilazione e ricompilazione adattiva, in base al comportamento dell'ambiente di runtime del programma.

Molte lingue consentono di eseguire codice durante la traduzione e di compilare un nuovo codice durante l'esecuzione del programma.

Fasi di trasmissione

La traduzione consiste nelle fasi di analisi e sintesi:

Codice sorgente → Analizzatore → Rappresentazione concettuale → Generatore (sintetizzatore) → Codice di destinazione.

Ciò è dovuto ai seguenti motivi:

  • Qualsiasi altro modo non è adatto. La traduzione letterale semplicemente non funziona.
  • Buona soluzione ingegneristica: se hai bisogno di scrivere traduttori per lingue di origine M e N lingue di destinazione, devi solo scrivere programmi M + N semplici (semi-compilatori), e non programmi M × N complessi (traduttori completi).

tipi di traduttori

Tuttavia, in pratica, il concettualela presentazione è molto raramente espressiva e abbastanza potente da comprendere tutte le possibili lingue di partenza e di arrivo. Anche se alcuni sono stati in grado di avvicinarsi a questo.

I veri compilatori passano attraverso moltistadi. Quando si crea il proprio compilatore, non è necessario ripetere tutto il duro lavoro che le persone hanno già fatto durante la creazione di viste e generatori. Puoi trasmettere la tua lingua direttamente in JavaScript o C e utilizzare i motori JavaScript esistenti ei compilatori C per fare il resto. È anche possibile utilizzare viste intermedie esistenti e macchine virtuali.

Registrazione del traduttore

Un traduttore è un programma o tecnicomezzi in cui sono coinvolte tre lingue: fonte, obiettivo e base. Possono essere scritti nella forma a T, posizionando la sorgente a sinistra, il bersaglio a destra e la base sottostante.

Esistono tre tipi di compilatori:

  • Un traduttore è un auto-compilatore se la sua lingua di origine corrisponde alla lingua di base.
  • Un compilatore il cui linguaggio di destinazione è uguale a quello di base è chiamato auto-residente.
  • Un traduttore è un cross-compiler se ha diverse lingue di destinazione e di base.

trasmissione del programma

Perché è così importante?

Anche se non si crea un vero compilatore, è utile conoscere la tecnologia della sua creazione, poiché i concetti utilizzati a questo scopo sono universalmente applicati, ad esempio, in:

  • formattazione del testo;
  • linguaggi di interrogazione del database;
  • estese architetture informatiche;
  • problemi di ottimizzazione generalizzata;
  • interfacce grafiche;
  • linguaggi di scripting;
  • controllori;
  • macchine virtuali;
  • traduzioni automatiche.

Inoltre, se hai bisogno di scrivere preprocessori, assemblatori, caricatori, debugger o profiler, devi seguire gli stessi passaggi di quando scrivi un compilatore.

Puoi anche imparare come scrivere programmi migliori.poiché la creazione di un traduttore per una lingua significa una migliore comprensione delle sue sottigliezze e delle sue ambiguità. Studiare i principi generali della traduzione ti permette anche di diventare un buon designer di lingue. Importa quanto è bello il linguaggio se non può essere implementato in modo efficace?

Tecnologia completa

La tecnologia del compilatore copre molte diverse aree dell'informatica:

  • teoria del linguaggio formale: grammatica, analisi, computabilità;
  • architettura del computer: set di istruzioni, RISC o CISC, pipelining, core, cicli di clock, ecc .;
  • concetti di linguaggio di programmazione: ad esempio, controllo sequenziale, esecuzione condizionale, iterazioni, ricorsione, scomposizione funzionale, modularità, sincronizzazione, metaprogrammazione, ambito, costanti, sottotipi, modelli, tipo di output, prototipi, annotazioni, flussi, monadi, caselle di posta, continuazioni, caratteri jolly espressioni regolari, memoria transazionale, ereditarietà, polimorfismo, modalità parametriche e così via;
  • linguaggi astratti e macchine virtuali;
  • algoritmi e strutture dati: espressioni regolari, algoritmi di analisi, algoritmi grafici, programmazione dinamica, formazione;
  • linguaggi di programmazione: sintassi, semantica (statica e dinamica), supporto di paradigmi (strutturale, OOP, funzionale, logico, stack, parallelismo, metaprogrammazione);
  • creazione di software (compilatori, di regola, grandi e complessi): localizzazione, memorizzazione nella cache, specifica dei componenti, interfacce API, riutilizzo, sincronizzazione.

conversione del programma

Design del compilatore

Alcuni problemi che sorgono quando si sviluppa un vero traduttore:

  • Problemi con la lingua d'origine È facile da compilare? C'è un preprocessore? Come vengono elaborati i tipi? Sono disponibili librerie?
  • Raggruppamento dei passaggi del compilatore: singolo o multi-pass?
  • Il grado di ottimizzazione desiderato. La trasmissione veloce e impura del programma con ottimizzazione minima o nulla può essere normale. L'eccessiva ottimizzazione rallenterà il compilatore, ma il miglior codice in fase di esecuzione potrebbe valerne la pena.
  • Tasso di rilevamento degli errori richiesto. Il traduttore può semplicemente fermarsi al primo errore? Quando dovrebbe fermarsi? Il compilatore affida la correzione dell'errore?
  • Disponibilità di strumenti. Se la lingua di origine non è molto piccola, sono necessari il generatore di scanner e analizzatore. Ci sono anche generatori di generatori di codice, ma non sono così comuni.
  • Il tipo di codice di destinazione da generare. Dovrebbe essere scelto tra codice macchina puro, aumentato o virtuale. O semplicemente scrivi un input che crea viste intermedie popolari, come LLVM, RTL o JVM. O effettuare la traduzione dal codice sorgente al codice sorgente in C o JavaScript.
  • Il formato del codice di destinazione. È possibile scegliere linguaggio assembly, codice macchina portatile, memoria immagine codice macchina.
  • Il retargeting. Con molti generatori, è bene avere un input comune. Per lo stesso motivo, è meglio avere un generatore per molte parti di input.

compilazione dinamica

Architettura del compilatore: componenti

Questi sono i principali componenti funzionali di un traduttore che genera codice macchina (se il programma di output è un programma C o una macchina virtuale, non sono necessari molti passaggi):

  • Il programma di input (flusso di caratteri) entra nello scanner (analizzatore lessicale), che lo converte in un flusso di token.
  • Un parser (parser) costruisce da loro un albero di sintassi astratto.
  • L'analizzatore semantico si decomponeinformazioni semantiche e verifica la presenza di errori nei nodi dell'albero. Di conseguenza, viene creato un grafico semantico: un albero sintattico astratto con proprietà aggiuntive e collegamenti stabiliti.
  • Il generatore di codice intermedio crea un diagramma di flusso (le tuple sono raggruppate in blocchi principali).
  • L'ottimizzatore di codice indipendente dalla macchina conduce entrambiottimizzazione locale (all'interno dell'unità di base) e globale (per tutti i blocchi), principalmente all'interno delle subroutine. Riduce il codice di ridondanza e semplifica i calcoli. Il risultato è un grafico di flusso modificato.
  • Il generatore del codice di destinazione collega i blocchi di base in un codice lineare con il trasferimento del controllo, creando un file oggetto in assembler con registri virtuali (possibilmente inefficace).
  • Ottimizzatore-linker specifico della macchinaalloca la memoria tra i registri ed esegue la pianificazione dei comandi. Esegue la conversione del programma sull'assemblatore all'assemblatore attuale con un buon uso del pipelining.

Inoltre, vengono utilizzati i sottosistemi di rilevamento degli errori e il gestore delle tabelle dei simboli.

file oggetto

Analisi lessicale (scansione)

Lo scanner converte il flusso di caratteri dal codice sorgente in un flusso di token, rimuovendo spazi, commenti e macro in espansione.

Gli scanner spesso incontrano problemi come se prendere o meno in considerazione i casi, i rientri, i feed di riga e i commenti nidificati.

Gli errori che possono verificarsi durante la scansione sono chiamati lessicali e includono:

  • simboli mancanti nell'alfabeto;
  • eccesso del numero di caratteri in una parola o linea;
  • non un carattere chiuso o una stringa letterale;
  • fine del file nei commenti.

Analisi (parsing)

Il parser converte una sequenza di token inalbero di sintassi astratto. Ogni nodo dell'albero viene salvato come oggetto con campi denominati, molti dei quali sono essi stessi nodi dell'albero. Non ci sono cicli in questa fase. Quando crei un parser, devi prestare attenzione al livello di complessità della grammatica (LL o LR) e scoprire se ci sono delle regole per la disambiguazione. Alcuni linguaggi richiedono un'analisi semantica.

Gli errori incontrati in questa fase sono chiamati sintattici. Ad esempio:

  • k = 5 * (7 - y;
  • j = / 5;
  • 56 = x * 4.

Analisi semantica

Durante l'analisi semanticaè necessario verificare le regole di ammissibilità e collegare parti dell'albero sintattico (risolvendo riferimenti di nomi, inserendo operazioni per conversioni di tipo implicito, ecc.) per formare un grafo semantico.

Ovviamente, l'insieme delle regole di ammissibilità per le diverse lingue è diverso. Se vengono compilate lingue simili a Java, i traduttori possono trovare:

  • più dichiarazioni variabili all'interno del suo ambito;
  • riferimenti a una variabile prima della sua dichiarazione;
  • collegamenti al nome non dichiarato;
  • violazione dell'accessibilità;
  • numero di argomenti troppo grande o insufficiente quando si chiama il metodo;
  • tipo mancata corrispondenza.

programma d'ingresso

generazione

La generazione di un codice intermedio produce un diagramma di flusso composto da tuple raggruppate in blocchi di base.

La generazione del codice produce codice macchina reale. Nei compilatori tradizionali per macchine RISC, al primo stadio viene creato un assemblatore con un numero infinito di registri virtuali. Per le macchine CISC, probabilmente questo non accadrà.

  • valutazione: