String < EmptyObject {

Patterns < EmptyObject {
  Base < Common::Patterns::Base { }

  AnyCharacter    < Base {
    construct: Constructions::AnyCharacter.new
  }
  Character       < Base { var code
    construct: Constructions::Character.new(code: code)
  }
  CharacterString < Base { var codes
    construct: Constructions::CharacterString.new(codes: codes)
  }
  CharacterSet    < Base { var codes
    construct: Constructions::CharacterSet.new(codes: codes)
  }
  CharacterRange  < Base { var start, var stop
    construct: Constructions::CharacterRange.new(start: start, stop: stop)
  }
}

Constructions < EmptyObject {
  Base < Common::Constructions::Base { }

  AnyCharacter    < Base { }
  Character       < Base { var code }
  CharacterString < Base { var codes }
  CharacterSet    < Base { var codes }
  CharacterRange  < Base { var start, var stop }
}

Constructions << {
  AnyCharacter << { bytecode: |g| {
    ##
    # overall_fail! if idx == subject.size
    # idx = idx + 1

    g.push_idx
      g.push_subject; g.send(:size, 0)
    g.send(:"==", 1)
    g.goto_if_true(g.overall_fail)

    g.meta_push_1
    g.increment_idx
  }}

  Character << { bytecode: |g| {
    ##
    # overall_fail! unless chr == subject[idx]
    # idx = idx + 1

    g.push_literal(code.chr)
      g.push_subject_chr_at_idx
    g.send(:"==", 1)
    g.goto_if_false(g.overall_fail)

    g.meta_push_1
    g.increment_idx
  }}

  # TODO: get rid of this ugly hack.
  STRING_HACK_PATCH: Ruby.eval("
    class ::String
      def compare_substring_safe(*args)
        compare_substring(*args)
      rescue Exception
        nil
      end
    end
  ")

  CharacterString << { bytecode: |g| {
    string = codes.map(&:chr).join
    ##
    # begin
    #   overall_fail! unless string.compare_substring(subject, idx, string.size) == 0
    # rescue
    #   overall_fail!
    # end
    # idx = idx + string.size

    g.push_literal(string)
      g.push_subject
      g.push_idx
      g.push_int(string.size)
    g.send(:compare_substring_safe, 3)
      g.meta_push_0

    g.goto_if_not_equal(g.overall_fail)

    g.push_int(string.size)
    g.increment_idx
  }}

  CharacterSet << { bytecode: |g| {
    string = codes.map(&:chr).join
    ##
    # overall_fail! unless subject[idx]
    # overall_fail! unless string.find_string(subject[idx], 0)
    # idx = idx + 1

    g.push_subject_chr_at_idx
    g.goto_if_nil(g.overall_fail) # TODO: avoid second lookup using register instructions

    g.push_literal(string)
      g.push_subject_chr_at_idx
      g.meta_push_0
    g.send(:find_string, 2)
    g.goto_if_false(g.overall_fail)

    g.meta_push_1
    g.increment_idx
  }}

  CharacterRange << { bytecode: |g| {
    codes = Range.new(start,stop); string = codes.map(&:chr).join
    ##
    # overall_fail! unless subject[idx]
    # overall_fail! unless string.find_string(subject[idx], 0)
    # idx = idx + 1

    g.push_subject_chr_at_idx
    g.goto_if_nil(g.overall_fail) # TODO: avoid second lookup using register instructions

    g.push_literal(string)
      g.push_subject_chr_at_idx
      g.meta_push_0
    g.send(:find_string, 2)
    g.goto_if_false(g.overall_fail)

    g.meta_push_1
    g.increment_idx
  }}
}

}