%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
%
% This test case tests whether the parser flattens nested disjunctions,
% both in normal clauses (predicate p), and in DCG clauses (predicate dcg_p).
%
% The main bodies of those predicates are disjunctions that are effectively
% switches on the value of A. Switch detection looks for unifications
% that could allow it to turn a disjunction into a switch in disjuncts
% of that disjunction, and in disjuncts inside those disjuncts; it does NOT
% look for them in disjuncts inside disjuncts inside disjuncts. In other
% words, it looks at unifications at a maximum depth of two levels.
%
% In the written form of these predicates, the unifications in the innermost
% disjunction "( A = 4 ; A = 5 )" are at a depth of three. Switch detection
% can nevertheless turn both predicate bodies into switches, because parsing
% has traditionally flattened disjunctions, which means that if a disjunct
% consists entirely of another disjunction, then it replaced that outer
% disjunct with the arms of the inner disjunction. In this case, this
% flattening brings the A = 4 and A = 5 unifications that used to be
% at depth three to depth two, where switch detection can see them.
%
% This test case tests that the parsers (parse_goal.m and parse_dcg_goal.m)
% do flatten disjunctions. If they don't, then switch detection will leave
% at least one disjunction in p and/or dcg_p, and the compilation of the
% affected predicate(s) will fail with a determinism error.
%
% The original code from which this test code is distilled is the
% read_parse_tree_src_components predicate in compiler/parse_module.m,
% which (as of 2022 may 5) has a switch on IOM that switch detection
% recognizes *only* if disjunctions have been flattened by then.
%
%---------------------------------------------------------------------------%

:- module flatten_disjunctions.
:- interface.

:- import_module io.

:- pred main(io::di, io::uo) is det.

:- implementation.

:- import_module int.
:- import_module list.
:- import_module string.

main(!IO) :-
    ( if p(6, B, 4, X) then
        io.format("p(6, %d, 4, %d) succeeded.\n", [i(B), i(X)], !IO)
    else
        io.format("p(6, _, 4, _) failed.\n", [], !IO)
    ),
    ( if dcg_p(6, DCG_B, 4, DCG_X) then
        io.format("dcg_p(6, %d, 4, %d) succeeded.\n",
            [i(DCG_B), i(DCG_X)], !IO)
    else
        io.format("dcg_p(6, _, 4, _) failed.\n", [], !IO)
    ).

:- pred p(int::in, int::out, int::in, int::out) is semidet.
:- pragma no_inline(pred(p/4)).

p(A, B, !X) :-
    (
        A = 1, B = 11
    ;
        ( A = 4
        ; A = 5
        ; A = 6
        ; A = 7
        ),
        (
            (
                ( A = 4
                ; A = 5
                )
            ;
                A = 6,
                !:X = !.X + 6
            ),
            (
                !.X = 10,
                B = 5
            ;
                !.X = 11,
                B = 6
            )
        ;
            A = 7,
            B = A
        )
    ).

:- pred dcg_p(int::in, int::out, int::in, int::out) is semidet.
:- pragma no_inline(pred(dcg_p/4)).

dcg_p(A, B) -->
    (
        { A = 1, B = 11 }
    ;
        ( { A = 4 }
        ; { A = 5 }
        ; { A = 6 }
        ; { A = 7 }
        ),
        (
            (
                ( { A = 4 }
                ; { A = 5 }
                )
            ;
                { A = 6 },
                =(X0),
                :=(X0 + 6)
            ),
            (
                =(X1),
                { X1 = 10 },
                { B = 5 }
            ;
                =(X2),
                { X2 = 11 },
                { B = 6 }
            )
        ;
            { A = 7 },
            { B = A }
        )
    ).
