#!/usr/bin/awk -f
# vim: ts=4 sw=4 et ft=awk
#---------------------------------------------------------------------------#
# Copyright (C) 2020, 2025 The Mercury team.
# This file may only be copied under the terms of the GNU General
# Public License - see the file COPYING in the Mercury distribution.
#---------------------------------------------------------------------------#
#
# The input to this file, make_optimization_options_db, has one line
# for every optimization option. Each line must have either three
# or four fields.
#
# - The first field indicates what kind of option this is. It must be
#   one of "bool", "int" and "string".
#
# - The second field gives the initial value of the option.
#
#   - If the option is boolean, the initial value must be either "n" or "y".
#
#   - If the option is an integer, the initial value must be an integer.
#
#   - If the option is a string, the initial value will be set
#     to the empty string regardless of the contents of the second field.
#
# - The third field is the name of the option in options.m.
#
# - The fourth field, if it exists, gives the name of the option
#   in the file this script helps generate, optimization_options.m.
#   If there is no fourth field, the name is taken to be the same
#   as the name in the third field.
#
#   For boolean options, the name in make_optimization_options_db should be
#   a verb, because when we generate a bespoke type for it, its two function
#   symbols will be named "verb" and "do_not_verb".
#

BEGIN {
        next_bool_opt = 0;
        next_int_opt = 0;
        next_string_opt = 0;

        next_bool_n_opt = 0;
        next_bool_y_opt = 0;
    }
    {
        if (NF == 3 || NF == 4) {
            kind = $1;
            initial_value = $2;
            option_name = $3;
            if (NF == 4) {
                option_field_name = $4;
            } else {
                option_field_name = $3;
            }

            if (kind == "bool") {
                bool_opts[next_bool_opt] = option_name;
                bool_field_names[next_bool_opt] = option_field_name;
                if (initial_value == "y") {
                    bool_defaults[next_bool_opt] = option_field_name;

                    bool_y_opts[next_bool_y_opt] = option_name;
                    next_bool_y_opt++;
                } else if (initial_value == "n") {
                    bool_defaults[next_bool_opt] = "do_not_" option_field_name;

                    bool_n_opts[next_bool_n_opt] = option_name;
                    next_bool_n_opt++;
                } else {
                    printf("ERROR: bad bool default <%s>\n", $0);
                }
                bool_initial_value[next_bool_opt] = initial_value;
                next_bool_opt++;
            } else if (kind == "int") {
                int_opts[next_int_opt] = $3;
                int_field_names[next_int_opt] = option_field_name;
                int_defaults[next_int_opt] = initial_value + 0;
                next_int_opt++;
            } else if (kind == "string") {
                string_opts[next_string_opt] = $3;
                string_field_names[next_string_opt] = option_field_name;
                # String options are always initialized to the empty string.
                # To avoid this messing up awk's field counting, we require
                # a dash to fill in the black.
                if (initial_value == "-") {
                    string_defaults[next_int_opt] = "";
                } else {
                    printf("ERROR: bad string default <%s>\n", $0);
                }
                next_string_opt++;
            } else {
                printf("ERROR: line with unknown type: <%s>\n", $0);
            }
        } else {
            printf("ERROR: line with %d fields: <%s>\n", NF, $0);
        }
    }
END {
        doc_switch_type_name = "documented_optimization_option";
        doc_switch_type_functor_name = "doc_oo";
        switch_type_name = "optimization_option";
        doc_switch_var_name = "DocOptOption";
        switch_var_name = "OptOption";
        record_type_name = "opt_tuple";
        record_var_name = "OptTuple";

        for (i = 0; i < next_bool_opt; i++) {
            printf(":- type maybe_%s\n", bool_field_names[i]);
            printf("    --->    %s\n", bool_field_names[i]);
            printf("    ;       do_not_%s.\n", bool_field_names[i]);
        }

        printf("\n%%---------------------%%\n\n");

        printf(":- type %s\n", switch_type_name);
        for (i = 0; i < next_bool_opt; i++) {
            prefix=";";
            suffix="";

            if (i == 0)
                prefix = "--->";

            printf("%-4s%-4s%-4soo_%s(bool)%s\n",
                "", prefix, "", bool_field_names[i], suffix);
        }
        for (i = 0; i < next_int_opt; i++) {
            prefix=";";
            suffix="";
            printf("%-4s%-4s%-4soo_%s(int)%s\n", "",
                prefix, "", int_field_names[i], suffix);
        }
        for (i = 0; i < next_string_opt; i++) {
            prefix=";";
            suffix="";

            printf("%-4s%-4s%-4soo_%s(string)%s\n",
                "", prefix, "", string_field_names[i], suffix);
        }
        printf("%-4s%-4s%-4soo_opt_level(int)\n", "", prefix, "");
        printf("%-4s%-4s%-4soo_opt_for_space.\n", "", prefix, "");

        printf("\n%%---------------------%%\n\n");

        printf(":- type %s\n", record_type_name);
        printf("%-4s--->%-4s%s(\n", "", "", record_type_name);
        for (i = 0; i < next_bool_opt; i++) {
            suffix = ",";
            printf("%-16sot_%-26s :: maybe_%s%s\n",
                "", bool_field_names[i], bool_field_names[i], suffix);
        }
        for (i = 0; i < next_int_opt; i++) {
            suffix = ",";
            printf("%-16sot_%-26s :: int%s\n", "", int_field_names[i], suffix);
        }
        for (i = 0; i < next_string_opt; i++) {
            suffix = ",";
            if (i == next_string_opt-1)
                suffix = "";

            printf("%-16sot_%-26s :: string%s\n",
                "", string_field_names[i], suffix);
        }
        printf("%-12s).\n", "");

        # Put this predicate first, even though it is the least important
        # exported predicate here and should therefore come last. The reason
        # for that is that opts_enabled_at_level is not generated here.
        # Instead, it is defined in make_optimization_options_db_end,
        # which *guarantees* that it will come after any predicate defined
        # by the output of this script. We put this predicate at the front
        # to avoid putting it in the middle of a related-to-each-other
        # predicates.
        printf("\n");
        printf(":- pred bool_option_initial_n_y(%s::out, %s::out) is det.\n",
            "list(option)", "list(option)");

        printf("\n");
        printf(":- pred process_optimization_options(option_table::in,\n");
        printf("%-4slist(%s)::in, %s::out) is det.\n",
            "", switch_type_name, record_type_name);
        printf("\n");

        printf(":- type %s\n", doc_switch_type_name);
        printf("%-4s--->%-4s%s(\n", "", "", doc_switch_type_functor_name);
        printf("%-16s%s,\n", "", switch_type_name);
        printf("%-16soption,\n", "");
        printf("%-16soption_data_bool_int\n", "");
        printf("%-12s).\n", "");
        printf("\n");

        printf(":- type option_data_bool_int =< option_data\n");
        printf("%-4s--->%-4s%s\n", "", "", "bool(bool)");
        printf("%-4s;%-7s%s\n", "", "", "int(int).");
        printf("\n");

        printf(":- pred opts_enabled_at_level(int::in, list(string)::out,\n");
        printf("%-4slist(%s)::out) is semidet.\n", "", doc_switch_type_name);

        printf("\n");
        printf("%%---------------------------------------------------------------------------%%\n");
        printf("\n");

        printf(":- implementation.\n");

        printf("\n");
        printf(":- import_module int.\n");
        printf(":- import_module map.\n");
        printf(":- import_module string.\n");

        printf("\n%%---------------------%%\n");

        printf("\n");
        printf("bool_option_initial_n_y(No, Yes) :-\n");
        printf("%-4sNo = [\n", "");
        for (i = 0; i < next_bool_n_opt; i++) {
            suffix = ",";
            if (i == next_bool_n_opt-1) {
                suffix = "";
            }
            printf("%-8soptopt_%s%s\n", "", bool_n_opts[i], suffix);
        }
        printf("%-4s],\n", "");
        printf("%-4sYes = [\n", "");
        for (i = 0; i < next_bool_y_opt; i++) {
            suffix = ",";
            if (i == next_bool_y_opt-1) {
                suffix = "";
            }
            printf("%-8soptopt_%s%s\n", "", bool_y_opts[i], suffix);
        }
        printf("%-4s].\n", "");

        printf("\n%%---------------------%%\n\n");

        printf("process_optimization_options(OptionTable, OptOptions, !:OptTuple) :-\n");
        printf("%-4s!:OptTuple = init_opt_tuple,\n", "");
        printf("%-4slist.foldl2(\n", "");
        printf("%-8supdate_opt_tuple(not_from_opt_level, OptionTable),\n", "");
        printf("%-8sOptOptions, !OptTuple, not_seen_opt_level, MaybeSeenOptLevel),\n", "");
        printf("%-4s(\n", "");
        printf("%-8sMaybeSeenOptLevel = not_seen_opt_level,\n", "");
        printf("%-8sget_default_opt_level(OptionTable, DefaultOptLevel),\n", "");
        printf("%-8sset_opts_upto_level(OptionTable, 0, DefaultOptLevel,\n",
            "");
        printf("%-12s!OptTuple, MaybeSeenOptLevel, _)\n", "");
        printf("%-4s;\n", "");
        printf("%-8sMaybeSeenOptLevel = seen_opt_level\n", "");
        printf("%-4s).\n", "");

        printf("\n%%---------------------%%\n");

        printf("\n");
        printf(":- func init_opt_tuple = %s.\n\n", record_type_name);

        printf("init_opt_tuple =\n");
        printf("%-4s%s(\n", "", record_type_name);
        for (i = 0; i < next_bool_opt; i++) {
            printf("%-8s%s,\n", "", bool_defaults[i]);
        }
        for (i = 0; i < next_int_opt; i++) {
            printf("%-8s%s,\n", "", int_defaults[i]);
        }
        for (i = 0; i < next_string_opt; i++) {
            suffix = ",";
            if (i == next_string_opt-1) {
                suffix = "";
            }
            printf("%-8s%s%s\n", "", "\"\"", suffix);
        }
        printf("%-4s).\n", "");

        printf("\n%%---------------------%%\n");

        printf("\n");
        printf(":- type maybe_seen_opt_level\n");
        printf("%-4s%-8snot_seen_opt_level\n", "", "--->");
        printf("%-4s%-8sseen_opt_level.\n", "", ";");

        printf("\n");
        printf(":- type maybe_from_opt_level\n");
        printf("%-4s%-8snot_from_opt_level\n", "", "--->");
        printf("%-4s%-8sfrom_opt_level.\n", "", ";");

        printf("\n");
        printf(":- pred update_opt_tuple(maybe_from_opt_level::in, ");
        printf("option_table::in,\n");
        printf("%-4s%s::in, %s::in, %s::out,\n",
            "", switch_type_name, record_type_name, record_type_name);
        printf("%-4smaybe_seen_opt_level::in, maybe_seen_opt_level::out) is det.\n", "");
        printf("\n");

        # Each arm of the switch that handles an ordinary bool or int option
        # forwards its work to a separate tiny predicate. We used to generate
        # inline code for them, but this led to the Java bytecode generated
        # for update_opt_tuple to exceed the 64k limit on the size of a single
        # method. This was Mantis bug #522.

        printf("update_opt_tuple(FromOptLevel, OptionTable, %s, !%s,\n",
            switch_var_name, record_var_name);
        printf("%-8s!MaybeSeenOptLevel) :-\n",
            "", switch_var_name, record_var_name);
        printf("%-4srequire_complete_switch [%s]\n", "", switch_var_name);
        lparen_or_semi = "(";
        for (i = 0; i < next_bool_opt; i++) {
            printf("%-4s%s\n", "", lparen_or_semi);
            lparen_or_semi = ";";
            printf("%-8s%s = oo_%s(Bool),\n", "",
                switch_var_name, bool_field_names[i]);
            if (bool_field_names[i] == "opt_delay_slot") {
                printf("%-8supdate_opt_tuple_bool_%s(OptionTable, Bool, !%s)\n",
                    "", bool_field_names[i], record_var_name);
            } else {
                printf("%-8supdate_opt_tuple_bool_%s(Bool, !%s)\n",
                    "", bool_field_names[i], record_var_name);
            }
        }
        for (i = 0; i < next_int_opt; i++) {
            printf("%-4s%s\n", "", lparen_or_semi);
            lparen_or_semi = ";";
            printf("%-8s%s = oo_%s(N),\n",
                "", switch_var_name, int_field_names[i]);
            printf("%-8supdate_opt_tuple_int_%s(FromOptLevel, N, !%s)\n",
                "", int_field_names[i], record_var_name);
        }
        for (i = 0; i < next_string_opt; i++) {
            printf("%-4s%s\n", "", lparen_or_semi);
            lparen_or_semi = ";";
            printf("%-8s%s = oo_%s(Str),\n",
                "", switch_var_name, string_field_names[i]);
            printf("%-8s!%s ^ ot_%s := Str\n",
                "", record_var_name, string_field_names[i]);
        }
        printf("%-4s%s\n", "", lparen_or_semi);
        lparen_or_semi = ";";
        printf("%-8s%s = oo_opt_level(OptLevel),\n", "", switch_var_name);
        printf("%-8sset_opts_upto_level(OptionTable, 0, OptLevel, !%s, !.MaybeSeenOptLevel, _),\n",
            "", record_var_name);
        printf("%-8s!:MaybeSeenOptLevel = seen_opt_level\n", "");

        printf("%-4s%s\n", "", lparen_or_semi);
        printf("%-8s%s = oo_opt_for_space,\n", "", switch_var_name);
        printf("%-8sset_opts_for_space(!%s)\n", "", record_var_name);
        printf("%-4s).\n\n", "");

        for (i = 0; i < next_bool_opt; i++) {
            if (bool_field_names[i] == "opt_delay_slot") {
                printf(":- pred update_opt_tuple_bool_%s(",
                    bool_field_names[i]);
                printf("option_table::in, bool::in,\n");
                printf("%-4s%s::in, %s::out) is det.\n\n",
                    "", record_type_name, record_type_name);
                printf("update_opt_tuple_bool_%s(OptionTable, Bool, !%s) :-\n",
                    bool_field_names[i], record_var_name);
            } else {
                printf(":- pred update_opt_tuple_bool_%s(",
                    bool_field_names[i]);
                printf("bool::in,\n");
                printf("%-4s%s::in, %s::out) is det.\n\n",
                    "", record_type_name, record_type_name);
                printf("update_opt_tuple_bool_%s(Bool, !%s) :-\n",
                    bool_field_names[i], record_var_name);
            }

            printf("%-4sOldValue = !.%s ^ ot_%s,\n",
                "", record_var_name, bool_field_names[i]);
            printf("%-4s( if\n", "");
            if (bool_field_names[i] == "opt_delay_slot") {
                printf("%-8sgetopt.lookup_bool_option(OptionTable, have_delay_slot, yes),\n",
                    "");
            }
            printf("%-8sBool = yes\n", "");
            printf("%-4sthen\n", "");
            printf("%-8s(\n", "");
            printf("%-12sOldValue = do_not_%s,\n", "", bool_field_names[i]);
            printf("%-12s!%s ^ ot_%s := %s\n",
                "", record_var_name, bool_field_names[i], bool_field_names[i]);
            printf("%-8s;\n", "");
            printf("%-12sOldValue = %s\n", "", bool_field_names[i]);
            printf("%-8s)\n", "");
            printf("%-4selse\n", "");
            printf("%-8s(\n", "");
            printf("%-12sOldValue = do_not_%s\n", "", bool_field_names[i]);
            printf("%-8s;\n", "");
            printf("%-12sOldValue = %s,\n", "", bool_field_names[i]);
            printf("%-12s!%s ^ ot_%s := do_not_%s\n",
                "", record_var_name, bool_field_names[i], bool_field_names[i]);
            printf("%-8s)\n", "");
            printf("%-4s).\n\n", "");
        }

        for (i = 0; i < next_int_opt; i++) {
            printf(":- pred update_opt_tuple_int_%s(\n",
                int_field_names[i]);
            printf("%-4smaybe_from_opt_level::in, int::in,\n", "");
            printf("%-4s%s::in, %s::out) is det.\n\n",
                "", record_type_name, record_type_name);
            printf("update_opt_tuple_int_%s(FromOptLevel, N, !%s) :-\n",
                int_field_names[i], record_var_name);

            printf("%-4s(\n", "");
            printf("%-8sFromOptLevel = not_from_opt_level,\n", "");
            printf("%-8s!%s ^ ot_%s := N\n",
                "", record_var_name, int_field_names[i]);
            printf("%-4s;\n", "");
            printf("%-8sFromOptLevel = from_opt_level,\n", "");
            printf("%-8sOldN = !.%s ^ ot_%s,\n",
                "", record_var_name, int_field_names[i]);
            printf("%-8s!%s ^ ot_%s := int.max(OldN, N)\n",
                "", record_var_name, int_field_names[i]);
            printf("%-4s).\n\n", "");
        }

        ###################################################################

        # The code from here to the end of the script generates code that
        # should be included in the special_handler predicate in options.m.

        handler_file = "handler_file";
        arm_start = "(";
        for (i = 0; i < next_bool_opt; i++) {
            printf("%-8s%s\n", "", arm_start) > handler_file;
            arm_start = ";";
            printf("%-12sOption = optopt_%s,\n",
                "", bool_opts[i]) > handler_file;
            printf("%-12sSpecialData = bool(Bool),\n", "") > handler_file;
            printf("%-12sOptOption = oo_%s(Bool)\n",
                "", bool_field_names[i]) > handler_file;
        }
        for (i = 0; i < next_int_opt; i++) {
            printf("%-8s;\n", "") > handler_file;
            printf("%-12sOption = optopt_%s,\n",
                "", int_opts[i]) > handler_file;
            printf("%-12sSpecialData = int(N),\n", "") > handler_file;
            printf("%-12sOptOption = oo_%s(N)\n",
                "", int_field_names[i]) > handler_file;
        }
        for (i = 0; i < next_string_opt; i++) {
            printf("%-8s;\n", "") > handler_file;
            printf("%-12sOption = optopt_%s,\n",
                "", string_opts[i]) > handler_file;
            printf("%-12sSpecialData = string(Str),\n", "") > handler_file;
            printf("%-12sOptOption = oo_%s(Str)\n",
                "", string_field_names[i]) > handler_file;
        }

        printf("%-8s;\n", "") > handler_file;
        printf("%-12sOption = opt_level,\n", "") > handler_file;
        printf("%-12sSpecialData = int(N),\n", "") > handler_file;
        printf("%-12sOptOption = oo_opt_level(N)\n", "") > handler_file;

        printf("%-8s;\n", "") > handler_file;
        printf("%-12sOption = opt_space,\n", "") > handler_file;
        printf("%-12sSpecialData = none,\n", "") > handler_file;
        printf("%-12sOptOption = oo_opt_for_space\n", "") > handler_file;

        printf("%-8s),\n", "") > handler_file;
    }
