% --------------------------------------------------------------------------
%   FILE:       TEXTGEN.PL
%   AUTHOR:     Tim Duncan
%   UPDATED:    Tue Jul 28 19:44:17 BST 1987
%   PURPOSE:    Interpret a PROSENET network and return the text.
% --------------------------------------------------------------------------


:- op(100,fx,*).
:- op(100,xfy,:).



/* text/2
Toplevel entry to a PROSENET net. Provides a neater interface
to textgen/3. Some calls are made direct to textgen/3. Note
that textgen/3 uses difference lists for more efficient appending,
but that lists are appended, hence the need to flatten the result.
*/


text(Net,Text) :-
    textgen(Net,X/X,Diff/[]),
    flatten(Diff,Text).



/* The text produced is saved, and can be replayed */

savetxt(T/X) :- !, recordz(dl_text,T/X,_).
savetxt(T) :- recordz(text,T,_).

replaytxt :-
    recorded(text,T,_),
    runoff(T),
    fail.
replaytxt.



/* textgen/3
Mode: textgen(+,+,-)
Args: textgen(Node,Acc,Text)
Interpret a PROSENET network or subnet, since textgen/3
is called recursively. A difference list is used as an
accumulator for the text in order to make appending prose
fragments more efficient. Since the fragments are lists the
result is a nested list which has to be flattened later.
*/



textgen(Node,Acc,Text) :-
    reset_redund_list(Node),
    prosenet(Node,Arclist),
    select(arc(Type,Next,_),Arclist),
    interpret(Type,Next,Acc,Text).



reset_redund_list(risk_s) :-
    recorded(redundant,(sentence_level,_),Rf),
    erase(Rf), !.
reset_redund_list(Node) :-
    member(Node,[int,imx,n,minors]),
    recorded(redundant,(paragraph_level,_),Rf),
    erase(Rf), !.
reset_redund_list(_).


/* select/2
Select an arc from the list of arcs leaving a node.
An arc can only be taken if its action routine returns
true (see ACTION.PL for action routines).
*/

select(arc(A,To,P),[arc(A,To,P)|Rest]) :- test_procedure(A,P).
select(Arc,[_|Rest]) :- select(Arc,Rest).
select(_,Arcs) :- runoff([*allcaps,prosenet,failure,:,Arcs]), nl, !, fail.



/* test_procedure/2
Test an action routine to see if an arc can be used.
If the action routine exists as a predicate it is called,
else the user is presented with the arc and its action
routine (ask/2). This is useful for debugging purposes.
*/

test_procedure(S,not(P)) :- ( call(P), !, fail ; true ), !.
test_procedure(S,P) :-
    ( functor(P,F,A),
      prolog_system_predicate(F,A)
    ; current_predicate(_,P) ), !,
    call(P).
test_procedure(A,P) :- ask(A,P).




/* interpret/4
Mode: interpret(+,+,+,-)
Args: interpret(F,N,A,R)
Interpret a PROSENET arc, and append whatever prose
is produced, to the accumulator (which is a difference
list), then pass control to the next node.
The arc types are:

    pop         = return from net or subnet
    push(N)     = push to a subnet
    jump        = do nothing, and hand on control
    text(T)     = append this prose fragment
    func(F)     = call the function and append
                  the prose it produces
    opt(L)      = select at random from these arcs
    sequence(S) = take the next arc in this sequence
    endsequence = set the sequence back to its initial
*/


interpret(pop,_,Acc,Acc).
interpret(push(N),Next,Acc,Return) :-
    textgen(N,Acc,Sub),                 % do subnet ...
    textgen(Next,Sub,Return).           % ... and continue
interpret(text(T),Next,Acc,Return) :-
    append_dl(Acc,[T|N]/N,A2),
    textgen(Next,A2,Return).
interpret(func(F),Next,Acc,Return) :-
    function_call(F),                   % call a function
    getenv(function_output,FO),         % and recover its output
    delenv(function_output,_),
    append_dl(Acc,[FO|N]/N,A2),
    textgen(Next,A2,Return).
interpret(opt(Id,Opts),_,Acc,Return) :-
    random_choice(arc(Type,Next,Proc),Opts),
    do(test_procedure(opt(Id),Proc)),   % do the action routine for side effects
    interpret(Type,Next,Acc,Return).
interpret(sequence(Id,Seq),_,Acc,Return) :-
    getenv(Id,[arc(Type,Next,Proc)|Rest]),
    ( Rest = []
    ; setenv(Id,Rest) ), !,
    interpret(Type,Next,Acc,Return).
interpret(sequence(Id,[arc(Type,Next,Proc)|Seq]),_,Acc,Return) :-
    getenv(Id,[]),           % not started yet
    setenv(Id,Seq),
    interpret(Type,Next,Acc,Return).
interpret(endsequence(Id),Next,Acc,Return) :-
    delenv(Id,_),
    textgen(Next,Acc,Return).
interpret(jump,Next,Acc,Return) :-
    textgen(Next,Acc,Return).




/* Select one arc at random from the list.
Uses a call to POP-11 for 'oneof' and thus
has to quote each arc to prevent POP trying
to evaluate them.
*/

random_choice(Arc,Arclist) :-
    quote_items(Arclist,Qlist),
    repeat,
    prolog_eval(oneof(Qlist),Arc).



/* Quote each item in a list, to prevent
POP-11 from trying to evaluate it.
*/

quote_items([],[]).
quote_items([H|T],[quote(H)|Q]) :-
    quote_items(T,Q).
