Возможно, мое частичное решение поможет прояснить, что я имел в виду в вопросе.
Допустим, у вас есть несколько нетривиальный парсер:
class MyParser < Parslet::Parser
rule(:dollars) {
match('[0-9]').repeat(1).as(:dollars)
}
rule(:comma_separated_dollars) {
match('[0-9]').repeat(1, 3).as(:dollars) >> ( match(',') >> match('[0-9]').repeat(3, 3).as(:dollars) ).repeat(1)
}
rule(:cents) {
match('[0-9]').repeat(2, 2).as(:cents)
}
rule(:currency) {
(str('$') >> (comma_separated_dollars | dollars) >> str('.') >> cents).as(:currency)
# order is important in (comma_separated_dollars | dollars)
}
end
Теперь, если мы хотим проанализировать строку валюты фиксированной ширины; это не самое простое дело. Конечно, вы могли бы точно определить, как выразить повторяющиеся выражения с точки зрения конечной ширины, но это становится действительно излишне сложным, особенно в случае с разделителями-запятыми. Кроме того, в моем случае валюта — это всего лишь один пример. Я хочу иметь простой способ придумать определения фиксированной ширины для адресов, почтовых индексов и т.д....
Это похоже на то, с чем должен справиться PEG. Мне удалось написать прототип, используя Lookahead как шаблон:
class FixedWidth < Parslet::Atoms::Base
attr_reader :bound_parslet
attr_reader :width
def initialize(width, bound_parslet) # :nodoc:
super()
@width = width
@bound_parslet = bound_parslet
@error_msgs = {
:premature => "Premature end of input (expected #{width} characters)",
:failed => "Failed fixed width",
}
end
def try(source, context) # :nodoc:
pos = source.pos
teststring = source.read(width).to_s
if (not teststring) || teststring.size != width
return error(source, @error_msgs[:premature]) #if not teststring && teststring.size == width
end
fakesource = Parslet::Source.new(teststring)
value = bound_parslet.apply(fakesource, context)
return value if not value.error?
source.pos = pos
return error(source, @error_msgs[:failed])
end
def to_s_inner(prec) # :nodoc:
"FIXED-WIDTH(#{width}, #{bound_parslet.to_s(prec)})"
end
def error_tree # :nodoc:
Parslet::ErrorTree.new(self, bound_parslet.error_tree)
end
end
# now we can easily define a fixed-width currency rule:
class SHPParser
rule(:currency15) {
FixedWidth.new(15, currency >> str(' ').repeat)
}
end
Конечно, это довольно взломанное решение. Помимо прочего, номера строк и сообщения об ошибках не подходят для ограничения фиксированной ширины. Хотелось бы, чтобы эта идея была реализована более качественно.
person
Jonathan
schedule
13.04.2011