After creating so many things I have realized there MUST be more abstraction between parsing and code execution than what I currently have, which leads to my main question(s).
Non proprio. In una tipica implementazione linguistica ad alte prestazioni di qualità industriale, in genere sono molte altre fasi, ma non è affatto una necessità. Interpreti semplici di tree-walking come MRI Ruby, ad esempio, in realtà solo lex, analizzano, quindi camminano sull'albero di sintassi astratto ed eseguono uno snippet di codice per ogni nodo visitato. Questo è tutto. Anche gli interpreti basati su linee più semplici non creano nemmeno un AST.
My first question is, this. What exactly is the job of a language virtual machine, like for example the JVM […].
La cosa più importante da capire è che una VM non è nulla di speciale. Una macchina virtuale è qualsiasi cosa che estrae completamente un'API da un'API diversa. Questo è tutto. Ad esempio, nella programmazione orientata agli oggetti (almeno nella forma immaginata da Alan Kay), ogni oggetto è una macchina virtuale.
Più specificamente nel contesto delle lingue: ogni lingua è una VM e ogni VM definisce una lingua.
In realtà l'unica differenza tra un linguaggio di programmazione e un set di istruzioni VM è intent : la sintassi di un linguaggio di programmazione è progettata per essere facilmente letta dagli umani, la semantica è progettata per esprimere elegantemente problemi complessi . La sintassi (formato) di un set di istruzioni VM è progettata per essere facilmente analizzata dalle macchine e la sua semantica è progettata per essere facilmente interpretata (o compilata) e anche facile da compilare a .
Ma ancora: un set di istruzioni VM è solo un linguaggio di programmazione come gli altri, ed è eseguito esattamente allo stesso modo di qualsiasi altro linguaggio di programmazione: interpretandolo o compilandolo in un linguaggio diverso che è già possibile interpretare.
What does it do? Does it actually sort of emulate hardware and execute low level instructions on the emulated hardware? Or does it simply provide an abstraction and execute lower level managed instructions on the system?
Dipende da ciò che vuoi che la VM faccia. Il set di istruzioni LLVM, ad esempio, è progettato per essere un codice macchina indipendente dalla macchina, se lo si desidera. Il suo obiettivo è quello di essere facilmente compilato in un codice macchina efficiente. Quindi, cerca di essere il più basso e vicino ai set di istruzioni della corrente principale della CPU (x86, AMD64, IA-64, SPARC, MIPS, ARM, ecc.) Senza legarlo a un set di istruzioni specifico della CPU.
Il set di istruzioni JVML OTOH è stato progettato per essere facilmente interpretato , e come tale è molto più alto del set di istruzioni LLVM. Il set di istruzioni CIL è ugualmente di alto livello come JVML, ma è stato progettato per essere compilato , e quindi fa alcune scelte diverse.
Second question. What exactly is byte code? Is it some sort of machine code for the virtual machine? Or is it the human readable possibly assembly like instructions,
Il codice byte è semplicemente il nome di una lingua in cui le istruzioni sono codificate come byte, anziché come testo. Questo è tutto.
Si noti che molte lingue erroneamente chiamate "codice byte" in realtà non lo sono. Ad esempio, sulla CLI, le istruzioni sono codificate come inte, non come byte, quindi CIL è un codice int, non un codice byte.
and are these instructions directly executed or somehow compiled to native code?
In entrambi. Tutti e due. Tu decidi. Il primo è chiamato "interpretazione", il secondo è chiamato "compilazione". A proposito: non è necessario per compilare codice nativo, puoi compilare qualsiasi cosa per cui hai un interprete o un compilatore. Ad esempio, la Fantom VM compila il proprio set di istruzioni su JVML o CIL, a seconda che venga eseguito sulla piattaforma Java o sulla CLI.
Ogni linguaggio può essere implementato da un compilatore e la lingua ogni può essere implementata da un interprete. I set di istruzioni VM non sono diversi. Ad esempio, ci sono JVM che interpretano il codice byte JVML, ci sono JVM che compongono il codice byte JVML nel codice nativo mentre il programma è in esecuzione, ci sono JVM che compongono il codice byte JVML nel codice nativo prima dell'esecuzione del programma, ci sono JVM che compila codice byte JVML in codice sorgente JavaScript e molti altri ancora. Esistono anche CPU che eseguono direttamente il codice byte JVML (che in realtà è solo un caso speciale della prima opzione, in cui l'interprete è implementato in silicio anziché software).
Third question. How would I go about implementing this? I consider myself a competent programmer and don't need step by step instructions by no means, just an overview of what exactly I need to be doing, and how low level this is exactly.
Un set di istruzioni VM è una lingua come qualsiasi altra lingua. Lo implementa esattamente come qualsiasi altra lingua:
- l'analisi
- analisi semantica
- tipo inferenza
- verifica del tipo
- ottimizzazione
- generazione del codice (per un compilatore) o esecuzione di codice (per un interprete)
La fase di analisi è solitamente banale, poiché i set di istruzioni VM sono progettati per essere facilmente analizzati. L'inferenza del tipo e il controllo del tipo del corso hanno senso solo se il set di istruzioni VM è stato digitato. L'ottimizzazione è facoltativa, se non si desidera creare una macchina virtuale ad alte prestazioni. OTOH, se fai vuoi costruire una macchina virtuale ad alte prestazioni, allora l'ottimizzazione è dove spenderai il 99,999% del tuo sforzo di sviluppo.