Una macchina, virtuale o meno, ha bisogno di un modello di calcolo che descrive come viene eseguito il calcolo su di esso. Per definizione, non appena calcola, implementa un modello di calcolo. La domanda quindi è: quale modello dovremmo scegliere per la nostra VM? Le macchine fisiche sono limitate da ciò che può essere fatto in modo efficace ed efficiente nell'hardware. Tuttavia, come si nota, le macchine virtuali non hanno vincoli di questo tipo, sono definite nel software utilizzando linguaggi di alto livello arbitrari.
Esistono, infatti, macchine virtuali di alto livello come descrivi. Sono chiamati linguaggi di programmazione . Lo standard C ad esempio dedica la maggior parte delle sue pagine alla definizione di un modello per la cosiddetta "macchina astratta C" che descrive come si comportano i programmi C, e per estensione (come regola if) come un compilatore C conforme (o interprete) dovrebbe comportarsi.
Naturalmente, di solito non chiamiamo una macchina virtuale. Di solito una macchina virtuale significa qualcosa di più basso, più vicino all'hardware, non destinato a essere programmato direttamente, progettato per essere eseguito in modo efficiente. Questo errore di selezione significa che qualcosa che accetta codice componibile di alto livello (come quello che descrivi) non sarebbe considerato una VM perché è eseguito codice di alto livello.
Ma per ottenere il punto, ecco alcuni motivi per fare una VM (come in, qualcosa preso di mira da un compilatore bytecode) basata su registro o simili. Le macchine stack e register sono estremamente semplici. C'è una sequenza di istruzioni, alcuni stati e semantica per ogni istruzione (una funzione Stato - > Stato). Nessuna riduzione complessa dell'albero, nessuna precedenza dell'operatore. L'analisi, l'analisi e l'esecuzione è molto semplice, perché è un linguaggio minimale (lo zucchero sintattico è compilato) e progettato per essere letto da macchina piuttosto che da lettura umana.
Al contrario, analizzare anche i linguaggi C più semplici è piuttosto difficile, e l'esecuzione richiede analisi non locali come il controllo e la propagazione dei tipi, la risoluzione dei sovraccarichi, la manutenzione di una tabella dei simboli, la risoluzione degli identificatori string , trasformando il testo lineare in un AST preceduto dalla precedenza e così via. Si basa su concetti che sono naturali per gli esseri umani, ma devono essere accuratamente invertiti dalle macchine.
Il bytecode JVM, ad esempio, viene emesso da javac
. Non ha praticamente bisogno di essere letto o scritto dagli umani, quindi è naturale orientarlo verso il consumo da parte delle macchine. Se lo hai ottimizzato per gli esseri umani, la JVM dovrebbe solo leggere ogni avvio del codice, analizzarlo, analizzarlo e convertirlo in una rappresentazione intermedia simile a un modello di macchina semplificato comunque . Potrebbe anche tagliare l'uomo medio.