Una macchina, virtuale o no, ha bisogno di un modello di calcolo che descriva come viene eseguito il calcolo su di essa. Per definizione, non appena calcola, implementa alcuni modelli di calcolo. La domanda allora è: quale modello dovremmo scegliere per la nostra VM? Le macchine fisiche sono vincolate da ciò che può essere fatto in modo efficace ed efficiente nell'hardware. Ma, come notate, le macchine virtuali non hanno tali vincoli, sono definite nel software usando linguaggi di livello arbitrariamente alto.
Esistono, infatti, macchine virtuali di alto livello come da te descritto. Si chiamano 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 il comportamento dei programmi C e, per estensione (regola as-if), come un compilatore (o interprete) C conforme dovrebbe comportarsi.
Naturalmente, di solito non la chiamiamo una macchina virtuale. Una VM di solito significa qualcosa di livello inferiore, 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 verrebbe considerato una macchina virtuale perché esegue un codice di alto livello.
Ma per arrivare al punto, ecco alcuni motivi per creare una macchina virtuale (come in, qualcosa di mirato 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 semantiche per ogni istruzione (una funzione State -> State). Nessuna riduzione complessa dell'albero, nessuna precedenza per l'operatore. Analizzarlo, analizzarlo ed eseguirlo è molto semplice, perché è un linguaggio minimale (lo zucchero sintattico è compilato via) e progettato per essere letto automaticamente piuttosto che umano.
Al contrario, analizzare anche i linguaggi simil-C più semplici è piuttosto difficile ed eseguirlo richiede analisi non locali come il controllo e la propagazione dei tipi, la risoluzione di sovraccarichi, la manutenzione di una tabella di simboli, la risoluzione di identificatori di stringhe , la trasformazione di testo lineare in un AST basato sulla precedenza , e così via. Si basa su concetti che diventano naturali per l'uomo ma devono essere accuratamente ingegnerizzati dalle macchine.
Il bytecode JVM, ad esempio, viene emesso da javac
. Non ha praticamente mai bisogno di essere letto o scritto dagli umani, quindi è naturale orientarlo verso il consumo da parte delle macchine. Se ottimizzato per gli esseri umani, la JVM sarebbe solo ad ogni avvio di leggere il codice, analizzarlo, analizzare è, e quindi convertirlo in una rappresentazione intermedia simile ad una semplificata tale modello di macchina in ogni caso . Potrebbe anche tagliare l'uomo di mezzo.