【发布时间】:2017-08-20 13:02:53
【问题描述】:
目前,我正在为 Python 中的一小部分 Python 编写编译器。我已经设法构建了一个语法树,但是在编写树遍历时遇到了一些问题(这对于生成代码至关重要)。因此,我将首先向您展示我的数据结构:
class AbstractSyntaxTree(object):
def __init__(self, startSymbol):
self.root = Node(startSymbol, val=None)
self.nodes = [self.root]
def addNode(self, name, val, parentId):
parent = self.nodes[parentId]
self.nodes.append(Node(name=name, val=val, parent=parent))
return len(self.nodes)-1
def getLastId(self):
return len(self.nodes)-1
def __iter__(self):
for node in self.root:
yield node
这是我的节点定义:
class Node:
def __init__(self, name, val, parent=None):
self.name = name
self.val = val
self.parent = parent
self.children = []
if parent:
parent.children.append(self)
def __iter__(self):
yield self
for child in self.children:
for node in child:
yield node
我的解析器是一个递归下降解析器,其中每个语法符号都是一个调用其他语法符号的函数。 program 是我的开始符号。
def program(self, indentLvl=0):
parent = self.synTree.getLastId()
if self.smellAndConsume(TOK.EOF, parentId=parent): return
self.smellAndConsume(TOK.NEWL, parentId=parent)
self.synTree.addNode(name=VAR.statement, val=None, parentId=parent)
self.statement()
while self.accept and not self.isConsumable(TOK.EOF):
self.consume(TOK.NEWL, parentId=parent)
self.synTree.addNode(name=VAR.statement, val=None, parentId=parent)
self.statement()
self.consume(TOK.EOF, parentId=parent)
现在很好奇,如果在成功解析后,我能够通过首先使用在Node 和AbstractSyntaxTree 中定义的__iter__ 生成器迭代深度来打印语法树中的所有节点。但是
def test_tree_traversal():
for node in miniPyGrammar.synTree:
print(node)
不打印所有节点!当我调试我的代码时,我意识到我的根节点在其子节点列表中没有任何子节点,尽管我使用根节点 ID 调用 addNode。有谁知道这里发生了什么?
如果您需要更多信息或更多代码 sn-ps,请随时询问。
编辑:我刚刚找到了解决方案(尽管我仍然觉得这里发生的事情很奇怪。)此代码现在按预期运行:
def test_tree_traversal(code):
grammar = Grammar()
grammar.parse(code)
for node in grammar.synTree:
print(node)
def execute_tests():
for name, code in programs.items():
parse_test(name, code)
test_tree_traversal(code)
在我有一个全局语法对象之前,execute_tests 会对该语法调用 parse,之后我运行 test_tree_traversal,它访问语法对象 synTree。奇怪的是,在调用之间,垃圾收集删除了 AST 中的一些节点。为什么我认为这是垃圾收集?因为行为是不确定的。
编辑:这是容易出错的代码: 注意唯一的区别是我在执行测试之前实例化了一个新的语法对象。 Grammar 有一个 'parse' 方法,如果程序在语法上正确,则返回 true,并构造一个可以通过 Grammar.synTree 访问的 AST。
miniPyGrammar = Grammar()
def parse_test(
programName: str,
programCode: str):
success = miniPyGrammar.parse(programCode)
if success:
print('{} is a valid miniPyProgram :)'.format(programName))
else:
print('{} is not a valid miniPyProgram'.format(programName))
print(miniPyGrammar.synTree)
def tree_traversal(code):
for node in miniPyGrammar.synTree:
print(node)
def execute_tests():
for name, code in programs.items():
parse_test(name, code)
tree_traversal(code)
if __name__ == '__main__':
execute_tests()
【问题讨论】:
-
我不认为你会得到关于你的代码的非工作版本的答案,因为你没有充分描述它。我们无法对看不到的代码进行故障排除!在
for node in miniPyGrammar.synTree:中,miniPyGrammar是什么,您为什么希望它保存您的语法树?您确定您的解析器的任何方法中都没有任何破坏树的错误吗?你只向我们展示了一种方法,我不明白它在做什么,因为它正在调用你没有展示的方法。尝试创建一个 minimal reproducible example 来证明您的问题。 -
是的,这是真的,但是因为我不知道错误来自哪里,所以我不知道要显示我的代码的哪些部分。我认为问题可能来自错误的迭代器代码。我将创建另一个编辑以指出问题出在哪里。
标签: python compiler-construction generator abstract-syntax-tree depth-first-search