为了尝试在JavaScript中实现PEG而不会使旧版浏览器从堆栈溢出中崩溃,我想制作一个解析表达式语法,以非递归方式解析字符串。 你怎么做到这一点? 感到心灵弯曲。

假设你有这样的结构:

  • grammar有很多表达方式
  • expression有很多matchers
  • matcher有许多tokens (或者其他任何更好的词)
  • token可以指向另一个expression ,也可以是原始字符串或正则表达式。 因此,如果它指向另一个表达式,则这是递归开始的位置。

所以说你定义这样的层次结构:

var grammar = new Grammar('math');
var expression = grammar.expression;

expression('math')
  .match(':number', ':operator', ':number', function(left, operator, right){
    switch (operator) {
      case '+': return left + right;
      case '-': return left - right;
      case '*': return left * right;
      case '/': return left / right;
    }
  });

expression('number')
  .match(/\d+/, parseInt);

expression('operator')
  .match('+')
  .match('-')
  .match('*')
  .match('/');

var val = grammar.parse('6*8'); // 42

当你调用grammar.parse ,它从根表达式开始(它与它的名字相同,所以“math”)。 然后它遍历每个匹配器,然后遍历每个标记,如果标记是表达式,则递归。 基本上这个(解析器将跟踪它匹配模式的字符串的偏移/位置;这只是伪代码):

function parse(str, expression, position) {
  var result = [];

  expression.matchers.forEach(function(matcher){
    matcher.tokens.forEach(function(token){
      var val;
      if (token.expression) {
        val = parse(str, token.expression, position);
      } else {
        val = token.parse(str, position);
      }
      if (val) result.push(val);
    });
  });

  return result;
}

parse('6*8', grammar.root, 0);

因此,对于像6*8这样的简单表达式,递归非常少,但您可以快速找到具有多层嵌套的复杂表达式。 再加上嵌套for循环的嵌套,并且堆栈变大(我实际上并没有使用forEach ,我用于循环,但在for循环中它大部分时间调用一个函数,所以它最终很漂亮几乎相同)。

问题是, 你如何“扁平化”? 而不是做递归,你如何做到这一点,所以它基本上是这样的:

while (token = stack.pop()) {
  val = token.parse(val);
  if (val) result.push(val);
}

我不是在寻找如何实现这个特定PEG问题的解决方案的细节,我更倾向于寻找以非递归方式跟踪递归状态的一般方法。

===============>>#1 票数:1

通常,您所做的是在代码中编写堆栈,并将“本地”变量放在“堆栈框架”上下文对象中,并保留在该堆栈中。 然后,在您将进行“递归调用”的地方,存储当前堆栈帧并为新的当前上下文创建一个新堆栈帧。 做“返回”只是扭转操作的问题。 它并不是特别复杂,但它确实使代码有点混乱。 唯一需要注意的是,当你完成解析表达式时,你会到达堆栈的底部(这样尾随令牌和丢失的令牌不会导致问题)。

这非常类似于机器代码中维护的堆栈所发生的情况,除了您不限于原始值并且因此可以使事情变得更加整洁(在数据结构级别)。

如果你有时间,考虑写(或使用别人的)LR(1)解析器。 那些维护很少的系统堆栈并且在语法中处理许多恶意案例比你的家庭卷LL(k)语法更好。 然而,他们中相当大 ,他们比你现在的本事如何工作更加神秘。

===============>>#2 票数:1

我更倾向于寻找以非递归方式跟踪递归状态的一般方法。

在堆栈(数组)中使用推送和弹出。
如果你有goto的话会更容易。
VBA中的(阶乘)方法(因为goto而更清晰)。

Option Explicit
Sub Main()
  MsgBox fac(1)
  MsgBox fac(5)
End Sub
Function fac(n&)
  Dim answer&, level&, stackn&(99)
  level = 0
zentry:
  If n = 1 Then answer = 1: GoTo zreturn
  level = level + 1 ' push n
  stackn(level) = n
  n = n - 1 ' call fac(n-1)
  GoTo zentry
zreturn:
  If level = 0 Then fac = answer: Exit Function
  n = stackn(level) ' pop n
  level = level - 1
  answer = n * answer ' factorial
  GoTo zreturn
End Function

javascript中的相同方法。

console.log(fac(1));
console.log(fac(5));
function fac(n) { // non-recursive
  var answer, level; 
  var stackn = []; 
  level = 0;
  while (true) { // no goto's
    if (n == 1) { answer = 1; break; }
    level = level + 1; // push n
    stackn[level] = n;
    n = n - 1; } // call fac(n-1) 
  while (true) { // no goto's
    if (level == 0) { return answer; }
    n = stackn[level]; // pop n
    level = level - 1;
    answer = n * answer; } // factorial
  }

  ask by Lance Pollard translate from so

未解决问题?本站智能推荐:

4回复

在JavaScript中使用Split的递归解析器

我有一个算法,用户将输入一个字符串,然后将其解析为2维以上的数组。 因此,例如,用户可以输入1,2,3; 4,5,6并设置要由分号和逗号解析的文本。 第一次通过将创建一个包含2个条目的数组。 第二次通过将在两个先前位置创建一个3入口阵列。 用户可以添加或删除用于解析原始字符串的文本项
1回复

如何跟踪有关迭代(非递归)函数的嵌套信息

假设我有一个递归下降解析器 ,它定义了一堆嵌套规则。 说我是对的●此处是流程中的第二个Value : 这意味着我在嵌套级别中位于此处的某个地方,可以这样说: 当使用递归下降进行解析时,它是递归的,因此当Value返回时,我们返回到“ sequence” *解析节点,然后返
3回复

Java-使用哈希函数进行递归字符串解析

假设有一个 如何递归哈希字符串中sha256(和)之间的所有内容? 我考虑过正则表达式,但不确定这种情况下的最佳工具。 我想要的-首先是内部值是哈希,然后是外部值。 我只需要使用sha256()来做这件事,但是我认为假设存在另一个哈希函数( md5() , sha1() ...
3回复

使用递归函数从父节点获取数据

我的数据结构如下: 我试图做一个递归函数来获取name元素,只要有一个父元素。 我想出了以下代码,但是它不起作用,因为它将结果转换为字符串(而不是对象)。 有人可以给我一些有关如何最好地实现这一目标的建议。 父元素的嵌套是变化的,不是固定的(即使在上面我仅指定了3层)。 范围是1
1回复

调用包含Java承诺的递归函数

我正在尝试通过解析xml文件来构建json树。 这些文件可能包含对其他xml文件的引用。 我想解析的所有文件都具有toc\\d.js类的名称。 输出的树应具有以下形式: 应当产生这种效果的xml可能类似于以下内容(toc.xml) 如您所见,它包含引用其他“ toc”文件的
1回复

左递归语法识别

通常我们想重构一个无上下文语法来删除左递归。 有许多算法来实现这种转换; 例如这里或这里 。 无论是否存在左递归,这些算法都将重构语法。 这具有负面的副作用,例如从原始语法产生不同的解析树,可能具有不同的关联性。 理想情况下,语法只有在绝对必要时才会被转换。 是否有算法或工具
3回复

在递归下降解析器中避免递归算法中的stackoverflow

我从事与解析器相关的项目,并使用递归下降解析器实现了它。 但是,问题很容易导致堆栈溢出。 有什么技术可以解决这类问题? 为了说明起见,这是一个简单的数学表达式解析器,具有加,减,乘和除法支持。 可以使用分组括号,并且显然会触发递归。 这是完整的代码: 如果运行,则可以键
1回复

如何将此递归函数转换为迭代函数?

此函数接收二叉树,例如[1,[[2,3],[4,5]]]并递归应用一系列转换。 有没有办法转换该函数,以便 相反,它接收一个扁平化的二叉树,例如[N,1,N,N,2,3,N,4,5] ; 它不使用递归吗?
4回复

将递归函数转换为非递归函数

我正在尝试将递归函数转换为伪代码中的非递归解决方案。 我遇到问题的原因是该方法中有两个递归调用。 任何帮助都会很棒。 谢谢。
2回复

请帮我理解这个递归函数

我试图理解这个递归函数,并且更好地教育自己一般的递归。 我似乎无法遵循这段代码是如何做到的。 我希望有人能指引我完成这一切,这样我才能理解发生了什么。 此函数获取数组的最大值,并检查数字的任何组合是否与此值相加。 以下是我从调用此结果得到的结果。 我已对他们发表评论,以显示我迷路