我正在编写一个使用 Cont Monad 的变体在功能上实现的解释器。受 Smalltalk 使用图像捕获正在运行的程序的启发,我正在研究如何序列化正在执行的托管程序,并且需要帮助确定如何在高级别上完成此任务。
问题陈述
使用 Cont monad,我可以在解释器中捕获正在运行的程序的当前延续。存储当前延续允许通过调用延续来恢复解释器的执行。我想序列化这个延续,以便可以将正在运行的程序的状态保存到磁盘或由另一个解释器实例加载。但是,我的语言(我的目标和工作都在 Javascript 中)不支持以这种方式序列化函数。
我会对一种方法感兴趣,该方法可用于在给定的执行点建立延续给定的一些元数据,而无需再次运行整个程序,直到它到达该点。优选地,将对解释器本身的实现做出最小的改变。
考虑的方法
一种可行的方法是将所有控制流逻辑推入程序状态。例如,我目前使用宿主语言对循环行为的递归来表达 C 风格的循环:
var forLoop = function(init, test, update, body) {
var iter = function() {
// When test is true, evaluate the loop body and start iter again
// otherwise, evaluate an empty statement and finish
return branch(test,
next(
next(body, update),
iter()),
empty);
};
return next(
init,
iter());
};
这是一个很好的解决方案,但是如果我通过 for 循环中途暂停程序,我不知道如何序列化已建立的延续。
我知道我可以使用跳转序列化转换后的程序,并且可以从跳转操作构造 for 循环。我的解释器的第一遍将生成代码块并将它们保存在程序状态中。这些块将捕获托管语言中的一些操作,并可能执行其他块。预处理程序看起来像这样:
Label Actions (Block of code, there is no sequencing)
-----------------------------------
start: init, GOTO loop
loop: IF test GOTO loop_body ELSE GOTO end
loop_body: body, GOTO update
update: update, GOTO loop
end: ...
这使得每个代码块都是独立的,只依赖于存储在程序状态中的值。
要序列化,我会保存当前标签名称和输入时的状态。反序列化将预处理输入代码以再次构建标签,然后以给定状态在给定标签处恢复。但是现在我在实现我的解释器时必须考虑这些块。即使使用合成来隐藏其中的一些似乎有点难看。
问题
有没有什么好的现有方法来解决这个问题?我是否正在考虑以完全错误的方式序列化程序?对于这样的结构,这甚至可能吗?