安装graphviz库
安装对应软件包
graphviz官网
我选的是stable_windows_10_cmake_Release_x64_graphviz-install-2.47.1-win64.exe
安装python库
解析语法分析器输出
在LL(1)无回溯的递归下降语法分析器里实现了语法分析器。要获取语法分析的输出,只需要将构建脚本中的:
改为:
1
| parser.exe code.txt > output.txt
|
这样就将输出的结果重定向到了output.txt
。
得到的output.txt
中每一行的格式类似于:
1
| parent -> child1 child2 child3 ...
|
按照空格分隔,第一个是父节点名,第二个是->
,之后是一系列子节点名。
按照最左推导
的定义,如果child1
是非终结符,则child1
有子节点,并且一定就在下一行,比如:
1 2
| parent -> child1 child2 child3 ... child1 -> child11 child12 ...
|
否则依次考虑child2
、child3
...
所以这是一个深度优先遍历语法分析树的过程,只要用一个栈就可以将这棵树建立起来。
graphviz绘图
graphviz
中Digraph
和Graph
分别对应有向图和无向图,这里使用的是无向图。
其基本用法是:
1 2 3 4 5
| dot = Graph() dot.node('node_name0', 'node_label0') dot.node('node_name1', 'node_label1') dot.edge('node_name0', 'node_name1') dot.render('output_name', format='svg', view=True)
|
完整代码&测试运行
test.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
import sys from graphviz import Graph
dot = Graph() nodes_cnt = 0
lines = [] words_stk = []
def new_node(label: str) -> str: global nodes_cnt new_node_name = 'node' + str(nodes_cnt) dot.node(new_node_name, label) nodes_cnt += 1 return new_node_name
def draw(): words_stk.append(('Program', new_node('Program'))) for line in lines: words = line.split() if len(words) > 2: parent = words[0] while len(words_stk) > 0 and words_stk[len(words_stk) - 1][0] != parent: words_stk.pop() if len(words_stk) > 0 and words_stk[len(words_stk)-1][0] == parent: parent_name = words_stk.pop()[1] else: parent_name = new_node(parent) print('Error at node: ' + parent_name + ' ' + parent) tmp = [] for child in words[2:]: child_name = new_node(child) tmp.append((child, child_name)) dot.edge(parent_name, child_name) while len(tmp) > 0: words_stk.append(tmp.pop()) dot.render('test', format='svg', view=True)
def has_error() -> bool: has_err = False for line in lines: if 'Error' in line or 'Unknown' in line: has_err = True print(line) return has_err
if __name__ == '__main__': if len(sys.argv) > 1: for file in sys.argv[1:]: print('Processing file: '+file) lines = open(file, 'r').readlines() if not has_error(): draw() else: print('No input file!')
|
output.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| Program -> Block Block -> { Decls Stmts } Decls -> Decl Decls Decl -> Type id ; Type -> int Decls -> epsilon Stmts -> Stmt Stmts Stmt -> id = Expr ; Expr -> Term Expr1 Term -> Factor Term1 Factor -> num Term1 -> epsilon Expr1 -> epsilon Stmts -> Stmt Stmts Stmt -> while ( Bool ) Stmt Bool -> Expr Bool1 Expr -> Term Expr1 Term -> Factor Term1 Factor -> id Term1 -> epsilon Expr1 -> epsilon Bool1 -> <= Expr Expr -> Term Expr1 Term -> Factor Term1 Factor -> num Term1 -> epsilon Expr1 -> epsilon Stmt -> Block Block -> { Decls Stmts } Decls -> epsilon Stmts -> Stmt Stmts Stmt -> id = Expr ; Expr -> Term Expr1 Term -> Factor Term1 Factor -> id Term1 -> epsilon Expr1 -> + Term Expr1 Term -> Factor Term1 Factor -> id Term1 -> epsilon Expr1 -> epsilon Stmts -> Stmt Stmts Stmt -> id = Expr ; Expr -> Term Expr1 Term -> Factor Term1 Factor -> id Term1 -> epsilon Expr1 -> + Term Expr1 Term -> Factor Term1 Factor -> num Term1 -> epsilon Expr1 -> epsilon Stmts -> epsilon Stmts -> epsilon
|
在构建脚本中添加或手动运行:
1
| python test.py output.txt
|
输出:
![]()