Альтернативы Parslet Не анализировать всю строку

У меня есть следующие характеристики

  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 Parser

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. Я хочу что-то, что будет потреблять любое количество выражений в последовательности и обычный текст, и хотя правило, которое у меня есть, будет потреблять каждый отдельный атом, использование их обоих в одной строке приводит к сбою.


person DVG    schedule 10.12.2012    source источник
comment
Для RSpec вы обычно используете #should и, вероятно, не хотите #puts.   -  person Eric Walker    schedule 10.12.2012
comment
Он использует должен, у меня просто были некоторые операторы puts, пока я экспериментировал.   -  person DVG    schedule 10.12.2012
comment
Попался - не видел.   -  person Eric Walker    schedule 10.12.2012


Ответы (2)


Для вашего document правила вы хотите использовать repeat:

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

Вам также нужно будет изменить правило text; в настоящее время, если он начнет совпадать, он будет потреблять все, включая любые [, которые должны запускать новый expression. Что-то вроде этого должно работать:

rule(:text) { match['^\['].repeat(1).as(:text) }
person matt    schedule 10.12.2012
comment
В итоге я пришел к тому же выводу, переместив выражение | текстовое решение в другое правило с именем section и документ с именем section.repeat(1) - person DVG; 11.12.2012

То, что вы искали, похоже на это...

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

Если вы исключите здесь открытые и закрытые теги... вы знаете, что имеете дело только с текстом. Примечание. Мне нравится этот метод использования «любого» после того, как вы исключили вещи, которые вам не нужны, но помните об этом, если вы позже будете проводить рефакторинг, поскольку ваш список исключений может увеличиться. Примечание 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. :)

person Nigel Thorne    schedule 09.02.2013