PLY调试笔记
1、在使用Yacc时,parser.py文件显示126行报错:
Syntax error. Expected ':'
虽然报错意思是.处应该是':',实际上将126行'|'后紧接着的inner_binitplace改为‘| inner_binitplace’,即加上一个tab即可
2、输入Yacc的字符串只有26行,但打印出来发现多了很多空行(不影响使用):
3、开启debug模式定位bug
1 import logging 2 3 4 logging.basicConfig( 5 level = logging.DEBUG, 6 filename = "parselog.txt", 7 filemode = "w", 8 format = "%(filename)10s:%(lineno)4d:%(message)s" 9 ) 10 log = logging.getLogger() 11 12 # lex.lex(debug=True,debuglog=log) 13 parser = yacc.yacc(debug=True, debuglog=log)
输入以上代码后执行yacc.py(我的命名为parser.py)会产生parselog.txt文件,根据文件内的error查找bug。根据官网:https://www.dabeaz.com/ply/ply.html#ply_nn28b 该文件'.'处表示这里有语法错误。以以下输入为例:
yacc.py: 435:Stack : CHRCONST EQUAL TOPOSTART LH DYCNODESTART LH CHRCONST EQUAL WIFISTANODE LBRAKET os COMMA memory COMMA NET EQUAL . LexToken(NETCONST,'172.16.1.2/24',28,92) yacc.py: 445:Action : Shift and goto state 137 yacc.py: 410:
可以看到栈按照输入token流的顺序存放了许多终结符和规约后以及当前正在输入将要规约的非终结符,‘.’之前都是正常,.处表示该位置的token出错(以上示例是正常的)。只要根据stack中的特殊一些的token,对照parser.py中相应的方法定义去找就可以了。
4、定位token所在行
在token流中没有行的概念,在veclexer.py中自定义“行”后,执行词法分析器veclexr.py或语法分析器parser.py后控制台输出的token会显示行号。例如这里23是行号:
在veclexr.py中定义行的概念:
# Define a rule so we can track line numbers def t_newline(t): r'\n+' # 下面一行要写成这样自定义的行号才会和平时换行的“行”的概念一致 # 行号指的是输入的字符串(topo)的行号 t.lexer.lineno += len(t.value) print('Line:{}'.format(t.lexer.lineno))
5、yacc定位错误
使用yacc模块做语法分析语义分析时经常报的一个错误就是:
SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip())
file部分是我导入yacc模块的py文件parser.py,dline是报错的行号。当时报错是215,始终不明白这个215行号是在哪里,因为在pycharm中看代码是不会有任何标红的地方。
尝试打断点,发现程序会从parser.py进入自行生成的parsertab.py里执行语法分析时报错,但是一步步调试在debug栏也看不出区别。
后来搜索错误代码,找到PLY的github源码报错处:
1 # ----------------------------------------------------------------------------- 2 # parse_grammar() 3 # 4 # This takes a raw grammar rule string and parses it into production data 5 # ----------------------------------------------------------------------------- 6 def parse_grammar(doc, file, line): 7 grammar = [] 8 # Split the doc string into lines 9 pstrings = doc.splitlines() 10 lastp = None 11 dline = line 12 for ps in pstrings: 13 dline += 1 14 p = ps.split() 15 if not p: 16 continue 17 try: 18 if p[0] == '|': 19 # This is a continuation of a previous rule 20 if not lastp: 21 raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline)) 22 prodname = lastp 23 syms = p[1:] 24 else: 25 prodname = p[0] 26 lastp = prodname 27 syms = p[2:] 28 assign = p[1] 29 if assign != ':' and assign != '::=': 30 raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline)) 31 32 grammar.append((file, dline, prodname, syms)) 33 except SyntaxError: 34 raise 35 except Exception: 36 raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip())) 37 38 return grammar
可以看到dlin是对doc做行分割后doc.splitlines(),每执行一行dline加一,也就是doc的行号。那doc是什么呢,发现报错信息中有file,file显示的就是我的parser.py文件,那doc应该是该文件的字符串。把parser.py报错的行215附近的代码替换成上一个版本的,发现不再报错,因此定位错误成功。
参考:https://github.com/dabeaz/ply/blob/master/ply/yacc.py
参考: