/* PS.PL */ :- module ps. :- public /. /* SPECIFICATION ------------- */ /* IMPLEMENTATION -------------- bagof not portable. */ ps( Time, Rules, STM0, StopPred, SR, WE, Context0, STM ) :- WE:report( ps, start, Time ), WE:report( ps, stm, stm_(STM0,4) ), match( Rules, STM0, Matched ), WE:report( ps, matched(Matched) ), Matched \= [], !, resolve( Matched, STM0, SR, Context0, Resolved ), we_report( WE, ps, 'Resolved to give rule'...rules_(Resolved,4)~ ), fire( Resolved, Time, STM0, Context0, STM1, Context1 ), we_report( WE, ps, 'STM is'...stm_(STM1,4)~~ ), Time1 is Time + 1, ps_continue_if_necessary( Time1, Rules, STM1, StopPred, SR, WE, Context1, STM ). ps( _, _, _, _, _, _, _, _ ) :- true. /* if no rules matched. */ ps_continue_if_necessary( Time, Rules, STM, StopPred, SR, WE, Context, STM ) :- Goal =.. [ StopPred, STM ], call( Goal ), !. ps_continue_if_necessary( Time, Rules, STM0, StopPred, SR, WE, Context, STM ) :- ps( Time, Rules, STM0, StopPred, SR, WE, Context, STM ). ps( Rules, STM0, StopPred, SR, WE, STM ) :- ps( 1, Rules, STM0, StopPred, SR, WE, [], STM ). ps( Rules, STM0, StopPred, SR, STM ) :- ps( Rules, STM0, StopPred, SR, no_ved, STM ). fire( Rule, Time, STM0, Context0, STM, Context ) :- rule_vs_action( Rule, Action ), fire_all( STM0, Time, Action, STM ), Context = [ fired(Rule,Time) | Context0 ]. fire_all( STM, _, [], STM ) :- !. fire_all( STM0, Time, [Action1|ActionsN], STM ) :- fire_one( STM0, Time, Action1, STM1 ), fire_all( STM1, Time, ActionsN, STM ). fire_one( STM0, Time, erase(Term), STM1 ) :- !, stm_delete( STM0, Time, Term, STM1 ). fire_one( STM0, Time, Term, STM1 ) :- stm_insert( STM0, Time, Term, STM1 ). /* 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( X>Y, _ ) :- !, X > Y. match_primitive_cond( X>=Y, _ ) :- !, X >= Y. match_primitive_cond( X resolve_by_specificity( Rules, STM, MostSpecific ), resolve_by_recency( MostSpecific, STM, Context, [Rule|_] ) ; SR = rs -> resolve_by_recency( Rules, STM, Context, LeastRecent ), resolve_by_specificity( LeastRecent, STM, [Rule|_] ) ; SR = s -> resolve_by_specificity( Rules, STM, [Rule|_] ) ; SR = r -> resolve_by_recency( Rules, STM, Context, [Rule|_] ) ; bug( 'resolve: bad SR', [Rules, STM, SR, Context] ) ). resolve_by_specificity( Rules, STM, MostSpecific ) :- tag_by_unspecificity( Rules, TaggedRules ), keysort( TaggedRules, Sorted ), untag( Sorted, MostSpecific ). resolve_by_recency( Rules, STM, Context, LeastRecent ) :- tag_by_time( Rules, Context, TaggedRules ), keysort( TaggedRules, Sorted ), untag( Sorted, LeastRecent ). tag_by_unspecificity( [], [] ) :- !. tag_by_unspecificity( [Rule|Rules], [MinusS-Rule|Taggeds] ) :- rule_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_time( [], _, [] ) :- !. tag_by_time( [Rule|Rules], Context, [Time-Rule|Taggeds] ) :- member( fired(Rule,Time), Context ), !, tag_by_time( Rules, Context, Taggeds ). tag_by_time( [Rule|Rules], Context, [0-Rule|Taggeds] ) :- tag_by_time( Rules, Context, Taggeds ). untag( [], [] ) :- !. untag( [_-H|T], [H|T1] ) :- untag( T, T1 ). /* Rules. ------ */ rule_vs_action( rule(_,Action), Action ). rule_vs_condition( rule(Cond,_), Cond ). rules_member( Rule, Rules ) :- member( Rule, Rules ). rule_insert( Rules, Rule, [Rule|Rules] ). show_rules( Rules, Indent ) :- forall( rule_member( rule(Cond,Action), Rules ), output( spaces_(Indent)<>rule(Cond,Action)~ ) ). /* 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) ) :- !, output( Rules ). /* temp. */ :- endmodule.