也許我的部分解決方案將有助於澄清我在問題中的含義。
比方說,你有幾分不平凡的解析器:
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
當然,這是一個相當砍死解決方案。除此之外,行號和錯誤消息在固定寬度約束內不好。我很想看到這個想法以更好的方式實現。
+1不與召喚邪神一個正則表達式。 – 2011-04-13 22:38:42