1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    2%
    3% FILE      : Eval/evalbat.pl
    4%
    5%       Basic Action Projector (BAT)
    6%
    7%  AUTHOR : Sebastian Sardina (prev. Hector Levesque & Maurice Pagnucco)
    8%  EMAIL  : ssardina@cs.toronto.edu
    9%  WWW    : www.cs.toronto.edu/~ssardina www.cs.toronto.edu/cogrobo
   10%  TYPE   : system independent code
   11%  TESTED : SWI Prolog 5.0.10 http://www.swi-prolog.org
   12%           ECLIPSE 5.4 http://www.icparc.ic.ac.uk/eclipse/
   13%
   14%  This file allows for the projection of conditions wrt
   15%  basic action theories.
   16%
   17%  The main tool provided in this file is the following predicate:        
   18%
   19% -- eval(P,H,B):  B=true/false/unknown is the truth value of P at history H 
   20%
   21%           For more information on Golog and some of its variants, see:
   22%               http://www.cs.toronto.edu/~cogrobo/
   23%
   24%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   25%
   26%                             March, 2002
   27%
   28% This software was developed by the Cognitive Robotics Group under the
   29% direction of Hector Levesque and Ray Reiter.
   30% 
   31%        Do not distribute without permission.
   32%        Include this notice in any copy made.
   33% 
   34% 
   35%         Copyright (c) 2000 by The University of Toronto,
   36%                        Toronto, Ontario, Canada.
   37% 
   38%                          All Rights Reserved
   39% 
   40% Permission to use, copy, and modify, this software and its
   41% documentation for non-commercial research purpose is hereby granted
   42% without fee, provided that the above copyright notice appears in all
   43% copies and that both the copyright notice and this permission notice
   44% appear in supporting documentation, and that the name of The University
   45% of Toronto not be used in advertising or publicity pertaining to
   46% distribution of the software without specific, written prior
   47% permission.  The University of Toronto makes no representations about
   48% the suitability of this software for any purpose.  It is provided "as
   49% is" without express or implied warranty.
   50% 
   51% THE UNIVERSITY OF TORONTO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
   52% SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
   53% FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF TORONTO BE LIABLE FOR ANY
   54% SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
   55% RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
   56% CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
   57% CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   58%
   59%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   60%
   61% This file provides the following:
   62%
   63% -- eval(P, H, B)  (MAIN PREDICATE, used by the transition system)
   64%           B is the truth value of P at history H 
   65%
   66% SYSTEM TOOLS (used by the top-level cycle)::
   67%
   68% -- initializeDB/0
   69%           initialize the projector
   70% -- finalizeDB/0
   71%           finalize the projector
   72% -- can_roll(+H1) 
   73%       check if the DB CAN roll forward
   74% -- must_roll(+H1) 
   75%       check if the DB MUST roll forward
   76% -- roll_db(+H1,-H2) 
   77%       perform roll forward with current history H1 and new history H2
   78% -- handle_sensing(+A, +H, +S, -H2) 
   79%           H2 is H plus action A with sensing result S
   80% -- debug(+A, +H, -S)
   81%           perform debug tasks with current action A, sensing outcome S,
   82%           and history H
   83% -- system_action(+A)       
   84%           action A is an action used by the system (e.g., action outcomes e(_,_))
   85%
   86%
   87% OTHER TOOLS (used by the transition system)::
   88%
   89% -- sensing(+A, -L)
   90%           action A is a sensing action with a list L of possible outcomes
   91% -- sensed(+A, +S, +H)
   92%           action A, when executed at history H, got sensing result S
   93% -- inconsistent(+H)
   94%           last action turned history H inconsistent, i.e., impossible 
   95% -- domain(-V, +D)       
   96%           object V is an element of domain D
   97% -- rdomain(-V, +D)       
   98%           object V is an element of domain D (random)
   99% -- getdomain(+D, -L)   
  100%            L is the list representing domain D
  101% -- calc_arg(+A1, -A2, +H)  
  102%           action A2 is action A1 with its arguments replaced wrt history H
  103% -- before(+H1, +H2)
  104%           history H1 is a previous history of H2
  105% -- assume(+F, +V, +H1, -H2) 
  106%           H2 is the history resulting from assuming fluent F to 
  107%           have value V at history H1
  108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  109%
  110%  A basic action theory (BAT) is described with:
  111%
  112% -- fun_fluent(fluent)     : for each functional fluent (non-ground)
  113%								(at least 1 clause is required)
  114% -- rel_fluent(fluent)     : for each relational fluent (non-ground)
  115%								(at least 1 clause is required)
  116% -- cache(fluent)          : fluent should be cached (at least 1) MANDATORY
  117%
  118%           e.g., rel_fluent(painted(C)).
  119%           e.g., fun_fluent(color(C)).
  120%
  121% -- prim_action(action)    : for each primitive action (ground)
  122% -- exog_action(action)    : for each exogenous action (ground)
  123%
  124%           e.g., prim_action(clean(C)) :- domain(C,country).
  125%           e.g., exog_action(painte(C,B)):- domain(C,country), domain(B,color).
  126%
  127% -- senses(action,fluent)  : for each sensing action that senses fluent directly
  128%
  129%           e.g, senses(check_painted(C),  painted(C)).
  130%           e.g, senses(senseTemperature,  temp).
  131%
  132% -- senses(action,outcome,fluent,value,cond) :
  133%			for each sensing action that senses fluent *indirectly*
  134%
  135%           e.g, senses(senseTemperature, T, temp, T, true).
  136%				: equivalent to senses(senseTemperature,  temp).
  137%           e.g, senses(senseTemperature, T, temp, T2, T2 is T+10).
  138%           e.g, senses(senseTemperature, T, isHot, true,  T>30).
  139%           e.g, senses(senseTemperature, T, isHot, false, T<=30).
  140%
  141% -- forget(action,fluent)  : action makes fluent unknown
  142%
  143%           e.g, poss(checkFloor,  lightFloor).
  144% -- poss(action,cond)      : when cond, action is executable
  145%
  146%           e.g, poss(clean(C),   and(painted(C),holding(cleanear))).
  147%
  148% -- initially(fluent,value): fluent has value in S0 (ground)
  149%								(at least 1 clause is required)
  150%
  151%          e.g., initially(painted(C), false):- domain(C,country), C\=3.
  152%                initially(painted(3), true).
  153%                initially(color(3), blue).
  154%
  155% -- causes_val(action,fluent,value,cond)
  156%          when cond holds, doing act causes functional fluent to have value
  157%
  158%            e.g., causes_val(paint(C2,V), color(C), V, C = C2).
  159%               or causes_val(paint(C,V), color(C), V, true).
  160%
  161% -- causes_true(action,fluent,cond)
  162%          when cond holds, doing act causes relational fluent to hold
  163% -- causes_false(action,fluent,cond)
  164%          when cond holds, doing act causes relational fluent to not hold
  165%
  166%            e.g., causes_true(paint(C2,_), painted(C), C = C2).
  167%               or causes_true(paint(C,_), painted(C), true).
  168%            e.g., causes_false(clean(C2),  painted(C), C = C2).
  169%               or causes_false(clean(C),  painted(C), true).
  170%
  171% -- sort-name(domain_of_sort).      : defines a sort
  172%        e.g., color([blue, green, yellow, red]).       
  173%              temperature([-30..45]).
  174%
  175%
  176% A high-level program-controller is described with:
  177%
  178% -- proc(name,P): for each procedure P 
  179% -- simulator(N,P): P is the N exogenous action simulator
  180%
  181% The interface for real-world execution is described with:
  182%
  183% -- actionNum(action, num)  
  184%         action has RCX code num
  185% -- simulateSensing(action)
  186%         sensing result for action should be asked to the user
  187% -- translateSensing(action, sensorValue, sensorResult) 
  188%         translate the sensorValue of action to sensorResult
  189% -- translateExogAction(codeAction, action) 
  190%         translateSensing action name into codeAction and vice-versa
  191%
  192% Requirements:
  193%
  194% -- is_list(+L) : L is a list
  195% -- subv(X1,X2,T1,T2) :  T2 is T1 with X1 replaced by X2
  196% -- multifile/1
  197% -- get0/1
  198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  199%% :- module(evalbat,
  200%%           [eval/3,
  201%%            initializeDB/0,
  202%%            finalizeDB/0,
  203%%            handle_sensing/4,
  204%%            sensing/2,
  205%%            sensed/3,
  206%%            domain/2,
  207%%            getdomain/2,
  208%%            calc_arg/3,
  209%%            before/2,
  210%%            inconsistent/1,
  211%%            assume/4
  212%%           ]).
  213%% 
  214%% :- use_module(library(quintus)).
  215
  216:- dynamic 
  217   currently/2,	% Used to store the actual initial fluent values
  218   simulator/2,	% There may be no simulator
  219   senses/2,
  220   senses/5,	% There may be no sensing action
  221   forget/2,	% There may be no action that "forgets" a fluent
  222   has_valc/3.	% used for caching some values
  223
  224% Predicates that they have definitions here but they can defined elsewhere
  225:- multifile(prim_action/1).  226:- multifile(causes_val/4).  227%:- multifile(causes_true/3).
  228%:- multifile(causes_false/3).
  229%:- multifile(exog_action/1).
  230:- multifile(poss/2).  231
  232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  233%  PREDICATES TO BE EXPORTED
  234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  235
  236   /* Move initially(-,-) to currently(-,-) and clear exog actions  */
  237initializeDB:- 
  238	retractall(currently(_,_)), 
  239	initially(F,V),
  240	assert(currently(F,V)),
  241	clean_cache,	
  242	fail.
  243initializeDB.
  244
  245  /* Clean all the currently(-.-) predicates */
  246%finalizeDB:-  retractall(currently(_,_)), clean_cache.
  247finalizeDB.
  248
  249% eval(P,H,B): this is the interface of the projector
  250eval(P,H,true):- holds(P,H).
  251
  252% Change the history H to encode the sensing result of action A at H
  253%handle_sensing(A,H,Sr,[e(F,Sr)|H]):- senses(A,F). (OLD WAY)
  254handle_sensing(A,H,Sr,[e(A,Sr)|H]). 
  255
  256
  257% clean_cache: remove all has_valc/3
  258clean_cache :- retractall(has_valc(_,_,_)).
  259
  260% Set F to value V at H, return H1 (add e(F,V) to history H)
  261assume(F,V,H,[e(F,V)|H]).
  262
  263% system_action/1 defines actions that are used by the projector for managment
  264system_action(e(_,_)). 
  265
  266% Action A is a sensing action
  267sensing(A,_):- senses(A,_) ; senses(A, _, _, _, _).
  268
  269% sensed(+A,?V,+H): action A got sensing result V w.r.t. history H
  270sensed(A,V,[e(F,V2)|_]):- senses(A,F), !, V=V2.
  271sensed(A,V,[_|H])      :- sensed(A,V,H).
  272
  273% domain/2: assigns a user-defined domain to a variable. 
  274domain(V, D)  :- getdomain(D, L), member(V, L).
  275rdomain(V, D) :- getdomain(D, L), shuffle(L,L2), !, member(V, L2).
  276
  277% L is the list-domain associated to name D
  278getdomain(D, L) :- is_list(D) -> L=D ; (P =.. [D,L], call(P)).
  279
  280% Computes the arguments of an action or a fluent P
  281% Action/Fluent P1 is action/fluent P with all arguments evaluated 
  282calc_arg(P,P1,H):- (is_an_action(P) ; prim_fluent(P)),
  283	(atomic(P)-> P1=P ;
  284                    (P =..[Function|LArg], subfl(LArg,LArg2,H), 
  285                     P1=..[Function|LArg2])).
  286
  287% History H1 is a previous history of H2
  288before(H1,H2):- append(_,H1,H2).
  289
  290% No action can make a history inconsistent (simplification)
  291inconsistent(_):- fail.
  292
  293
  294
  295
  296%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  297%  OTHER PREDICATES NEEDED BUT NOT EXPORTED
  298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  299
  300% A primitive fluent is either a relational or a functional fluent
  301prim_fluent(P):- rel_fluent(P) ; fun_fluent(P).
  302
  303% Check if A has "the form" of a primitive action, though, its arguments
  304% may need to be evaluated yet
  305% We need to do  this hack because actions are defined all ground
  306is_an_action(A):- \+ \+ (prim_action(A) ; exog_action(A)), !.
  307is_an_action(A):- \+ atomic(A),
  308	           A =..[F|Arg], length(Arg,LArg), length(ArgV,LArg),
  309                   NA =..[F|ArgV], (prim_action(NA) ; exog_action(A)).
  310
  311% Simulation of an action A has the same effects as action A itself
  312causes_val(sim(A),F,V,C)  :- !, causes_val(A,F,V,C).
  313
  314% Build causes_val/4 for relational fluents
  315causes_val(A,F,true,C)  :- causes_true(A,F,C).
  316causes_val(A,F,false,C) :- causes_false(A,F,C).
  317
  318% Abort if P is not grounded (to use before negations as failure)
  319checkgr(P):- ground(P)-> true ; once(warn(['CWA applied to formula: ',P])).
  320
  321
  322% Update the cache information by stripping out the subhistory H
  323update_cache(H) :-
  324	retract(has_valc(F, V, H2)),
  325	append(H1, H, H2),
  326	assert(has_valc(F, V, H1)),
  327	fail.
  328update_cache(_).
  329
  330
  331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  332%  HOLDS - Here starts the evaluation procedure for projection
  333%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  334% kwhether(F,H): fluent F is known true or false in H
  335% Assumes that after sensing F, F may change but it will remain known
  336% We may probably want to add some "forgeting" mechanism.. either by a 
  337%      state condition or special actions
  338holds(kwhether(F),[])     :- !, initially(F,_).
  339holds(kwhether(F),[Act|_]):- (senses(Act,F) ; Act=e(F,_)), !.
  340holds(kwhether(F),[_|H])  :- holds(kwhether(F),H).
  341
  342% know(F): Fluent F evaluates to something
  343holds(know(F),H)     :- !, holds(F=_,H).
  344
  345% holds(P,H): P holds in H
  346holds(and(P1,P2),H)	:- !, holds(P1,H), holds(P2,H).
  347holds(or(P1,P2),H)	:- !, (holds(P1,H) ; holds(P2,H)).
  348holds(neg(P),H)	:- !, checkgr(P), \+ holds(P,H). /* Negation by failure */
  349holds(some([],P),H)	:- !, holds(P,H).
  350holds(some([V|L],P),H)	:- !, holds(some(V,some(L,P)), H).
  351holds(some([],P),H)	:- !, holds(P,H).
  352holds(some([V|L],P),H)	:- !, holds(some(V,some(L,P)), H).
  353holds(some(V,P),H)	:- !, subv(V,_,P,P1), holds(P1,H).
  354holds(some((V,D),P),H) :- !, domain(O,D), subv(V,O,P,P1), holds(P1,H).
  355holds(all([],P),H)	:- !, holds(P, H).
  356holds(all([V|L],P),H)	:- !, holds(all(V,all(L,P)), H).
  357holds(all((V,D),P),H)	:- !, holds(neg(some(V,D),neg(P)), H).
  358holds(impl(P1,P2),H) :- !, holds(or(neg(P1),P2),H).
  359holds(P,H) :- proc(P,P1), !, (ground(P) -> (holds(P1,H), !) ; holds(P1,H)).
  360holds(P,H) :- ground(P), rel_fluent(P), !, subf(P,true,H), !.
  361holds(P,H) :- \+ \+ rel_fluent(P), !, rel_fluent(P), subf(P,true,H).
  362holds(P,H) :- subf(P,P1,H), (ground(P) -> (call(P1), !) ; call(P1)).
  363
  364       /*  P2 is P1 with all fluents replaced by their values at H */
  365subf(P1,P2,_)  :- (var(P1) ; number(P1)), !, P2 = P1.
  366subf(P1,P2,H)  :- atom(P1), !, subf2(P1,P2,H).
  367subf(P1,P2,H)  :- P1=..[F|L1], subfl(L1,L2,H), P3=..[F|L2], subf2(P3,P2,H).
  368
  369subf2(P3,P2,H) :- prim_fluent(P3), has_value(P3,P2,H).
  370subf2(P2,P2,_) :- \+ prim_fluent(P2).
  371
  372subfl([],[],_).
  373subfl([T1|L1],[T2|L2],H) :- subf(T1,T2,H), subfl(L1,L2,H).
  374
  375
  376%
  377% has_value(F,V,H): Fluent F has value V at history H
  378%
  379has_value(F,V,H) :- ground(F) -> (has_valg(F,V,H), !) ; has_valo(F,V,H).
  380
  381% has_valg/3: check cache, then normal query (for ground queries)
  382has_valg(F,V,H)  :- cache(F), !,
  383	(has_valc(F,V,H) -> true ; (has_val(F,V,H),  assert(has_valc(F,V,H))) ).
  384has_valg(F,V,H)  :- has_val(F,V,H), !.  % F is a fluent with NO cache
  385
  386% has_valo/3: check cache, then normal query (for ground queries)
  387has_valo(F,V,H)  :- cache(F), !,
  388	(has_valc(F,V,H) ; (has_val(F,V,H),  \+ has_valc(F,V,H),  assert(has_valc(F,V,H)))).
  389has_valo(F,V,H)  :- has_val(F,V,H).  % F is a fluent with NO cache
  390
  391
  392% has_val/3: the usual way of reasoning using regression and sensing
  393has_val(F,V,[])		:- currently(F,V).
  394has_val(F,V,[A|H])	:- sets_val(A,F,V,H).
  395has_val(F,V,[A|H])	:- \+ forget(A,H,F), has_value(F,V,H), \+ sets_val(A,F,_,H).
  396
  397sets_val(e(F,V),F,V,_)	:- prim_fluent(F), !.  		% Fluent V is explicitly set by e(_,_)
  398sets_val(e(A,V),F,V,_)	:- senses(A,F).	% Action A sets F directly
  399sets_val(e(A,V),F,V2,H)	:- !, senses(A,V,F,V2,P), holds(P,H). % A sets F indirectly
  400sets_val(A,F,V,H)	:- causes_val(A,F,V,P), holds(P,H).   % Non-sensing reasoning
  401
  402% So far, one forgets the value of F when it is sensed (may be improved)
  403forget(Act, _, F) :- forget(Act, F).
  404
  405% Special high-level actions to set and unset fluent F: set(F) and unset(F)
  406prim_action(set(_)).
  407prim_action(unset(_)).
  408poss(set(F), ground(F)).
  409poss(unset(F), ground(F)).
  410has_val(F,V,[set(F)|_])  :- !, V=true.
  411has_val(F,V,[unset(F)|_]):- !, V=false.
  412
  413
  414
  415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  416%  ROLL DATABASE FORWARD
  417%
  418%  Rolling forward means advancing the predicate currently(-,-) and
  419%  discarding the corresponding tail of the history.
  420%  There are 3 parameters specified by roll_parameters(L,N,M).
  421%     L: the history has to be longer than this, or dont bother
  422%     M: if the history is longer than this, forced roll
  423%     N: the length of the tail of the history to be preserved
  424%		(set N=0 to never roll forward)
  425%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  426:- dynamic temp/2.         % Temporal predicate used for rolling forward
  427
  428%roll_parameters(1,1,0).  % Never roll forward
  429roll_parameters(20,40,5).
  430
  431can_roll(H) :- roll_parameters(L,_,N), length(H,L1), L1 > L, N>0.
  432must_roll(H) :- roll_parameters(_,M,N), length(H,L1), L1 > M, N>0.
  433
  434% H1 is the current history (H1 = H2 + H3)
  435% H2 will be the new history
  436% H3 is the tail of H1 that is going to be dropped
  437roll_db(H1,H2) :- 
  438	roll_parameters(_,_,N), 	
  439	split(N,H1,H2,H3), 
  440	preserve(H3),
  441	update_cache(H3).	    % Update the cache wrt the preserved history H3
  442
  443	/* split(N,H,H1,H2) succeeds if append(H1,H2,H) and length(H1)=N. */
  444split(0,H,[],H).
  445split(N,[A|H],[A|H1],H2) :- N > 0, N1 is N-1, split(N1,H,H1,H2).
  446
  447% preserve(H) : rolls forward the initial database from [] to H
  448preserve([]).
  449preserve([A|H]) :- 
  450	preserve(H), 
  451	roll_action(A), 
  452	update_cache([A]).
  453
  454% roll_action(A): roll currently/2 database with respect to action A
  455roll_action(A) :-
  456	sets_val(A, F, V, []),
  457	prim_fluent(F),
  458	(\+ temp(F, V) -> assert(temp(F, V)) ; true),
  459	fail.
  460roll_action(_) :-
  461	retract(temp(F,V)),
  462	retractall(currently(F,_)),	% There should be just one currently/2 for F!
  463	assert(currently(F,V)),
  464	fail.
  465roll_action(_).
  466
  467
  468
  469
  470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  471% DEBUG ROUTINES
  472%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  473% debug(+Action, +History, -SensingResult): 
  474% If Action=debug then a snapshot of the system is printed out
  475% Otherwise, the sendRcxActionNumber/2
  476%     predicate failed (RCX panicked or there was a problem with the
  477%     communication). This predicate attempts to provide some basic debug
  478%     and error recovery.
  479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  480debug(debug, History, _) :- !,
  481    write('-------------------------------------------------------------'), nl,
  482    write('********* A SNAPSHOT OF THE SYSTEM WAS REQUESTED ************'), nl,
  483    errorRecoveryData(History),
  484    write('-------------------------------------------------------------'), nl.
  485
  486debug(Action, History, SensingResult) :-
  487    write('** There is a problem with the RCX. It may need to be reset.'), nl,
  488    errorRecoveryData(History),
  489    errorRecoveryProc,
  490    execute(Action, History, SensingResult). % Try action again
  491
  492% errorRecoveryData(+History): Extract values of primitive fluents at
  493%     the point where Hist actions are performed.
  494errorRecoveryData(History) :-
  495    write('    Actions performed so far: '),
  496    write(History), nl,
  497    bagof(U, prim_fluent(U), FluentList),
  498    printFluentValues(FluentList, History).
  499
  500% printFluentValues(+FluentList, +History): Print value of primitive fluents
  501%     at the point where History actions have been performed
  502printFluentValues([], _).
  503
  504printFluentValues([Hf | FluentList], History) :-
  505    (has_value(Hf, Hv, History),    % Print all instances of Hf 
  506     write('    PRIMITIVE FLUENT '),
  507     write(Hf),
  508     write(' HAS VALUE '),
  509     write(Hv), nl, fail) ; 
  510    printFluentValues(FluentList, History). % Continue with other fluents
  511
  512% errorRecoveryProc: What to do in case of error. In this case, ask the user
  513%     to reposition the RCX so that last action can be re-attempted
  514errorRecoveryProc:-
  515    write('If you wish to abort, enter "a".'), nl,
  516    write('If you wish to continue execution, place RCX in a position'), nl,
  517    write('consistent with these values and hit any other key.'), nl,
  518    get0(Val),
  519    get0(_),                     % Clear carriage return
  520    (Val == 65; Val == 97) ->    % 65 is ASCII 'A', 97 is ASCII 'a'
  521         abort;
  522         true.
  523
  524
  525%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  526% EOF: Eval/evalbat.pl
  527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%