Parsing 如何编写*可中断*递归(?)下降分析器

Parsing 如何编写*可中断*递归(?)下降分析器,parsing,recursive-descent,interruption,non-recursive,Parsing,Recursive Descent,Interruption,Non Recursive,我有一个非常标准的递归下降解析器。该算法非常简单:每个函数从流读取字符,然后返回失败或调用后续解析函数(由相应的语法规则指定): 我想解决这样一种情况:我没有一次得到完整的流——我异步地得到它的片段。因此,我需要的是一个“可中断性”特性——我必须能够保存解析器的状态,然后从该点开始继续 遗憾的是,这是嵌套函数调用不可能做到的一件事,因为解析器的所有状态都“存储”在调用堆栈上(按函数顺序和它们的局部变量) 因此,我必须以不同的方式构造parse*函数 我使用的语言是JavaScript 有人能告诉

我有一个非常标准的递归下降解析器。该算法非常简单:每个函数从
读取字符,然后返回
失败
或调用后续解析函数(由相应的语法规则指定):

我想解决这样一种情况:我没有一次得到完整的
——我异步地得到它的片段。因此,我需要的是一个“可中断性”特性——我必须能够保存解析器的状态,然后从该点开始继续

遗憾的是,这是嵌套函数调用不可能做到的一件事,因为解析器的所有状态都“存储”在调用堆栈上(按函数顺序和它们的局部变量)

因此,我必须以不同的方式构造
parse*
函数

我使用的语言是JavaScript

有人能告诉我如何进行吗?

编辑:

  • 我似乎很清楚,我需要某种状态机。我不能把我的头放在发电机或连续传球的风格上,在我看来,在挽救一个州和恢复的过程中有很多小故障。对我来说,我能想到的最清晰的途径是将嵌套调用转换成某种堆栈,将局部变量重写为存储在堆栈项中的某个hashmap,并以不同的线性方式构造解析代码,这样我就可以轻松地“转到”某个状态

  • 我看到的子问题之一可能是:由于我没有完整的
    ,我想我必须尝试多个路径并存储所有部分解析的尝试。例如,如果语法说
    a=b | c
    ,那么
    b
    可能太长,以至于它不完全在
    流中。因此,我无法在解析
    b
    时“挂起”,我必须同时尝试这两种方法并保存部分计算。我说得对吗


这取决于所使用的编程语言

您基本上需要一些支持(您可以理解为调用堆栈的抽象)。换句话说,您可能需要编码(CPS)

如果用C编写代码,您可能会感兴趣


如果使用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;
}