Pyparsing:嵌套的Markdown强调

3 投票
2 回答
594 浏览
提问于 2025-04-21 01:09

我正在玩一些简单的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 个回答

2

想想你在问什么。当第二个 * 是用来结束强调,还是用来开始嵌套强调时,这个问题并不明确。你没有写出任何规则来区分这两种情况。因为这总是100%模糊不清,所以你能得到的结果只有:

  • 强调永远无法结束,或者
  • 强调永远无法嵌套。

我怀疑你并不是在问如何从第二种情况切换到第一种。

那么你到底在问什么呢?

你需要制定一些规则来区分这两种可能性。

实际上,如果你查看你链接的文档,它们有一套复杂的规则,明确规定了什么时候 * 可以开始强调,什么时候不可以,结束的规则也是如此;根据这些规则,如果仍然模糊不清,就会结束强调。你需要实现这些规则。

2

根据这些额外的规则,我觉得你根本不需要担心递归的问题,只要处理好找到的开头和结尾的强调表达式就行,无论它们是否匹配:

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解析器根本处理不了。

撰写回答