【问题标题】:Implementing parser for markdown-like language为类似markdown的语言实现解析器
【发布时间】:2010-08-21 00:13:44
【问题描述】:

我有标记语言,类似于 markdown 和 SO 使用的那种。

旧版解析器基于正则表达式,维护起来简直是一场噩梦,因此我提出了基于 EBNF 语法并通过 mxTextTools/SimpleParse 实现的自己的解决方案。

但是,一些可能相互包含的令牌存在问题,我没有看到“正确”的方法。

这是我的语法的一部分:

newline          := "\r\n"/"\n"/"\r"
indent           := ("\r\n"/"\n"/"\r"), [ \t]
number           := [0-9]+
whitespace       := [ \t]+
symbol_mark      := [*_>#`%]
symbol_mark_noa  := [_>#`%]
symbol_mark_nou  := [*>#`%]
symbol_mark_nop  := [*_>#`]
punctuation      := [\(\)\,\.\!\?]
noaccent_code    := -(newline / '`')+
accent_code      := -(newline / '``')+
symbol           := -(whitespace / newline)
text             := -newline+
safe_text        := -(newline / whitespace / [*_>#`] / '%%' / punctuation)+/whitespace
link             := 'http' / 'ftp', 's'?, '://', (-[ \t\r\n<>`^'"*\,\.\!\?]/([,\.\?],?-[ \t\r\n<>`^'"*]))+
strikedout       := -[ \t\r\n*_>#`^]+
ctrlw            := '^W'+
ctrlh            := '^H'+
strikeout        := (strikedout, (whitespace, strikedout)*, ctrlw) / (strikedout, ctrlh)
strong           := ('**', (inline_nostrong/symbol), (inline_safe_nostrong/symbol_mark_noa)* , '**') / ('__' , (inline_nostrong/symbol), (inline_safe_nostrong/symbol_mark_nou)*, '__')
emphasis              := ('*',?-'*', (inline_noast/symbol), (inline_safe_noast/symbol_mark_noa)*, '*') / ('_',?-'_', (inline_nound/symbol), (inline_safe_nound/symbol_mark_nou)*, '_')
inline_code           := ('`' , noaccent_code , '`') / ('``' , accent_code , '``')
inline_spoiler        := ('%%', (inline_nospoiler/symbol), (inline_safe_nop/symbol_mark_nop)*, '%%')
inline                := (inline_code / inline_spoiler / strikeout / strong / emphasis / link)
inline_nostrong       := (?-('**'/'__'),(inline_code / reference / signature / inline_spoiler / strikeout / emphasis / link))
inline_nospoiler       := (?-'%%',(inline_code / emphasis / strikeout / emphasis / link))
inline_noast          := (?-'*',(inline_code / inline_spoiler / strikeout / strong / link))
inline_nound          := (?-'_',(inline_code / inline_spoiler / strikeout / strong / link))
inline_safe           := (inline_code / inline_spoiler / strikeout / strong / emphasis / link / safe_text / punctuation)+
inline_safe_nostrong  := (?-('**'/'__'),(inline_code / inline_spoiler / strikeout / emphasis / link / safe_text / punctuation))+
inline_safe_noast     := (?-'*',(inline_code / inline_spoiler / strikeout / strong / link / safe_text / punctuation))+
inline_safe_nound     := (?-'_',(inline_code / inline_spoiler / strikeout / strong / link / safe_text / punctuation))+
inline_safe_nop        := (?-'%%',(inline_code / emphasis / strikeout / strong / link / safe_text / punctuation))+
inline_full           := (inline_code / inline_spoiler / strikeout / strong / emphasis / link / safe_text / punctuation / symbol_mark / text)+
line                  := newline, ?-[ \t], inline_full?
sub_cite              := whitespace?, ?-reference, '>'
cite                  := newline, whitespace?, '>', sub_cite*, inline_full?
code                  := newline, [ \t], [ \t], [ \t], [ \t], text
block_cite            := cite+
block_code            := code+
all                   := (block_cite / block_code / line / code)+

第一个问题是,剧透、强烈和强调可以以任意顺序相互包含。以后我可能需要更多这样的内联标记。

我目前的解决方案只是为每个组合(inline_noast、inline_nostrong 等)创建单独的标记,但显然,随着标记元素数量的增加,此类组合的数量增长过快。

第二个问题是这些强/强调的前瞻在某些不良标记的情况下表现非常糟糕,例如__._.__*__.__...___._.____.__**___***(大量随机放置的标记符号)。解析几 kb 的此类随机文本需要几分钟时间。

是我的语法有问题还是我应该使用其他类型的解析器来完成这项任务?

【问题讨论】:

  • cletus 有一长串文章描述了他在解析 Markdown on his blog 方面的工作。它们有“Markdown、块解析和地狱之路”之类的标题。您可以在那里找到一些相关信息或见解。
  • @Greg 这很有趣,谢谢分享。但是,他似乎也没有解决内联标记,我对块标记没有任何问题。

标签: python parsing grammar markup ebnf


【解决方案1】:

如果一个事物包含另一个事物,那么通常您会将它们视为单独的标记,然后将它们嵌套在语法中。 Lepl(我写的http://www.acooke.org/lepl)和 PyParsing(它可能是最流行的纯 Python 解析器)都允许您递归地嵌套事物。

所以在 Lepl 中你可以编写如下代码:

# these are tokens (defined as regexps)
stg_marker = Token(r'\*\*')
emp_marker = Token(r'\*') # tokens are longest match, so strong is preferred if possible
spo_marker = Token(r'%%')
....
# grammar rules combine tokens
contents = Delayed() # this will be defined later and lets us recurse
strong = stg_marker + contents + stg_marker
emphasis = emp_marker + contents + emp_marker
spoiler = spo_marker + contents + spo_marker
other_stuff = .....
contents += strong | emphasis | spoiler | other_stuff # this defines contents recursively

然后你可以看到,我希望,内容将如何匹配强、强调等的嵌套使用。

对于您的最终解决方案,要做的远不止这些,效率可能是任何纯 Python 解析器的问题(有些解析器是用 C 实现的,但可以从 Python 调用。这些会更快,但可能使用起来比较棘手;我不能推荐任何,因为我没有用过)。

【讨论】:

猜你喜欢
  • 2013-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-03
  • 1970-01-01
  • 2012-03-14
相关资源
最近更新 更多