2

我有以下规格

  it "parses a document with only an expression" do
    puts parser.document.should parse("[b]Hello World[/b]")
  end
  it "parses a document with only text" do
    puts parser.document.should parse(" Hello World")
  end
  it "parses a document with both an expression and text" do
    puts parser.document.should parse("[b]Hello World[/b] Yes hello")
  end

对于以下 Parslet 解析器

class Parser < Parslet::Parser

rule(:open_tag) do
  parslet = str('[')
  parslet = parslet >> (str(']').absent? >> match("[a-zA-Z]")).repeat(1).as(:open_tag_name)
  parslet = parslet >> str(']')
  parslet
end

rule(:close_tag) do
  parslet = str('[/')
  parslet = parslet >> (str(']').absent? >> match("[a-zA-Z]")).repeat(1).as(:close_tag_name)
  parslet = parslet >> str(']')
  parslet
end

rule(:text) { any.repeat(1).as(:text) }

rule(:expression) do
  # [b]Hello World[/b]
  # open tag, any text up until closing tag, closing tag
  open_tag.present?
  close_tag.present?
  parslet = open_tag >> match("[a-zA-Z\s?]").repeat(1).as(:enclosed_text) >> close_tag
  parslet
end

rule(:document) do
  expression | text
end

前两个测试通过就好了,我可以通过put将它们输出到命令行来看到原子的类型是正确的。但是,当我尝试使用表达式和纯文本解析文档时,它无法解析纯文本,并出现以下错误

Parslet::UnconsumedInput: Don't know what to do with " Yes hello" at line 1 char 19.

我想我在定义 :document 规则方面遗漏了一些东西。我想要的是会消耗任意数量的序列表达式和纯文本的东西,虽然我拥有的规则将消耗每个原子个体,但在同一个字符串中使用它们会导致失败。

4

2 回答 2

4

你正在寻找的是这样的东西......

require 'parslet'

class ExampleParser < Parslet::Parser
  rule(:open_tag) do
    str('[') >> 
      match["a-zA-Z"].repeat(1).as(:open_tag_name) >>
    str(']')
  end

open_tag 规则不需要排除 ']' 字符,因为匹配只允许字母。

  rule(:close_tag) do
    str('[/') >> 
      match["a-zA-Z"].repeat(1).as(:close_tag_name) >>
    str(']')
  end

同样在这里

  rule(:text) do 
    (open_tag.absent? >> 
      close_tag.absent? >> 
        any).repeat(1).as(:text) 
  end

如果您在此处排除打开和关闭标签..您知道您只处理文本。注意:一旦你排除了你不想要的东西,我喜欢这种使用“any”的技术,但如果你稍后重构,因为你的排除列表可能需要增长,请记住这一点。注意2:您可以进一步简化如下..

  rule(:text) do 
    (str('[').absent? >> any).repeat(1).as(:text) 
  end

..如果您根本不想在文本中使用任何方括号。

  rule(:expression) do
    # [b]Hello World[/b]
    open_tag >> text.as(:enclosed_text) >> close_tag
  end

这变得更加简单,因为文本不能包含 close_tag

  rule(:document) do
    (expression | text).repeat
  end

我在你错过的重复中添加了(正如马特指出的那样)

end

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

describe 'example' do
  let(:parser) { ExampleParser.new }
  context 'document' do
    it "parses a document with only an expression" do
      parser.document.should parse("[b]Hello World[/b]")
    end
    it "parses a document with only text" do
      parser.document.should parse(" Hello World")
    end
    it "parses a document with both an expression and text" do
      parser.document.should parse("[b]Hello World[/b] Yes hello")
    end
  end
end


RSpec::Core::Runner.run([])

希望这能给您一些使用 Parslet 的提示。:)

于 2013-02-09T14:18:13.467 回答
2

对于您document要使用的规则repeat

rule(:document) do
  (expression | text).repeat
end

您还需要更改text规则;目前,如果它开始匹配,它将消耗所有内容,包括[应该开始一个新的expression. 像这样的东西应该工作:

rule(:text) { match['^\['].repeat(1).as(:text) }
于 2012-12-10T20:12:34.130 回答