- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
想象一个简单的语法:
(a|ab)c
其中读取 (a 或 ab) 后跟 c。解析树看起来像这样:
and
/ \
or c
/ \
a ab
现在给它这个输入:
abc
我们会首先向下遍历树的左侧,匹配“a”,然后返回上一级。由于“a”匹配,“或”也为真,所以转到“c”。 “c”不匹配,我们走到了尽头。
但是它可以选择一条替代路径;如果我们向下遍历到“ab”,我们就会找到一个匹配项。
所以我想为“或”节点做的基本上是这样的:
然后每当解析器遇到死胡同时,我想从堆栈中弹出一个项目并从那里再次继续。
那是我想不通的部分……我如何从根本上保存当前的调用堆栈?我可以将“ab”节点保存在一个堆栈中,这样我就知道我接下来必须执行那个节点,但它仍然需要知道它需要回退到“or”之后。
我认为Chris正在做某事。我们必须找到一种方法来翻译树,这样就不必像那样跳过分支。例如,这个等效的解析树没有那个问题:
or
/ \
and and
/ \ / \
a c ab c
这次我们向左下方解析,点击“a”,它通过了,所以我们尝试它旁边的“c”节点,失败,“and”失败,“or”必须尝试右分支,.. . “ab”通过,另一个“c”通过,然后整个表达式通过。
最佳答案
你提出问题的方式就有了问题的答案。
您需要保存状态。棘手的部分是识别状态。保存它很容易。
你的问题是解析器在开始解析某些语法规则时“有一个状态”。 (如果您使用 LALR 解析器,这会变得更加困惑,它将许多规则的解析合并到一个状态中)。该状态包括:
当您正在解析并面临您所描述的选择时,您需要“保存状态”,对第一个术语运行试解析。如果成功,您可以丢弃保存的状态并继续。如果失败,恢复状态,并尝试第 2 个(和第 n 个备选方案)。 (不管你是否面临备选方案,无脑和保存状态更容易,但这取决于你)。
如何保存状态?将其插入堆栈。 (你通常有一个解析栈,那是一个非常方便的地方!如果你不喜欢那个,添加另一个栈,但你会发现它和解析栈通常同步移动;我只是让解析栈包含一个记录我需要的所有东西,包括输入空间。你会发现“调用堆栈”对部分状态很方便;见下文)。
第一件事是保存输入的位置;这可能是文件源位置,出于优化原因可能是缓冲区索引。那只是一个标量,所以很容易保存。 恢复输入流可能更难;无法保证解析器输入扫描器就在它原来的位置附近。所以你需要重新定位文件,重新读取任何缓冲区,并重新定位任何输入缓冲区指针。一些简单的检查可以使这在统计上很便宜:存储任何缓冲区第一个字符的文件位置;然后确定是否必须重新读取缓冲区是将保存的文件位置与缓冲区起始文件位置进行比较的问题。其余的应该是显而易见的。
如果你重新安排你的语法以最小化它,你将通过更少的缓冲区回溯(例如,你的解析器运行得更快)。在您的特定语法中,您有“(a | ab ) c”,可以手写为“a b?c”。后者至少不会回溯 a 代表的任何内容。
奇怪的部分是保存解析堆栈。好吧,你不必这样做,因为你的试验解析只会扩展你拥有的解析堆栈,并将其恢复到你拥有的解析状态,无论你的子解析是成功还是失败。
“解析器失败的地方”和“成功的地方”只是另外两个标量。您可以将它们表示为解析代码块和程序计数器(例如,延续)的索引,或者表示为调用堆栈上的返回地址(参见?另一个并行堆栈!),然后是成功/失败的条件测试。
如果您想了解有关后者的一些详细信息,请查看我的 SO answer on hand-coded recursive descent parsers.
如果您开始构建树,或做一些其他作为解析副作用的事情,您将必须弄清楚如何捕获/保存副作用实体的状态,并恢复它。但无论它是什么,您最终都会将其压入堆栈。
关于c# - 如何将当前执行状态压入堆栈以便稍后继续执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9740291/
出于好奇 - 我知道有 LAMP - Linux、Apache、MySQL 和 PHP。但是还有哪些其他 Web 堆栈替代方案的缩写呢?像 LAMR - Linux、Apache、MySQL Ruby
我有以下代码。 var stackMapIn = []; var stackMapOut = []; var stackBack = []; stackMapOut.push("m1"); $scop
我遇到了导致我的堆栈无法恢复的情况,我别无选择,只能将其删除。使用完全相同的模板,我继续创建了另一个同名的堆栈。 The following resource(s) failed to create:
这是我第一次查看 Node 堆栈,自从我学习使用 Ruby on Rails 进行 Web 开发以来,我对一些基本的东西有点困惑。我了解 Rails 目录是什么样的。 demo/ ..../app .
本文实例讲述了C语言使用深度优先搜索算法解决迷宫问题。分享给大家供大家参考,具体如下: 深度优先搜索 伪代码 (Pseudocode)如下: ?
我正在按照指南 here ,它告诉我: The stack setup will download the compiler if necessary in an isolatedlocation (
同时 trying to debug a different question ,我安装了一个似乎与我安装的其他一些软件包冲突的软件包。 我跑了 $ stack install regex-pcre-
我花了几个小时创建了一个方法,该方法将从堆栈 s1 中获取 null 元素,并将它们放入 s2 中。然后该类应该打印堆栈。方法如下 import net.datastructures.ArraySta
我有一个类Floor,它有一个Stack block ,但我不知道如何初始化它。我曾尝试过这样的: public class Floor { private Stack stack;
我知道这个问题已经问过很多次了,但搜索一个小时后我仍然遇到问题。 我想使用一个 lifo 堆栈,它可以存储最大数量的元素。达到最大数量后,首先删除该元素并将其替换为新元素,这样在第一次弹出时我可以获取
我需要编写一个方法,压缩以执行以下操作; 目标compress方法是从栈s1中移除所有null元素。剩余(非空)元素应按其初始顺序保留在 s1 上。辅助堆栈 s2 应用作s1 中元素的临时存储。在该方
我正在尝试验证以下代码发生的顺序。 function square(n) { return n * n; } setTimeout(function(){ console.log("H
我需要一个字符数组,其中包含基于特定文件夹中文件数量的动态数量的字符数组。我能够通过初始化 char (*FullPathNames)[MAX_FILENAME_AND_PATHNAME_LENGTH
我正在编写一些日志逻辑并想要进行一些缩进。了解是否存在任何函数调用或某个函数是否已完成的最简单方法是查看堆栈/帧的当前地址。让我们假设堆栈颠倒增长。然后,如果 log() 调用中的堆栈地址小于前一次调
所以内存分段在x86-64中被放弃了,但是当我们使用汇编时,我们可以在代码中指定.code和.data段/段,并且还有堆栈指针寄存器。 还有堆栈段、数据段和代码段寄存器。 代码/数据/堆栈的划分是如何
void main() { int x = 5; // stack-allocated Console.WriteLine(x); } 我知道 x 是堆栈分配的。但是关于 x 的堆栈中
这是我关于 SO 的第一个问题。这可能是一个愚蠢的问题,但到目前为止我还没弄明白。 考虑下面的程序 Reader.java: public class Reader { public
java中有没有一种快速的方法来获取嵌套/递归级别? 我正在编写一个函数来创建组及其成员的列表。成员也可以是团体。我们最终可能会得到一组循环的组/成员。 我想在某个任意级别停止。 我知道我可以将变量保
考虑以下代码: struct A{...}; A a[100]; A* pa = new A[100]; delete[] pa; a/pa 元素的销毁顺序是由标准定义的还是实现定义的(对于第二种情况
我在下面有一些代码。此代码是一个基本的压入/弹出堆栈类,我将其创建为模板以允许某人压入/弹出堆栈。我有一个家庭作业,我现在要做的是创建一个具有多个值的堆栈。 所以我希望能够创建一个基本上可以发送三个整
我是一名优秀的程序员,十分优秀!