Parsing 如何编写*可中断*递归(?)下降分析器
我有一个非常标准的递归下降解析器。该算法非常简单:每个函数从Parsing 如何编写*可中断*递归(?)下降分析器,parsing,recursive-descent,interruption,non-recursive,Parsing,Recursive Descent,Interruption,Non Recursive,我有一个非常标准的递归下降解析器。该算法非常简单:每个函数从流读取字符,然后返回失败或调用后续解析函数(由相应的语法规则指定): 我想解决这样一种情况:我没有一次得到完整的流——我异步地得到它的片段。因此,我需要的是一个“可中断性”特性——我必须能够保存解析器的状态,然后从该点开始继续 遗憾的是,这是嵌套函数调用不可能做到的一件事,因为解析器的所有状态都“存储”在调用堆栈上(按函数顺序和它们的局部变量) 因此,我必须以不同的方式构造parse*函数 我使用的语言是JavaScript 有人能告诉
流
读取字符,然后返回失败
或调用后续解析函数(由相应的语法规则指定):
我想解决这样一种情况:我没有一次得到完整的流
——我异步地得到它的片段。因此,我需要的是一个“可中断性”特性——我必须能够保存解析器的状态,然后从该点开始继续
遗憾的是,这是嵌套函数调用不可能做到的一件事,因为解析器的所有状态都“存储”在调用堆栈上(按函数顺序和它们的局部变量)
因此,我必须以不同的方式构造parse*
函数
我使用的语言是JavaScript
有人能告诉我如何进行吗?
编辑:
- 我似乎很清楚,我需要某种状态机。我不能把我的头放在发电机或连续传球的风格上,在我看来,在挽救一个州和恢复的过程中有很多小故障。对我来说,我能想到的最清晰的途径是将嵌套调用转换成某种堆栈,将局部变量重写为存储在堆栈项中的某个hashmap,并以不同的线性方式构造解析代码,这样我就可以轻松地“转到”某个状态
- 我看到的子问题之一可能是:由于我没有完整的
,我想我必须尝试多个路径并存储所有部分解析的尝试。例如,如果语法说流
,那么a=b | c
可能太长,以至于它不完全在b
流中。因此,我无法在解析
时“挂起”,我必须同时尝试这两种方法并保存部分计算。我说得对吗b
如果使用Javascript编码,您可能需要使用CPS手工编写代码。另请参见、、页等……事实上,在某些语言中,这很简单。您使用的是哪种语言?如果您可以依赖相对较新的语言添加,您可以尝试使用来构建某种协同程序。如果(ret==FAIL)实际上不应该是
If(ret!=FAIL)
?取决于语法规则。它可能是foo=bar-quax
或foo=bar | quax
。无论如何,这段代码并不是一个精确的例子。我可能对协同路由有偏见,但IMHO完全基于延续传递的风格是矫枉过正和过于复杂的。只需在一个点上恢复,而不是在回溯时恢复任意数量。换句话说,一个生成器,甚至不是一个协程,更不用说一个完整的延续了。为此,一个简单的ish状态机应该也能工作,代码和间接寻址更少,但CPS很容易编码。当然,你需要在一开始就考虑。你看,这是我有偏见的部分。我必须认真思考如何将所有的控制流映射到CPS,而对于状态机,我会编写生成器pseudo-yield
s,并简单地进行一些机械变换(显式状态参数、切换以找到恢复位置、将局部变量移动到状态参数)。当然,经常编写CPS代码而很少使用状态机的人将处于相反的位置。
function parseFoo() {
var ret = parseBar(stream);
if (ret == FAIL) {
ret = parseQuax(stream);
...
}
...
return ret;
}