%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1997-2003, 2005-2006, 2012 The University of Melbourne.
% Copyright (C) 2014-2016, 2018, 2020-2022 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: prolog.m.
% Main author: fjh.
% Stability: high.
%
% This file contains predicates that are intended to help people
% porting Prolog programs, or writing programs in the intersection
% of Mercury and Prolog.
%
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%

:- module prolog.
:- interface.

:- import_module io.
:- import_module list.
:- import_module pair.
:- import_module univ.

%---------------------------------------------------------------------------%
%
% Prolog arithmetic operators.
%

:- pred T =:= T.            % In Mercury, just use =
:- mode in =:= in is semidet.

:- pred T =\= T.            % In Mercury, just use \=
:- mode in =\= in is semidet.

%---------------------------------------------------------------------------%
%
% Prolog term comparison operators.
%

:- pred T == T.             % In Mercury, just use =
:- mode in == in is semidet.

:- pred T \== T.            % In Mercury, just use \=
:- mode in \== in is semidet.

% Prolog's so-called "univ" operator, `=..'.
% Note: this is not related to Mercury's "univ" type!
% In Mercury, use `deconstruct.deconstruct' instead.

:- pred T =.. univ_result.
:- mode in =.. out is det.

    % Note that the Mercury =.. is a bit different to the Prolog one.
    % We could make it slightly more similar by overloading '.'/2,
    % but that would cause ambiguities that might prevent type
    % inference in a lot of cases.
    %
% :- type univ_result ---> '.'(string, list(univ)).
:- type univ_result == pair(string, list(univ)).

    % arg/3.
    % In Mercury, use arg/4 (defined in module deconstruct) instead:
    %
    %   arg(ArgNum, Term, Data) :-
    %       deconstruct.arg(Term, canonicalize, ArgNum - 1, Data).
    %
:- pred arg(int::in, T::in, univ::out) is semidet.

    % det_arg/3: like arg/3, but calls error/1 rather than failing
    % if the index is out of range.
    %
:- pred det_arg(int::in, T::in, univ::out) is det.

%---------------------------------------------------------------------------%
%
% Prolog style predicates for opening and switching streams.
%

    % see(FileName, Result, !IO):
    % Attempts to open the named file for input, and if successful,
    % sets the current input stream to the newly opened stream.
    % Result is either 'ok' or 'error(ErrorCode)'.
    %
:- pred see(string::in, io.res::out, io::di, io::uo) is det.

    % Attempts to open a file for binary input, and if successful sets
    % the current binary input stream to the newly opened stream.
    % Result is either 'ok' or 'error(ErrorCode)'.
    %
:- pred see_binary(string::in, io.res::out, io::di, io::uo) is det.

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

    % seen(!IO):
    % Closes the current input stream.
    % The current input stream reverts to standard input.
    % This will throw an io.error exception if an I/O error occurs.
    %
:- pred seen(io::di, io::uo) is det.

    % Closes the current input stream. The current input stream reverts
    % to standard input. This will throw an io.error exception if
    % an I/O error occurs.
    %
:- pred seen_binary(io::di, io::uo) is det.

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

    % tell(FileName, Result, !IO):
    % Attempts to open the named file for output, and if successful,
    % sets the current output stream to the newly opened stream.
    % Result is either 'ok' or 'error(ErrCode)'.
    %
:- pred tell(string::in, io.res::out, io::di, io::uo) is det.

    % Attempts to open a file for binary output, and if successful sets
    % the current binary output stream to the newly opened stream.
    % As per Prolog tell/1. Result is either 'ok' or 'error(ErrCode)'.
    %
:- pred tell_binary(string::in, io.res::out, io::di, io::uo) is det.

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

    % told(!IO):
    % Closes the current output stream.
    % The current output stream reverts to standard output.
    % This will throw an io.error exception if an I/O error occurs.
    %
:- pred told(io::di, io::uo) is det.

    % Closes the current binary output stream. The default binary output
    % stream reverts to standard output. As per Prolog told/0. This will
    % throw an io.error exception if an I/O error occurs.
    %
:- pred told_binary(io::di, io::uo) is det.

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

:- implementation.

:- import_module deconstruct.
:- import_module int.
:- import_module require.

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

'=:='(X, X).

'=\\='(X, Y) :-
    X \= Y.

'=='(X, X).
'\\=='(X, Y) :-
    X \= Y.

'=..'(Term, Functor - Args) :-
    deconstruct(Term, canonicalize, Functor, _Arity, Args).

% We use module qualifiers here to avoid overriding
% the builtin Prolog versions.
prolog.arg(ArgumentIndex, Type, Univ) :-
    deconstruct.arg(Type, canonicalize, ArgumentIndex - 1, Arg),
    type_to_univ(Arg, Univ).

det_arg(ArgumentIndex, Type, Argument) :-
    ( if arg(ArgumentIndex, Type, Arg) then
        Argument = Arg
    else
        error("det_arg: arg failed")
    ).

%---------------------------------------------------------------------------%
%
% Prolog style predicates for opening and switching streams.
%

see(File, Result, !IO) :-
    io.open_input(File, Result0, !IO),
    (
        Result0 = ok(Stream),
        io.set_input_stream(Stream, _, !IO),
        Result = ok
    ;
        Result0 = error(Error),
        Result = error(Error)
    ).

see_binary(File, Result, !IO) :-
    io.open_binary_input(File, Result0, !IO),
    (
        Result0 = ok(Stream),
        io.set_binary_input_stream(Stream, _, !IO),
        Result = ok
    ;
        Result0 = error(Error),
        Result = error(Error)
    ).

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

seen(!IO) :-
    io.stdin_stream(Stdin, !IO),
    io.set_input_stream(Stdin, OldStream, !IO),
    io.close_input(OldStream, !IO).

seen_binary(!IO) :-
    io.stdin_binary_stream(Stdin, !IO),
    io.set_binary_input_stream(Stdin, OldStream, !IO),
    io.close_binary_input(OldStream, !IO).

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

tell(File, Result, !IO) :-
    io.open_output(File, Result0, !IO),
    (
        Result0 = ok(Stream),
        io.set_output_stream(Stream, _, !IO),
        Result = ok
    ;
        Result0 = error(Msg),
        Result = error(Msg)
    ).

tell_binary(File, Result, !IO) :-
    io.open_binary_output(File, Result0, !IO),
    (
        Result0 = ok(Stream),
        io.set_binary_output_stream(Stream, _, !IO),
        Result = ok
    ;
        Result0 = error(Msg),
        Result = error(Msg)
    ).

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

told(!IO) :-
    io.stdout_stream(Stdout, !IO),
    io.set_output_stream(Stdout, OldStream, !IO),
    io.close_output(OldStream, !IO).

told_binary(!IO) :-
    io.stdout_binary_stream(Stdout, !IO),
    io.set_binary_output_stream(Stdout, OldStream, !IO),
    io.close_binary_output(OldStream, !IO).

%---------------------------------------------------------------------------%
:- end_module prolog.
%---------------------------------------------------------------------------%
