# From: "Dat Nguyen" # Subject: Module rule-engine # Date: Wed, 19 May 2004 01:16:37 -0400 # # Animal Taxonomy is a favorite subject of specialized rule-based programming # languages. With some creative construction, C-Kermit can solve that problem # in the same manner. The cheetah.kr script exposes the same look and feel of # many rule-based programs. Many configuration problems in computer # administration and network should be solvable similarly, provided the rule # set can be identified. # # This demonstration comes in two modules: rule-engine and cheetah. # To run the demonstration, tell Kermit to TAKE the cheetah module. # # Here is rule-engine: define say { echo \%1 return 1 } define askContent { local \%v ask \%v {[\%1]: } _asg \%1 \%v return \%1 } define askYesNo { local ans while true { ask ans {\%1 (y|n): } if \find(y,\m(ans)) return 1 if \find(n,\m(ans)) return 0 } } define askANumber { local \%n while true { echo Enter a Value ask \%n {[\%1]: } if numeric \%n return \%n } } define disableRule { local \%i for \%i 1 \v(argc)-1 1 { asg RuleDatabase \m(RuleDatabase)\&_[\%i]| incr RuleNumber } } define enableRule { local \%i for \%i 1 \v(argc)-1 1 { asg RuleDatabase \freplace(\m(RuleDatabase),|\&_[\%i]|,|) decr RuleNumber } } define availableRule { if \find(\%1,\m(RuleDatabase)) return 0 disableRule {\%1} return 1 } define eligibleRule { (! \find(\%1,\m(RuleDatabase))) } define clearRuleDatabase { asg RuleDatabase | (setq RuleNumber 0) } define addFact { local \%i for \%i 1 \v(argc)-1 1 { if \find(\&_[\%i],\m(FactDatabase)) continue asg FactDatabase \m(FactDatabase)\&_[\%i]| incr FactNumber } # showFact } define addATrueFact { # \%1 Name of the Boolean fact # \%2 Value of Boolean fact # Overwrite previous value local \%s \%w \%f asg \%s \fword(\%1,1)_\fword(\%1,2) asg \%w \freplace(\%1,{\fword(\%1,1) \fword(\%1,2) }) _asg \%s |\%w| addFact {\%s} return 1 } define addAlsoTrueFact { local \%s \%w \%f asg \%s \fword(\%1,1)_\fword(\%1,2)_also asg \%w \freplace(\%1,{\fword(\%1,1) \fword(\%1,2) }) _asg \%s \m(\%s)|\%w| addFact {\%s} return 1 } define addTrueFact { # add and initialize with True _asg \%1 1 addFact {\%1} } define addFalseFact { _asg \%1 0 # add and initialize with False addFact {\%1} } define addValueFact { if \find(\%1,\m(FactDatabase)) return \m(\%1) _asg \%1 \fsexpr(askANumber '(\%1)) addFact {\%1} return \m(\%1) } define addcontentFact { if \find(\%1,\m(FactDatabase)) return \m(\%1) _asg \%1 \fsexpr(askContent '(\%1)) addFact {\%1} return \m(\%1) } define removeFact { local \%i for \%i 1 \v(argc)-1 1 { asg FactDatabase \freplace(\m(FactDatabase),|\&_[\%i]|,|) decr FactNumber } # showFact } define isItTrueThat { # Use for fact that can have only one of two possible value. # a buble can be either ON of OFF local \%s \%w \%f asg \%s \fword(\%1,1)_\fword(\%1,2) asg \%w \freplace(\%1,{\fword(\%1,1) \fword(\%1,2) }) asg \%f \find(\%s,\m(FactDatabase)) if \%f { # term in factdatabase if \find(|\%w|,\m(\%s)) return 1 return 0 } else { # term not in factdatabase local \%a asg \%a \fsexpr(askYesNo '(\%1)) if \%a { _asg \%s |\%w| # save fact only when not in database before if not \%f addFact {\%s} } return \%a } } define isTrueFact { # This responds with the content of the fact (True or False) if \find(\%1,\m(FactDatabase)) return \m(\%1) _asg \%1 \fsexpr(askYesNo '(\%1)) addFact {\%1} return \m(\%1) } define gotFact { # This gives user a chance to create a state fact with y(yes) or n(no) if \find(\%1,\m(FactDatabase)) return 1 echo \%1 local \%a asg \%a \fexec(askYesNo) if \%a { # yes, create fact addFact {\%1} } return \%a } define hasFact { # This does not expect user to respond (\find(\%1,\m(FactDatabase))) } define anyFact { (FactNumber) } define removeAllFact { asg FactDatabase | (setq FactNumber 0) } define showFact { show mac FactDatabase show mac FactNumber } define addGoal { local \%i for \%i 1 \v(argc)-1 1 { asg GoalDatabase \m(GoalDatabase)\&_[\%i]| incr GoalNumber } # showGoal } define removeGoal { local \%i for \%i 1 \v(argc)-1 1 { asg GoalDatabase \freplace(\m(GoalDatabase),|\&_[\%i]|,|) decr GoalNumber } # showGoal } define allGoalDone { (! GoalNumber) } define hasGoal { (\find(\%1,\m(GoalDatabase))) } define removeAllGoal { asg GoalDatabase | (setq GoalNumber 0) } define showGoal { show mac GoalDatabase show mac GoalNumber } define fireRule { local \&e[] \%n \%i \%f asg \%n \fsplit(\%1,&e,{ }) while true { for \%i 1 \%n 1 { if define \m(\&e[\%i]) { asg \%f \fexec(\&e[\%i]) # if rule was fired, reevaluate all rules if > \%f 0 { asg firedRules \m(firedRules)|\&e[\%i] echo \&e[\%i] was fired if = 2 \%f END break } } } if not \%f break # no rule was fired if = 1 \fexec(allGoalDone) break # all goals achieved } echo echo {That's all there is} echo } define rule_sample { (if (hasGoal 'Goal_xxx) (if (hasFact 'Fact_yyy) (if (not (hasGoal 'Goal_zzz)) (. (removeGoal 'Goal_xxx) # Goal achieved (removeFact 'Fact_yyy) # Fact changed (addGoal 'Fact_www) # New goal (addFact 'Fact_yyy) # New fact (1) ) (0) ) (0) ) (0) ) } define mutual_exclude { local \%i \%s asg \%s + for \%i 1 \v(argc)-1 1 { asg \%s \%s\&_[\%i]+ } _asg \v(macro)_term \m(\v(macro)_term)|\%s } define addThisFact { asg allTheFacts \m(allTheFacts)|\%1 registerThisFact {\%1} 1 0 } define excludeThisFact { asg allTheFacts \freplace(\m(allTheFacts),|\%1|,|) registerThisFact {\%1} 0 1 } define showAllFacts { local \%i \&w[] for \%i 1 \fsplit(\m(allTheFacts),&w,|) 1 { echo \&w[\%i] } } define clearAllFacts { asg allTheFacts } define registerThisFact { local \%i \%k \%n \&w[] \%f asg \%n \fsplit(\m(mutual_exclude_term),&w,|) if \%n { local \%m \&e[] # There is mutual exclusive fact asg \%f \farraylook(^+\%1+*,&w) if > \%f 0 { asg \%m \fsplit(\&w[\%f],&e,{+}) # This is a mutual exclusive fact if \%2 { # This is a true fact, turn off all others for \%i 1 \%m 1 { _asg \&e[\%i] \%3 } } else { # This is a false fact, turn off only for boolean fact if = 2 \%m { # This is a boolean fact for \%i 1 \%m 1 { _asg \&e[\%i] \%3 } } } } } _asg \%1 \%2 } define consultUser { asg consult_user 1 } define donotConsultUser { asg consult_user 0 } donotConsultUser define isThisFactTrue { if define \m(\%1) return \m(\%1) local \%a if == \m(consult_user) 1 { asg \%a \fsexpr(askYesNo '(\%1)) } else { asg \%a 0 } if \find(+\%1+,\m(mutual_exclude_term)) { # term with mutual exclusion if = 1 \%a { addThisFact {\%1} } else { excludeThisFact {\%1} } } else { # term withOUT mutual exclusion _asg \%1 \%a } return \%a } # \farraylook(oofa*,&a)