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