【问题标题】:Encode FIRST and FOLLOW sets into a recursive descent parser将 FIRST 和 FOLLOW 集编码为递归下降解析器
【发布时间】:2012-03-18 18:56:32
【问题描述】:

这是我之前问过How to encode FIRST & FOLLOW sets inside a compiler 的问题的后续,但这个问题更多的是关于我的程序设计。

我正在通过编写递归下降解析器来实现编译器的语法分析阶段。我需要能够利用 FIRST 和 FOLLOW 集,以便更有效地处理源程序语法中的错误。我已经为我的所有非终端计算了 FIRST 和 FOLLOW,但是我无法决定在我的程序中逻辑地将它们放在哪里以及这样做的最佳数据结构是什么。

注意:所有代码都是伪代码

选项 1) 使用映射,并将所有非终端按其名称映射到包含其 FIRST 和 FOLLOW 集的两个集合:

class ParseConstants
    Map firstAndFollowMap = #create a map .....
    firstAndFollowMap.put("<program>", FIRST_SET, FOLLOW_SET)
end

这似乎是一个可行的选择,但在我的解析器内部,我需要这样的丑陋代码来检索 FIRST 和 FOLLOW 并传递给错误函数:

#processes the <program> non-terminal
def program
    List list    = firstAndFollowMap.get("<program>")
    Set FIRST    = list.get(0)
    Set FOLLOW   = list.get(1)  

   error(current_symbol, FOLLOW)
end

选项 2)为每个非终端创建一个类并具有 FIRST 和 FOLLOW 属性:

class Program
    FIRST    = .....
    FOLLOW = .... 
end

这导致代码看起来更好:

#processes the <program> non-terminal
def program
   error(current_symbol, Program.FOLLOW)
end

这是我想到的两个选项,我很想听听任何其他关于如何编码这两个集合的建议,以及对我发布的两种方式的任何批评和补充都会有所帮助。 谢谢

我也在这里发布了这个问题:http://www.coderanch.com/t/570697/java/java/Encode-FIRST-FOLLOW-sets-recursive

【问题讨论】:

    标签: parsing data-structures compiler-construction recursive-descent


    【解决方案1】:

    您实际上并不需要 FIRST 和 FOLLOW 集。您需要计算这些以获取解析表。这是一个{&lt;non-terminal, token&gt; -&gt; &lt;action, rule&gt;} if LL(k) 的表(这意味着在堆栈中看到non-terminal,在输入中看到token,采用哪个action,如果适用,则适用哪个rule),或者{&lt;state, token&gt; -&gt; &lt;action, state&gt;} 的表 if (C|LA|)LR(k) (这意味着在堆栈中给定state,在输入中给定tokenaction 取哪个state

    拿到这张表后,就不再需要 FIRST 和 FOLLOWS 了。


    如果您正在编写语义分析器,则必须假设解析器工作正常。短语级错误处理(即处理解析错误)与语义分析完全正交。

    这意味着在解析错误的情况下,短语级错误处理程序 (PLEH) 将尝试修复错误。如果不能,解析停止。如果可以,语义分析器不应该知道是否有错误已被修复,或者根本没有任何错误!

    您可以查看my compiler library 的示例。


    关于短语级别的错误处理,您再次不需要 FIRST 和 FOLLOW。现在让我们谈谈 LL(k)(只是因为关于 LR(k) 我还没有考虑太多)。建立语法表后,您有很多条目,就像我说的那样:

    <non-terminal, token> -> <action, rule>
    

    现在,当您解析时,您将获取堆栈中的任何内容,如果它是终端,那么您必须将其与输入匹配。如果不匹配,短语级错误处理程序将启动:

    作用一:处理缺失的终端 - 只需在词法分析器中生成所需类型的假终端,然后让解析器重试。你也可以做其他事情(例如在输入中提前检查,如果你有你想要的标记,从词法分析器中删除一个标记)

    如果您从堆栈中得到的是非终端 (T),则必须查看您的词法分析器,获取 lookahead 并查看您的表格。如果条目&lt;T, lookahead&gt; 存在,那么您就可以开始了。按照操作并从堆栈中推入/弹出。但是,如果不存在这样的条目,则短语级错误处理程序再次启动:

    作用二:处理意外的终端 - 你可以做很多事情来通过这个。你做什么取决于Tlookahead 是什么以及你对语法的专业知识。

    你可以做的事情的例子是:

    • 失败!你什么都做不了
    • 忽略此终端。这意味着您将lookahead 推入堆栈(在再次推回T 之后)并让解析器继续。解析器将首先匹配lookahead,将其丢弃并继续。示例:如果您有这样的表达式:*1+2/0.5,您可以通过这种方式删除意外的*
    • lookahead 更改为可接受的值,将T 推回并重试。例如,像这样的表达式:5id = 10; 可能是非法的,因为您不接受以数字开头的 id。例如,您可以将其替换为 _5id 以继续

    【讨论】:

    • 没问题。实际上,我现在正在为我的编译器开发更多功能,所以它在我的头上
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-07
    • 1970-01-01
    • 2012-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多