Интерпретаторы, компиляторы и виртуальные машины
Код интерпретатора организован в виде цикла, в котором командный счетчик рс обходит массив указателей на фрикции:
Этот цикл моделирует в программном виде на изобретенной нами стековой машине то, что происходит на самом деле в настоящем компьютере:
Обратите внимание на то, что проверка делимого на ноль осуществляется в divop, а не в generate.
Условное исполнение, ветвление и циклы модифицируют счетчик программы внутри функции-операции, осуществляя тем самым доступ к массиву функций с какой-то новой точки. Например, операция goto всегда переустанавливает значение переменной рс, а операция условного ветвления – только в том случае, если условие есть истина.
Массив code является, естественно, внутренним для интерпретатора, однако представим себе, что нам захотелось сохранить сгенерированную программу в файл. Если бы мы записывали адреса функций, то результат получился бы абсолютно непереносимым, да и вообще ненадежным. Однако мы могли бы записывать константы, представляющие функции, например 1000 для addop, 1001 для pushop и т. д., и переводить их обратно в указатели на функции при чтении программы для интерпретации.
Если внимательно посмотреть на файл, который создает эта процедура, можно понять, что он выглядит как поток команд виртуальной машины. Эти команды реализуют базовые операции нашего рабочего языка, функция generate – это компилятор, транслирующий язык на виртуальную машину. Виртуальные машины – стародавняя идея, обретшая в последнее время новую популярность благодаря Java и виртуальной машине Java (Java Virtual Machine, JVM); виртуальные машины представляют простой способ создавать переносимые и эффективные реализации программ, написанных на языках высокого уровня.