Hydrogen-python/syntax/syntax.py
2025-06-11 19:51:42 +08:00

768 lines
30 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
语法分析
"""
from syntax.rule import Sign, Production, terminal_sign_type, non_terminal_sign_type, productions, grammar_start
from error import SyntaxRuleError, SyntaxError, SemanticRuleError
from semantic.rule import SemanticRule, SemanticError, SemanticRuleFactory
class PredictingAnalysisTable:
"""
预测分析表
"""
def __init__(self):
"""
构造
"""
# 错误
self.__error = None
# 预测分析表
self.__table = list()
# 所有的非终结符
self.__non_terminal_signs = list()
# 所有的终结符
self.__terminal_signs = list()
# 载入所有的符号
for i in non_terminal_sign_type:
self.__non_terminal_signs.append(Sign(i))
for i in terminal_sign_type:
self.__terminal_signs.append(Sign(i))
# 根据非终结符和终结符的数量为预测分析表分配空间,并且为每一个格子预先填上 None
for i in non_terminal_sign_type:
self.__table.append(list())
for i in range(0, len(non_terminal_sign_type)):
for j in terminal_sign_type:
self.__table[i].append(None)
# 为每一个非终结符建立 first 集和 follow 集
self.__firsts = list()
self.__follows = list()
# 为每一个非终结符的 first 集和 follow 集分配空间
for i in non_terminal_sign_type:
self.__firsts.append(list())
self.__follows.append(list())
def compile(self):
"""
编译预测分析表
"""
# 对每一个文法元素求其 first 集
self.__calculate_firsts()
# 对每一个文法元素求其 follow 集
self.__calculate_follows()
# 根据 first 集和 follow 集生成预测分析表
success = self.__generate_table()
return success
def get_production(self, non_terminal_sign, terminal_sign):
"""
从预测分析表中获取产生式
:param non_terminal_sign: 非终结符
:param terminal_sign: 终结符
:return: 产生式
"""
x = self.__get_non_terminal_sign_index(non_terminal_sign)
y = self.__get_terminal_sign_index(terminal_sign)
return self.__table[x][y]
@classmethod
def __set_add(cls, container, sign):
"""
将 sign 添加到 container 中并返回 True如果其中已经有该元素了则返回 False
:param container: 要添加到的集合
:param sign: 符号
:return: 添加是否成功
"""
exist = False
for elem in container:
if elem.type == sign.type:
exist = True
if not exist:
container.append(sign)
return not exist
def __get_terminal_sign_index(self, terminal_sign):
"""
获取终结符的索引
:param terminal_sign: 终结符
:return: 索引(寻找失败返回 -1)
"""
for i in range(0, len(self.__terminal_signs)):
if terminal_sign.type == self.__terminal_signs[i].type:
return i
return -1
def __get_non_terminal_sign_index(self, non_terminal_sign):
"""
获取非终结符的索引
:param non_terminal_sign: 非终结符
:return: 索引(寻找失败返回 -1)
"""
for i in range(0, len(self.__non_terminal_signs)):
if non_terminal_sign.type == self.__non_terminal_signs[i].type:
return i
return -1
def __get_non_terminal_sign_first(self, non_terminal_sign):
"""
获取目标非终结符的 first 集的引用
:param non_terminal_sign: 目标非终结符
:return: 其 first 集的引用
"""
return self.__firsts[self.__get_non_terminal_sign_index(non_terminal_sign)]
def __get_non_terminal_sign_first_no_empty(self, non_terminal_sign):
"""
获取目标非终结符的 first 集的非空拷贝
:param non_terminal_sign: 目标非终结符
:return: 其 first 集的非空拷贝
"""
result = list()
for i in self.__get_non_terminal_sign_first(non_terminal_sign):
if not i.is_empty_sign():
result.append(i)
return result
def __is_empty_in_non_terminal_sign_first(self, non_terminal_sign):
"""
目标非终结符的 first 集中是否有空字
:param non_terminal_sign: 目标非终结符
:return: True/False
"""
for i in self.__get_non_terminal_sign_first(non_terminal_sign):
if i.is_empty_sign():
return True
return False
def __get_non_terminal_sign_follow(self, non_terminal_sign):
"""
获取非终结符的 follow 集
:param non_terminal_sign: 非终结符
:return: 其 follow 集
"""
return self.__follows[self.__get_non_terminal_sign_index(non_terminal_sign)]
def __calculate_firsts(self):
"""
求所有的 first 集
"""
# 立一个 flag用来标志 firsts 集是否增大
flag = True
# 开始循环
while flag:
flag = False
# 在每一次循环之中遍历所有产生式
for production in productions:
# 如果产生式右边为空
if len(production.right) == 0:
# 将空字加入其 first 集
if self.__set_add(self.__get_non_terminal_sign_first(production.left), Sign('empty')):
flag = True
# 如果产生式右边不为空
else:
# 如果是以终结符开头,将终结符添加到其 first 集
if production.right[0].is_terminal_sign():
if self.__set_add(self.__get_non_terminal_sign_first(production.left), production.right[0]):
flag = True
# 如果是以非终结符开头
elif production.right[0].is_non_terminal_sign():
# (1) 将开头非终结符的 first 集中的所有非空元素添加到产生式左边非终结符的 first 集中
bigger = False
for i in self.__get_non_terminal_sign_first_no_empty(production.right[0]):
if self.__set_add(self.__get_non_terminal_sign_first(production.left), i):
bigger = True
if bigger:
flag = True
# (2) 从第一个非终结符开始循环,如果其 first 集中包含空字,那么将它下一个符号的 first
# 集添加到产生式左边非终结符的 first 集中去
for i in range(0, len(production.right)):
if production.right[i].is_non_terminal_sign():
# 如果包含空字
if self.__is_empty_in_non_terminal_sign_first(production.right[i]):
# 如果它是最后一个,将空字填入
if i == len(production.right) - 1:
if self.__set_add(self.__get_non_terminal_sign_first(production.left),
Sign('empty')):
flag = True
# 如果不是最后一个
else:
# 如果它之后是终结符
if production.right[i + 1].is_terminal_sign():
if self.__set_add(self.__get_non_terminal_sign_first(production.left),
production.right[i + 1]):
flag = True
# 如果它之后是非终结符
elif production.right[i + 1].is_non_terminal_sign():
bigger = False
for j in self.__get_non_terminal_sign_first_no_empty(
production.right[i + 1]):
if self.__set_add(
self.__get_non_terminal_sign_first(production.left), j):
bigger = True
if bigger:
flag = True
else:
self.__error = SyntaxRuleError('终结符或非终结符类型错误')
return False
# 如果不包含空字
else:
break
else:
break
# 否则报错
else:
self.__error = SyntaxRuleError('终结符或非终结符类型错误')
return False
def __calculate_follows(self):
"""
求所有的 follow 集
"""
first = list()
flag = True
while flag:
flag = False
# 遍历所有产生式
for production in productions:
# 如果产生式左边是开始符号
if production.left.type == grammar_start.type:
if self.__set_add(self.__get_non_terminal_sign_follow(production.left), Sign('pound')):
flag = True
# 遍历产生式右边
for i in range(0, len(production.right)):
# 如果是非终结符
if production.right[i].is_non_terminal_sign():
# 如果它是产生式最后一个符号
if i == len(production.right) - 1:
# 将产生式左边非终结符的 follow 集添加到这个符号的 follow 集中
bigger = False
for j in self.__get_non_terminal_sign_follow(production.left):
if self.__set_add(self.__get_non_terminal_sign_follow(production.right[i]), j):
bigger = True
if bigger:
flag = True
# 否则观察其之后的元素
else:
# 求他之后所有符号集合的 first 集
first.clear()
first += self.__calculate_set_first(production.right[i + 1:])
# (1) 将 first 中所有非空元素填入 follow
empty_find = False
for f in first:
if not f.is_empty_sign():
self.__set_add(self.__get_non_terminal_sign_follow(production.right[i]), f)
else:
empty_find = True
# (2) 如果 first 中含有空
if empty_find:
# 将产生式左边非终结符的 follow 集添加到这个符号的 follow 集中
bigger = False
for j in self.__get_non_terminal_sign_follow(production.left):
if self.__set_add(self.__get_non_terminal_sign_follow(production.right[i]), j):
bigger = True
if bigger:
flag = True
# # 如果他后面是一个终结符
# if production.right[i + 1].is_terminal_sign():
# if self.__set_add(self.__get_non_terminal_sign_follow(production.right[i]),
# production.right[i + 1]):
# flag = True
# # 如果他后面是一个非终结符
# elif production.right[i + 1].is_non_terminal_sign():
# # (1) 将后面非终结符的 first 集中的所有非空元素填入
# bigger = False
# for j in self.__get_non_terminal_sign_first_no_empty(production.right[i + 1]):
# if self.__set_add(self.__get_non_terminal_sign_follow(production.right[i]), j):
# bigger = True
# if bigger:
# flag = True
#
# # (2) 如果后面所有的非终结符 first 集都包含空
# # 那么,则将产生式左边的 follow 集添加到该非终结符的 follow 集中去
# all_empty_in_first = True
# for j in range(i + 1, len(production.right)):
# if not self.__is_empty_in_non_terminal_sign_first(production.right[j]):
# all_empty_in_first = False
# break
# if all_empty_in_first:
# bigger = False
# for j in self.__get_non_terminal_sign_follow(production.left):
# if self.__set_add(self.__get_non_terminal_sign_follow(production.right[i]), j):
# bigger = True
# if bigger:
# flag = True
# # 否则报错
# else:
# self.__error = SyntaxRuleError('终结符或非终结符类型错误')
# return False
# 如果是终结符
elif production.right[i].is_terminal_sign():
continue
# 否则报错
else:
self.__error = SyntaxRuleError('终结符或非终结符类型错误')
return False
def __calculate_set_first(self, container):
"""
计算一系列符号的 first 集
:param container: 符号集合
:return: first 集
"""
first = list()
# 开始求 first 集
# 如果集合为空
first = list()
# 开始求 first 集
# 如果产生式右边为空
if len(container) == 0:
# 将空字加入其 first 集
self.__set_add(first, Sign('empty'))
# 如果产生式右边补位空
else:
# 如果是以终结符开头,将终结符添加到 first 集
if container[0].is_terminal_sign():
self.__set_add(first, container[0])
# 如果是以非终结符开头
elif container[0].is_non_terminal_sign():
# (1) 将开头非终结符的 first 集中的所有非空元素添加到 first 中
for i in self.__get_non_terminal_sign_first_no_empty(container[0]):
self.__set_add(first, i)
# (2) 从第一个非终结符开始循环,如果其 first 集中包含空字,那么将它的下一个符号的 first
# 集添加到 first 中
for i in range(0, len(container)):
if container[i].is_non_terminal_sign():
# 如果包含空字
if self.__is_empty_in_non_terminal_sign_first(container[i]):
# 如果它是最后一个,将空字填入
if i == len(container) - 1:
self.__set_add(first, Sign('empty'))
# 如果不是最后一个
else:
# 如果它之后是终结符
if container[i + 1].is_terminal_sign():
self.__set_add(first, container[i + 1])
# 如果它之后是非终结符
elif container[i + 1].is_non_terminal_sign():
for j in self.__get_non_terminal_sign_first_no_empty(container[i + 1]):
self.__set_add(first, j)
# 否则报错
else:
self.__error = SyntaxRuleError('终结符或非终结符类型错误')
return False
# 如果不含空字
else:
break
else:
break
# 否则报错
else:
self.__error = SyntaxRuleError('终结符或非终结符类型错误')
return False
return first
def __insert_to_table(self, production, terminal):
"""
将产生式插入预测分析表对应位置
:param production: 产生式
:param terminal: 终结符
:return: 是否插入成功
"""
# 先判断应该插入到的位置
x = self.__get_non_terminal_sign_index(production.left)
y = self.__get_terminal_sign_index(terminal)
# 如果那个位置已经有产生式了
if self.__table[x][y]:
# 判断这个产生式是不是与要插入的产生式一样
same_left = production.left.type == self.__table[x][y].left.type
if same_left:
same_right = True
if len(production.right) != len(self.__table[x][y].right):
self.__error = SyntaxRuleError("文法非LL(1)" + production.str)
return False
else:
for i in range(0, len(production.right)):
if production.right[i].type != self.__table[x][y].right[i].type:
same_right = False
if same_right:
# 执行插入
del self.__table[x][y]
self.__table[x].insert(y, production)
return True
else:
self.__error = SyntaxRuleError("文法非LL(1)" + production.str)
return False
else:
self.__error = SyntaxRuleError("文法非LL(1)" + production.str)
return False
# 如果那个位置为空,说明可以填入
else:
# 执行插入
del self.__table[x][y]
self.__table[x].insert(y, production)
return True
@classmethod
def __set_have_repeat(cls, set1, set2):
"""
判断两个集合是否有交集
:param set1: 集合1
:param set2: 集合2
:return: 是否有交集
"""
for i in set1:
for j in set2:
if i.type == j.type:
return True
return False
def __grammar_rule_debug(self):
"""
调试使用,求一个非终结符对应的所有产生式右边的 first 集中是否有相交元素
"""
# 一个非终结符对应的所有产生式
his_productions = list()
# 那些产生式对应的 first 集
firsts = list()
# 错误
errors = list()
# 对于所有的非终结符
for non_terminal_sign in self.__non_terminal_signs:
# 寻找他对应的所有产生式
his_productions.clear()
firsts.clear()
for production in productions:
if non_terminal_sign.type == production.left.type:
his_productions.append(production)
# 对于那些产生式,分别求 first 集
for production in his_productions:
firsts.append(self.__calculate_set_first(production.right))
# 是否有交集
have_repeat = False
# 查看这些产生式的 first 集两两之间是否有交集
for i in range(0, len(his_productions) - 1):
for j in range(i + 1, len(his_productions)):
if self.__set_have_repeat(firsts[i], firsts[j]):
have_repeat = True
break
# 如果有交集
if have_repeat:
errors.append('产生式 First 集重叠 ' + '非终结符: ' + non_terminal_sign.type)
# 如果非终结符的 First 集中包含空字
if self.__is_empty_in_non_terminal_sign_first(non_terminal_sign):
# 如果他的 First 集和 Follow 集有交集
if self.__set_have_repeat(self.__get_non_terminal_sign_first(non_terminal_sign),
self.__get_non_terminal_sign_follow(non_terminal_sign)):
errors.append('产生式 First 集和 Follow 集重叠 ' + '非终结符: ' + non_terminal_sign.type)
return
def __generate_table(self):
"""
根据 first 集和 follow 集生成预测分析表
:return: 是否生成成功
"""
# 调试
self.__grammar_rule_debug()
# 对每一条产生式应用规则
for production in productions:
# 先求出该产生式右边部分的 first 集
first = self.__calculate_set_first(production.right)
# 对每一个 first 集中的每一个终结符执行操作
empty_find = False
for i in list(first):
if i.type == 'empty':
empty_find = True
else:
if not self.__insert_to_table(production, i):
return False
# 如果其 first 集中有空字,则对 follow 集中的每一个终结符执行操作
if empty_find:
for i in self.__get_non_terminal_sign_follow(production.left):
if not self.__insert_to_table(production, i):
return False
return True
class Node:
"""
树节点
"""
def __init__(self, data):
"""
树节点
:param data: 节点数据
"""
self.data = data
self.str = data.type
self.children = list()
self.parent = None
# 属性
self.lexical = None
self.array = None
self.code = list()
self.type = None
self.id = None
self.length = None
self.fun = None
self.num = None
self.names = list()
self.bool = None
self.op = None
self.add = None
self.mul = None
def get_pre_brother(self, index):
"""
获取它前 index 位的兄弟
:param index: ..
:return: 兄弟
"""
self_index = 0
for i in range(0, len(self.parent.children)):
if self.parent.children[i] is self:
self_index = i
return self.parent.children[self_index - index]
class Tree:
"""
"""
def __init__(self, root):
"""
构造
:param root: 树的根节点
"""
self.root = root
class Stack:
"""
"""
def __init__(self):
"""
构造
"""
self.__container = list()
def push(self, elem):
"""
入栈
:param elem: 入栈元素
"""
self.__container.append(elem)
def pop(self):
"""
将栈顶元素出栈
:return: 栈顶元素
"""
top = self.top()
self.__container.pop()
return top
def top(self):
"""
获取栈顶元素
:return: 栈顶元素
"""
return self.__container[-1]
def empty(self):
"""
栈是否为空
:return: 栈是否为空
"""
return len(self.__container) == 0
class Syntax:
"""
语法分析器
"""
def __init__(self):
"""
构造
"""
# 语法树的构建
self.__grammar_tree = None
# 准备存放错误
self.__error = list()
# 预测分析表的构建
self.__pa_table = PredictingAnalysisTable()
# 编译预测分析表
if self.__pa_table.compile():
self.__error.append(SyntaxRuleError('预测分析表编译失败'))
# 准备存放词法分析的结果
self.__source = list()
# 将词法分析产生的 token 转换成的终结符
self.__terminals = list()
def put_source(self, source):
"""
装填词法分析结果
:param source: 词法分析结果
"""
self.__source.clear()
self.__terminals.clear()
# 装填词法分析结果
for s in source:
self.__source.append(s)
# 将 tokens 转换成终结符
for s in self.__source:
self.__terminals.append(Sign(s.type, s.str, s.line))
# 在所有 tokens 的最后填入一个 #
self.__terminals.append(Sign('pound'))
def get_result(self):
"""
获取语法树
:return: 语法树
"""
return self.__grammar_tree
def get_error(self):
"""
获取错误
:return: 错误
"""
return self.__error
def execute(self):
"""
执行操作
:return: 语法分析是否成功
"""
# 新建栈
stack = Stack()
# 清空错误
self.__error = None
# 新建临时语法树
grammar_tree = Tree(Node(Sign(grammar_start.type)))
# 将 # 入栈
stack.push(Node(Sign('pound')))
# 将语法树根节点入栈
stack.push(grammar_tree.root)
# 拷贝转换之后的终结符到输入串
inputs = list()
for sign in self.__terminals:
inputs.append(sign)
# 设置当前输入符号索引
input_index = 0
# 立下 flag
flag = True
while flag:
# 如果栈顶是语义动作
if isinstance(stack.top(), SemanticRule):
stack.top().execute()
if len(stack.top().errors) > 0:
self.__error = stack.top().errors[-1]
break
else:
stack.pop()
# 如果栈顶是符号
else:
# 如果 top 是非终结符
if stack.top().data.is_non_terminal_sign():
# 查看分析表
production = self.__pa_table.get_production(stack.top().data, inputs[input_index])
# 如果分析表对应位置存有产生式
if production:
# 判断语义规则是否合法
if len(production.right) != len(production.semantic_children):
self.__error = SemanticRuleError('语义规则数量与产生式右边数量不一致 ' + production.str)
break
# 执行 start 语义
semantic_start = SemanticRuleFactory.get_instance(production.semantic_start, stack.top())
if semantic_start:
semantic_start.execute()
if len(semantic_start.errors) > 0:
self.__error = semantic_start.errors[-1]
break
# 将语法树按照产生式进行生长
for i in range(0, len(production.right)):
stack.top().children.append(Node(Sign(production.right[i].type)))
stack.top().children[i].parent = stack.top()
# 将 top 出栈
top = stack.pop()
# 将 end 语义规则入栈
semantic_end = SemanticRuleFactory.get_instance(production.semantic_end, top)
if semantic_end:
stack.push(semantic_end)
# 将 top 的孩子节点反序入栈
for i in range(len(production.right) - 1, -1, -1):
# for child in top.children[::-1]:
stack.push(top.children[i])
semantic_child = SemanticRuleFactory.get_instance(production.semantic_children[i],
top.children[i])
if semantic_child:
stack.push(semantic_child)
# 如果分析表中存放着错误信息
else:
self.__error = SyntaxError('语法错误 ' + inputs[input_index].str, inputs[input_index].line)
break
# 如果 top 是终结符
else:
# 如果 top = input
if stack.top().data.type == inputs[input_index].type:
# 如果 top = #,宣布分析成功
if stack.top().data.type == 'pound':
flag = False
# 如果 top != #
else:
# 计算 top 的 lexical 属性
stack.top().lexical = inputs[input_index].str
# 将 top 出栈,让 input_index 自增
stack.pop()
input_index += 1
# 如果 top != input
else:
self.__error = SyntaxError('语法错误 ' + inputs[input_index].str, inputs[input_index].line)
break
if self.__error:
return False
else:
self.__grammar_tree = grammar_tree
return True