%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2015-2024 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.
%-----------------------------------------------------------------------------%
%
% File: prog_item_stats.m.
%
% This module has facilities for gathering and printing statistics
% about how often different kinds of items and goals occur in parse trees.
%
%-----------------------------------------------------------------------------%

:- module parse_tree.prog_item_stats.
:- interface.

:- import_module parse_tree.prog_parse_tree.

:- import_module io.

%-----------------------------------------------------------------------------%

    % Gather statistics about the given augmented compilation unit,
    % and write them to the given stream.
    %
:- pred gather_and_write_item_stats(io.text_output_stream::in,
    aug_compilation_unit::in, io::di, io::uo) is det.

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%

:- implementation.

:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.maybe_error.
:- import_module parse_tree.prog_item.

:- import_module int.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module pair.
:- import_module string.

%-----------------------------------------------------------------------------%

gather_and_write_item_stats(Stream, AugCompUnit, !IO) :-
    gather_stats_in_aug_comp_unit(AugCompUnit, CompUnitStats),
    ParseTreeModuleSrc = AugCompUnit ^ acu_module_src,
    ModuleName = ParseTreeModuleSrc ^ ptms_module_name,
    write_comp_unit_stats(Stream, ModuleName, CompUnitStats, !IO).

%-----------------------------------------------------------------------------%

:- type item_stats
    --->    item_stats(
                % Each field contains the number of occurrences
                % of the named kind of item.
                %
                % We gather separate stats for some but not all kinds of
                % pragmas. For the ones we don't track individually, we
                % do record whether they are of a kind that is currently
                % processed in pass 2 or pass 3 by make_hlds_passes.m.
                item_num_clauses                    :: int,
                item_num_type_defn                  :: int,
                item_num_inst_defn                  :: int,
                item_num_mode_defn                  :: int,
                item_num_pred_decl                  :: int,
                item_num_mode_decl                  :: int,
                item_num_fim                        :: int,
                item_num_foreign_proc               :: int,
                item_num_foreign_enum               :: int,
                item_num_foreign_export_enum        :: int,
                item_num_pragma_decl_term           :: int,
                item_num_pragma_decl_term2          :: int,
                item_num_pragma_decl_other          :: int,
                item_num_marker_decl                :: int,
                item_num_pragma_impl                :: int,
                item_num_marker_impl                :: int,
                item_num_pragma_unused_args         :: int,
                item_num_pragma_exceptions          :: int,
                item_num_pragma_trailing            :: int,
                item_num_pragma_mm_tabling          :: int,
                item_num_promise                    :: int,
                item_num_typeclass                  :: int,
                item_num_instance                   :: int,
                item_num_initialise                 :: int,
                item_num_finalise                   :: int,
                item_num_mutable                    :: int,
                item_num_type_repn                  :: int
            ).

:- type goal_stats
    --->    goal_stats(
                % Each field contains the number of occurrences
                % of the named kind of goal.
                goal_num_conj                       :: int,
                goal_num_par_conj                   :: int,
                goal_num_true                       :: int,
                goal_num_disj                       :: int,
                goal_num_fail                       :: int,
                goal_num_some                       :: int,
                goal_num_all                        :: int,
                goal_num_some_state_vars            :: int,
                goal_num_all_state_vars             :: int,
                goal_num_promise_purity             :: int,
                goal_num_promise_eqv_solns          :: int,
                goal_num_promise_eqv_sets           :: int,
                goal_num_promise_arbitrary          :: int,
                goal_num_req_detism                 :: int,
                goal_num_req_compl_switch           :: int,
                goal_num_req_arm_detism             :: int,
                goal_num_disable_warnings           :: int,
                goal_num_trace                      :: int,
                goal_num_atomic                     :: int,
                goal_num_try                        :: int,
                goal_num_implies                    :: int,
                goal_num_equivalent                 :: int,
                goal_num_not                        :: int,
                goal_num_if_then_else               :: int,
                goal_num_event                      :: int,
                goal_num_call                       :: int,
                goal_num_unify                      :: int
            ).

:- type section_stats
    --->    section_stats(item_stats, goal_stats).

:- type comp_unit_stats == map(string, section_stats).

%-----------------------------------------------------------------------------%

    % Initialize an item_stats structure.
    %
:- func init_item_stats = item_stats.
:- pragma consider_used(func(init_item_stats/0)).

init_item_stats =
    item_stats(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0).

    % Initialize a goal_stats structure.
    %
:- func init_goal_stats = goal_stats.
:- pragma consider_used(func(init_goal_stats/0)).

init_goal_stats =
    goal_stats(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0).

%-----------------------------------------------------------------------------%

    % Update an item_stats structure based on the contents of the given
    % item block.
    %
:- pred gather_stats_in_aug_comp_unit(aug_compilation_unit::in,
    comp_unit_stats::out) is det.

gather_stats_in_aug_comp_unit(AugCompUnit, !:CompUnitStats) :-
    AugCompUnit = aug_compilation_unit(_ModuleVersionNumbers,
        _ParseTreeModuleSrc, _AncestorIntSpecs,
        _DirectIntSpecs, _IndirectIntSpecs,
        _PlainOptSpecs, _TransOptSpecs, _IntForOptSpecs, _TypeRepnSpecs),
    map.init(!:CompUnitStats).
    % XXX The existing code in this module gathers statistics about
    % the old structure of aug_compilation_units. It should be adapted
    % to work on their new structure the next time we need statistics
    % about item kinds.
%   gather_stats_in_item_blocks(section_name_src, SrcItemBlocks,
%       !CompUnitStats),
%   gather_stats_in_item_blocks(section_name_int, DirectIntItemBlocks,
%       !CompUnitStats),
%   gather_stats_in_item_blocks(section_name_int, IndirectIntItemBlocks,
%       !CompUnitStats),
%   gather_stats_in_item_blocks(section_name_int_for_opt, IntForOptItemBlocks,
%       !CompUnitStats),
%   gather_stats_in_item_blocks(section_name_opt, OptItemBlocks,
%       !CompUnitStats).

% :- func section_name_src(src_module_section) = string.
% section_name_src(sms_interface) = "src_int".
% section_name_src(sms_implementation) = "src_impl".
% section_name_src(sms_impl_but_exported_to_submodules) = "src_impl_sub".
%
% :- func section_name_int(int_module_section) = string.
% section_name_int(ims_imported_or_used(_, _, _, iou_imported)) =
%     "int_imported".
% section_name_int(ims_imported_or_used(_, _, _, iou_used)) = "int_used".
% section_name_int(ims_imported_or_used(_, _, _, iou_used_and_imported)) =
%     "int_used_and_imported".
% section_name_int(ims_abstract_imported(_, _)) = "int_abstract_imported".

%-----------------------------------------------------------------------------%

%     % Update an item_stats structure based on the contents of the given
%     % item block.
%     %
% :- pred gather_stats_in_item_blocks((func(MS) = string)::in,
%     list(item_block(MS))::in,
%     comp_unit_stats::in, comp_unit_stats::out) is det.
%
% gather_stats_in_item_blocks(_, [], !CompUnitStats).
% gather_stats_in_item_blocks(SectionFunc, [ItemBlock | ItemBlocks],
%         !CompUnitStats) :-
%     ItemBlock = item_block(_, Section, _, _, _, Items),
%     SectionName = SectionFunc(Section),
%     ( if map.search(!.CompUnitStats, SectionName, SectionStats0) then
%         SectionStats0 = section_stats(ItemStats0, GoalStats0),
%         gather_stats_in_items(Items,
%             ItemStats0, ItemStats, GoalStats0, GoalStats),
%         SectionStats = section_stats(ItemStats, GoalStats),
%         map.det_update(SectionName, SectionStats, !CompUnitStats)
%     else
%         gather_stats_in_items(Items,
%             init_item_stats, ItemStats, init_goal_stats, GoalStats),
%         SectionStats = section_stats(ItemStats, GoalStats),
%         map.det_insert(SectionName, SectionStats, !CompUnitStats)
%     ),
%     gather_stats_in_item_blocks(SectionFunc, ItemBlocks, !CompUnitStats).

:- pred gather_stats_in_items(list(item)::in,
    item_stats::in, item_stats::out, goal_stats::in, goal_stats::out) is det.
:- pragma consider_used(pred(gather_stats_in_items/5)).

gather_stats_in_items([], !ItemStats, !GoalStats).
gather_stats_in_items([Item | Items], !ItemStats, !GoalStats) :-
    gather_stats_in_item(Item, !ItemStats, !GoalStats),
    gather_stats_in_items(Items, !ItemStats, !GoalStats).

:- pred gather_stats_in_item(item::in,
    item_stats::in, item_stats::out,
    goal_stats::in, goal_stats::out) is det.

gather_stats_in_item(Item, !ItemStats, !GoalStats) :-
    (
        Item = item_clause(ItemClauseInfo),
        !ItemStats ^ item_num_clauses := !.ItemStats ^ item_num_clauses + 1,

        ItemClauseInfo = item_clause_info(_, _, _, _, MaybeGoal, _, _),
        (
            MaybeGoal = ok2(Goal, _GoalWarningSpecs),
            gather_stats_in_goal(Goal, !GoalStats)
        ;
            MaybeGoal = error2(_)
        )
    ;
        Item = item_type_defn(_),
        !ItemStats ^ item_num_type_defn := !.ItemStats ^ item_num_type_defn + 1
    ;
        Item = item_inst_defn(_),
        !ItemStats ^ item_num_inst_defn := !.ItemStats ^ item_num_inst_defn + 1
    ;
        Item = item_mode_defn(_),
        !ItemStats ^ item_num_mode_defn := !.ItemStats ^ item_num_mode_defn + 1
    ;
        Item = item_pred_decl(_),
        !ItemStats ^ item_num_pred_decl := !.ItemStats ^ item_num_pred_decl + 1
    ;
        Item = item_mode_decl(_),
        !ItemStats ^ item_num_mode_decl := !.ItemStats ^ item_num_mode_decl + 1
    ;
        Item = item_foreign_proc(_),
        !ItemStats ^ item_num_foreign_proc :=
            !.ItemStats ^ item_num_foreign_proc + 1
    ;
        Item = item_foreign_enum(_),
        !ItemStats ^ item_num_foreign_enum :=
            !.ItemStats ^ item_num_foreign_enum + 1
    ;
        Item = item_foreign_export_enum(_),
        !ItemStats ^ item_num_foreign_export_enum :=
            !.ItemStats ^ item_num_foreign_export_enum + 1
    ;
        Item = item_decl_pragma(ItemDeclPragmaInfo),
        gather_stats_in_item_decl_pragma(ItemDeclPragmaInfo, !ItemStats)
    ;
        Item = item_decl_marker(_),
        !ItemStats ^ item_num_marker_decl :=
            !.ItemStats ^ item_num_marker_decl + 1
    ;
        Item = item_impl_pragma(_),
        !ItemStats ^ item_num_pragma_impl :=
            !.ItemStats ^ item_num_pragma_impl + 1
    ;
        Item = item_impl_marker(_),
        !ItemStats ^ item_num_marker_impl :=
            !.ItemStats ^ item_num_marker_impl + 1
    ;
        Item = item_generated_pragma(ItemGenPragmaInfo),
        gather_stats_in_item_gen_pragma(ItemGenPragmaInfo, !ItemStats)
    ;
        Item = item_promise(_),
        !ItemStats ^ item_num_promise := !.ItemStats ^ item_num_promise + 1
    ;
        Item = item_typeclass(_),
        !ItemStats ^ item_num_typeclass := !.ItemStats ^ item_num_typeclass + 1
    ;
        Item = item_instance(_),
        !ItemStats ^ item_num_instance := !.ItemStats ^ item_num_instance + 1
    ;
        Item = item_initialise(_),
        !ItemStats ^ item_num_initialise :=
            !.ItemStats ^ item_num_initialise + 1
    ;
        Item = item_finalise(_),
        !ItemStats ^ item_num_finalise := !.ItemStats ^ item_num_finalise + 1
    ;
        Item = item_mutable(_),
        !ItemStats ^ item_num_mutable := !.ItemStats ^ item_num_mutable + 1
    ;
        Item = item_type_repn(_),
        !ItemStats ^ item_num_type_repn := !.ItemStats ^ item_num_type_repn + 1
    ).

:- pred gather_stats_in_item_decl_pragma(item_decl_pragma_info::in,
    item_stats::in, item_stats::out) is det.

gather_stats_in_item_decl_pragma(DeclPragma, !ItemStats) :-
    (
        DeclPragma = decl_pragma_termination(_),
        !ItemStats ^ item_num_pragma_decl_term :=
            !.ItemStats ^ item_num_pragma_decl_term + 1
    ;
        DeclPragma = decl_pragma_termination2(_),
        !ItemStats ^ item_num_pragma_decl_term2 :=
            !.ItemStats ^ item_num_pragma_decl_term2 + 1
    ;
        ( DeclPragma = decl_pragma_format_call(_)
        ; DeclPragma = decl_pragma_type_spec(_)
        ; DeclPragma = decl_pragma_type_spec_constr(_)
        ; DeclPragma = decl_pragma_obsolete_pred(_)
        ; DeclPragma = decl_pragma_obsolete_proc(_)
        ; DeclPragma = decl_pragma_oisu(_)
        ; DeclPragma = decl_pragma_struct_sharing(_)
        ; DeclPragma = decl_pragma_struct_reuse(_)
        ),
        !ItemStats ^ item_num_pragma_decl_other :=
            !.ItemStats ^ item_num_pragma_decl_other + 1
    ).

:- pred gather_stats_in_item_gen_pragma(item_generated_pragma_info::in,
    item_stats::in, item_stats::out) is det.

gather_stats_in_item_gen_pragma(GenPragma, !ItemStats) :-
    (
        GenPragma = gen_pragma_unused_args(_),
        !ItemStats ^ item_num_pragma_unused_args :=
            !.ItemStats ^ item_num_pragma_unused_args + 1
    ;
        GenPragma = gen_pragma_exceptions(_),
        !ItemStats ^ item_num_pragma_exceptions :=
            !.ItemStats ^ item_num_pragma_exceptions + 1
    ;
        GenPragma = gen_pragma_trailing(_),
        !ItemStats ^ item_num_pragma_trailing :=
            !.ItemStats ^ item_num_pragma_trailing + 1
    ;
        GenPragma = gen_pragma_mm_tabling(_),
        !ItemStats ^ item_num_pragma_mm_tabling :=
            !.ItemStats ^ item_num_pragma_mm_tabling + 1
    ).

%-----------------------------------------------------------------------------%

:- pred gather_stats_in_goals(list(goal)::in,
    goal_stats::in, goal_stats::out) is det.

gather_stats_in_goals([], !GoalStats).
gather_stats_in_goals([Goal | Goals], !GoalStats) :-
    gather_stats_in_goal(Goal, !GoalStats),
    gather_stats_in_goals(Goals, !GoalStats).

:- pred gather_stats_in_goal(goal::in, goal_stats::in, goal_stats::out) is det.

gather_stats_in_goal(Goal, !GoalStats) :-
    (
        Goal = conj_expr(_, SubGoalA, SubGoalsB),
        !GoalStats ^ goal_num_conj := !.GoalStats ^ goal_num_conj + 1,
        gather_stats_in_goal(SubGoalA, !GoalStats),
        gather_stats_in_goals(SubGoalsB, !GoalStats)
    ;
        Goal = par_conj_expr(_, SubGoalA, SubGoalsB),
        !GoalStats ^ goal_num_par_conj := !.GoalStats ^ goal_num_par_conj + 1,
        gather_stats_in_goal(SubGoalA, !GoalStats),
        gather_stats_in_goals(SubGoalsB, !GoalStats)
    ;
        Goal = true_expr(_),
        !GoalStats ^ goal_num_true := !.GoalStats ^ goal_num_true + 1
    ;
        Goal = disj_expr(_, SubGoal1, SubGoal2, SubGoals),
        !GoalStats ^ goal_num_disj := !.GoalStats ^ goal_num_disj + 1,
        gather_stats_in_goal(SubGoal1, !GoalStats),
        gather_stats_in_goal(SubGoal2, !GoalStats),
        gather_stats_in_goals(SubGoals, !GoalStats)
    ;
        Goal = fail_expr(_),
        !GoalStats ^ goal_num_fail := !.GoalStats ^ goal_num_fail + 1
    ;
        Goal = quant_expr(QuantType, QuantVarsKind, _, _, SubGoal),
        (
            QuantType = quant_some,
            QuantVarsKind = quant_ordinary_vars,
            !GoalStats ^ goal_num_some := !.GoalStats ^ goal_num_some + 1
        ;
            QuantType = quant_some,
            QuantVarsKind = quant_state_vars,
            !GoalStats ^ goal_num_some_state_vars :=
                !.GoalStats ^ goal_num_some_state_vars + 1
        ;
            QuantType = quant_all,
            QuantVarsKind = quant_ordinary_vars,
            !GoalStats ^ goal_num_all := !.GoalStats ^ goal_num_all + 1
        ;
            QuantType = quant_all,
            QuantVarsKind = quant_state_vars,
            !GoalStats ^ goal_num_all_state_vars :=
                !.GoalStats ^ goal_num_all_state_vars + 1
        ),
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = promise_purity_expr(_, _, SubGoal),
        !GoalStats ^ goal_num_promise_purity :=
            !.GoalStats ^ goal_num_promise_purity + 1,
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = promise_equivalent_solutions_expr(_, _, _, _, _, SubGoal),
        !GoalStats ^ goal_num_promise_eqv_solns :=
            !.GoalStats ^ goal_num_promise_eqv_solns + 1,
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = promise_equivalent_solution_sets_expr(_, _, _, _, _, SubGoal),
        !GoalStats ^ goal_num_promise_eqv_sets :=
            !.GoalStats ^ goal_num_promise_eqv_sets + 1,
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = promise_equivalent_solution_arbitrary_expr(_, _, _, _, _,
            SubGoal),
        !GoalStats ^ goal_num_promise_arbitrary :=
            !.GoalStats ^ goal_num_promise_arbitrary + 1,
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = require_detism_expr(_, _, SubGoal),
        !GoalStats ^ goal_num_req_detism :=
            !.GoalStats ^ goal_num_req_detism + 1,
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = require_complete_switch_expr(_, _, SubGoal),
        !GoalStats ^ goal_num_req_compl_switch :=
            !.GoalStats ^ goal_num_req_compl_switch + 1,
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = require_switch_arms_detism_expr(_, _, _, SubGoal),
        !GoalStats ^ goal_num_req_arm_detism :=
            !.GoalStats ^ goal_num_req_arm_detism + 1,
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = disable_warnings_expr(_, _, _, SubGoal),
        !GoalStats ^ goal_num_disable_warnings :=
            !.GoalStats ^ goal_num_disable_warnings + 1,
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = trace_expr(_, _, _, _, _, SubGoal),
        !GoalStats ^ goal_num_trace := !.GoalStats ^ goal_num_trace + 1,
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = atomic_expr(_, _, _, _, MainGoal, OrElseGoals),
        !GoalStats ^ goal_num_atomic := !.GoalStats ^ goal_num_atomic+ 1,
        gather_stats_in_goal(MainGoal, !GoalStats),
        gather_stats_in_goals(OrElseGoals, !GoalStats)
    ;
        Goal = try_expr(_, _, MainGoal, ThenGoal, MaybeElseGoal,
            Catches, MaybeCatchAny),
        !GoalStats ^ goal_num_try := !.GoalStats ^ goal_num_try + 1,
        gather_stats_in_goal(MainGoal, !GoalStats),
        gather_stats_in_goal(ThenGoal, !GoalStats),
        (
            MaybeElseGoal = yes(ElseGoal),
            gather_stats_in_goal(ElseGoal, !GoalStats)
        ;
            MaybeElseGoal = no
        ),
        gather_stats_in_catch_exprs(Catches, !GoalStats),
        (
            MaybeCatchAny = yes(catch_any_expr(_, CatchAnyGoal)),
            gather_stats_in_goal(CatchAnyGoal, !GoalStats)
        ;
            MaybeCatchAny = no
        )
    ;
        Goal = implies_expr(_, SubGoalA, SubGoalB),
        !GoalStats ^ goal_num_implies := !.GoalStats ^ goal_num_implies + 1,
        gather_stats_in_goal(SubGoalA, !GoalStats),
        gather_stats_in_goal(SubGoalB, !GoalStats)
    ;
        Goal = equivalent_expr(_, SubGoalA, SubGoalB),
        !GoalStats ^ goal_num_equivalent :=
            !.GoalStats ^ goal_num_equivalent + 1,
        gather_stats_in_goal(SubGoalA, !GoalStats),
        gather_stats_in_goal(SubGoalB, !GoalStats)
    ;
        Goal = not_expr(_, SubGoal),
        !GoalStats ^ goal_num_not := !.GoalStats ^ goal_num_not + 1,
        gather_stats_in_goal(SubGoal, !GoalStats)
    ;
        Goal = if_then_else_expr(_, _, _, CondGoal, ThenGoal, ElseGoal),
        !GoalStats ^ goal_num_if_then_else :=
            !.GoalStats ^ goal_num_if_then_else + 1,
        gather_stats_in_goal(CondGoal, !GoalStats),
        gather_stats_in_goal(ThenGoal, !GoalStats),
        gather_stats_in_goal(ElseGoal, !GoalStats)
    ;
        Goal = event_expr(_, _, _),
        !GoalStats ^ goal_num_event := !.GoalStats ^ goal_num_event + 1
    ;
        Goal = call_expr(_, _, _, _),
        !GoalStats ^ goal_num_call := !.GoalStats ^ goal_num_call + 1
    ;
        Goal = unify_expr(_, _, _, _),
        !GoalStats ^ goal_num_unify := !.GoalStats ^ goal_num_unify + 1
    ).

:- pred gather_stats_in_catch_exprs(list(catch_expr)::in,
    goal_stats::in, goal_stats::out) is det.

gather_stats_in_catch_exprs([], !GoalStats).
gather_stats_in_catch_exprs([CatchExpr | CatchExprs], !GoalStats) :-
    CatchExpr = catch_expr(_, Goal),
    gather_stats_in_goal(Goal, !GoalStats),
    gather_stats_in_catch_exprs(CatchExprs, !GoalStats).

%-----------------------------------------------------------------------------%

:- pred write_comp_unit_stats(io.text_output_stream::in, module_name::in,
    comp_unit_stats::in, io::di, io::uo) is det.

write_comp_unit_stats(Stream, ModuleName, CompUnitStats, !IO) :-
    io.format(Stream, "MODULE %s\n", [s(sym_name_to_string(ModuleName))], !IO),
    map.to_assoc_list(CompUnitStats, SectionStatPairs),
    list.foldl(write_section_stats(Stream), SectionStatPairs, !IO).

:- pred write_section_stats(io.text_output_stream::in,
    pair(string, section_stats)::in, io::di, io::uo) is det.

write_section_stats(Stream, SectionName - SectionStats, !IO) :-
    SectionStats = section_stats(ItemStats, GoalStats),
    write_item_stats(Stream, SectionName, ItemStats, !IO),
    write_goal_stats(Stream, SectionName, GoalStats, !IO).

:- pred write_item_stats(io.text_output_stream::in, string::in, item_stats::in,
    io::di, io::uo) is det.

write_item_stats(Stream, SectionName, ItemStats, !IO) :-
    ItemStats = item_stats(Clause, TypeDefn, InstDefn, ModeDefn,
        PredDecl, ModeDecl, FIM, ForeignProc, ForeignEnum, ForeignExportEnum,
        PragmaDeclTerm, PragmaDeclTerm2, PragmaDeclOther, MarkerDecl,
        PragmaImpl, MarkerImpl,
        PragmaUArgs, PragmaExcp, PragmaTrail, PragmaMM,
        Promise, Typeclass, Instance, Initialise, Finalise, Mutable, TypeRepn),
    write_one_stat(Stream, SectionName, "item_clause", Clause, !IO),
    write_one_stat(Stream, SectionName, "item_type_defn", TypeDefn, !IO),
    write_one_stat(Stream, SectionName, "item_inst_defn", InstDefn, !IO),
    write_one_stat(Stream, SectionName, "item_mode_defn", ModeDefn, !IO),
    write_one_stat(Stream, SectionName, "item_pred_decl", PredDecl, !IO),
    write_one_stat(Stream, SectionName, "item_mode_decl", ModeDecl, !IO),
    write_one_stat(Stream, SectionName, "item_fim", FIM, !IO),
    write_one_stat(Stream, SectionName, "item_foreign_proc", ForeignProc, !IO),
    write_one_stat(Stream, SectionName, "item_foreign_enum", ForeignEnum, !IO),
    write_one_stat(Stream, SectionName, "item_foreign_export_enum",
        ForeignExportEnum, !IO),
    write_one_stat(Stream, SectionName, "item_pragma_decl_term",
        PragmaDeclTerm, !IO),
    write_one_stat(Stream, SectionName, "item_pragma_decl_term2",
        PragmaDeclTerm2, !IO),
    write_one_stat(Stream, SectionName, "item_pragma_decl_other",
        PragmaDeclOther, !IO),
    write_one_stat(Stream, SectionName, "item_marker_decl", MarkerDecl, !IO),
    write_one_stat(Stream, SectionName, "item_pragma_impl", PragmaImpl, !IO),
    write_one_stat(Stream, SectionName, "item_marker_impl", MarkerImpl, !IO),
    write_one_stat(Stream, SectionName, "item_pragma_uargs", PragmaUArgs, !IO),
    write_one_stat(Stream, SectionName, "item_pragma_excp", PragmaExcp, !IO),
    write_one_stat(Stream, SectionName, "item_pragma_trail", PragmaTrail, !IO),
    write_one_stat(Stream, SectionName, "item_pragma_mm", PragmaMM, !IO),
    write_one_stat(Stream, SectionName, "item_promise", Promise, !IO),
    write_one_stat(Stream, SectionName, "item_typeclass", Typeclass, !IO),
    write_one_stat(Stream, SectionName, "item_instance", Instance, !IO),
    write_one_stat(Stream, SectionName, "item_promise", Promise, !IO),
    write_one_stat(Stream, SectionName, "item_initialise", Initialise, !IO),
    write_one_stat(Stream, SectionName, "item_finalise", Finalise, !IO),
    write_one_stat(Stream, SectionName, "item_mutable", Mutable, !IO),
    write_one_stat(Stream, SectionName, "item_type_repn", TypeRepn, !IO).

:- pred write_goal_stats(io.text_output_stream::in, string::in, goal_stats::in,
    io::di, io::uo) is det.

write_goal_stats(Stream, SectionName, GoalStats, !IO) :-
    GoalStats = goal_stats(Conj, ParConj, True, Disj, Fail,
        Some, All, SomeStateVars, AllStateVars,
        PromisePurity, PromiseEqvSolns, PromiseEqvSolnSets, PromiseArbitrary,
        ReqDetism, ReqComplSwitch, ReqSwitchArmDetism, DisableWarnings,
        Trace, Atomic, Try, Implies, Equivalent, Not, IfThenElse,
        Event, Call, Unify),
    write_one_stat(Stream, SectionName, "goal_conj", Conj, !IO),
    write_one_stat(Stream, SectionName, "goal_par_conj", ParConj, !IO),
    write_one_stat(Stream, SectionName, "goal_true", True, !IO),
    write_one_stat(Stream, SectionName, "goal_disj", Disj, !IO),
    write_one_stat(Stream, SectionName, "goal_fail", Fail, !IO),
    write_one_stat(Stream, SectionName, "goal_some", Some, !IO),
    write_one_stat(Stream, SectionName, "goal_all", All, !IO),
    write_one_stat(Stream, SectionName, "goal_some_state_vars",
        SomeStateVars, !IO),
    write_one_stat(Stream, SectionName, "goal_all_state_vars",
        AllStateVars, !IO),
    write_one_stat(Stream, SectionName, "goal_pro_purity", PromisePurity, !IO),
    write_one_stat(Stream, SectionName, "goal_pro_eqv_solns",
        PromiseEqvSolns, !IO),
    write_one_stat(Stream, SectionName, "goal_pro_eqv_soln_sets",
        PromiseEqvSolnSets, !IO),
    write_one_stat(Stream, SectionName, "goal_pro_arbitrary",
        PromiseArbitrary, !IO),
    write_one_stat(Stream, SectionName, "goal_req_detism", ReqDetism, !IO),
    write_one_stat(Stream, SectionName, "goal_req_compl_switch",
        ReqComplSwitch, !IO),
    write_one_stat(Stream, SectionName, "goal_req_arm_detism",
        ReqSwitchArmDetism, !IO),
    write_one_stat(Stream, SectionName, "goal_disable_warnings",
        DisableWarnings, !IO),
    write_one_stat(Stream, SectionName, "goal_trace", Trace, !IO),
    write_one_stat(Stream, SectionName, "goal_atomic", Atomic, !IO),
    write_one_stat(Stream, SectionName, "goal_try", Try, !IO),
    write_one_stat(Stream, SectionName, "goal_implies", Implies, !IO),
    write_one_stat(Stream, SectionName, "goal_equivalent", Equivalent, !IO),
    write_one_stat(Stream, SectionName, "goal_not", Not, !IO),
    write_one_stat(Stream, SectionName, "goal_if_then_else", IfThenElse, !IO),
    write_one_stat(Stream, SectionName, "goal_event", Event, !IO),
    write_one_stat(Stream, SectionName, "goal_call", Call, !IO),
    write_one_stat(Stream, SectionName, "goal_unify", Unify, !IO).

:- pred write_one_stat(io.text_output_stream::in, string::in, string::in,
    int::in, io::di, io::uo) is det.

write_one_stat(Stream, SectionName, StatName, StatNum, !IO) :-
    io.write_string(Stream, SectionName, !IO),
    io.write_string(Stream, " ", !IO),
    io.write_string(Stream, StatName, !IO),
    io.write_string(Stream, " ", !IO),
    io.write_int(Stream, StatNum, !IO),
    io.nl(Stream, !IO),
    io.flush_output(Stream, !IO).

%-----------------------------------------------------------------------------%
:- end_module parse_tree.prog_item_stats.
%-----------------------------------------------------------------------------%
