La risposta breve è: hai ragione nei tuoi sospetti, hai sempre bisogno di un altro interprete scritto in X o di un compilatore da Y in un'altra lingua per la quale hai già un interprete. Gli interpreti eseguono, i compilatori traducono solo da una lingua all'altra, ad un certo punto del tuo sistema, ci deve essere un interprete ... anche solo la CPU.
Indipendentemente dal numero di nuovi interpreti che scrivi nella lingua Y , dovrai sempre usare il primo interprete scritto in X per interpretare gli interpreti successivi. Questo sembra essere un problema semplicemente a causa della natura degli interpreti.
Corretta. Che cosa si può fare è scrivere un compilatore da Y a X (o un'altra lingua per la quale si dispone di un interprete), e si può anche fare in Y . Quindi puoi eseguire il tuo compilatore Y scritto in Y sull'interprete Y scritto in X (o sull'interprete Y scritto in Y in esecuzione sull'interprete Y scritto in X , oppure sull'interprete Y scritto in Y in esecuzione sull'interprete Y scritto in Y in esecuzione su Yinterprete scritto in X , o ... all'infinito) per compilare il tuo interprete Y scritto in Y in X , in modo da poterlo eseguire su un interprete X. In questo modo, ti sei sbarazzato del tuo interprete Y scritto in X , ma ora hai bisogno dell'interprete X (sappiamo che ne abbiamo già uno, però, poiché altrimenti non potremmo eseguire l' interprete X scritto in Y ) e tu ho dovuto scrivere prima un compilatore da Y a X.
Tuttavia , il rovescio della medaglia, l'articolo di Wikipedia sugli interpreti in realtà parla di interpreti self-hosting. Ecco un piccolo estratto che è rilevante:
Un auto-interprete è un interprete del linguaggio di programmazione scritto in un linguaggio di programmazione che può interpretare se stesso; un esempio è un interprete BASIC scritto in BASIC. Gli auto-interpreti sono correlati ai compilatori self-hosting.
Se non esiste un compilatore per la lingua da interpretare, la creazione di un interprete automatico richiede l'implementazione della lingua in una lingua host (che può essere un altro linguaggio di programmazione o assemblatore). Avendo un primo interprete come questo, il sistema viene avviato e le nuove versioni dell'interprete possono essere sviluppate nella lingua stessa
Non mi è ancora chiaro, tuttavia, come si farebbe esattamente. Sembra che, indipendentemente da ciò, sarai sempre costretto a utilizzare la prima versione dell'interprete scritta nella lingua host.
Corretta. Nota che l'articolo di Wikipedia afferma esplicitamente che hai bisogno di una seconda implementazione della tua lingua e non dice che puoi sbarazzarti della prima.
Ora l'articolo sopra menzionato si collega a un altro articolo in cui Wikipedia fornisce alcuni esempi di presunti interpreti self-hosting. Ad un esame più attento, tuttavia, sembra che la parte "interpretante" principale di molti di quegli interpreti self-hosting (in particolare alcuni dei più comuni come PyPy o Rubinius) siano effettivamente scritti in altre lingue come C ++ o C.
Ancora una volta, corretto. Questi sono esempi davvero negativi. Prendi Rubinio, per esempio. Sì, è vero che la parte Ruby di Rubinius è ospitata autonomamente, ma è un compilatore, non un interprete: si compila dal codice sorgente Ruby al bytecode Rubinius. La parte dell'interprete OTOH non è auto-ospitata: interpreta il bytecode di Rubinius, ma è scritta in C ++. Quindi, chiamare Rubinius come "interprete auto-ospitato" è sbagliato: la parte ospitata non è un interprete e la parte interprete non è ospitata da sé .
PyPy è simile, ma anche più errato: non è nemmeno scritto in Python, in primo luogo, è scritto in RPython, che è un linguaggio diverso. È sintatticamente simile a Python, semanticamente un "sottoinsieme esteso", ma in realtà è un linguaggio tipicamente statico all'incirca sullo stesso livello di astrazione di Java, e la sua implementazione è un compilatore con backend multipli che compila RPython in codice sorgente C, ECMAScript codice sorgente, codice byte CIL, codice byte JVM o codice sorgente Python.
Quindi è possibile ciò che descrivo sopra? Un interprete self-host può essere indipendente dal suo host originale? In tal caso, come si farebbe esattamente?
No, non da solo. Dovresti mantenere l'interprete originale o scrivere un compilatore e compilare il tuo interprete personale.
Ci sono alcune VM meta-circolari, come Klein (scritto in Self ) e Maxine (scritto in Java). Si noti, tuttavia, che qui la definizione di "meta-circolare" è ancora diversa: queste macchine virtuali non sono scritte nella lingua che eseguono: Klein esegue il bytecode Self ma è scritto in Self, Maxine esegue il bytecode JVM ma è scritto in Java. Tuttavia, il codice sorgente Self / Java della VM viene effettivamente compilato in bytecode Self / JVM e quindi eseguito dalla VM, quindi quando la VM viene eseguita, è nella lingua che esegue. Uff.
Si noti inoltre che questo è diverso dalle macchine virtuali come SqueakVM e Jikes RVM . Jikes è scritto in Java e SqueakVM è scritto in Slang (un sottoinsieme sintattico e semantico tipicamente statico di Smalltalk all'incirca sullo stesso livello di astrazione di un assemblatore di alto livello) ed entrambi vengono compilati staticamente in codice nativo prima di essere eseguiti. Non corrono dentro di sé. È possibile , tuttavia, eseguirli sopra di sé (o sopra un'altra VM / JVM Smalltalk). Ma questo non è "meta-circolare" in questo senso.
Maxine e Klein, OTOH docorrere dentro di sé; eseguono il proprio bytecode usando la propria implementazione. Questo è davvero strabiliante! Consente alcune interessanti opportunità di ottimizzazione, ad esempio poiché la VM si esegue da sola insieme al programma utente, può incorporare le chiamate dal programma utente alla VM e viceversa, ad esempio la chiamata al garbage collector o l'allocatore di memoria può essere incorporata nell'utente codice e richiamate riflettenti nel codice utente possono essere integrate nella VM. Inoltre, tutti i trucchi di ottimizzazione intelligenti che fanno le moderne VM, in cui guardano il programma in esecuzione e lo ottimizzano in base al carico di lavoro e ai dati effettivi, la VM può applicare quegli stessi trucchi a se stessa mentre esegue il programma utente mentre il programma utente sta eseguendo il carico di lavoro specifico. In altre parole, la VM altamente specializzato per sé cheprogramma particolare che esegue quel particolare carico di lavoro.
Tuttavia, noti che ho aggirato l'uso della parola "interprete" sopra, e ho sempre usato "esegui"? Bene, quelle macchine virtuali non sono costruite attorno agli interpreti, sono costruite attorno a compilatori (JIT). In seguito è stato aggiunto un interprete a Maxine, ma è sempre necessario il compilatore: è necessario eseguire la VM una volta sopra un'altra VM (ad esempio Oracle HotSpot nel caso di Maxine), in modo che la VM possa (JIT) compilarsi da sola. Nel caso di Maxine, JIT compilerà la propria fase di avvio, quindi serializzerà quel codice nativo compilato su un'immagine VM bootstrap e applicherà un bootloader molto semplice di fronte (l'unico componente della VM scritto in C, anche se è solo per comodità , potrebbe essere anche in Java). Ora puoi usare Maxine per eseguire se stesso.