Проще, чем написать компилятор и виртуальную машину, - зарегистрировать и перехватить ваш интерпретатор.Поскольку у вас есть интерпретатор, а не компилятор (я полагаю), вам понадобится всего пара простых преобразований, чтобы получить надлежащую поддержку хвостовых вызовов.
Сначала вам придется написать все в стиле передачи с продолжением, которыйможет быть странно думать и делать на C / C ++.Учебное пособие ParentheC Дэна Фридмана поможет вам преобразовать высокоуровневую рекурсивную программу в форму, которая может быть переведена машиной на C.
В конце концов, вы 'В сущности, мы реализуем простую виртуальную машину, в которой вместо обычных вызовов функций для выполнения eval, applyProc и т. д. вы передаете аргументы, устанавливая глобальные переменные, а затем делаете goto
для следующего аргумента (или используете цикл и программу верхнего уровняcounter) ...
return applyProc(rator, rand)
становится
reg_rator = rator
reg_rand = rand
reg_pc = applyProc
return
То есть все ваши функции, которые обычно рекурсивно вызывают друг друга, сводятся к псевдосборке, в которой они являются просто блокамикода, который не повторяется.Цикл верхнего уровня управляет программой:
for(;;) {
switch(reg_pc) {
case EVAL:
eval();
break;
case APPLY_PROC:
applyProc();
break;
...
}
}
Редактировать: Я прошел через тот же процесс для моего хобби-интерпретатора Scheme, написанного на JavaScript.Я воспользовался многими анонимными процедурами, но это может помочь в качестве конкретной ссылки.Посмотрите История коммитов FoxScheme , начиная с 2011-03-13 ( 30707a0432563ce1632a ) до 2011-03-15 ( 5dd3b521dac582507086 ).
Редактировать ^ 2: Не-хвостовая рекурсия будет по-прежнему потреблять память, даже если она не находится в стеке.