/* PS.PL */ /* :- module ps. */ /* SPECIFICATION ------------- */ /* IMPLEMENTATION -------------- bagof not portable. */ :- reconsult('$eden_src/outputt'). :- reconsult('$eden_src/control'). :- reconsult('$eden_src/lists'). ps( Time, Rules, Raps, STM0, StopPred, SRP, Context0, STM ) :- diagnose( 'PS', 'Starting cycle'...Time<>':'~ ), diagnose( 'PS', 'STM is'...stm_(STM0,4)~ ), match( Rules, STM0, Matched ), diagnose( 'PS', 'Matched rules'...rules_(Matched,4)~ ), Matched \= [], !, resolve( Matched, STM0, SRP, Context0, Resolved ), diagnose( 'PS', 'Firing rule'...rules_([Resolved],4)~ ), fire( Resolved, Raps, Time, STM0, Context0, STM1, SRP, Context1 ), diagnose( 'PS', 'STM is'...stm_(STM1,4)~~ ), Time1 is Time + 1, ps_continue_if_necessary( Time1, Rules, Raps, STM1, StopPred, SRP, Context1, STM ). ps( _, _, _, _, _, _, _, _ ) :- diagnose( 'PS', 'No rules matched, so exiting'~ ), true. /* if no rules matched. */ ps_continue_if_necessary( Time, Rules, Raps, STM, StopPred, SRP, Context, STM ) :- ( StopPred = '$no_goal'(G) -> not(stm_member( G, STM )) ; Goal =.. [ StopPred, STM ], call( Goal ) ). ps_continue_if_necessary( Time, Rules, Raps, STM0, StopPred, SRP, Context, STM ) :- ps( Time, Rules, Raps, STM0, StopPred, SRP, Context, STM ). ps( Rules, Raps, STM0, StopPred, SRP, STM ) :- ps( 1, Rules, Raps, STM0, StopPred, SRP, [], STM ). ps( Rules, STM0, StopPred, SRP, STM ) :- ps( 1, Rules, [], STM0, StopPred, SRP, [], STM ). fire( Rule, Raps, Time, STM0, Context0, STM, SRP, Context ) :- rule_vs_action( Rule, Action ), fire_all( STM0, Raps, Time, Action, SRP, STM ), rule_vs_rule_id( Rule, RuleId ), Context = [ fired(RuleId,Time) | Context0 ]. fire_all( STM, _, _, [], _, STM ) :- !. fire_all( STM0, Raps, Time, [Action1|ActionsN], SRP, STM ) :- fire_one( STM0, Raps, Time, Action1, SRP, STM1 ), fire_all( STM1, Raps, Time, ActionsN, SRP, STM ). fire_one( STM0, Raps, Time, Term, Context, STM ) :- fire_one_1( STM0, Raps, Time, Term, Context, STM ), !. fire_one( STM0, Raps, Time, Term, Context, STM ) :- bug( 'fire_one: failed', [Term] ). fire_one_1( STM0, Raps, Time, erase(Term), _, STM1 ) :- !, stm_delete( STM0, Time, Term, STM1 ). fire_one_1( STM0, Raps, Time, '$at'(Term), SRP, STM ) :- copy( Raps, Raps_ ), raps_member( Rap, Raps_ ), rap_vs_goal( Rap, '$at'(Term) ), !, rap_vs_plan( Rap, Plan ), diagnose( 'PS', 'Starting RAP'...Rap~ ), stm_insert( STM0, Time, Term, STM1 ), do_plan( STM1, Raps, Time, Term, Plan, SRP, STM2 ), stm_delete( STM2, Time, Term, STM ). fire_one_1( STM0, Raps, Time, '$at'(Term), _, STM1 ) :- !, stm_insert( STM0, Time, Term, STM1 ). fire_one_1( STM, Raps, Time, Term, _, STM ) :- !, call( Term ). fire_one_1( STM0, Raps, Time, sub(Name), SRP, STM ) :- !, '$sub_rules'( Name, Rules ), ps( Time, Rules, Raps, STM0, '$finish', SRP, [], STM ). /* Matching. --------- */ match( Rules, STM, Matched ) :- fast_bagof( Rule, ( rules_member( Rule, Rules ), rule_vs_condition( Rule, Cond ), match_cond( Cond, STM ) ), Matched ). match_cond( [], _ ) :- !. match_cond( [C1|CN], STM ) :- !, match_primitive_cond( C1, STM ), match_cond( CN, STM ). match_primitive_cond( '$at'(Term), STM ) :- !, stm_member( Term, STM ). match_primitive_cond( not('$at'(Term)), STM ) :- !, not(stm_member( Term, STM )). match_primitive_cond( Term, STM ) :- call( Term ). /* Conflict resolution. -------------------- */ resolve( [], STM, SRP, Context, _ ) :- !, bug( 'resolve: no rules', [STM,SRP,Context] ). resolve( [Rule], _, _, _, Rule ) :- !. resolve( [Rule|_], _, [], _, Rule ) :- !. resolve( Rules, STM, [s|Rest], Context, Resolved ) :- !, resolve_by_specificity( Rules, STM, MostSpecific ), resolve( MostSpecific, STM, Rest, Context, Resolved ). resolve( Rules, STM, [r|Rest], Context, Resolved ) :- !, resolve_by_recency( Rules, STM, Context, LeastRecent ), resolve( LeastRecent, STM, Rest, Context, Resolved ). resolve( Rules, STM, [p|Rest], Context, Resolved ) :- !, resolve_by_priority( Rules, STM, MostImportant ), resolve( MostImportant, STM, Rest, Context, Resolved ). resolve( Rules, _, SRP, _, _ ) :- bug( 'resolve: bad SRP', [SRP] ). resolve_by_specificity( Rules, STM, MostSpecific ) :- tag_by_unspecificity( Rules, TaggedRules ), keysort( TaggedRules, Sorted ), diagnose( 'PS', 'Sorted by specificity to give rules'~<>tagged_rules_(Sorted,4)~ ), untag_and_take_best( Sorted, MostSpecific ). resolve_by_priority( Rules, STM, MostImportant ) :- tag_by_priority( Rules, TaggedRules ), keysort( TaggedRules, Sorted ), diagnose( 'PS', 'Sorted by priority to give rules'~<>tagged_rules_(Sorted,4)~ ), untag_and_take_best( Sorted, MostImportant ). resolve_by_recency( Rules, STM, Context, LeastRecent ) :- tag_by_time( Rules, Context, TaggedRules ), keysort( TaggedRules, Sorted ), diagnose( 'PS', 'Sorted by recency to give rules'~<>tagged_rules_(Sorted,4)~ ), untag_and_take_best( Sorted, LeastRecent ). tag_by_unspecificity( [], [] ) :- !. tag_by_unspecificity( [Rule|Rules], [MinusS-Rule|Taggeds] ) :- rule_vs_condition( Rule, Cond ), conditions_specificity( Cond, S ), MinusS is -S, !, tag_by_unspecificity( Rules, Taggeds ). conditions_specificity( L, S ) :- conditions_specificity( L, 0, 0, S ). conditions_specificity( [], Sum, Count, S ) :- !, S is Sum. /* Don't take average. */ conditions_specificity( [C1|Cn], Sum0, Count0, S ) :- condition_specificity( C1, CS ), Sum1 is Sum0 + CS, Count1 is Count0 + 1, conditions_specificity( Cn, Sum1, Count1, S ). condition_specificity( C1, CS ) :- C1 =.. [ _ | Args ], args_specificity( Args, AS ), CS is AS + 1. args_specificity( Args, S ) :- args_specificity( Args, 0, S ). args_specificity( [], S, S ) :- !. args_specificity( [A1|AN], S0, S ) :- var(A1), !, S1 is S0 + 0.5, args_specificity( AN, S1, S ). args_specificity( [A1|AN], S0, S ) :- S1 is S0 + 1, args_specificity( AN, S1, S ). tag_by_priority( [], [] ) :- !. tag_by_priority( [Rule|Rules], [MinusP-Rule|Taggeds] ) :- rule_vs_priority( Rule, P ), MinusP is -P, !, tag_by_priority( Rules, Taggeds ). tag_by_time( [], _, [] ) :- !. tag_by_time( [Rule|Rules], Context, [Time-Rule|Taggeds] ) :- member( fired(RuleId,Time), Context ), rule_vs_rule_id( Rule, RuleId ), !, tag_by_time( Rules, Context, Taggeds ). tag_by_time( [Rule|Rules], Context, [0-Rule|Taggeds] ) :- tag_by_time( Rules, Context, Taggeds ). untag_and_take_best( [], [] ) :- !. untag_and_take_best( [K-H1,K-H2|T], [H1|T_] ) :- !, untag_and_take_best( [K-H2|T], T_ ). untag_and_take_best( [_-H|_], [H] ). /* Plans. ------ */ do_plan( STM, _, _, _, [], _, STM ) :- !. do_plan( STM0, Raps, Time, Goal, [A1|An], SRP, STM ) :- diagnose( 'PS', 'Doing RAP action'...A1~ ), do_a( STM0, Raps, Time, Goal, A1, SRP, STM1 ), do_plan( STM1, Raps, Time, Goal, An, SRP, STM ). do_a( STM0, Raps, Time, Goal, ::(Prec,A), SRP, STM ) :- call(Prec), !, do_a( STM0, Raps, Time, Goal, A, SRP, STM ). do_a( STM, _, _, _, ::(_,_), _, STM ). do_a( STM0, Raps, Time, Goal, '$rules'(Rules), SRP, STM ) :- ps( Time, Rules, Raps, STM0, '$no_goal'(Goal), SRP, [], STM ). do_a( STM0, Raps, Time, Goal, '$at'(Term), SRP, STM ) :- copy( Raps, Raps_ ), raps_member( Rap, Raps_ ), rap_vs_goal( Rap, '$at'(Term) ), !, rap_vs_plan( Rap, Plan ), diagnose( 'PS', 'Starting RAP'...Rap~ ), stm_insert( STM0, Time, Term, STM1 ), do_plan( STM1, Raps, Time, Term, Plan, SRP, STM2 ), stm_delete( STM2, Time, Term, STM ). do_a( STM0, Raps, Time, Goal, '$at'(Term), SRP, STM ) :- !, stm_insert( STM0, Time, Term, STM ). do_a( STM0, Raps, Time, Goal, (C->A1;A2), SRP, STM ) :- !, ( call(C) -> do_a( STM0, Raps, Time, Goal, A1, SRP, STM ) ; do_a( STM0, Raps, Time, Goal, A2, SRP, STM ) ). do_a( STM, Raps, Time, Goal, A, SRP, STM ) :- call( A ). /* Rules. ------ */ rule_vs_action( rule(_,_,Action,_), Action ). rule_vs_condition( rule(_,Cond,_,_), Cond ). rule_vs_priority( rule(Priority,_,_,_), Priority ). rule_vs_rule_id( rule(_,_,_,RuleId), RuleId ). rules_member( Rule, Rules ) :- member( Rule, Rules ). rule_insert( Rules, Rule, [Rule|Rules] ). :- assert( '$rule_id'(0) ). new_rule( rule(_,_,_,RuleId) ) :- '$rule_id'(RuleId), retract( '$rule_id'(_) ), Next is RuleId + 1, assert( '$rule_id'(Next) ). /* Subs. ----- */ sub_vs_plan( sub(_,Plan), Plan ). sub_vs_goal( sub(Goal,_), Goal ). subs_member( Sub, Subs ) :- member( Sub, Subs ). sub_insert( Subs, Sub, [Sub|Subs] ). /* Raps. ----- */ rap_vs_plan( rap(_,Plan), Plan ). rap_vs_goal( rap(Goal,_), Goal ). raps_member( Rap, Raps ) :- member( Rap, Raps ). rap_insert( Raps, Rap, [Rap|Raps] ). /* STM. ---- */ new_stm( 'stm'(0,0,[]) ). stm_insert( 'stm'(LastTime,LastTag,Facts0), Time, Term, 'stm'(Time,Tag,Facts) ) :- ( LastTime = Time -> Tag is LastTag + 1 ; Tag = 1 ), TimeStamp is Time*1000 + Tag, Facts = [ fact(Term,TimeStamp) | Facts0 ]. stm_delete( 'stm'(LastTime,LastTag,Facts0), Time, Term, 'stm'(Time,Tag,Facts) ) :- ( LastTime = Time -> Tag is LastTag + 1 ; Tag = 1 ), delete( Facts0, fact(Term,_), Facts ). /* Variables in Term must remain unbound. This is ensured by 'delete'. */ stm_member( Term, TimeStamp, 'stm'(_,_,Facts) ) :- member( fact(Term,TimeStamp), Facts ). stm_member( Term, 'stm'(_,_,Facts) ) :- stm_member( Term, TimeStamp, 'stm'(_,_,Facts) ). show_stm( STM, Indent ) :- forall( stm_member( Term, TimeStamp, STM ), output( spaces_(Indent)<>Term...'('<>timestamp_(TimeStamp)<>')'~ ) ). list_to_stm( L, STM ) :- new_stm( STM0 ), list_to_stm( L, STM0, STM ). list_to_stm( [], STM, STM ) :- !. list_to_stm( [Term|Terms], STM0, STM ) :- stm_insert( STM0, 0, Term, STM1 ), list_to_stm( Terms, STM1, STM ). /* Output. ------- */ :- add_user_output( ps_output ). ps_output( timestamp_(T) ) :- !, Time is T div 1000, Tag is T mod 1000, output( Time<>'.'<>Tag ). ps_output( stm_(STM,Indent) ) :- !, show_stm(STM,Indent). ps_output( rules_(Rules,_) ) :- !, show_rules( Rules ). ps_output( tagged_rules_(Rules,_) ) :- !, show_tagged_rules( Rules ). ps_output( cond_(C) ) :- !, show_cond(C). ps_output( action_(C) ) :- !, show_action(C). ps_output( simple_ac_(AC) ) :- !, show_simple_ac(AC). show_rules( [] ) :- !. show_rules( [R1|Rn] ) :- show_rule( R1 ), ( Rn \= [] -> output(nl_), show_rules( Rn ) ; true ). show_rule( Rule ) :- rule_vs_condition( Rule, C ), rule_vs_action( Rule, A ), output( cond_(C)...'=>'...action_(A) ). show_tagged_rules( [] ) :- !. show_tagged_rules( [R1|Rn] ) :- show_tagged_rule( R1 ), ( Rn \= [] -> output(nl_), show_tagged_rules( Rn ) ; true ). show_tagged_rule( Tag-Rule ) :- rule_vs_condition( Rule, C ), rule_vs_action( Rule, A ), output( Tag...cond_(C)...'=>'...action_(A) ). show_cond( C ) :- output( seplist_(C,',',Elt,simple_ac_(Elt)) ). show_action( A ) :- output( seplist_(A,',',Elt,simple_ac_(Elt)) ). show_simple_ac( '$at'(A) ) :- !, output( '@'<>A ). show_simple_ac( A ) :- output( A ). /* :- endmodule. */