所以我正在做一个解析器,我更喜欢灵活性而不是速度,我希望它易于编写语法,例如没有棘手的变通规则(解决冲突的假规则等,就像你在 yacc/bison 等中必须做的那样.)
有一个手动编码的 Lexer,带有一组固定的标记(例如 PLUS、DECIMAL、STRING_LIT、NAME 等),现在有三种类型的规则:
- TokenRule:匹配特定的令牌
- SequenceRule:匹配一个有序的规则列表
- GroupRule:匹配列表中的任何规则
例如,假设我们有 TokenRule 'varAccess',它匹配令牌 NAME(大致 /[A-Za-z][A-Za-z0-9_]*/)和 SequenceRule 'assignment',它匹配 [表达式,TokenRule(PLUS),表达式]。
表达式是与“assignment”或“varAccess”匹配的 GroupRule(我正在测试的实际规则集更完整,但对于示例来说就是这样)
但现在假设我要解析
var1 = var2
假设解析器以规则表达式开头(定义它们的顺序无关紧要 - 稍后将解决优先级)。假设 GroupRule 表达式将首先尝试“赋值”。然后由于“表达式”是“赋值”中要匹配的第一个规则,它会尝试再次解析表达式,依此类推,直到堆栈被填满并且计算机 - 正如预期的那样 - 只是在一个闪亮的段错误中放弃。
所以我所做的是 - SequenceRules 将自己作为“叶子”添加到他们的第一条规则中,并成为非根规则。根规则是解析器首先尝试的规则。当其中一个被应用并匹配时,它会尝试一个接一个地子应用它的每个叶子,直到一个匹配。然后它尝试匹配叶子的叶子,依此类推,直到不再匹配。
这样它就可以解析表达式
var1 = var2 = var3 = var4
恰到好处 =) 现在有趣的东西。这段代码:
var1 = (var2 + var3)
不会解析。发生的情况是,var1 被解析(varAccess),assign 被子应用,它寻找一个表达式,尝试'括号',开始,在'('之后寻找一个表达式,找到 var2,然后在'+ ' 因为它期待一个 ')'。
为什么它不匹配 'var2 + var3' ?(是的,在你问之前有一个“添加”SequenceRule)。因为“添加”不是根规则(以避免使用 parse-expression-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 的每个元素中指定是否应该对其进行叶子测试?告诉我你的想法(除了扔掉整个系统 - 无论如何这可能会在几个月内发生)
PS:拜托,拜托,请不要回答诸如“去阅读这本 400 页的书,否则您甚至不值得我们花时间”之类的问题,如果您觉得有必要 - 请克制自己并在 reddit 上大肆抨击。好的?提前致谢。