ruby - 解析markdown缩进代码块

标签 ruby parsing markdown parslet

我正在尝试使用用 Parslet 编写的语法来解析 Markdown。但是,我无法跳过缩进的代码块,因为到目前为止我尝试的所有内容都陷入了递归。它们看起来像这样:

    This is a indented code block.
    Second line.

    Code block continues after blank line.

    There can be any number of chunks, 
    separated by not more than one blank line.

为了解决这个问题,我编写了一个最小的示例,它将行(包括 \n)替换为 a 和空行(\n\n)带空格,例如:a aaa aa

# recurring_group_parser.rb

require 'parslet'
require 'rspec'
require 'parslet/rig/rspec'

class RecurringGroupParser < Parslet::Parser
  root(:block)

  rule :block do
    chunk.repeat(1,3)
  end

  rule :chunk do
    str('a').repeat(1,3) >> space
  end

  rule :space do
    str(' ') | chunk.absent?
  end
end

describe RecurringGroupParser do
  it 'should parse a' do
    is_expected.to parse "a"
  end

  it 'should parse aa' do
    is_expected.to parse "aa"
  end

  it 'should parse aaa' do
    is_expected.to parse "aaa"
  end

  it 'should parse a a' do
    is_expected.to parse "a a"
  end

  it 'should parse aa a' do
    is_expected.to parse "aa a"
  end

  it 'should parse aaa a' do
    is_expected.to parse "aaa a"
  end

  it 'should parse a aa' do
    is_expected.to parse "a aa"
  end

  it 'should parse a aaa' do
    is_expected.to parse "a aaa"
  end

  it 'should parse aa a' do
    is_expected.to parse "aa a"
  end

  it 'should parse aa aa' do
    is_expected.to parse "aa aa"
  end

  it 'should parse aa aaa' do
    is_expected.to parse "aa aaa"
  end

  it 'should parse aaa aa' do
    is_expected.to parse "aaa aa"
  end

  it 'should parse aaa aaa' do
    is_expected.to parse "aaa aaa"
  end

  it 'should parse a a a' do
    is_expected.to parse "a a a"
  end

  it 'should parse aa a a' do
    is_expected.to parse "aa a a"
  end

  it 'should parse aaa a a' do
    is_expected.to parse "aaa a a"
  end

  it 'should parse a aa a' do
    is_expected.to parse "a aa a"
  end

  it 'should parse aa aa a' do
    is_expected.to parse "aa aa a"
  end

  it 'should parse aaa aa a' do
    is_expected.to parse "aaa aa a"
  end

  it 'should parse a aaa a' do
    is_expected.to parse "a aaa a"
  end

  it 'should parse aa aaa a' do
    is_expected.to parse "aa aaa a"
  end

  it 'should parse aaa aaa a' do
    is_expected.to parse "aaa aaa a"
  end

  it 'should parse a a aa' do
    is_expected.to parse "a a aa"
  end

  it 'should parse aa a aa' do
    is_expected.to parse "aa a aa"
  end

  it 'should parse aaa a aa' do
    is_expected.to parse "aaa a aa"
  end

  it 'should parse a aa aa' do
    is_expected.to parse "a aa aa"
  end

  it 'should parse aa aa aa' do
    is_expected.to parse "aa aa aa"
  end

  it 'should parse aaa aa aa' do
    is_expected.to parse "aaa aa aa"
  end

  it 'should parse a aaa aa' do
    is_expected.to parse "a aaa aa"
  end

  it 'should parse aa aaa aa' do
    is_expected.to parse "aa aaa aa"
  end

  it 'should parse aaa aaa aa' do
    is_expected.to parse "aaa aaa aa"
  end

  it 'should parse a a aaa' do
    is_expected.to parse "a a aaa"
  end

  it 'should parse aa a aaa' do
    is_expected.to parse "aa a aaa"
  end

  it 'should parse aaa a aaa' do
    is_expected.to parse "aaa a aaa"
  end

  it 'should parse a aa aaa' do
    is_expected.to parse "a aa aaa"
  end

  it 'should parse aa aa aaa' do
    is_expected.to parse "aa aa aaa"
  end

  it 'should parse aaa aa aaa' do
    is_expected.to parse "aaa aa aaa"
  end

  it 'should parse a aaa aaa' do
    is_expected.to parse "a aaa aaa"
  end

  it 'should parse aa aaa aaa' do
    is_expected.to parse "aa aaa aaa"
  end

  it 'should parse aaa aaa aaa' do
    is_expected.to parse "aaa aaa aaa"
  end
end

运行rspec recurring_group_parser.rb工作正常。只有当我放回换行符时,它才会停止:

# recurring_group_parser.rb

require 'parslet'
require 'rspec'
require 'parslet/rig/rspec'

class RecurringGroupParser < Parslet::Parser
  root(:block)

  rule :block do
    chunk.repeat(1,3)
  end

  rule :chunk do
    line.repeat(1,3) >> blank_line
  end

  rule :line do
    str('a') >> newline
  end

  rule :blank_line do
    newline.repeat(2) | chunk.absent?
  end

  rule :newline do
    str("\n") | any.absent?
  end
end

describe RecurringGroupParser do
  it 'should parse a' do
    is_expected.to parse "a"
  end

  it 'should parse aa' do
    is_expected.to parse "a\na"
  end

  it 'should parse aaa' do
    is_expected.to parse "a\na\na"
  end

  it 'should parse a a' do
    is_expected.to parse "a\n\na"
  end

  it 'should parse aa a' do
    is_expected.to parse "a\na\n\na"
  end

  it 'should parse aaa a' do
    is_expected.to parse "a\naa\n\na"
  end

  it 'should parse a aa' do
    is_expected.to parse "a\n\na\na"
  end

  it 'should parse a aaa' do
    is_expected.to parse "a\n\na\na\na"
  end

  it 'should parse aa a' do
    is_expected.to parse "a\na\n\na"
  end

  it 'should parse aa aa' do
    is_expected.to parse "a\na\n\na\na"
  end

  it 'should parse aa aaa' do
    is_expected.to parse "a\na\n\na\na\na"
  end

  it 'should parse aaa aa' do
    is_expected.to parse "a\naa\n\na\na"
  end

  it 'should parse aaa aaa' do
    is_expected.to parse "a\naa\n\na\na\na"
  end

  it 'should parse a a a' do
    is_expected.to parse "a\n\na\n\na"
  end

  it 'should parse aa a a' do
    is_expected.to parse "a\na\n\na\n\na"
  end

  it 'should parse aaa a a' do
    is_expected.to parse "a\naa\n\na\n\na"
  end

  it 'should parse a aa a' do
    is_expected.to parse "a\n\na\na\n\na"
  end

  it 'should parse aa aa a' do
    is_expected.to parse "a\na\n\na\na\n\na"
  end

  it 'should parse aaa aa a' do
    is_expected.to parse "a\naa\n\na\na\n\na"
  end

  it 'should parse a aaa a' do
    is_expected.to parse "a\n\na\naa\n\na"
  end

  it 'should parse aa aaa a' do
    is_expected.to parse "a\na\n\na\naa\n\na"
  end

  it 'should parse aaa aaa a' do
    is_expected.to parse "a\naa\n\na\naa\n\na"
  end

  it 'should parse a a aa' do
    is_expected.to parse "a\n\na\n\na\na"
  end

  it 'should parse aa a aa' do
    is_expected.to parse "a\na\n\na\n\na\na"
  end

  it 'should parse aaa a aa' do
    is_expected.to parse "a\naa\n\na\n\na\na"
  end

  it 'should parse a aa aa' do
    is_expected.to parse "a\n\na\na\n\na\na"
  end

  it 'should parse aa aa aa' do
    is_expected.to parse "a\na\n\na\na\n\na\na"
  end

  it 'should parse aaa aa aa' do
    is_expected.to parse "a\naa\n\na\na\n\na\na"
  end

  it 'should parse a aaa aa' do
    is_expected.to parse "a\n\na\naa\n\na\na"
  end

  it 'should parse aa aaa aa' do
    is_expected.to parse "a\na\n\na\naa\n\na\na"
  end

  it 'should parse aaa aaa aa' do
    is_expected.to parse "a\naa\n\na\naa\n\na\na"
  end

  it 'should parse a a aaa' do
    is_expected.to parse "a\n\na\n\na\na\na"
  end

  it 'should parse aa a aaa' do
    is_expected.to parse "a\na\n\na\n\na\na\na"
  end

  it 'should parse aaa a aaa' do
    is_expected.to parse "a\naa\n\na\n\na\na\na"
  end

  it 'should parse a aa aaa' do
    is_expected.to parse "a\n\na\na\n\na\na\na"
  end

  it 'should parse aa aa aaa' do
    is_expected.to parse "a\na\n\na\na\n\na\na\na"
  end

  it 'should parse aaa aa aaa' do
    is_expected.to parse "a\naa\n\na\na\n\na\na\na"
  end

  it 'should parse a aaa aaa' do
    is_expected.to parse "a\n\na\naa\n\na\na\na"
  end

  it 'should parse aa aaa aaa' do
    is_expected.to parse "a\na\n\na\naa\n\na\na\na"
  end

  it 'should parse aaa aaa aaa' do
    is_expected.to parse "a\naa\n\na\naa\n\na\na\na"
  end
end

为了简化这一点,行只能由单个 a 组成,并且不缩进,但以后可以轻松更改,并且与无法完成解析无关。我也很确定 rule :blank_line 中的 chunk.absent?rule 中的 any.absent? 之间存在冲突:newline 但我不知道如何解决这个问题并提供打破递归的标准。需要任何帮助!

最佳答案

在这种情况下,换行符可以是 eof。在这种情况下换行。 Repeat(2) 重复匹配 eof。你想要“重复(2,2)”。你可以让这些错误很容易被发现:)...只需使用我的 fork 。

您可以使用my fork of parslet来检测它是如何循环的。 。 它捕获循环并告诉您发生了什么。 它比通常的 Parslet 慢,因此切换回生产解析。

使用这个 Gemfile:

source "https://rubygems.org"

gem "parslet" , :git => "https://github.com/NigelThorne/parslet.git"
gem 'rspec'

您会得到以下结果:

 9:23:40.20 > bundle exec rspec parser.rb
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Failures:

  1) RecurringGroupParser should parse a
     Failure/Error: is_expected.to parse "a"
     RuntimeError:
       Grammar contains an infinite loop applying 'NEWLINE{2, }' at char position 1
       ...a<-- here
     # ./parser.rb:33:in `block (2 levels) in <top (required)>'

  2) RecurringGroupParser should parse aa
     Failure/Error: is_expected.to parse "a\na"
     RuntimeError:
       Grammar contains an infinite loop applying 'NEWLINE{2, }' at char position 3
       ...a
       a<-- here
     # ./parser.rb:37:in `block (2 levels) in <top (required)>'

  3) RecurringGroupParser should parse aaa
     Failure/Error: is_expected.to parse "a\na\na"
     RuntimeError:
       Grammar contains an infinite loop applying 'NEWLINE{2, }' at char position 5
       ...a
       a
       a<-- here
     # ./parser.rb:41:in `block (2 levels) in <top (required)>'

  4) RecurringGroupParser should parse a a
     Failure/Error: is_expected.to parse "a\n\na"
       expected BLOCK to be able to parse "a\n\na"
     # ./parser.rb:45:in `block (2 levels) in <top (required)>'

  5) RecurringGroupParser should parse aa a
     Failure/Error: is_expected.to parse "a\na\n\na"
       expected BLOCK to be able to parse "a\na\n\na"
     # ./parser.rb:49:in `block (2 levels) in <top (required)>'

  6) RecurringGroupParser should parse aaa a
     Failure/Error: is_expected.to parse "a\naa\n\na"
       expected BLOCK to be able to parse "a\naa\n\na"
     # ./parser.rb:53:in `block (2 levels) in <top (required)>'

  7) RecurringGroupParser should parse a aa
     Failure/Error: is_expected.to parse "a\n\na\na"
       expected BLOCK to be able to parse "a\n\na\na"
     # ./parser.rb:57:in `block (2 levels) in <top (required)>'

  8) RecurringGroupParser should parse a aaa
     Failure/Error: is_expected.to parse "a\n\na\na\na"
       expected BLOCK to be able to parse "a\n\na\na\na"
     # ./parser.rb:61:in `block (2 levels) in <top (required)>'

  9) RecurringGroupParser should parse aa a
     Failure/Error: is_expected.to parse "a\na\n\na"
       expected BLOCK to be able to parse "a\na\n\na"
     # ./parser.rb:65:in `block (2 levels) in <top (required)>'

  10) RecurringGroupParser should parse aa aa
     Failure/Error: is_expected.to parse "a\na\n\na\na"
       expected BLOCK to be able to parse "a\na\n\na\na"
     # ./parser.rb:69:in `block (2 levels) in <top (required)>'

  11) RecurringGroupParser should parse aa aaa
     Failure/Error: is_expected.to parse "a\na\n\na\na\na"
       expected BLOCK to be able to parse "a\na\n\na\na\na"
     # ./parser.rb:73:in `block (2 levels) in <top (required)>'

  12) RecurringGroupParser should parse aaa aa
     Failure/Error: is_expected.to parse "a\naa\n\na\na"
       expected BLOCK to be able to parse "a\naa\n\na\na"
     # ./parser.rb:77:in `block (2 levels) in <top (required)>'

  13) RecurringGroupParser should parse aaa aaa
     Failure/Error: is_expected.to parse "a\naa\n\na\na\na"
       expected BLOCK to be able to parse "a\naa\n\na\na\na"
     # ./parser.rb:81:in `block (2 levels) in <top (required)>'

  14) RecurringGroupParser should parse a a a
     Failure/Error: is_expected.to parse "a\n\na\n\na"
       expected BLOCK to be able to parse "a\n\na\n\na"
     # ./parser.rb:85:in `block (2 levels) in <top (required)>'

  15) RecurringGroupParser should parse aa a a
     Failure/Error: is_expected.to parse "a\na\n\na\n\na"
       expected BLOCK to be able to parse "a\na\n\na\n\na"
     # ./parser.rb:89:in `block (2 levels) in <top (required)>'

  16) RecurringGroupParser should parse aaa a a
     Failure/Error: is_expected.to parse "a\naa\n\na\n\na"
       expected BLOCK to be able to parse "a\naa\n\na\n\na"
     # ./parser.rb:93:in `block (2 levels) in <top (required)>'

  17) RecurringGroupParser should parse a aa a
     Failure/Error: is_expected.to parse "a\n\na\na\n\na"
       expected BLOCK to be able to parse "a\n\na\na\n\na"
     # ./parser.rb:97:in `block (2 levels) in <top (required)>'

  18) RecurringGroupParser should parse aa aa a
     Failure/Error: is_expected.to parse "a\na\n\na\na\n\na"
       expected BLOCK to be able to parse "a\na\n\na\na\n\na"
     # ./parser.rb:101:in `block (2 levels) in <top (required)>'

  19) RecurringGroupParser should parse aaa aa a
     Failure/Error: is_expected.to parse "a\naa\n\na\na\n\na"
       expected BLOCK to be able to parse "a\naa\n\na\na\n\na"
     # ./parser.rb:105:in `block (2 levels) in <top (required)>'

  20) RecurringGroupParser should parse a aaa a
     Failure/Error: is_expected.to parse "a\n\na\naa\n\na"
       expected BLOCK to be able to parse "a\n\na\naa\n\na"
     # ./parser.rb:109:in `block (2 levels) in <top (required)>'

  21) RecurringGroupParser should parse aa aaa a
     Failure/Error: is_expected.to parse "a\na\n\na\naa\n\na"
       expected BLOCK to be able to parse "a\na\n\na\naa\n\na"
     # ./parser.rb:113:in `block (2 levels) in <top (required)>'

  22) RecurringGroupParser should parse aaa aaa a
     Failure/Error: is_expected.to parse "a\naa\n\na\naa\n\na"
       expected BLOCK to be able to parse "a\naa\n\na\naa\n\na"
     # ./parser.rb:117:in `block (2 levels) in <top (required)>'

  23) RecurringGroupParser should parse a a aa
     Failure/Error: is_expected.to parse "a\n\na\n\na\na"
       expected BLOCK to be able to parse "a\n\na\n\na\na"
     # ./parser.rb:121:in `block (2 levels) in <top (required)>'

  24) RecurringGroupParser should parse aa a aa
     Failure/Error: is_expected.to parse "a\na\n\na\n\na\na"
       expected BLOCK to be able to parse "a\na\n\na\n\na\na"
     # ./parser.rb:125:in `block (2 levels) in <top (required)>'

  25) RecurringGroupParser should parse aaa a aa
     Failure/Error: is_expected.to parse "a\naa\n\na\n\na\na"
       expected BLOCK to be able to parse "a\naa\n\na\n\na\na"
     # ./parser.rb:129:in `block (2 levels) in <top (required)>'

  26) RecurringGroupParser should parse a aa aa
     Failure/Error: is_expected.to parse "a\n\na\na\n\na\na"
       expected BLOCK to be able to parse "a\n\na\na\n\na\na"
     # ./parser.rb:133:in `block (2 levels) in <top (required)>'

  27) RecurringGroupParser should parse aa aa aa
     Failure/Error: is_expected.to parse "a\na\n\na\na\n\na\na"
       expected BLOCK to be able to parse "a\na\n\na\na\n\na\na"
     # ./parser.rb:137:in `block (2 levels) in <top (required)>'

  28) RecurringGroupParser should parse aaa aa aa
     Failure/Error: is_expected.to parse "a\naa\n\na\na\n\na\na"
       expected BLOCK to be able to parse "a\naa\n\na\na\n\na\na"
     # ./parser.rb:141:in `block (2 levels) in <top (required)>'

  29) RecurringGroupParser should parse a aaa aa
     Failure/Error: is_expected.to parse "a\n\na\naa\n\na\na"
       expected BLOCK to be able to parse "a\n\na\naa\n\na\na"
     # ./parser.rb:145:in `block (2 levels) in <top (required)>'

  30) RecurringGroupParser should parse aa aaa aa
     Failure/Error: is_expected.to parse "a\na\n\na\naa\n\na\na"
       expected BLOCK to be able to parse "a\na\n\na\naa\n\na\na"
     # ./parser.rb:149:in `block (2 levels) in <top (required)>'

  31) RecurringGroupParser should parse aaa aaa aa
     Failure/Error: is_expected.to parse "a\naa\n\na\naa\n\na\na"
       expected BLOCK to be able to parse "a\naa\n\na\naa\n\na\na"
     # ./parser.rb:153:in `block (2 levels) in <top (required)>'

  32) RecurringGroupParser should parse a a aaa
     Failure/Error: is_expected.to parse "a\n\na\n\na\na\na"
       expected BLOCK to be able to parse "a\n\na\n\na\na\na"
     # ./parser.rb:157:in `block (2 levels) in <top (required)>'

  33) RecurringGroupParser should parse aa a aaa
     Failure/Error: is_expected.to parse "a\na\n\na\n\na\na\na"
       expected BLOCK to be able to parse "a\na\n\na\n\na\na\na"
     # ./parser.rb:161:in `block (2 levels) in <top (required)>'

  34) RecurringGroupParser should parse aaa a aaa
     Failure/Error: is_expected.to parse "a\naa\n\na\n\na\na\na"
       expected BLOCK to be able to parse "a\naa\n\na\n\na\na\na"
     # ./parser.rb:165:in `block (2 levels) in <top (required)>'

  35) RecurringGroupParser should parse a aa aaa
     Failure/Error: is_expected.to parse "a\n\na\na\n\na\na\na"
       expected BLOCK to be able to parse "a\n\na\na\n\na\na\na"
     # ./parser.rb:169:in `block (2 levels) in <top (required)>'

  36) RecurringGroupParser should parse aa aa aaa
     Failure/Error: is_expected.to parse "a\na\n\na\na\n\na\na\na"
       expected BLOCK to be able to parse "a\na\n\na\na\n\na\na\na"
     # ./parser.rb:173:in `block (2 levels) in <top (required)>'

  37) RecurringGroupParser should parse aaa aa aaa
     Failure/Error: is_expected.to parse "a\naa\n\na\na\n\na\na\na"
       expected BLOCK to be able to parse "a\naa\n\na\na\n\na\na\na"
     # ./parser.rb:177:in `block (2 levels) in <top (required)>'

  38) RecurringGroupParser should parse a aaa aaa
     Failure/Error: is_expected.to parse "a\n\na\naa\n\na\na\na"
       expected BLOCK to be able to parse "a\n\na\naa\n\na\na\na"
     # ./parser.rb:181:in `block (2 levels) in <top (required)>'

  39) RecurringGroupParser should parse aa aaa aaa
     Failure/Error: is_expected.to parse "a\na\n\na\naa\n\na\na\na"
       expected BLOCK to be able to parse "a\na\n\na\naa\n\na\na\na"
     # ./parser.rb:185:in `block (2 levels) in <top (required)>'

  40) RecurringGroupParser should parse aaa aaa aaa
     Failure/Error: is_expected.to parse "a\naa\n\na\naa\n\na\na\na"
       expected BLOCK to be able to parse "a\naa\n\na\naa\n\na\na\na"
     # ./parser.rb:189:in `block (2 levels) in <top (required)>'

Finished in 0.01702 seconds (files took 0.26725 seconds to load)
40 examples, 40 failures    

参见this question关于用 Parslet 解析缩进。

关于ruby - 解析markdown缩进代码块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30485292/

相关文章:

ruby-on-rails - 用于 haml 页面的 Ruby/Rails 绑定(bind) css

java - Android 从 RSS xml feed 解析 pubDate 标签

Python lxml XPath 语法错误 : invalid predicate

Markdown 不缩进第一段

regex - 在 Pandoc Markdown 输出中生成内联而不是列表样式的脚注?

latex - 使用Pandoc从Markdown转换为PDF时设置双倍行距和行号

ruby - 类/对象悖论混淆

ruby-on-rails - rails 5 : How to import data in Elastic Search and perform conditional search?

ruby - 可能用三元运算符表达条件 HAML

php - 您如何在PHP中解析和处理HTML/XML?