我是pyparsing的新手,一直在阅读这些例子,看这里并尝试一些事情。 我创建了一个语法并提供了一个缓冲区。然而,我在过去的lex / yacc中有着沉重的背景。
我有一两般的问题。
我现在正在看
ParseException:预期的行尾(在char 7024处),(第213行,col:2)
然后终止
由于我的缓冲区的性质,换行符有意义,我做了:
ParserElement.setDefaultWhitespaceChars('') # <-- zero len string
这个错误是否意味着我的作品中的某个地方,我有一条寻找LineEnd()
的规则而且该规则恰好是'最后'?
它正在死亡的位置是“文件结束”。我尝试使用parseFile
但我的文件包含字符&gt; ord(127)所以我把它加载到内存中,过滤所有&gt; ord(127)chars,然后调用parseString
。
我尝试打开verbose_stacktrace=True
我的语法中的一些元素,我认为问题源于此。
是否有更好的方法可以追踪出现此类错误时正在尝试识别的确切ParserElement
?或者我可以获得“堆栈或最近认可的生产追踪?”
我没有意识到我可以在这里编辑...... 我的崩溃是这样的:
[centos@new-host /tmp/sample]$ ./zooparser.py
!(zooparser.py) TEST test1: valid message type START
Ready to roll
Parsing This message: ( ignore leading>>> and trailing <<< ) >>>
ZOO/STATUS/FOOD ALLOCATION//
TOPIC/BIRD FEED IS RUNNING LOW//
FREE/WE HAVE DISCOVERED MOTHS INFESTED THE BIRDSEED AND IT IS NO
LONGER USABLE.//
<<<
Match {Group:({Group:({Group:({[LineEnd]... "ZOO" Group:({[LineEnd]... "/" [Group:({{{W:(abcd...) | LineEnd | "://" | " " | W:(!@#$...) | ":"}}... ["/"]...})]... {W:(abcd...) | LineEnd | "://" | " " | W:(!@#$...)}}) "//"}) Group:({LineEnd "TOPIC" {Group:({[LineEnd]... Group:({"/" {W:(abcd...) | Group:({W:(abcd...) [{W:(abcd...)}...]... W:(abcd...)}) | Group:({{{"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ'"}... | Group:({{"0123456789"}... ":"})} {W:(abcd...) | Group:({W:(abcd...) [{W:(abcd...)}...]... W:(abcd...)})}}) | "-"}})})}... [LineEnd]... "//"})}) [Group:({LineEnd "FREE" Group:({[LineEnd]... "/" [Group:({{{W:(abcd...) | LineEnd | "://" | " " | W:(!@#$...) | ":"}}... ["/"]...})]... {W:(abcd...) | LineEnd | "://" | " " | W:(!@#$...)}}) "//"})]...}) [LineEnd]... StringEnd} at loc 0(1,1)
Match Group:({Group:({[LineEnd]... "ZOO" Group:({[LineEnd]... "/" [Group:({{{W:(abcd...) | LineEnd | "://" | " " | W:(!@#$...) | ":"}}... ["/"]...})]... {W:(abcd...) | LineEnd | "://" | " " | W:(!@#$...)}}) "//"}) Group:({LineEnd "TOPIC" {Group:({[LineEnd]... Group:({"/" {W:(abcd...) | Group:({W:(abcd...) [{W:(abcd...)}...]... W:(abcd...)}) | Group:({{{"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ'"}... | Group:({{"0123456789"}... ":"})} {W:(abcd...) | Group:({W:(abcd...) [{W:(abcd...)}...]... W:(abcd...)})}}) | "-"}})})}... [LineEnd]... "//"})}) at loc 0(1,1)
Match Group:({[LineEnd]... "ZOO" Group:({[LineEnd]... "/" [Group:({{{W:(abcd...) | LineEnd | "://" | " " | W:(!@#$...) | ":"}}... ["/"]...})]... {W:(abcd...) | LineEnd | "://" | " " | W:(!@#$...)}}) "//"}) at loc 0(1,1)
Exception raised:None
Exception raised:None
Exception raised:None
Traceback (most recent call last):
File "./zooparser.py", line 319, in <module>
test1(pgm)
File "./zooparser.py", line 309, in test1
test(pgm, zooMsg, 'test1: valid message type' )
File "./zooparser.py", line 274, in test
tokens = zg.getTokensFromBuffer(fileName)
File "./zooparser.py", line 219, in getTokensFromBuffer
tokens = self.text.parseString(filteredBuffer,parseAll=True)
File "/usr/local/lib/python2.7/site-packages/pyparsing-1.5.7-py2.7.egg/pyparsing.py", line 1006, in parseString
raise exc
pyparsing.ParseException: Expected end of line (at char 148), (line:8, col:2)
[centos@new-host /tmp/sample]$
答案 0 :(得分:2)
pyparsing对解析的看法与lex / yacc不同。你必须让这些课做一些工作。以下是代码中的示例:
self.columnHeader = OneOrMore(self.aucc) \
| OneOrMore(nums) \
| OneOrMore(self.blankCharacter) \
| OneOrMore(self.specialCharacter)
您将OneOrMore
与正则表达式的“+”字符等同起来。在pyparsing中,ParseElements也是如此,但在字符级别,pyparsing使用Word
类:
self.columnHeader = Word(self.aucc + nums + self.blankCharacter + self.specialCharacter)
OneOrMore
适用于ParseElements,而非字符。看看:
OneOrMore(nums)
nums
是字符串“0123456789”,因此OneOrMore(nums)
将匹配“0123456789”,“01234567890123456789”等,但不匹配“123”。这就是Word
的用途。 OneOrMore
将接受字符串参数,但会隐式将其转换为Literal
。
这是使用pyparsing和lex / yacc之间的根本区别,我认为这是代码中复杂性的主要来源。
其他一些建议:
你的代码中有一些过早的优化 - 你写道:
aucc = ''.join(set([alphas.upper(),"'"]))
假设这将用于定义单词,只需执行:
aucc = alphas.upper() + "'"
在aucc中使用重复字符没有任何害处,Word
会将其转换为内部集合。
为要解析的内容编写BNF。它不必像lex / yacc那样过于严谨。从您的示例中,它看起来像:
# sample
ZOO/STATUS/FOOD ALLOCATION//
TOPIC/BIRD FEED IS RUNNING LOW//
FREE/WE HAVE DISCOVERED MOTHS INFESTED THE BIRDSEED AND IT IS NO
LONGER USABLE.//
parser :: header topicEntry+
header :: "ZOO" sep namedValue
namedValue :: uppercaseWord sep valueBody
valueBody :: (everything up to //)
topicEntry :: topicHeader topicBody
topicHeader :: "TOPIC" sep valuebody
topicBody :: freeText
freeText :: "FREE" sep valuebody
sep :: "/"
转换为pyparsing,这看起来像:
SEP = Literal("/")
BODY_TERMINATOR = Literal("//")
FREE_,TOPIC_,ZOO_ = map(Keyword,"FREE TOPIC ZOO".split())
uppercaseWord = Word(alphas.upper())
valueBody = SkipTo(BODY_TERMINATOR) # adjust later, but okay for now...
freeText = FREE_ + SEP + valueBody
topicBody = freeText
topicHeader = TOPIC_ + SEP + valueBody
topicEntry = topicHeader + topicBody
namedValue = uppercaseWord + SEP + valueBody
zooHeader = ZOO_ + SEP + namedValue
parser = zooHeader + OneOrMore(topicEntry)
(valueBody
在添加对值中嵌入的'://'的支持时必须更详细,但保存第2轮。)
在你至少完成一些简单的工作之前,不要让事情变得非常复杂。