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