📜  使用 LR 解析器解析歧义文法(1)

📅  最后修改于: 2023-12-03 15:06:48.490000             🧑  作者: Mango

使用 LR 解析器解析歧义文法

在编译原理中,语法分析器的任务是将输入的符号串按照文法规则进行解析,并生成相应的语法树。但是,有些文法存在二义性,即一个符号串可以被解析成不同的语法树,这时需要使用解析器来解决歧义。

LR 解析器是一种自下而上的语法分析器,可以处理大部分上下文无关文法。它使用状态机和栈来进行解析,具有较高的效率和可扩展性。

下面介绍使用 LR 解析器解析歧义文法的方法。

第一步:定义文法

首先需要定义待解析文法。假设我们有以下文法:

S → E
E → E + E
  | E - E
  | E * E
  | E / E
  | ( E )
  | id

其中,S 为文法的开始符号,E 为非终结符号,+、-、* 和 / 为运算符号,( 和 ) 为左右括号,id 为标识符。

第二步:构建 LR 分析表

使用 LR 解析器需要构建 LR 分析表,其中包括状态转移表和动作表。状态转移表记录解析过程中状态的转移,动作表则记录需要进行的动作,例如移进、规约等。

构建 LR 分析表需要以下步骤:

  1. 生成所有 LR(0) 项集,并将其合并为一个项目集规范族。
  2. 对每个项目进行闭包操作,得到带有后继符号的闭包项集。
  3. 对每个项集进行状态转移操作,并在转移后得到新的闭包项集。
  4. 对于每个产生式 $A \to \beta$ 和终结符号 $a$,确定在项集规范族中的所有项中,哪些是“移进 $a$”的项,标记状态转移表中对应的动作为“移进”。
  5. 对于每个产生式 $A \to \beta$,确定在项集规范族中的所有项中,哪些是“规约 $A \to \beta$”的项,标记动作表中对应的动作为“规约”。
  6. 对于每个项集中同时存在“移进”和“规约”的动作,则标记冲突,这就是文法中存在的歧义。

通过上述步骤,我们得到 LR 分析表如下:

| 状态 | + | - | * | / | ( | ) | id | $ | S | E | | :--: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | | 0 | s5 | s6 | s7 | s8 | s9 | | s4 | | 1 | 2 | | 1 | r1 | r1 | r1 | r1 | r1 | r1 | | acc | | | | 2 | s5 | s6 | s7 | s8 | s9 | | s4 | | | 10 | | 3 | r4 | r4 | s11 | s12 | r4 | r4 | | | | | | 4 | s5 | s6 | s7 | s8 | s9 | | s4 | | | 13 | | 5 | r6 | r6 | r6 | r6 | r6 | r6 | | | | | | 6 | r7 | r7 | r7 | r7 | r7 | r7 | | | | | | 7 | r8 | r8 | s11 | s12 | r8 | r8 | | | | | | 8 | r9 | r9 | s11 | s12 | r9 | r9 | | | | | | 9 | s5 | s6 | s7 | s8 | s9 | | s4 | | | 14 | | 10 | r0 | r0 | r0 | r0 | r0 | r0 | | | | | | 11 | s5 | s6 | s7 | s8 | s9 | | s4 | | | 15 | | 12 | s5 | s6 | s7 | s8 | s9 | | s4 | | | 16 | | 13 | r2 | r2 | s7 | s8 | r2 | r2 | | | | | | 14 | r3 | r3 | s7 | s8 | r3 | r3 | | | | | | 15 | r5 | r5 | r5 | r5 | r5 | r5 | | | | | | 16 | r10 | r10 | r10 | r10 | r10 | r10 | | | | |

第三步:解析输入符号串

有了 LR 分析表,可以使用该表来解析输入的符号串。假设输入的符号串为 id + id * id

  1. 将起始状态压入栈中。
  2. 读入第一个符号 id,根据状态转移表和动作表进行处理。在状态 0 中存在“移进”的动作,因此将其压入栈中,并根据该动作进入新的状态 5。
  3. 读入第二个符号 +,在状态 5 中不存在“移进”的动作,产生冲突。因此需要进行错误处理,并退出分析。
  4. 解析结束。

也可以使用代码来实现上述过程。

# 定义文法
rules = [
    ('S', ['E']),
    ('E', ['E', '+', 'E']),
    ('E', ['E', '-', 'E']),
    ('E', ['E', '*', 'E']),
    ('E', ['E', '/', 'E']),
    ('E', ['(', 'E', ')']),
    ('E', ['id'])
]

# 定义 LR 分析表
action = {0: {'+': 's5', '-': 's6', '*': 's7', '/': 's8', '(': 's9', 'id': 's4'},
          1: {'$': 'acc'},
          2: {'+': 's5', '-': 's6', '*': 's7', '/': 's8', '(': 's9', 'id': 's4'},
          3: {'+': 'r4', '-': 'r4', '*': 's11', '/': 's12', ')': 'r4', '$': 'r4'},
          4: {'+': 's5', '-': 's6', '*': 's7', '/': 's8', '(': 's9', 'id': 's4'},
          5: {'+': 'r6', '-': 'r6', '*': 'r6', '/': 'r6', ')': 'r6', '$': 'r6'},
          6: {'+': 'r7', '-': 'r7', '*': 'r7', '/': 'r7', ')': 'r7', '$': 'r7'},
          7: {'+': 'r8', '-': 'r8', '*': 's11', '/': 's12', ')': 'r8', '$': 'r8'},
          8: {'+': 'r9', '-': 'r9', '*': 's11', '/': 's12', ')': 'r9', '$': 'r9'},
          9: {'+': 's5', '-': 's6', '*': 's7', '/': 's8', '(': 's9', 'id': 's4'},
          10: {'$': 'r0'},
          11: {'+': 's5', '-': 's6', '*': 's7', '/': 's8', '(': 's9', 'id': 's4'},
          12: {'+': 's5', '-': 's6', '*': 's7', '/': 's8', '(': 's9', 'id': 's4'},
          13: {'+': 'r2', '-': 'r2', '*': 's7', '/': 's8', ')': 'r2', '$': 'r2'},
          14: {'+': 'r3', '-': 'r3', '*': 's7', '/': 's8', ')': 'r3', '$': 'r3'},
          15: {'+': 'r5', '-': 'r5', '*': 'r5', '/': 'r5', ')': 'r5', '$': 'r5'},
          16: {'+': 'r10', '-': 'r10', '*': 'r10', '/': 'r10', ')': 'r10', '$': 'r10'}}
goto = {0: {'S': 1, 'E': 2},
        2: {'E': 10},
        3: {'E': 13},
        4: {'E': 14},
        9: {'E': 15},
        11: {'E': 16}}

# 定义分析函数
def parse(tokens):
    stack = [0]  # 初始状态
    i = 0  # 指向当前要处理的符号
    while True:
        state = stack[-1]
        token = tokens[i]
        if token not in action[state]:
            print('Error')
            break
        action_type, action_value = action[state][token][0], int(action[state][token][1:])
        if action_type == 's':
            i += 1
            stack.append(action_value)
        elif action_type == 'r':
            left, right = rules[action_value]
            for j in range(len(right)):
                stack.pop()
            next_state = stack[-1]
            stack.append(goto[next_state][left])
        elif action_type == 'a':
            print('Accept')
            break

# 解析输入符号串
tokens = ['id', '+', 'id', '*', 'id', '$']
parse(tokens)

解析结果为:

Error
总结

使用 LR 解析器解析歧义文法的步骤包括定义文法、构建 LR 分析表和解析输入符号串。其中,构建 LR 分析表是较为复杂的步骤,需要对所有 LR(0) 项集进行闭包和状态转移操作,并将结果填入动作表和状态转移表中。通过构建好的 LR 分析表,可以快速解析输入符号串,并生成相应的语法树。