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

最初の2つのテストは問題なく合格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 に答える