    /*
     *  call-seq:
     *     QueryParser.new(options = {}) -> QueryParser
     *
     *  Create a new QueryParser. The QueryParser is used to convert string
     *  queries into Query objects. The options are;
     *
     *  === Options
     *
     *  :default_field::         Default: "*" (all fields). The default field to
     *                           search when no field is specified in the search
     *                           string. It can also be an array of fields.
     *  :analyzer::              Default: StandardAnalyzer. Analyzer used by the
     *                           query parser to parse query terms
     *  :wild_card_downcase::    Default: true. Specifies whether wild-card queries
     *                           and range queries should be downcased or not since
     *                           they are not passed through the parser
     *  :fields::                Default: []. Lets the query parser know what
     *                           fields are available for searching, particularly
     *                           when the "*" is specified as the search field
     *  :tokenized_fields::      Default: :fields. Lets the query parser know which
     *                           fields are tokenized so it knows which fields to
     *                           run the analyzer over.
     *  :validate_fields::       Default: false. Set to true if you want an
     *                           exception to be raised if there is an attempt to
     *                           search a non-existent field
     *  :or_default::            Default: true. Use "OR" as the default boolean
     *                           operator
     *  :default_slop::          Default: 0. Default slop to use in PhraseQuery
     *  :handle_parse_errors::   Default: true. QueryParser will quietly handle all
     *                           parsing errors internally. If you'd like to handle
     *                           them yourself, set this parameter to false.
     *  :clean_string::          Default: true. QueryParser will do a quick
     *                           once-over the query string make sure that quotes
     *                           and brackets match up and special characters are
     *                           escaped
     *  :max_clauses::           Default: 512. the maximum number of clauses
     *                           allowed in boolean queries and the maximum number
     *                           of terms allowed in multi, prefix, wild-card or
     *                           fuzzy queries when those queries are generated by
     *                           rewriting other queries
     *  :use_keywords::          Default: true. By default AND, OR, NOT and REQ are
     *                           keywords used by the query parser. Sometimes this
     *                           is undesirable. For example, if your application
     *                           allows searching for US states by their
     *                           abbreviation, then OR will be a common query
     *                           string. By setting :use_keywords to false, OR will
     *                           no longer be a keyword allowing searches for the
     *                           state of Oregon. You will still be able to use
     *                           boolean queries by using the + and - characters.
     *  :use_typed_range_query:: Default: false. Use TypedRangeQuery instead of
     *                           the standard RangeQuery when parsing
     *                           range queries. This is useful if you have number
     *                           fields which you want to perform range queries
     *                           on. You won't need to pad or normalize the data
     *                           in the field in anyway to get correct results.
     *                           However, performance will be a lot slower for
     *                           large indexes, hence the default.
     *                           Note: the default is set to true in the Index
     *                           class.
     */
    static VALUE
    frb_qp_init(int argc, VALUE *argv, VALUE self)
    {
        VALUE roptions = Qnil;
        VALUE rval;
        Analyzer *analyzer = NULL;
        bool has_options = false;

        HashSet *all_fields = NULL;
        HashSet *tkz_fields = NULL;
        HashSet *def_fields = NULL;
        QParser *qp;

        if (rb_scan_args(argc, argv, "01", &roptions) > 0) {
            if (TYPE(roptions) == T_HASH) {
                has_options = true;
                if (Qnil != (rval = rb_hash_aref(roptions, sym_default_field))) {
                    def_fields = frb_get_fields(rval);
                }
                if (Qnil != (rval = rb_hash_aref(roptions, sym_analyzer))) {
                    analyzer = frb_get_cwrapped_analyzer(rval);
                }
                if (Qnil != (rval = rb_hash_aref(roptions, sym_all_fields))) {
                    all_fields = frb_get_fields(rval);
                }
                if (Qnil != (rval = rb_hash_aref(roptions, sym_fields))) {
                    all_fields = frb_get_fields(rval);
                }
                if (Qnil != (rval = rb_hash_aref(roptions, sym_tkz_fields))) {
                    tkz_fields = frb_get_fields(rval);
                }
            } else {
                def_fields = frb_get_fields(roptions);
                roptions = Qnil;
            }
        }
        if (all_fields == NULL) {
            all_fields = hs_new_ptr(NULL);
        }

        if (!analyzer) {
            analyzer = mb_standard_analyzer_new(true);
        }

        qp = qp_new(analyzer);
        hs_destroy(qp->all_fields);
        hs_destroy(qp->def_fields);
        //hs_destroy(qp->tokenized_fields);

        if (def_fields) hs_safe_merge(all_fields, def_fields);
        if (tkz_fields) hs_safe_merge(all_fields, tkz_fields);
        qp->all_fields = all_fields;
        qp->def_fields = def_fields ? def_fields : all_fields;
        qp->tokenized_fields = tkz_fields ? tkz_fields : all_fields;
        qp->fields_top->fields = def_fields;

        qp->allow_any_fields = true;
        qp->clean_str = true;
        qp->handle_parse_errors = true;
        /* handle options */
        if (roptions != Qnil) {
            if (Qnil != (rval = rb_hash_aref(roptions, sym_handle_parse_errors))) {
                qp->handle_parse_errors = RTEST(rval);
            }
            if (Qnil != (rval = rb_hash_aref(roptions, sym_validate_fields))) {
                qp->allow_any_fields = !RTEST(rval);
            }
            if (Qnil != (rval = rb_hash_aref(roptions, sym_wild_card_downcase))) {
                qp->wild_lower = RTEST(rval);
            }
            if (Qnil != (rval = rb_hash_aref(roptions, sym_or_default))) {
                qp->or_default = RTEST(rval);
            }
            if (Qnil != (rval = rb_hash_aref(roptions, sym_default_slop))) {
                qp->def_slop = FIX2INT(rval);
            }
            if (Qnil != (rval = rb_hash_aref(roptions, sym_clean_string))) {
                qp->clean_str = RTEST(rval);
            }
            if (Qnil != (rval = rb_hash_aref(roptions, sym_max_clauses))) {
                qp->max_clauses = FIX2INT(rval);
            }
            if (Qnil != (rval = rb_hash_aref(roptions, sym_use_keywords))) {
                qp->use_keywords = RTEST(rval);
            }
            if (Qnil != (rval = rb_hash_aref(roptions, sym_use_typed_range_query))){
                qp->use_typed_range_query = RTEST(rval);
            }
        }
        Frt_Wrap_Struct(self, frb_qp_mark, frb_qp_free, qp);
        object_add(qp, self);
        return self;
    }