Pyparsing:嵌套的Markdown强调
我正在玩一些简单的Markdown文本,想通过这个过程学习Pyparsing和语法相关的知识。没过多久,我就遇到了一个问题,搞得我很困惑。我想解析一个简单的CommonMark规范中的强调部分。在这个设置中,允许嵌套的强调,所以
*foo *bar* baz*
应该返回:
<em>foo <em>bar</em> baz</em>
我尝试使用递归定义来匹配这个,但没有成功。这是一些示例代码:
from pyparsing import *
text = Word(printables,excludeChars="*")
enclosed = Forward()
emphasis = QuotedString("*").setParseAction(lambda x: "<em>%s</em>" % x[0],contents=enclosed)
enclosed << emphasis | text
test = """
*foo *bar* bar*
"""
print emphasis.transformString(test)
但是我从中得到的结果是:
<em>foo </em>bar<em> bar</em>
请原谅我的菜鸟水平;有没有人能给我指个方向?
编辑:
为了回应abarnert的提问,我来做个说明。我只是随便玩玩,所以我可以使用一种任意限制的符号形式。我假设只会出现单个的'*',并且它们不会相互靠近。这样,空格就可以帮助区分:*后面没有空格表示开始强调,而*前面没有空格表示结束强调。
即使这样,我也不太确定如何继续使用Pyparsing。是不是应该用某种基于栈的方法,打开的*入栈,验证为关闭时出栈?用Pyparsing怎么实现这个呢?或者有没有更有效的方法?
2 个回答
想想你在问什么。当第二个 *
是用来结束强调,还是用来开始嵌套强调时,这个问题并不明确。你没有写出任何规则来区分这两种情况。因为这总是100%模糊不清,所以你能得到的结果只有:
- 强调永远无法结束,或者
- 强调永远无法嵌套。
我怀疑你并不是在问如何从第二种情况切换到第一种。
那么你到底在问什么呢?
你需要制定一些规则来区分这两种可能性。
实际上,如果你查看你链接的文档,它们有一套复杂的规则,明确规定了什么时候 *
可以开始强调,什么时候不可以,结束的规则也是如此;根据这些规则,如果仍然模糊不清,就会结束强调。你需要实现这些规则。
根据这些额外的规则,我觉得你根本不需要担心递归的问题,只要处理好找到的开头和结尾的强调表达式就行,无论它们是否匹配:
from pyparsing import *
openEmphasis = (LineStart() | White()) + Suppress('*')
openEmphasis.setParseAction(lambda x: ''.join(x.asList()+['<em>']))
closeEmphasis = '*' + FollowedBy(White() | LineEnd())
closeEmphasis.setParseAction(lambda x: '</em>')
emphasis = (openEmphasis | closeEmphasis).leaveWhitespace()
test = """
*foo *bar* bar*
"""
print test
print emphasis.transformString(test)
输出结果:
*foo *bar* bar*
<em>foo <em>bar</em> bar</em>
你并不是第一个在这种应用上绊倒的人。当我在PyCon'06上演讲时,有位热情的观众立刻想要解析一些markdown格式的内容,输入字符串大概是 "****a** b**** c**"
之类的。我们一起研究了一下,但由于消歧义的规则太依赖上下文,基本的pyparsing解析器根本处理不了。