解析规则 - 如何让它们一起玩得很好

时间:2009-10-01 20:58:06

标签: parsing token rules

所以我正在做一个Parser,我喜欢灵活性而不是速度,我希望它能够很容易地编写语法,例如:没有棘手的解决方法规则(解决冲突的假规则等,就像你必须在yacc / bison等中做的那样)。

有一个带有固定令牌集的手工编码Lexer(例如PLUS,DECIMAL,STRING_LIT,NAME等),现在有三种类型的规则:

  • TokenRule:匹配特定标记
  • SequenceRule:匹配有序的规则列表
  • GroupRule:匹配列表中的任何规则

例如,假设我们有TokenRule'varAccess',它匹配令牌NAME(大致为/ [A-Za-z] [A-Za-z0-9 _] * /)和SequenceRule'赋值',匹配[表达式,TokenRule(PLUS),表达式]。

表达式是一个与“赋值”或“varAccess”匹配的GroupRule(我正在测试的实际规则集有点完整,但是这样做也是如此)

但现在让我说要解析

var1 = var2

让我们说Parser从规则表达开始(它们的定义顺序无关紧要 - 优先级将在以后解决)。让我们说GroupRule表达式将首先尝试“赋值”。然后,因为'expression'是第一个在'赋值'中匹配的规则,它将尝试再次解析一个表达式,依此类推,直到堆栈被填满,而计算机 - 正如预期的那样 - 只是放弃了一个闪闪发光的段错误。

所以我所做的是 - SequenceRules将自己添加为第一个规则的'leafs',并成为非roôt规则。根规则是解析器首先尝试的规则。当其中一个被应用并匹配时,它会尝试逐个子地应用它的每个叶子,直到一个匹配。然后它尝试匹配叶子的叶子,依此类推,直到不再匹配为止。

这样它就可以解析像

这样的表达式
var1 = var2 = var3 = var4

恰到好处=)现在有趣的东西。这段代码:

var1 = (var2 + var3)

无法解析。会发生什么,var1 get parsed(varAccess),assign是子应用,它查找表达式,尝试'括号',开始,在'(',找到var2之后查找表达式,然后在'+上找到chokes '因为它期待'''。

为什么它不匹配'var2 + var3'? (是的,在你问之前,有一个'添加'SequenceRule)。因为'add'不是根规则(为了避免使用parse-expresssion-beginning-with-expression-etc.进行无限递归),并且叶子不在SequenceRules中测试,否则它会解析像

这样的东西
reader readLine() println()

作为

reader (readLine() println())

(例如'1 = 3'是add所期望的表达式,varAccess a的叶子)

而我们希望它是左关联的,例如解析为

(reader readLine()) println()

所以无论如何,现在我们遇到了这个问题,我们应该能够解析SequenceRules中的表达式,如'1 + 2'。该怎么办?添加一个特殊情况,当SequenceRules以TokenRule开头时,它包含的GroupRules是否会测试叶子?在那个特定的例子之外,这甚至是否有意义?或者是否应该能够在SequenceRule的每个元素中指定是否应该测试叶子?告诉我你的想法(除了扔掉整个系统 - 这可能会在几个月内发生)

P.S:请你好,请不要回答“去读这本400页的书,或者你甚至不配得到我们的时间”这样的话。如果你觉得有必要 - 只是克制自己,继续抨击reddit。好的?提前谢谢。

2 个答案:

答案 0 :(得分:2)

LL(k)解析器(自顶向下递归,无论是自动还是手工编写)都需要重构语法以避免左递归,并且通常需要特殊的超前规范(例如ANTLR)才能处理k-token前瞻。由于语法很复杂,你可以通过实验来发现k,这正是你想要避免的。

YACC / LALR(1)语法避免了左递归的问题,这是向前迈出的一大步。坏消息是没有真正的编程语言(除了Wirth的原始PASCAL)是LALR(1)。因此,您可以破解您的语法,将其从LR(k)更改为LALR(1),再次强迫您进行暴露奇怪案例的实验,并破解语法减少逻辑以尝试在解析器时处理K-lookaheads生成器(YACC,BISON,......你的名字)生成1-lookahead解析器。

GLR解析器(http://en.wikipedia.org/wiki/GLR_parser)允许您避免几乎所有这些废话。如果你可以编写一个无上下文解析器,在大多数实际情况下,GLR解析器将不经过进一步的努力就解析它。当你尝试编写任意语法时,这是一个巨大的缓解。一个非常好的GLR解析器将直接生成一个树。

BISON已经过增强,可以进行GLR解析。您仍然需要编写复杂的逻辑来生成所需的AST,并且您必须担心如何处理失败的解析器以及清理/删除相应的(失败的)树。 DMS Software Reengineering Tookit为任何上下文无关语法提供标准GLR解析器,并自动构建AST,而无需您做任何额外的工作;模糊树是自动构建的,可以通过后解析语义分析来清理。我们用这个来定义包括C语言在内的30多种语言语法,包括C ++(人们普遍认为它很难解析[并且几乎不可能用YACC解析]但是真正的GLR是直截了当的);请参阅基于DMS的C+++ front end parser and AST builder

结论:如果您想以直接的方式编写语法规则,并获得解析器来处理它们,请使用GLR解析技术。 Bison 几乎有效。 DM 真的有效。

答案 1 :(得分:0)

我最喜欢的解析技术是从PEG语法规范创建递归下降(RD)解析器。它们通常非常快速,简单且灵活。一个很好的优点是你不必担心单独的标记化传递,并且担心将语法压缩成某种LALR形式是不存在的。一些PEG文库列在[这里] [1]。

很抱歉,我知道这会丢掉系统,但是你几乎没有出现问题并切换到PEG RD解析器,现在就消除了你的头痛。