module Raabro::ModuleMethods
def self.included(target)
def self.included(target) target.instance_eval do extend ::Raabro::ModuleMethods extend self end end
def _match(name, input, parter, regex_or_string)
def _match(name, input, parter, regex_or_string) r = Raabro::Tree.new(name, parter, input) if l = input.match(regex_or_string) r.result = 1 r.length = l input.offset += l end r end
def _narrow(parser)
def _narrow(parser) fail ArgumentError.new("lone quantifier #{parser}") if _quantify(parser) method(parser.to_sym) end
def _parse(parser, input)
def _parse(parser, input) #p [ caller.length, parser, input.tring ] #r = _narrow(parser).call(input) #p [ caller.length, parser, input.tring, r.to_a(children: false) ] #r _narrow(parser).call(input) end
def _quantify(parser)
def _quantify(parser) return nil if parser.is_a?(Symbol) && respond_to?(parser) # so that :plus and co can be overriden case parser when '?', :q, :qmark then [ 0, 1 ] when '*', :s, :star then [ 0, 0 ] when '+', :p, :plus then [ 1, 0 ] when '!' then :bang else nil end end
def all(name, input, parser)
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 if c.length < length input.offset = start else r.result = 1 r.length = c.length end r end
def alt(name, input, *parsers)
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 else cc.result = 0 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
def altg(name, input, *parsers)
def altg(name, input, *parsers) alt(name, input, *parsers, true) end
def eseq(name, input, startpa, eltpa, seppa=nil, endpa=nil)
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 c = nil if startpa c = _parse(startpa, input) r.children << c r.result = 0 if c.result != 1 end if r.result == 1 on_elt = false count = 0 empty_stack = 0 loop do on_elt = ! on_elt cr = _parse(on_elt ? eltpa : seppa, input) empty_stack = cr.empty? ? empty_stack + 1 : 0 cr.result = 0 if empty_stack > 1 # # prevent "no progress" r.children.push(cr) if cr.result != 1 if on_elt && count > 0 lsep = r.children[-2] lsep.result = 0 input.offset = lsep.offset end break end 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
def make_includable
def make_includable def self.included(target) target.instance_eval do extend ::Raabro::ModuleMethods extend self end end end
def method_added(name)
def method_added(name) m = method(name) return unless m.arity == 1 return unless m.parameters[0][1] == :i || m.parameters[0][1] == :input @last = name.to_sym end
def nott(name, input, parser)
def nott(name, input, parser) start = input.offset r = ::Raabro::Tree.new(name, :nott, input) c = _parse(parser, input) r.children << c r.length = 0 r.result = c.result == 1 ? 0 : 1 input.offset = start r end
def parse(input, opts={})
def parse(input, opts={}) d = opts[:debug].to_i opts[:rewrite] = false if d > 0 opts[:all] = false if d > 1 opts[:prune] = false if d > 2 opts[:prune] = true unless opts.has_key?(:prune) root = self.respond_to?(:root) ? :root : @last t = if opts[:all] == false _parse(root, Raabro::Input.new(input, opts)) else all(nil, Raabro::Input.new(input, opts), root) end return reparse_for_error(input, opts, t) if opts[:error] && t.result != 1 return nil if opts[:prune] != false && t.result != 1 t = t.children.first if t.parter == :all return rewrite(t) if opts[:rewrite] != false t end
def ren(name, input, parser)
def ren(name, input, parser) r = _parse(parser, input) r.name = name r end
def rep(name, input, parser, min, max=0)
def rep(name, input, parser, min, max=0) min = 0 if min == nil || min < 0 max = nil if max.nil? || max < 1 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 c.length < 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 reparse_for_error(input, opts, t)
def reparse_for_error(input, opts, t) t = opts[:prune] == false ? t : parse(input, opts.merge(error: false, rewrite: false, prune: false)) bro.pp(t, colours: true) t.extract_error end
def rewrite(tree)
def rewrite(tree) return !! methods.find { |m| m.to_s.start_with?('rewrite_') } if tree == 0 # return true when "rewrite_xxx" methods seem to have been provided send("rewrite_#{tree.name}", tree) end
def rewrite_(tree)
def rewrite_(tree) t = tree.lookup(nil) t ? rewrite(t) : nil end
def rex(name, input, regex_or_string)
def rex(name, input, regex_or_string) _match(name, input, :rex, Regexp.new(regex_or_string)) end
def seq(name, input, *parsers)
def seq(name, input, *parsers) r = ::Raabro::Tree.new(name, :seq, input) start = input.offset c = nil loop do pa = parsers.shift break unless pa if parsers.first == '!' parsers.shift c = nott(nil, input, pa) r.children << c elsif 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
def str(name, input, string)
def str(name, input, string) _match(name, input, :str, string) end