module Weskit::WML
class KPEGParser
  # :stopdoc:

    # This is distinct from setup_parser so that a standalone parser
    # can redefine #initialize and still have access to the proper
    # parser setup code.
    def initialize(str, debug=false)
      setup_parser(str, debug)
    end



    # Prepares for parsing +str+.  If you define a custom initialize you must
    # call this method before #parse
    def setup_parser(str, debug=false)
      @string = str
      @pos = 0
      @memoizations = Hash.new { |h,k| h[k] = {} }
      @result = nil
      @failed_rule = nil
      @failing_rule_offset = -1

      setup_foreign_grammar
    end

    attr_reader :string
    attr_reader :failing_rule_offset
    attr_accessor :result, :pos

    
    def current_column(target=pos)
      if c = string.rindex("\n", target-1)
        return target - c - 1
      end

      target + 1
    end

    def current_line(target=pos)
      cur_offset = 0
      cur_line = 0

      string.each_line do |line|
        cur_line += 1
        cur_offset += line.size
        return cur_line if cur_offset >= target
      end

      -1
    end

    def lines
      lines = []
      string.each_line { |l| lines << l }
      lines
    end



    def get_text(start)
      @string[start..@pos-1]
    end

    def show_pos
      width = 10
      if @pos < width
        "#{@pos} (\"#{@string[0,@pos]}\" @ \"#{@string[@pos,width]}\")"
      else
        "#{@pos} (\"... #{@string[@pos - width, width]}\" @ \"#{@string[@pos,width]}\")"
      end
    end

    def failure_info
      l = current_line @failing_rule_offset
      c = current_column @failing_rule_offset

      if @failed_rule.kind_of? Symbol
        info = self.class::Rules[@failed_rule]
        "line #{l}, column #{c}: failed rule '#{info.name}' = '#{info.rendered}'"
      else
        "line #{l}, column #{c}: failed rule '#{@failed_rule}'"
      end
    end

    def failure_caret
      l = current_line @failing_rule_offset
      c = current_column @failing_rule_offset

      line = lines[l-1]
      "#{line}\n#{' ' * (c - 1)}^"
    end

    def failure_character
      l = current_line @failing_rule_offset
      c = current_column @failing_rule_offset
      lines[l-1][c-1, 1]
    end

    def failure_oneline
      l = current_line @failing_rule_offset
      c = current_column @failing_rule_offset

      char = lines[l-1][c-1, 1]

      if @failed_rule.kind_of? Symbol
        info = self.class::Rules[@failed_rule]
        "@#{l}:#{c} failed rule '#{info.name}', got '#{char}'"
      else
        "@#{l}:#{c} failed rule '#{@failed_rule}', got '#{char}'"
      end
    end

    class ParseError < RuntimeError
    end

    def raise_error
      raise ParseError, failure_oneline
    end

    def show_error(io=STDOUT)
      error_pos = @failing_rule_offset
      line_no = current_line(error_pos)
      col_no = current_column(error_pos)

      io.puts "On line #{line_no}, column #{col_no}:"

      if @failed_rule.kind_of? Symbol
        info = self.class::Rules[@failed_rule]
        io.puts "Failed to match '#{info.rendered}' (rule '#{info.name}')"
      else
        io.puts "Failed to match rule '#{@failed_rule}'"
      end

      io.puts "Got: #{string[error_pos,1].inspect}"
      line = lines[line_no-1]
      io.puts "=> #{line}"
      io.print(" " * (col_no + 3))
      io.puts "^"
    end

    def set_failed_rule(name)
      if @pos > @failing_rule_offset
        @failed_rule = name
        @failing_rule_offset = @pos
      end
    end

    attr_reader :failed_rule

    def match_string(str)
      len = str.size
      if @string[pos,len] == str
        @pos += len
        return str
      end

      return nil
    end

    def scan(reg)
      if m = reg.match(@string[@pos..-1])
        width = m.end(0)
        @pos += width
        return true
      end

      return nil
    end

    if "".respond_to? :getbyte
      def get_byte
        if @pos >= @string.size
          return nil
        end

        s = @string.getbyte @pos
        @pos += 1
        s
      end
    else
      def get_byte
        if @pos >= @string.size
          return nil
        end

        s = @string[@pos]
        @pos += 1
        s
      end
    end

    def parse(rule=nil)
      # We invoke the rules indirectly via apply
      # instead of by just calling them as methods because
      # if the rules use left recursion, apply needs to
      # manage that.

      if !rule
        apply(:_root)
      else
        method = rule.gsub("-","_hyphen_")
        apply :"_#{method}"
      end
    end

    class MemoEntry
      def initialize(ans, pos)
        @ans = ans
        @pos = pos
        @result = nil
        @set = false
        @left_rec = false
      end

      attr_reader :ans, :pos, :result, :set
      attr_accessor :left_rec

      def move!(ans, pos, result)
        @ans = ans
        @pos = pos
        @result = result
        @set = true
        @left_rec = false
      end
    end

    def external_invoke(other, rule, *args)
      old_pos = @pos
      old_string = @string

      @pos = other.pos
      @string = other.string

      begin
        if val = __send__(rule, *args)
          other.pos = @pos
          other.result = @result
        else
          other.set_failed_rule "#{self.class}##{rule}"
        end
        val
      ensure
        @pos = old_pos
        @string = old_string
      end
    end

    def apply_with_args(rule, *args)
      memo_key = [rule, args]
      if m = @memoizations[memo_key][@pos]
        @pos = m.pos
        if !m.set
          m.left_rec = true
          return nil
        end

        @result = m.result

        return m.ans
      else
        m = MemoEntry.new(nil, @pos)
        @memoizations[memo_key][@pos] = m
        start_pos = @pos

        ans = __send__ rule, *args

        lr = m.left_rec

        m.move! ans, @pos, @result

        # Don't bother trying to grow the left recursion
        # if it's failing straight away (thus there is no seed)
        if ans and lr
          return grow_lr(rule, args, start_pos, m)
        else
          return ans
        end

        return ans
      end
    end

    def apply(rule)
      if m = @memoizations[rule][@pos]
        @pos = m.pos
        if !m.set
          m.left_rec = true
          return nil
        end

        @result = m.result

        return m.ans
      else
        m = MemoEntry.new(nil, @pos)
        @memoizations[rule][@pos] = m
        start_pos = @pos

        ans = __send__ rule

        lr = m.left_rec

        m.move! ans, @pos, @result

        # Don't bother trying to grow the left recursion
        # if it's failing straight away (thus there is no seed)
        if ans and lr
          return grow_lr(rule, nil, start_pos, m)
        else
          return ans
        end

        return ans
      end
    end

    def grow_lr(rule, args, start_pos, m)
      while true
        @pos = start_pos
        @result = m.result

        if args
          ans = __send__ rule, *args
        else
          ans = __send__ rule
        end
        return nil unless ans

        break if @pos <= m.pos

        m.move! ans, @pos, @result
      end

      @result = m.result
      @pos = m.pos
      return m.ans
    end

    class RuleInfo
      def initialize(name, rendered)
        @name = name
        @rendered = rendered
      end

      attr_reader :name, :rendered
    end

    def self.rule_info(name, rendered)
      RuleInfo.new(name, rendered)
    end


  # :startdoc:


  attr_accessor :result

  private

  def strip_chars string, chars
    string[chars, string.length - chars * 2]
  end


  # :stopdoc:
  def setup_foreign_grammar; end

  # id = < /[a-z][a-z_]*/i > { text }
  def _id

    _save = self.pos
    while true # sequence
      _text_start = self.pos
      _tmp = scan(/\A(?i-mx:[a-z][a-z_]*)/)
      if _tmp
        text = get_text(_text_start)
      end
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  text ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_id unless _tmp
    return _tmp
  end

  # ids = (ids:i1 - "," - ids:i2 { i1 + i2 } | id:i { [i] })
  def _ids

    _save = self.pos
    while true # choice

      _save1 = self.pos
      while true # sequence
        _tmp = apply(:_ids)
        i1 = @result
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:__hyphen_)
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = match_string(",")
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:__hyphen_)
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:_ids)
        i2 = @result
        unless _tmp
          self.pos = _save1
          break
        end
        @result = begin;  i1 + i2 ; end
        _tmp = true
        unless _tmp
          self.pos = _save1
        end
        break
      end # end sequence

      break if _tmp
      self.pos = _save

      _save2 = self.pos
      while true # sequence
        _tmp = apply(:_id)
        i = @result
        unless _tmp
          self.pos = _save2
          break
        end
        @result = begin;  [i] ; end
        _tmp = true
        unless _tmp
          self.pos = _save2
        end
        break
      end # end sequence

      break if _tmp
      self.pos = _save
      break
    end # end choice

    set_failed_rule :_ids unless _tmp
    return _tmp
  end

  # item = (attribute | attributes | element)
  def _item

    _save = self.pos
    while true # choice
      _tmp = apply(:_attribute)
      break if _tmp
      self.pos = _save
      _tmp = apply(:_attributes)
      break if _tmp
      self.pos = _save
      _tmp = apply(:_element)
      break if _tmp
      self.pos = _save
      break
    end # end choice

    set_failed_rule :_item unless _tmp
    return _tmp
  end

  # items = (items:i1 items:i2 { i1 + i2 } | item:i { (i.is_a? Array) ? i : [i] })
  def _items

    _save = self.pos
    while true # choice

      _save1 = self.pos
      while true # sequence
        _tmp = apply(:_items)
        i1 = @result
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:_items)
        i2 = @result
        unless _tmp
          self.pos = _save1
          break
        end
        @result = begin;  i1 + i2 ; end
        _tmp = true
        unless _tmp
          self.pos = _save1
        end
        break
      end # end sequence

      break if _tmp
      self.pos = _save

      _save2 = self.pos
      while true # sequence
        _tmp = apply(:_item)
        i = @result
        unless _tmp
          self.pos = _save2
          break
        end
        @result = begin;  (i.is_a? Array) ? i : [i] ; end
        _tmp = true
        unless _tmp
          self.pos = _save2
        end
        break
      end # end sequence

      break if _tmp
      self.pos = _save
      break
    end # end choice

    set_failed_rule :_items unless _tmp
    return _tmp
  end

  # contents = items?:i { i.to_a }
  def _contents

    _save = self.pos
    while true # sequence
      _save1 = self.pos
      _tmp = apply(:_items)
      @result = nil unless _tmp
      unless _tmp
        _tmp = true
        self.pos = _save1
      end
      i = @result
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  i.to_a ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_contents unless _tmp
    return _tmp
  end

  # attr = - id:n - "=" - val:v - eol { Attribute.new n, *v }
  def _attr

    _save = self.pos
    while true # sequence
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_id)
      n = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = match_string("=")
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_val)
      v = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_eol)
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  Attribute.new n, *v ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_attr unless _tmp
    return _tmp
  end

  # multiple_attrs = - ids:n - "=" - vals:v - eol { n.reduce(Array.new) do |attrs, name|                    value =  v.shift or [nil]                    attrs << Attribute.new(name, *value)                  end }
  def _multiple_attrs

    _save = self.pos
    while true # sequence
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_ids)
      n = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = match_string("=")
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_vals)
      v = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_eol)
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  n.reduce(Array.new) do |attrs, name|
                   value =  v.shift or [nil]
                   attrs << Attribute.new(name, *value)
                 end ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_multiple_attrs unless _tmp
    return _tmp
  end

  # attribute = blk_lines attr:a blk_lines { a }
  def _attribute

    _save = self.pos
    while true # sequence
      _tmp = apply(:_blk_lines)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_attr)
      a = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_blk_lines)
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  a ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_attribute unless _tmp
    return _tmp
  end

  # attributes = blk_lines multiple_attrs:a blk_lines { a }
  def _attributes

    _save = self.pos
    while true # sequence
      _tmp = apply(:_blk_lines)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_multiple_attrs)
      a = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_blk_lines)
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  a ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_attributes unless _tmp
    return _tmp
  end

  # code = < /<<.*?>>/m > { [strip_chars(text, 2), {:code => true}] }
  def _code

    _save = self.pos
    while true # sequence
      _text_start = self.pos
      _tmp = scan(/\A(?m-ix:<<.*?>>)/)
      if _tmp
        text = get_text(_text_start)
      end
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  [strip_chars(text, 2), {:code => true}] ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_code unless _tmp
    return _tmp
  end

  # in_brackets = < /\(.*?\)/m > { [strip_chars(text, 1)] }
  def _in_brackets

    _save = self.pos
    while true # sequence
      _text_start = self.pos
      _tmp = scan(/\A(?m-ix:\(.*?\))/)
      if _tmp
        text = get_text(_text_start)
      end
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  [strip_chars(text, 1)] ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_in_brackets unless _tmp
    return _tmp
  end

  # in_quotes = < /".*?"/m > { [strip_chars(text, 1)] }
  def _in_quotes

    _save = self.pos
    while true # sequence
      _text_start = self.pos
      _tmp = scan(/\A(?m-ix:".*?")/)
      if _tmp
        text = get_text(_text_start)
      end
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  [strip_chars(text, 1)] ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_in_quotes unless _tmp
    return _tmp
  end

  # raw = < /.*/ > { [text.strip] }
  def _raw

    _save = self.pos
    while true # sequence
      _text_start = self.pos
      _tmp = scan(/\A(?-mix:.*)/)
      if _tmp
        text = get_text(_text_start)
      end
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  [text.strip] ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_raw unless _tmp
    return _tmp
  end

  # escaped = (in_quotes:s1 escaped:s2 { s1[0] += '"' + s2[0] ; s1 } | in_quotes)
  def _escaped

    _save = self.pos
    while true # choice

      _save1 = self.pos
      while true # sequence
        _tmp = apply(:_in_quotes)
        s1 = @result
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:_escaped)
        s2 = @result
        unless _tmp
          self.pos = _save1
          break
        end
        @result = begin;  s1[0] += '"' + s2[0] ; s1 ; end
        _tmp = true
        unless _tmp
          self.pos = _save1
        end
        break
      end # end sequence

      break if _tmp
      self.pos = _save
      _tmp = apply(:_in_quotes)
      break if _tmp
      self.pos = _save
      break
    end # end choice

    set_failed_rule :_escaped unless _tmp
    return _tmp
  end

  # i18n = "_" - (in_brackets | in_quotes):s { [s[0], {:translatable => true}] }
  def _i18n

    _save = self.pos
    while true # sequence
      _tmp = match_string("_")
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end

      _save1 = self.pos
      while true # choice
        _tmp = apply(:_in_brackets)
        break if _tmp
        self.pos = _save1
        _tmp = apply(:_in_quotes)
        break if _tmp
        self.pos = _save1
        break
      end # end choice

      s = @result
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  [s[0], {:translatable => true}] ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_i18n unless _tmp
    return _tmp
  end

  # val = (val:v1 - "+" sp_lf val:v2 { v1[0] += v2[0] ; v1 } | escaped | i18n | code | in_brackets | in_quotes | raw)
  def _val

    _save = self.pos
    while true # choice

      _save1 = self.pos
      while true # sequence
        _tmp = apply(:_val)
        v1 = @result
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:__hyphen_)
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = match_string("+")
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:_sp_lf)
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:_val)
        v2 = @result
        unless _tmp
          self.pos = _save1
          break
        end
        @result = begin;  v1[0] += v2[0] ; v1 ; end
        _tmp = true
        unless _tmp
          self.pos = _save1
        end
        break
      end # end sequence

      break if _tmp
      self.pos = _save
      _tmp = apply(:_escaped)
      break if _tmp
      self.pos = _save
      _tmp = apply(:_i18n)
      break if _tmp
      self.pos = _save
      _tmp = apply(:_code)
      break if _tmp
      self.pos = _save
      _tmp = apply(:_in_brackets)
      break if _tmp
      self.pos = _save
      _tmp = apply(:_in_quotes)
      break if _tmp
      self.pos = _save
      _tmp = apply(:_raw)
      break if _tmp
      self.pos = _save
      break
    end # end choice

    set_failed_rule :_val unless _tmp
    return _tmp
  end

  # vals = (vals:v1 - "," - vals:v2 { v1 + v2 } | val:v { [v] })
  def _vals

    _save = self.pos
    while true # choice

      _save1 = self.pos
      while true # sequence
        _tmp = apply(:_vals)
        v1 = @result
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:__hyphen_)
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = match_string(",")
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:__hyphen_)
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:_vals)
        v2 = @result
        unless _tmp
          self.pos = _save1
          break
        end
        @result = begin;  v1 + v2 ; end
        _tmp = true
        unless _tmp
          self.pos = _save1
        end
        break
      end # end sequence

      break if _tmp
      self.pos = _save

      _save2 = self.pos
      while true # sequence
        _tmp = apply(:_val)
        v = @result
        unless _tmp
          self.pos = _save2
          break
        end
        @result = begin;  [v] ; end
        _tmp = true
        unless _tmp
          self.pos = _save2
        end
        break
      end # end sequence

      break if _tmp
      self.pos = _save
      break
    end # end choice

    set_failed_rule :_vals unless _tmp
    return _tmp
  end

  # amendment = - amending_tag:n - eol contents:c - closing_tag(n) - eol { Element.new(n, :amendment => true).push *c }
  def _amendment

    _save = self.pos
    while true # sequence
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_amending_tag)
      n = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_eol)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_contents)
      c = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply_with_args(:_closing_tag, n)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_eol)
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  Element.new(n, :amendment => true).push *c ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_amendment unless _tmp
    return _tmp
  end

  # regular = - opening_tag:n - eol contents:c - closing_tag(n) - eol { Element.new(n).push *c }
  def _regular

    _save = self.pos
    while true # sequence
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_opening_tag)
      n = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_eol)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_contents)
      c = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply_with_args(:_closing_tag, n)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:__hyphen_)
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_eol)
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  Element.new(n).push *c ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_regular unless _tmp
    return _tmp
  end

  # element = blk_lines (amendment | regular):e blk_lines { e }
  def _element

    _save = self.pos
    while true # sequence
      _tmp = apply(:_blk_lines)
      unless _tmp
        self.pos = _save
        break
      end

      _save1 = self.pos
      while true # choice
        _tmp = apply(:_amendment)
        break if _tmp
        self.pos = _save1
        _tmp = apply(:_regular)
        break if _tmp
        self.pos = _save1
        break
      end # end choice

      e = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_blk_lines)
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  e ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_element unless _tmp
    return _tmp
  end

  # amending_tag = "[+" id:n "]" { n }
  def _amending_tag

    _save = self.pos
    while true # sequence
      _tmp = match_string("[+")
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_id)
      n = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = match_string("]")
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  n ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_amending_tag unless _tmp
    return _tmp
  end

  # closing_tag = "[/" id:n "]" &{ n == m }
  def _closing_tag(m)

    _save = self.pos
    while true # sequence
      _tmp = match_string("[/")
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_id)
      n = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = match_string("]")
      unless _tmp
        self.pos = _save
        break
      end
      _save1 = self.pos
      _tmp = begin;  n == m ; end
      self.pos = _save1
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_closing_tag unless _tmp
    return _tmp
  end

  # opening_tag = "[" id:n "]" { n }
  def _opening_tag

    _save = self.pos
    while true # sequence
      _tmp = match_string("[")
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = apply(:_id)
      n = @result
      unless _tmp
        self.pos = _save
        break
      end
      _tmp = match_string("]")
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  n ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_opening_tag unless _tmp
    return _tmp
  end

  # eof = !.
  def _eof
    _save = self.pos
    _tmp = get_byte
    _tmp = _tmp ? nil : true
    self.pos = _save
    set_failed_rule :_eof unless _tmp
    return _tmp
  end

  # eol = ("
\n" | "\n")
  def _eol

    _save = self.pos
    while true # choice
      _tmp = match_string("\r\n")
      break if _tmp
      self.pos = _save
      _tmp = match_string("\n")
      break if _tmp
      self.pos = _save
      break
    end # end choice

    set_failed_rule :_eol unless _tmp
    return _tmp
  end

  # sp = (" " | "\t")
  def _sp

    _save = self.pos
    while true # choice
      _tmp = match_string(" ")
      break if _tmp
      self.pos = _save
      _tmp = match_string("\t")
      break if _tmp
      self.pos = _save
      break
    end # end choice

    set_failed_rule :_sp unless _tmp
    return _tmp
  end

  # - = sp*
  def __hyphen_
    while true
      _tmp = apply(:_sp)
      break unless _tmp
    end
    _tmp = true
    set_failed_rule :__hyphen_ unless _tmp
    return _tmp
  end

  # sp_lf = (sp | eol)*
  def _sp_lf
    while true

      _save1 = self.pos
      while true # choice
        _tmp = apply(:_sp)
        break if _tmp
        self.pos = _save1
        _tmp = apply(:_eol)
        break if _tmp
        self.pos = _save1
        break
      end # end choice

      break unless _tmp
    end
    _tmp = true
    set_failed_rule :_sp_lf unless _tmp
    return _tmp
  end

  # blk_lines = (- eol)*
  def _blk_lines
    while true

      _save1 = self.pos
      while true # sequence
        _tmp = apply(:__hyphen_)
        unless _tmp
          self.pos = _save1
          break
        end
        _tmp = apply(:_eol)
        unless _tmp
          self.pos = _save1
        end
        break
      end # end sequence

      break unless _tmp
    end
    _tmp = true
    set_failed_rule :_blk_lines unless _tmp
    return _tmp
  end

  # root = contents:c { Root.new.push *c }
  def _root

    _save = self.pos
    while true # sequence
      _tmp = apply(:_contents)
      c = @result
      unless _tmp
        self.pos = _save
        break
      end
      @result = begin;  Root.new.push *c ; end
      _tmp = true
      unless _tmp
        self.pos = _save
      end
      break
    end # end sequence

    set_failed_rule :_root unless _tmp
    return _tmp
  end

  Rules = {}
  Rules[:_id] = rule_info("id", "< /[a-z][a-z_]*/i > { text }")
  Rules[:_ids] = rule_info("ids", "(ids:i1 - \",\" - ids:i2 { i1 + i2 } | id:i { [i] })")
  Rules[:_item] = rule_info("item", "(attribute | attributes | element)")
  Rules[:_items] = rule_info("items", "(items:i1 items:i2 { i1 + i2 } | item:i { (i.is_a? Array) ? i : [i] })")
  Rules[:_contents] = rule_info("contents", "items?:i { i.to_a }")
  Rules[:_attr] = rule_info("attr", "- id:n - \"=\" - val:v - eol { Attribute.new n, *v }")
  Rules[:_multiple_attrs] = rule_info("multiple_attrs", "- ids:n - \"=\" - vals:v - eol { n.reduce(Array.new) do |attrs, name|                    value =  v.shift or [nil]                    attrs << Attribute.new(name, *value)                  end }")
  Rules[:_attribute] = rule_info("attribute", "blk_lines attr:a blk_lines { a }")
  Rules[:_attributes] = rule_info("attributes", "blk_lines multiple_attrs:a blk_lines { a }")
  Rules[:_code] = rule_info("code", "< /<<.*?>>/m > { [strip_chars(text, 2), {:code => true}] }")
  Rules[:_in_brackets] = rule_info("in_brackets", "< /\\(.*?\\)/m > { [strip_chars(text, 1)] }")
  Rules[:_in_quotes] = rule_info("in_quotes", "< /\".*?\"/m > { [strip_chars(text, 1)] }")
  Rules[:_raw] = rule_info("raw", "< /.*/ > { [text.strip] }")
  Rules[:_escaped] = rule_info("escaped", "(in_quotes:s1 escaped:s2 { s1[0] += '\"' + s2[0] ; s1 } | in_quotes)")
  Rules[:_i18n] = rule_info("i18n", "\"_\" - (in_brackets | in_quotes):s { [s[0], {:translatable => true}] }")
  Rules[:_val] = rule_info("val", "(val:v1 - \"+\" sp_lf val:v2 { v1[0] += v2[0] ; v1 } | escaped | i18n | code | in_brackets | in_quotes | raw)")
  Rules[:_vals] = rule_info("vals", "(vals:v1 - \",\" - vals:v2 { v1 + v2 } | val:v { [v] })")
  Rules[:_amendment] = rule_info("amendment", "- amending_tag:n - eol contents:c - closing_tag(n) - eol { Element.new(n, :amendment => true).push *c }")
  Rules[:_regular] = rule_info("regular", "- opening_tag:n - eol contents:c - closing_tag(n) - eol { Element.new(n).push *c }")
  Rules[:_element] = rule_info("element", "blk_lines (amendment | regular):e blk_lines { e }")
  Rules[:_amending_tag] = rule_info("amending_tag", "\"[+\" id:n \"]\" { n }")
  Rules[:_closing_tag] = rule_info("closing_tag", "\"[/\" id:n \"]\" &{ n == m }")
  Rules[:_opening_tag] = rule_info("opening_tag", "\"[\" id:n \"]\" { n }")
  Rules[:_eof] = rule_info("eof", "!.")
  Rules[:_eol] = rule_info("eol", "(\"
\\n\" | \"\\n\")")
  Rules[:_sp] = rule_info("sp", "(\" \" | \"\\t\")")
  Rules[:__hyphen_] = rule_info("-", "sp*")
  Rules[:_sp_lf] = rule_info("sp_lf", "(sp | eol)*")
  Rules[:_blk_lines] = rule_info("blk_lines", "(- eol)*")
  Rules[:_root] = rule_info("root", "contents:c { Root.new.push *c }")
  # :startdoc:
end

  end