分享
 
 
 

可爱的Python:了解DParserforPython

王朝other·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

首先初步了解 DParser 这一由 J. Plevyak 编写的简单而强大的解析工具。然后了解用于 Python 的 DParser,它为 Python 程序员提供了一个访问 DParser 的无缝接口,并看看它与上一期中介绍的解析器的比较。语法规则以类似于 Spark 或 PLY 的方式通过 Python 函数文档字符串加入到 DParser 中。

有很多可用的 Python 解析器程序库。我已经在本专栏中讨论过 mx.TextTools、SimpleParse 和 SPARK,并在我的书中介绍了 PLY(请参阅 参考资料,获得这些文档的链接)。无需考虑,我也知道有 PyGgy、Yapps、PLEX、PyLR、PyParsing 和 TPG,而且我还模糊地记得读过半打其他解析器的声明。用户可能会对此门类感到失落,不是因为缺少高质量程序库,而是太多了。

DParser 与所有其他解析器的不同之处是什么?是这样,类似于 PLY 和 Spark,用于 Python 的 DParser 使用函数文档字符串来表示其结果(productions)。这种风格使得您可以将动作代码直接插入到一个结果中,以处理当一个特定的语法规则得到满足时将发生的事件。与 PLY 或 Spark 相反,DParser 本身是用 C 编写的,因而可能会比纯粹的 Python 解析器快得多。用于 Python 的 DParser 是底层的 C 程序库之外的一个非常精简的包装器(wrapper) ―― 对 Python 的回调需要一些额外的时间,但是基本的解析是以 C 语言的速度来进行的。不过,就本文而言,我没有尝试进行任何具体的基准测试。所以,相对于其他解析器来说,DParser 到底有多快或多慢不是我所能直接评论的。

就我自己而言,我仍是非常喜欢 SimpleParse 的方法。SimpleParse 是快速的 mx.TextTools 程序库(也是用 C 所编写的)的一个包装器,可以将 EBNF 语法语言从 Python 代码中完全分离出来。一般来说,使用 SimpleParse 就意味着在一个函数调用中生成一个解析树,然后在分开的代码中遍历这个树。对于特别大的被解析的文档来说,这种两步方法可能是低效的,但是我发现这样更容易理解编写的代码。

尽管如此,还是有很多读者推荐说用于 Python DParaser 值得关注,虽然我更喜欢单独的 EBNF 定义。顺便提一句,如您将在示例中所看到的,DParser 不使用任何单独的标记传递,而只是直接解析。您可以通过定义保留的 d_whitespace() 函数来控制空格的识别(它分离解析符号);这样就使得您可以随意使用标记。

找到最长的结果

作为用于 Python 的 DParser 程序的第一个示例,我创建了一个查找几个模式的语法,这些模式依次为另一个的子结果。这个语法处理的问题类似于很多解析器遇到的“dangling else”问题。具体说,也就是您如何才能知道什么时候停止查找更长的结果?(例如,“if”后是否跟有“else”?)我的语法会去分析的短语可能按次序包括有以“a”、“b”和“c” 结尾的单词。所有没有被包括进来的单词只是短语的“head”或“tail”的部分。这需要一些例子来展示。首先,程序本身:

清单 1. 解析器 abc.py

#!/usr/bin/env python2.3

"Identify sequence of a-, b-, c- words"

#

#-- The grammar

def d_phrase(t, s):

'phrase : words ( ABC | AB | A ) words'

print "Head:", ''.join(s[0])

print t[1][0]+":", ''.join(s[1])

print "Tail:", ''.join(s[2])

def d_words(t):

'words : word*'

def d_word(t):

'word : "[a-z]+" '

def d_A(t):

'''A : "a[a-z]*" '''

return 'A'

def d_AB(t):

'''AB : A "b[a-z]*" '''

return 'AB'

def d_ABC(t):

'''ABC : AB "c[a-z]*" '''

return 'ABC'

#

#-- Parse STDIN

from dparser import Parser

from sys import argv, stdin

phrase, arg = stdin.read(), argv[-1]

Parser().parse(phrase,

print_debug_info=(arg=='--debug'))

让我们给出一些短语来运行这个解析器,如下:

清单 2. 简单地解析短语

$ echo -n "alpha" | ./abc.py

Head:

A: alpha

Tail:

echo -n "xavier alpha beta charlie will" | ./abc.py

Head: xavier

ABC: alpha beta charlie

Tail: will

$ echo -n "mable delta xavier bruce" | ./abc.py

Traceback (most recent call last): [...]

dparser.SyntaxError:

syntax error, line:1

mable delta xavier bruce[syntax error]

显然,到目前为止,一切都没问题。我的语法当其条件允许时找到了一个 ABC,但是当只能找到 A 或者 AB 时,也能满足于此。

不过说实话,当遇到含糊的短语时,我的语法会有很多问题。在大部分情况下,当 DParser 不能确定如何解析一个短语时,它会陷入一个无限循环(可能是最坏的结果;至少回溯或者报告的错误可以告诉您哪里出现了问题)。有时(至少在我的 Mac OSX 机器上),它会转而生成一个“Bus error”。那些情形我哪个都不喜欢。

处理含糊的短语

由于所有的最终结果都有相同的优先级,所以解析器不能确定如何解析类似如下的内容:

清单 3. 尝试解析一个含糊的短语

$ echo -n "alex bruce alice benny carl" | ./abc.py

AB 在前然后是单词?单词在前然后是 ABC?对于那个问题来说,它是全部都是单词吗(包括五个单词结果),它是不是应该引发一个 dparser.SyntaxError?我最后会得到一个“Bus error” 或停止了的任务,而不是一个解析。在先前的例子中,含糊的短语碰巧被解析出来的原因在于每个结果的急切性(eagerness);一旦找到一个 ABC,则先导和结尾单词就都各就其位。

实际上,在先前的语法可以生效的情况下,要确切地理解为什么能够生效很令人迷惑 ―― 在某种程度上,比理解为什么它有时不能生效更令人迷惑。

让我们假定我们希望解析一个短语,并当存在 ABC 结果时找它,即便在从左到右的遍历过程中,有一些其他的结果(也就是 AB)得到了满足。我可以通过提高 ABC 最终结果的优先级来完成:

清单 4. abc2.py 中修订的 d_ABC() 结果函数

def d_ABC(t):

'ABC : AB "c[a-z]*" $term 1'

return 'ABC'

如果没有指定优先级,则结果的优先级是 0。否则,任何正整数或负整数都可以用来对结果排序。现在我们可以运行:

清单 5. 成功地找到后面的 ABC

$ echo -n "alex bruce alice benny carl" | ./abc2.py

Head: alex bruce

ABC: alice benny carl

Tail:

注意,在解析器寻找末尾的单词之前,会尝试(ABC|AB|A)系列中的全部可选项。所以这样不需要任何优先级规范就可以成功。

清单 6. A 与 AB 之间不存在含糊短语问题

$ echo -n "alex alice benny" | ./abc.py

Head: alex

AB: alice benny

Tail:

在处理含糊短语时 DParser 的行为中,我发现了一些难以解释的异常现象。例如,添加一个绝对不是 A 的末尾单词,解析器可以工作 ―― 但 只能 在有调试信息的条件下运行!

清单 7. 处理含糊短语时的不稳定行为

$ echo -n "alex bruce alice benny carl dave" | ./abc.py

[...process freezes...]

$ echo -n "alex bruce alice benny carl dave" | ./abc.py --debug

[...debugging trace of speculative and final productions...]

Head: alex bruce

ABC: alice benny carl

Tail: dave

abc2.py 中的优先级规范会完成任意一种情况下的解析。

含糊短语的解析相当难以捉摸,难以确切理解。基本上,结果的生成是按遍历的顺序从左到右执行的,每一个结果都尝试去从左到右获取尽可能多的单词。只有当向前查找过程中发生明显错误时,才会进行回溯。总之,这只是大概。

调试简介

DParser 可以显示调试信息的选项,这是我所喜欢的它的一个方面。观察这些信息并不是直观地创建正确语法所必需的,但是至少可以通过它洞察当处理特定的短语时解析器所采取的动作。例如:

清单 8. 展示对不确定结果的追踪

#------- Showing a trace of speculative productions

$ echo -n "alex alice benny carl dave" | ./abc2.py --debug

d_words ???:

d_A ???:

alex

d_word ???:

alex

d_words ???:

d_phrase ???:

alex

d_words ???:

alex

d_A ???:

alice

d_word ???:

alice

d_words ???:

d_words ???:

alice

d_phrase ???:

alex alice

d_phrase ???:

alex alice

d_words ???:

alex alice

d_word ???:

benny

d_AB ???:

alice benny

d_words ???:

benny

d_words ???:

alice benny

d_words ???:

d_phrase ???:

al

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有