lib/raabro.rb in raabro-0.9.0 vs lib/raabro.rb in raabro-1.0.0
- old
+ new
@@ -24,29 +24,29 @@
#++
module Raabro
- VERSION = '0.9.0'
+ VERSION = '1.0.0'
class Input
attr_accessor :string, :offset
attr_reader :options
- def initialize(string, options={})
+ def initialize(string, offset=0, options={})
@string = string
- @offset = 0
- @options = options
+ @offset = offset.is_a?(Hash) ? 0 : offset
+ @options = offset.is_a?(Hash) ? offset : options
end
def match(str_or_regex)
if str_or_regex.is_a?(Regexp)
m = @string[@offset..-1].match(str_or_regex)
- m ? m[0].length : false
+ m && (m.offset(0).first == 0) ? m[0].length : false
else # String or whatever responds to #to_s
s = str_or_regex.to_s
l = s.length
@string[@offset, l] == s ? l : false
end
@@ -69,126 +69,344 @@
@offset = input.offset
@length = 0
@children = []
end
+ def successful_children
+
+ @children.select { |c| c.result == 1 }
+ end
+
+ def prune!
+
+ @children = successful_children
+ end
+
+ def shrink!
+
+ @children =
+ @children.inject([]) do |a, c|
+ a << c.shrink! if c.result == 1 && c.name
+ a
+ end
+
+ self
+ end
+
+ def string
+
+ @input.string[@offset, @length]
+ end
+
+ def lookup(name)
+
+ name = name.to_s
+
+ return self if @name.to_s == name
+ @children.each { |c| if n = c.lookup(name); return n; end }
+ nil
+ end
+
+ def gather(name, acc=[])
+
+ name = name.to_s
+
+ if @name.to_s == name
+ acc << self
+ else
+ @children.each { |c| c.gather(name, acc) }
+ end
+
+ acc
+ end
+
def to_a(opts={})
+ opts = Array(opts).inject({}) { |h, e| h[e] = true; h } \
+ unless opts.is_a?(Hash)
+
cn =
opts[:leaves] && (@result == 1) && @children.empty? ?
- @input.string[@offset, @length] :
+ string :
@children.collect { |e| e.to_a(opts) }
[ @name, @result, @offset, @length, @note, @parter, cn ]
end
- end
- def self.match(name, input, parter, regex_or_string)
+ def to_s(depth=0, io=StringIO.new)
- r = Tree.new(name, parter, input)
+ io.print "\n" if depth > 0
+ io.print ' ' * depth
+ io.print "#{@result} #{@name.inspect} #{@offset},#{@length}"
+ io.print result == 1 && children.size == 0 ? ' ' + string.inspect : ''
- if l = input.match(regex_or_string)
- r.result = 1
- r.length = l
- input.offset += l
- end
+ @children.each { |c| c.to_s(depth + 1, io) }
- r
+ depth == 0 ? io.string : nil
+ end
end
- def self.str(name, input, string)
+ module ModuleMethods
- match(name, input, :str, string)
- end
+ def _match(name, input, parter, regex_or_string)
- def self.rex(name, input, regex_or_string)
+ r = Raabro::Tree.new(name, parter, input)
- match(name, input, :rex, Regexp.new(regex_or_string))
- end
+ if l = input.match(regex_or_string)
+ r.result = 1
+ r.length = l
+ input.offset += l
+ end
- def self.narrow(parser)
+ r
+ end
- return parser if parser.is_a?(Method)
- return method(parser) if parser.is_a?(Symbol)
+ def str(name, input, string)
- k, m = parser.to_s.split('.')
- k, m = [ Object, k ] unless m
+ _match(name, input, :str, string)
+ end
- Kernel.const_get(k).method(m)
- end
+ def rex(name, input, regex_or_string)
- def self.parse(parser, input)
+ _match(name, input, :rex, Regexp.new(regex_or_string))
+ end
- narrow(parser).call(input)
- end
+ def _quantify(parser)
- def self.seq(name, input, *parsers)
+ return nil if parser.is_a?(Symbol) && respond_to?(parser)
+ # so that :plus and co can be overriden
- r = Tree.new(name, :seq, input)
+ case parser
+ when '?', :q, :qmark then [ 0, 1 ]
+ when '*', :s, :star then [ 0, 0 ]
+ when '+', :p, :plus then [ 1, 0 ]
+ else nil
+ end
+ end
- start = input.offset
- c = nil
+ def _narrow(parser)
- parsers.each do |pa|
- c = parse(pa, input)
- r.children << c
- break if c.result != 1
+ raise ArgumentError.new("lone quantifier #{parser}") if _quantify(parser)
+
+ return parser if parser.is_a?(Method)
+ return method(parser) if parser.is_a?(Symbol)
+
+ k, m = parser.to_s.split('.')
+ k, m = [ Object, k ] unless m
+
+ Kernel.const_get(k).method(m)
end
- if c && c.result == 1
- r.result = 1
- r.length = input.offset - start
- else
- input.offset = start
+ def _parse(parser, input)
+
+ _narrow(parser).call(input)
end
- r
- end
+ def seq(name, input, *parsers)
- def self.alt(name, input, *parsers)
+ r = ::Raabro::Tree.new(name, :seq, input)
- r = Tree.new(name, :alt, input)
+ start = input.offset
+ c = nil
- c = nil
+ loop do
- parsers.each do |pa|
- c = parse(pa, input)
- r.children << c
- break if c.result == 1
+ pa = parsers.shift
+ break unless pa
+
+ if q = _quantify(parsers.first)
+ parsers.shift
+ c = rep(nil, input, pa, *q)
+ r.children.concat(c.children)
+ else
+ c = _parse(pa, input)
+ r.children << c
+ end
+
+ break if c.result != 1
+ end
+
+ if c && c.result == 1
+ r.result = 1
+ r.length = input.offset - start
+ else
+ input.offset = start
+ end
+
+ r
end
- if c && c.result == 1
- r.result = 1
- r.length = c.length
+ def alt(name, input, *parsers)
+
+ greedy =
+ if parsers.last == true || parsers.last == false
+ parsers.pop
+ else
+ false
+ end
+
+ r = ::Raabro::Tree.new(name, greedy ? :altg : :alt, input)
+
+ start = input.offset
+ c = nil
+
+ parsers.each do |pa|
+
+ cc = _parse(pa, input)
+ r.children << cc
+
+ input.offset = start
+
+ if greedy
+ if cc.result == 1 && cc.length > (c ? c.length : -1)
+ c.result = 0 if c
+ c = cc
+ end
+ else
+ c = cc
+ break if c.result == 1
+ end
+ end
+
+ if c && c.result == 1
+ r.result = 1
+ r.length = c.length
+ input.offset = start + r.length
+ end
+
+ r.prune! if input.options[:prune]
+
+ r
end
- r
- end
+ def altg(name, input, *parsers)
- def self.rep(name, input, parser, min, max=0)
+ alt(name, input, *parsers, true)
+ end
- min = 0 if min == nil || min < 0
- max = nil if max.nil? || max < 1
+ def rep(name, input, parser, min, max=0)
- r = Tree.new(name, :rep, input)
- start = input.offset
- count = 0
+ min = 0 if min == nil || min < 0
+ max = nil if max.nil? || max < 1
- loop do
- c = parse(parser, input)
+ r = ::Raabro::Tree.new(name, :rep, input)
+ start = input.offset
+ count = 0
+
+ loop do
+ c = _parse(parser, input)
+ r.children << c
+ break if c.result != 1
+ count += 1
+ break if max && count == max
+ end
+
+ if count >= min && (max == nil || count <= max)
+ r.result = 1
+ r.length = input.offset - start
+ else
+ input.offset = start
+ end
+
+ r.prune! if input.options[:prune]
+
+ r
+ end
+
+ def ren(name, input, parser)
+
+ r = _parse(parser, input)
+ r.name = name
+
+ r
+ end
+ alias rename ren
+
+ def all(name, input, parser)
+
+ start = input.offset
+ length = input.string.length - input.offset
+
+ r = ::Raabro::Tree.new(name, :all, input)
+ c = _parse(parser, input)
r.children << c
- break if c.result != 1
- count += 1
- break if max && count == max
+
+ if c.length < length
+ input.offset = start
+ else
+ r.result = 1
+ r.length = c.length
+ end
+
+ r
end
- if count >= min && (max == nil || count <= max)
+ def eseq(name, input, startpa, eltpa, seppa=nil, endpa=nil)
+
+ jseq = false
+
+ if seppa.nil? && endpa.nil?
+ jseq = true
+ seppa = eltpa; eltpa = startpa; startpa = nil
+ end
+
+ start = input.offset
+ r = ::Raabro::Tree.new(name, jseq ? :jseq : :eseq, input)
r.result = 1
- r.length = input.offset - start
- else
- input.offset = start
+ c = nil
+
+ if startpa
+ c = _parse(startpa, input)
+ r.children << c
+ r.result = 0 if c.result != 1
+ end
+
+ if r.result == 1
+
+ i = 1
+ count = 0
+
+ loop do
+
+ i = (i + 1) % 2
+ pa = i == 0 ? eltpa : seppa
+
+ c = _parse(pa, input)
+ r.children << c
+
+ break if c.result != 1
+
+ count += 1
+ end
+
+ r.result = 0 if jseq && count < 1
+ end
+
+ if r.result == 1 && endpa
+ c = _parse(endpa, input)
+ r.children << c
+ r.result = 0 if c.result != 1
+ end
+
+ if r.result == 1
+ r.length = input.offset - start
+ else
+ input.offset = start
+ end
+
+ r.prune! if input.options[:prune]
+
+ r
end
+ alias jseq eseq
+ end
+ extend ModuleMethods
- r
+ def self.included(target)
+
+ target.instance_eval do
+ extend ::Raabro::ModuleMethods
+ extend self
+ end
end
end