Альтернативы 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.
Я думаю, что что-то упущено в определении правила: документа. То, что я хочу, это то, что потребляет любое количество выражений в последовательности и простой текст, и, хотя у меня есть правило, которое будет использовать каждый атом отдельно, использование их обоих в одной строке приводит к сбою.
2 ответа
Для тебя document
Правило, которое вы хотите использовать repeat
:
rule(:document) do
(expression | text).repeat
end
Вам также необходимо изменить text
править; в настоящее время, если он начинает совпадать, он будет потреблять все, включая любые [
это должно начать новый expression
, Примерно так должно работать:
rule(:text) { match['^\['].repeat(1).as(:text) }
То, что вы искали, это что-то вроде этого...
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.:)