%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2005-2007, 2009 The University of Melbourne.
% Copyright (C) 2015, 2018, 2022 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: cterm.m.
% Author: zs.
%
% This module provides a mechanism for matching terms from the running program
% against terms specified by debugger commands, which are implemented in C in
% runtime/mercury_trace_term.[ch].
%
%---------------------------------------------------------------------------%

:- module mdb.cterm.
:- interface.

:- import_module bool.

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

:- type cterm.
:- type cargs.

    % Succeed if and only if the given term matches the given cterm.
    %
:- pred match_with_cterm(T::in, cterm::in, bool::out) is cc_multi.

    % Implement deconstruct for cterms.
    %
:- pred cterm_deconstruct(cterm::in, string::out, cargs::out) is det.

    % Decompose a list of arguments into the first element and the rest.
    % Fail if the list is empty.
    %
:- pred cterm_head_tail(cargs::in, cterm::out, cargs::out) is semidet.

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

:- implementation.

:- import_module deconstruct.
:- import_module list.
:- import_module univ.

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

:- pragma foreign_decl(c, "
#include ""mercury_trace_term.h""
").

:- pragma foreign_type(c, cterm, "MR_CTerm", [can_pass_as_mercury_type]).
:- pragma foreign_type(c, cargs, "MR_CArgs", [can_pass_as_mercury_type]).

:- pragma foreign_export("C", match_with_cterm(in, in, out),
    "ML_BROWSE_match_with_cterm").

% Dummy types form non-C backends.
:- type cterm ---> cterm.
:- type cargs ---> cargs.

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

% Uncomment these and the unsafe_perform_ios below to debug match_with_cterm
% and its callers in the trace directory.
% :- import_module io.
% :- import_module unsafe.
% :- pragma promise_pure(match_with_cterm/3).

match_with_cterm(Term, CTerm, Match) :-
    deconstruct(Term, include_details_cc, TermFunctor, _, TermArgs),
    cterm_deconstruct(CTerm, CTermFunctor, CTermArgs),
    ( if CTermFunctor = TermFunctor then
        match_with_cterms(TermArgs, CTermArgs, Match)
    else if CTermFunctor = "_" then
        Match = yes
    else
        Match = no
    ).

:- pred match_with_cterms(list(univ)::in, cargs::in, bool::out) is cc_multi.

match_with_cterms(UnivArgs, CArgs, Match) :-
    ( if cterm_head_tail(CArgs, CHead, CTail) then
        (
            UnivArgs = [],
            Match = no
        ;
            UnivArgs = [UnivHead | UnivTail],
            Head = univ_value(UnivHead),
            match_with_cterm(Head, CHead, MatchHead),
            (
                MatchHead = no,
                Match = no
            ;
                MatchHead = yes,
                match_with_cterms(UnivTail, CTail, Match)
            )
        )
    else
        (
            UnivArgs = [],
            Match = yes
        ;
            UnivArgs = [_ | _],
            Match = no
        )
    ).

:- pragma foreign_proc(c,
    cterm_deconstruct(Term::in, Functor::out, Args::out),
    [will_not_call_mercury, promise_pure],
"
    if (Term == NULL) {
        MR_fatal_error(""cterm_deconstruct: NULL term"");
    }

    Functor = Term->MR_term_functor;
    Args = Term->MR_term_args;
").

:- pragma foreign_proc("Java",
    cterm_deconstruct(_Term::in, _Functor::out, _Args::out),
    [will_not_call_mercury, promise_pure],
"
    if (1 == 1) throw new Error(\"not supported in java grade\");
").

:- pragma foreign_proc(c,
    cterm_head_tail(Args::in, Head::out, Tail::out),
    [will_not_call_mercury, promise_pure],
"
    if (Args == NULL) {
        SUCCESS_INDICATOR = MR_FALSE;
    } else {
        Head = Args->MR_args_head;
        Tail = Args->MR_args_tail;
        SUCCESS_INDICATOR = MR_TRUE;
    }
").

:- pragma foreign_proc("Java",
    cterm_head_tail(_Args::in, _Head::out, _Tail::out),
    [will_not_call_mercury, promise_pure],
"
    if (1 == 1) throw new Error(\"not supported in java grade\");
").

%---------------------------------------------------------------------------%
:- end_module mdb.cterm.
%---------------------------------------------------------------------------%
