Parsing 首先将集合和跟随集合编码到递归下降解析器中

Parsing 首先将集合和跟随集合编码到递归下降解析器中,parsing,data-structures,compiler-construction,recursive-descent,Parsing,Data Structures,Compiler Construction,Recursive Descent,这是我之前问过的一个问题的后续,但这一个问题更多的是关于我的程序设计 我通过编写递归下降解析器来实现编译器的语法分析阶段。我需要能够利用第一组和后续组,以便能够更有效地处理源程序语法中的错误。我已经计算了所有非终端的第一个和第二个,但是我很难决定在我的程序中逻辑上把它们放在哪里,最好的数据结构是什么 注意:所有代码都是伪代码 选项1)使用映射,并将所有非端子按其名称映射到包含其第一个和第二个集合的两个集合: class ParseConstants Map firstAndFollowM

这是我之前问过的一个问题的后续,但这一个问题更多的是关于我的程序设计

我通过编写递归下降解析器来实现编译器的语法分析阶段。我需要能够利用第一组和后续组,以便能够更有效地处理源程序语法中的错误。我已经计算了所有非终端的第一个和第二个,但是我很难决定在我的程序中逻辑上把它们放在哪里,最好的数据结构是什么

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

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

class ParseConstants
    Map firstAndFollowMap = #create a map .....
    firstAndFollowMap.put("<program>", FIRST_SET, FOLLOW_SET)
end
这将导致代码看起来更好一些:

#processes the <program> non-terminal
def program
   error(current_symbol, Program.FOLLOW)
end
#处理非终端
def程序
错误(当前_符号,程序跟随)
结束
这是我想到的两个选项,我很想听到关于这两个集合编码方法的任何其他建议,而且对我发布的两种方法的任何评论和补充都会很有帮助。 谢谢


我也在这里发布了这个问题:

你并不真的需要第一套和第二套。您需要计算这些数据才能得到解析表。例如,这是一个
{->表格


关于短语级错误处理,您也不需要先做后做。现在让我们谈谈LL(k)(只是因为关于LR(k),我还没有考虑太多)。在构建语法表之后,您有许多条目,如我所说:

<non-terminal, token> -> <action, rule>
->
现在,当您解析时,您获取堆栈上的任何内容,如果它是终端,则必须将其与输入匹配。如果不匹配,则短语级错误处理程序将生效:

角色一:处理丢失的终端-只需在lexer中生成所需类型的假终端,并让解析器重试。您还可以执行其他操作(例如,在输入中提前检查,如果您有所需的令牌,请从lexer中删除一个令牌)

如果您得到的是非终端(
T
)相反,你必须从堆栈中查看你的lexer,获取
前瞻
并查看你的表。如果条目
存在,那么你就可以开始了。按照操作并从堆栈中推到/pop。但是,如果不存在这样的条目,那么短语级错误处理程序将再次启动:

角色二:处理意外的终端-你可以做很多事情来通过这个测试。你做什么取决于
T
lookahead
是什么,以及你对语法的专业知识

您可以做的事情有:

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

没问题。事实上,我现在正在为我的编译器开发更多的功能,所以这是我的当务之急
#processes the <program> non-terminal
def program
   error(current_symbol, Program.FOLLOW)
end
<non-terminal, token> -> <action, rule>