PEG解析器可视化

上一次,我们获得了一个简单的PEG解析器生成器。 现在,我将展示在解析程序时生成的解析器实际执行的操作。 我深入研究了ASCII艺术的复古世界,特别是一个名为“ curses”的库,该库可在Linux和Mac的标准Python发行版中找到,以及Windows的附加组件。




<在文章的结尾,在扰流板下面给出了一个gif。 在我看来,比静态图片更容易理解>


让我们看一下可视化。 屏幕按照连字符形式分为三部分(按ASCII惯例):


  • 上部显示了解析器的调用堆栈,您还记得,它是具有无限返回的递归下降分析器。 下面我将更详细地解释。
  • 中间的单行部分显示了令牌缓冲区的内容,其中光标指向下一步将要分析的令牌。
  • 下面我们可视化packrat解析器算法使用的缓存。 记录其元素有点类似于解析器堆栈的某些元素(带有结果)。

为了理解此可视化,您需要了解的主要内容是上部和下部中的线的缩进对应于令牌缓冲区。


  • 前两行(以statementassignment开头)显示了对当前正在执行分析的相应解析器方法的调用。 当令牌生成器指针位于第一个令牌( 'aap' )之前时,将调用它们。
  • 接下来的两行(以exprterm开头)与'cat'令牌的开头对齐,在该令牌中调用了相应的解析器方法。
  • 堆栈渲染的第五行也是最后一行显示了对expect('/')的调用,该调用返回None 。 他自愿担任'+'币职位。

高速缓存中项目的缩进还对应于令牌缓冲区中的位置。 例如,在高速缓存的底部有否定条目,它们是在令牌缓冲区的开头查找'if'令牌和if_statement规则时获得的。 我们还找到了与其他输入位置相对应的令牌'='NAME (尤其是'cat' )成功的缓存条目。


在分析器堆栈块和缓存块中,返回的调用都显示为function(args) -> result 。 有时,解析器堆栈上会显示几个返回的方法-我这样做是为了减少频繁更改内容引起的文本抖动。


(谈到抖动,当将调用添加到堆栈中时,分析器堆栈的顶行向上移动,而从堆栈中删除时则下降至少对我来说,看屏幕的这一部分不是问题,也许我们大脑的很大一部分都专用于跟踪运动对象。


高速缓存呈现为LRU,常用项目位于顶部; 屏幕上会出现更多稀有商品。 (我在之前的文章中展示的原型packrat解析器没有使用LRU,但是优化内存消耗可能是一个好主意。)


让我们看一下渲染解析栈时的一些细节。 前四个条目与仍在处理的解析器方法相对应,每行是语法中的一行。 突出显示的项目是产生下一个挑战的项目。


即 从屏幕截图来看,我们可以使用第二种statement ,即assignment 。 在此规则中,我们处于第三点,即expr 。 在expr规则中,我们仅位于第一个替代方案的第一段( term '+' expr ); 在term规则中,我们处于最后一个替代方案( atom )中。


在下面,我们看到导致第二种选择( atom '/' term )失败的结果: expect('/') -> None与令牌'+'缩进。 在下一步中,我们将看到它如何进入缓存。


但可以肯定的是,您希望自己看到整个动画!


我记录了对示例程序的完整分析


您也可以使用现成的代码 ,但是请记住,这不是运动。


当您查看记录的GIF时 ,有时不显示下一个标记似乎有些奇怪(例如,在检测到'aap'标记之前,堆栈一开始就增长了几条记录)。 这正是分析器所看到的:令牌缓冲区被延迟填充。 在解析器通过调用expect()函数请求令牌之前,不会对令牌进行扫描。 令牌一旦进入缓冲区,它将立即保留在该缓冲区中,即使分析器将令牌序列后退。 反向跟踪显示在令牌缓冲区中,并且光标向左跳转; 记录中有很多这样的情况。 当分析器未进行其他递归调用时,您还可以观察缓存填充。 (我必须突出显示发生这种情况的时间,但是我没有足够的时间。)


下周,我将开发一个解析器,也许会增加我对递归语法左规则的看法。 (他们很棒!)


致谢:为了记录,我使用ttygif来自Ilya Choli的ttygif和来自Matthew Jording的ttyrec。


本文和引用代码的许可: CC BY-NC-SA 4.0

Source: https://habr.com/ru/post/zh-CN471866/


All Articles