/*******************************************************/ /* "C" Language Integrated Production System */ /* */ /* CLIPS Version 6.40 02/19/20 */ /* */ /* ENGINE MODULE */ /*******************************************************/ /*************************************************************/ /* Purpose: Provides functionality primarily associated with */ /* the run and focus commands. */ /* */ /* Principal Programmer(s): */ /* Gary D. Riley */ /* */ /* Contributing Programmer(s): */ /* Bebe Ly */ /* Brian L. Dantes */ /* */ /* Revision History: */ /* */ /* 6.23: Correction for FalseSymbol/TrueSymbol. DR0859 */ /* */ /* Corrected compilation errors for files */ /* generated by constructs-to-c. DR0861 */ /* */ /* 6.24: Removed DYNAMIC_SALIENCE, INCREMENTAL_RESET, */ /* and LOGICAL_DEPENDENCIES compilation flags. */ /* */ /* Renamed BOOLEAN macro type to intBool. */ /* */ /* Added access functions to the HaltRules flag. */ /* */ /* Added EnvGetNextFocus, EnvGetFocusChanged, and */ /* EnvSetFocusChanged functions. */ /* */ /* 6.30: Added additional developer statistics to help */ /* analyze join network performance. */ /* */ /* Removed pseudo-facts used in not CEs. */ /* */ /* Added context information for run functions. */ /* */ /* Added before rule firing callback function. */ /* */ /* Changed garbage collection algorithm. */ /* */ /* Changed integer type/precision. */ /* */ /* Added EnvHalt function. */ /* */ /* Used gensprintf instead of sprintf. */ /* */ /* Removed conditional code for unsupported */ /* compilers/operating systems (IBM_MCW, */ /* MAC_MCW, and IBM_TBC). */ /* Added const qualifiers to remove C++ */ /* deprecation warnings. */ /* */ /* Converted API macros to function calls. */ /* */ /* 6.31: Fixed dangling construct issue. */ /* */ /* 6.40: Added Env prefix to GetEvaluationError and */ /* SetEvaluationError functions. */ /* */ /* Added Env prefix to GetHaltExecution and */ /* SetHaltExecution functions. */ /* */ /* Pragma once and other inclusion changes. */ /* */ /* Added support for booleans with . */ /* */ /* Removed use of void pointers for specific */ /* data structures. */ /* */ /* ALLOW_ENVIRONMENT_GLOBALS no longer supported. */ /* */ /* Callbacks must be environment aware. */ /* */ /* Incremental reset is always enabled. */ /* */ /* UDF redesign. */ /* */ /* Added GCBlockStart and GCBlockEnd functions */ /* for garbage collection blocks. */ /* */ /*************************************************************/ #include #include #include "setup.h" #if DEFRULE_CONSTRUCT #include "agenda.h" #include "argacces.h" #include "commline.h" #include "constant.h" #include "envrnmnt.h" #include "factmngr.h" #include "inscom.h" #include "memalloc.h" #include "modulutl.h" #include "prccode.h" #include "prcdrfun.h" #include "prntutil.h" #include "proflfun.h" #include "reteutil.h" #include "retract.h" #include "router.h" #include "ruledlt.h" #include "sysdep.h" #include "utility.h" #include "watch.h" #include "engine.h" /***************************************/ /* LOCAL INTERNAL FUNCTION DEFINITIONS */ /***************************************/ static Defmodule *RemoveFocus(Environment *,Defmodule *); static void DeallocateEngineData(Environment *); /*****************************************************************************/ /* InitializeEngine: Initializes the activations and statistics watch items. */ /*****************************************************************************/ void InitializeEngine( Environment *theEnv) { AllocateEnvironmentData(theEnv,ENGINE_DATA,sizeof(struct engineData),DeallocateEngineData); #if DEBUGGING_FUNCTIONS AddWatchItem(theEnv,"statistics",0,&EngineData(theEnv)->WatchStatistics,20,NULL,NULL); AddWatchItem(theEnv,"focus",0,&EngineData(theEnv)->WatchFocus,0,NULL,NULL); #endif } /*************************************************/ /* DeallocateEngineData: Deallocates environment */ /* data for engine functionality. */ /*************************************************/ static void DeallocateEngineData( Environment *theEnv) { FocalModule *tmpPtr, *nextPtr; DeallocateRuleFiredCallList(theEnv,EngineData(theEnv)->ListOfAfterRuleFiresFunctions); DeallocateRuleFiredCallList(theEnv,EngineData(theEnv)->ListOfBeforeRuleFiresFunctions); tmpPtr = EngineData(theEnv)->CurrentFocus; while (tmpPtr != NULL) { nextPtr = tmpPtr->next; rtn_struct(theEnv,focalModule,tmpPtr); tmpPtr = nextPtr; } } /**********************************************/ /* Run: C access routine for the run command. */ /**********************************************/ long long Run( Environment *theEnv, long long runLimit) { long long rulesFired = 0; UDFValue returnValue; RuleFiredFunctionItem *ruleFiresFunction; #if DEBUGGING_FUNCTIONS unsigned long maxActivations = 0, sumActivations = 0; #if DEFTEMPLATE_CONSTRUCT unsigned long maxFacts = 0, sumFacts = 0; #endif #if OBJECT_SYSTEM unsigned long maxInstances = 0, sumInstances = 0; #endif #if (! GENERIC) double endTime, startTime = 0.0; #endif unsigned long tempValue; #endif unsigned short i; struct patternEntity *theMatchingItem; struct partialMatch *theBasis = NULL; Activation *theActivation; const char *ruleFiring; #if PROFILING_FUNCTIONS struct profileFrameInfo profileFrame; #endif struct trackedMemory *theTM; int danglingConstructs; GCBlock gcb; bool error = false; /*=====================================================*/ /* Make sure the run command is not already executing. */ /*=====================================================*/ if (EngineData(theEnv)->AlreadyRunning) { return 0; } EngineData(theEnv)->AlreadyRunning = true; /*========================================*/ /* Set up the frame for tracking garbage. */ /*========================================*/ GCBlockStart(theEnv,&gcb); /*================================*/ /* Set up statistics information. */ /*================================*/ #if DEBUGGING_FUNCTIONS if (EngineData(theEnv)->WatchStatistics) { #if DEFTEMPLATE_CONSTRUCT maxFacts = GetNumberOfFacts(theEnv); sumFacts = maxFacts; #endif #if OBJECT_SYSTEM maxInstances = GetGlobalNumberOfInstances(theEnv); sumInstances = maxInstances; #endif maxActivations = GetNumberOfActivations(theEnv); sumActivations = maxActivations; #if (! GENERIC) startTime = gentime(); #endif } #endif /*=====================================*/ /* If embedded, clear the error flags. */ /*=====================================*/ if (EvaluationData(theEnv)->CurrentExpression == NULL) { ResetErrorFlags(theEnv); } /*=============================*/ /* Set up execution variables. */ /*=============================*/ EngineData(theEnv)->HaltRules = false; #if DEVELOPER EngineData(theEnv)->leftToRightComparisons = 0; EngineData(theEnv)->rightToLeftComparisons = 0; EngineData(theEnv)->leftToRightSucceeds = 0; EngineData(theEnv)->rightToLeftSucceeds = 0; EngineData(theEnv)->leftToRightLoops = 0; EngineData(theEnv)->rightToLeftLoops = 0; EngineData(theEnv)->findNextConflictingComparisons = 0; EngineData(theEnv)->betaHashListSkips = 0; EngineData(theEnv)->betaHashHTSkips = 0; EngineData(theEnv)->unneededMarkerCompare = 0; #endif /*=====================================================*/ /* Fire rules until the agenda is empty, the run limit */ /* has been reached, or a rule execution error occurs. */ /*=====================================================*/ theActivation = NextActivationToFire(theEnv); while ((theActivation != NULL) && (runLimit != 0) && (EvaluationData(theEnv)->HaltExecution == false) && (EngineData(theEnv)->HaltRules == false)) { /*========================================*/ /* Execute the list of functions that are */ /* to be called before each rule firing. */ /*========================================*/ for (ruleFiresFunction = EngineData(theEnv)->ListOfBeforeRuleFiresFunctions; ruleFiresFunction != NULL; ruleFiresFunction = ruleFiresFunction->next) { (*ruleFiresFunction->func)(theEnv,theActivation,ruleFiresFunction->context); } /*===========================================*/ /* Detach the activation from the agenda and */ /* determine which rule is firing. */ /*===========================================*/ DetachActivation(theEnv,theActivation); theTM = AddTrackedMemory(theEnv,theActivation,sizeof(struct activation)); ruleFiring = ActivationRuleName(theActivation); theBasis = (struct partialMatch *) GetActivationBasis(theEnv,theActivation); EngineData(theEnv)->ExecutingRule = GetActivationRule(theEnv,theActivation); /*=============================================*/ /* Update the number of rules that have fired. */ /*=============================================*/ rulesFired++; if (runLimit > 0) { runLimit--; } /*==================================*/ /* If rules are being watched, then */ /* print an information message. */ /*==================================*/ #if DEBUGGING_FUNCTIONS if (EngineData(theEnv)->ExecutingRule->watchFiring) { char printSpace[60]; gensprintf(printSpace,"FIRE %4lld ",rulesFired); WriteString(theEnv,STDOUT,printSpace); WriteString(theEnv,STDOUT,ruleFiring); WriteString(theEnv,STDOUT,": "); PrintPartialMatch(theEnv,STDOUT,theBasis); WriteString(theEnv,STDOUT,"\n"); } #endif /*=================================================*/ /* Remove the link between the activation and the */ /* completed match for the rule. Set the busy flag */ /* for the completed match to true (so the match */ /* upon which our RHS variables are dependent is */ /* not deleted while our rule is firing). Set up */ /* the global pointers to the completed match for */ /* routines which do variable extractions. */ /*=================================================*/ theBasis->marker = NULL; theBasis->busy = true; EngineData(theEnv)->GlobalLHSBinds = theBasis; EngineData(theEnv)->GlobalRHSBinds = NULL; /*===================================================================*/ /* Increment the count for each of the facts/objects associated with */ /* the rule activation so that the facts/objects cannot be deleted */ /* by garbage collection while the rule is executing. */ /*===================================================================*/ for (i = 0; i < theBasis->bcount; i++) { if (theBasis->binds[i].gm.theMatch == NULL) continue; theMatchingItem = theBasis->binds[i].gm.theMatch->matchingItem; if (theMatchingItem != NULL) { (*theMatchingItem->theInfo->incrementBasisCount)(theEnv,theMatchingItem); } } /*====================================================*/ /* If the rule has logical CEs, set up the pointer to */ /* the rules logical join so the assert command will */ /* attach the appropriate dependencies to the facts. */ /*====================================================*/ EngineData(theEnv)->TheLogicalJoin = EngineData(theEnv)->ExecutingRule->logicalJoin; if (EngineData(theEnv)->TheLogicalJoin != NULL) { EngineData(theEnv)->TheLogicalBind = FindLogicalBind(EngineData(theEnv)->TheLogicalJoin,EngineData(theEnv)->GlobalLHSBinds); EngineData(theEnv)->TheLogicalBind->busy = true; } else { EngineData(theEnv)->TheLogicalBind = NULL; } /*=============================================*/ /* Execute the rule's right hand side actions. */ /*=============================================*/ EvaluationData(theEnv)->CurrentEvaluationDepth++; SetEvaluationError(theEnv,false); EngineData(theEnv)->ExecutingRule->executing = true; danglingConstructs = ConstructData(theEnv)->DanglingConstructs; #if PROFILING_FUNCTIONS StartProfile(theEnv,&profileFrame, &EngineData(theEnv)->ExecutingRule->header.usrData, ProfileFunctionData(theEnv)->ProfileConstructs); #endif EvaluateProcActions(theEnv,EngineData(theEnv)->ExecutingRule->header.whichModule->theModule, EngineData(theEnv)->ExecutingRule->actions,EngineData(theEnv)->ExecutingRule->localVarCnt, &returnValue,NULL); #if PROFILING_FUNCTIONS EndProfile(theEnv,&profileFrame); #endif error = GetEvaluationError(theEnv); EngineData(theEnv)->ExecutingRule->executing = false; SetEvaluationError(theEnv,false); EvaluationData(theEnv)->CurrentEvaluationDepth--; if (EvaluationData(theEnv)->CurrentExpression == NULL) { ConstructData(theEnv)->DanglingConstructs = danglingConstructs; } /*========================================*/ /* Execute the list of functions that are */ /* to be called after each rule firing. */ /*========================================*/ for (ruleFiresFunction = EngineData(theEnv)->ListOfAfterRuleFiresFunctions; ruleFiresFunction != NULL; ruleFiresFunction = ruleFiresFunction->next) { (*ruleFiresFunction->func)(theEnv,theActivation,ruleFiresFunction->context); } /*=====================================*/ /* Remove information for logical CEs. */ /*=====================================*/ EngineData(theEnv)->TheLogicalJoin = NULL; if (EngineData(theEnv)->TheLogicalBind != NULL) { EngineData(theEnv)->TheLogicalBind->busy = false; EngineData(theEnv)->TheLogicalBind = NULL; } /*=====================================================*/ /* If rule execution was halted, then print a message. */ /*=====================================================*/ #if DEBUGGING_FUNCTIONS if ((EvaluationData(theEnv)->HaltExecution) || (EngineData(theEnv)->HaltRules && EngineData(theEnv)->ExecutingRule->watchFiring)) #else if ((EvaluationData(theEnv)->HaltExecution) || (EngineData(theEnv)->HaltRules)) #endif { const char *logName; if (error) { PrintErrorID(theEnv,"PRCCODE",4,false); logName = STDERR; } else { PrintWarningID(theEnv,"PRCCODE",4,false); logName = STDWRN; } WriteString(theEnv,logName,"Execution halted during the actions of defrule '"); WriteString(theEnv,logName,ruleFiring); WriteString(theEnv,logName,"'.\n"); } /*===================================================*/ /* Decrement the count for each of the facts/objects */ /* associated with the rule activation. */ /*===================================================*/ theBasis->busy = false; for (i = 0; i < (theBasis->bcount); i++) { if (theBasis->binds[i].gm.theMatch == NULL) continue; theMatchingItem = theBasis->binds[i].gm.theMatch->matchingItem; if (theMatchingItem != NULL) { (*theMatchingItem->theInfo->decrementBasisCount)(theEnv,theMatchingItem); } } /*========================================*/ /* Return the agenda node to free memory. */ /*========================================*/ RemoveTrackedMemory(theEnv,theTM); RemoveActivation(theEnv,theActivation,false,false); /*======================================*/ /* Get rid of partial matches discarded */ /* while executing the rule's RHS. */ /*======================================*/ FlushGarbagePartialMatches(theEnv); /*==================================*/ /* Get rid of other garbage created */ /* while executing the rule's RHS. */ /*==================================*/ CleanCurrentGarbageFrame(theEnv,NULL); CallPeriodicTasks(theEnv); /*==========================*/ /* Keep up with statistics. */ /*==========================*/ #if DEBUGGING_FUNCTIONS if (EngineData(theEnv)->WatchStatistics) { #if DEFTEMPLATE_CONSTRUCT tempValue = GetNumberOfFacts(theEnv); if (tempValue > maxFacts) maxFacts = tempValue; sumFacts += tempValue; #endif #if OBJECT_SYSTEM tempValue = GetGlobalNumberOfInstances(theEnv); if (tempValue > maxInstances) maxInstances = tempValue; sumInstances += tempValue; #endif tempValue = GetNumberOfActivations(theEnv); if (tempValue > maxActivations) maxActivations = tempValue; sumActivations += tempValue; } #endif /*==================================*/ /* Update saliences if appropriate. */ /*==================================*/ if (GetSalienceEvaluation(theEnv) == EVERY_CYCLE) { RefreshAllAgendas(theEnv); } /*========================================*/ /* If a return was issued on the RHS of a */ /* rule, then remove *that* rule's module */ /* from the focus stack */ /*========================================*/ if (ProcedureFunctionData(theEnv)->ReturnFlag == true) { RemoveFocus(theEnv,EngineData(theEnv)->ExecutingRule->header.whichModule->theModule); } ProcedureFunctionData(theEnv)->ReturnFlag = false; /*========================================*/ /* Determine the next activation to fire. */ /*========================================*/ theActivation = (struct activation *) NextActivationToFire(theEnv); /*==============================*/ /* Check for a rule breakpoint. */ /*==============================*/ if (theActivation != NULL) { if (GetActivationRule(theEnv,theActivation)->afterBreakpoint) { EngineData(theEnv)->HaltRules = true; WriteString(theEnv,STDOUT,"Breaking on rule "); WriteString(theEnv,STDOUT,ActivationRuleName(theActivation)); WriteString(theEnv,STDOUT,".\n"); } } } /*=====================================================*/ /* Make sure run functions are executed at least once. */ /*=====================================================*/ if (rulesFired == 0) { for (ruleFiresFunction = EngineData(theEnv)->ListOfAfterRuleFiresFunctions; ruleFiresFunction != NULL; ruleFiresFunction = ruleFiresFunction->next) { (*ruleFiresFunction->func)(theEnv,NULL,ruleFiresFunction->context); } } /*======================================================*/ /* If rule execution was halted because the rule firing */ /* limit was reached, then print a message. */ /*======================================================*/ if (runLimit == rulesFired) { WriteString(theEnv,STDOUT,"rule firing limit reached\n"); } /*==============================*/ /* Restore execution variables. */ /*==============================*/ EngineData(theEnv)->ExecutingRule = NULL; EngineData(theEnv)->HaltRules = false; /*=================================================*/ /* Print out statistics if they are being watched. */ /*=================================================*/ #if DEBUGGING_FUNCTIONS if (EngineData(theEnv)->WatchStatistics) { #if DEFTEMPLATE_CONSTRUCT || OBJECT_SYSTEM || DEVELOPER char printSpace[60]; #endif #if (! GENERIC) endTime = gentime(); #endif WriteInteger(theEnv,STDOUT,rulesFired); WriteString(theEnv,STDOUT," rules fired"); #if (! GENERIC) if (startTime != endTime) { WriteString(theEnv,STDOUT," Run time is "); WriteFloat(theEnv,STDOUT,endTime - startTime); WriteString(theEnv,STDOUT," seconds.\n"); WriteFloat(theEnv,STDOUT,(double) rulesFired / (endTime - startTime)); WriteString(theEnv,STDOUT," rules per second.\n"); } else { WriteString(theEnv,STDOUT,"\n"); } #else WriteString(theEnv,STDOUT,"\n"); #endif #if DEFTEMPLATE_CONSTRUCT gensprintf(printSpace,"%ld mean number of facts (%ld maximum).\n", (long) (((double) sumFacts / (rulesFired + 1)) + 0.5), maxFacts); WriteString(theEnv,STDOUT,printSpace); #endif #if OBJECT_SYSTEM gensprintf(printSpace,"%ld mean number of instances (%ld maximum).\n", (long) (((double) sumInstances / (rulesFired + 1)) + 0.5), maxInstances); WriteString(theEnv,STDOUT,printSpace); #endif gensprintf(printSpace,"%ld mean number of activations (%ld maximum).\n", (long) (((double) sumActivations / (rulesFired + 1)) + 0.5), maxActivations); WriteString(theEnv,STDOUT,printSpace); #if DEVELOPER gensprintf(printSpace,"%9ld left to right comparisons.\n", EngineData(theEnv)->leftToRightComparisons); WriteString(theEnv,STDOUT,printSpace); gensprintf(printSpace,"%9ld left to right succeeds.\n", EngineData(theEnv)->leftToRightSucceeds); WriteString(theEnv,STDOUT,printSpace); gensprintf(printSpace,"%9ld left to right loops.\n", EngineData(theEnv)->leftToRightLoops); WriteString(theEnv,STDOUT,printSpace); gensprintf(printSpace,"%9ld right to left comparisons.\n", EngineData(theEnv)->rightToLeftComparisons); WriteString(theEnv,STDOUT,printSpace); gensprintf(printSpace,"%9ld right to left succeeds.\n", EngineData(theEnv)->rightToLeftSucceeds); WriteString(theEnv,STDOUT,printSpace); gensprintf(printSpace,"%9ld right to left loops.\n", EngineData(theEnv)->rightToLeftLoops); WriteString(theEnv,STDOUT,printSpace); gensprintf(printSpace,"%9ld find next conflicting comparisons.\n", EngineData(theEnv)->findNextConflictingComparisons); WriteString(theEnv,STDOUT,printSpace); gensprintf(printSpace,"%9ld beta hash list skips.\n", EngineData(theEnv)->betaHashListSkips); WriteString(theEnv,STDOUT,printSpace); gensprintf(printSpace,"%9ld beta hash hash table skips.\n", EngineData(theEnv)->betaHashHTSkips); WriteString(theEnv,STDOUT,printSpace); gensprintf(printSpace,"%9ld unneeded marker compare.\n", EngineData(theEnv)->unneededMarkerCompare); WriteString(theEnv,STDOUT,printSpace); #endif } #endif /*==========================================*/ /* The current module should be the current */ /* focus when the run finishes. */ /*==========================================*/ if (EngineData(theEnv)->CurrentFocus != NULL) { if (EngineData(theEnv)->CurrentFocus->theModule != GetCurrentModule(theEnv)) { SetCurrentModule(theEnv,EngineData(theEnv)->CurrentFocus->theModule); } } /*================================*/ /* Restore the old garbage frame. */ /*================================*/ GCBlockEnd(theEnv,&gcb); CallPeriodicTasks(theEnv); /*===================================*/ /* Return the number of rules fired. */ /*===================================*/ EngineData(theEnv)->AlreadyRunning = false; return rulesFired; } /***********************************************************/ /* NextActivationToFire: Returns the next activation which */ /* should be executed based on the current focus. */ /***********************************************************/ Activation *NextActivationToFire( Environment *theEnv) { struct activation *theActivation; Defmodule *theModule; /*====================================*/ /* If there is no current focus, then */ /* focus on the MAIN module. */ /*====================================*/ if (EngineData(theEnv)->CurrentFocus == NULL) { theModule = FindDefmodule(theEnv,"MAIN"); Focus(theModule); } /*===========================================================*/ /* Determine the top activation on the agenda of the current */ /* focus. If the current focus has no activations on its */ /* agenda, then pop the focus off the focus stack until */ /* a focus that has an activation on its agenda is found. */ /*===========================================================*/ theActivation = EngineData(theEnv)->CurrentFocus->theDefruleModule->agenda; while ((theActivation == NULL) && (EngineData(theEnv)->CurrentFocus != NULL)) { if (EngineData(theEnv)->CurrentFocus != NULL) PopFocus(theEnv); if (EngineData(theEnv)->CurrentFocus != NULL) theActivation = EngineData(theEnv)->CurrentFocus->theDefruleModule->agenda; } /*=========================================*/ /* Return the next activation to be fired. */ /*=========================================*/ return(theActivation); } /***************************************************/ /* RemoveFocus: Removes the first occurence of the */ /* specified module from the focus stack. */ /***************************************************/ static Defmodule *RemoveFocus( Environment *theEnv, Defmodule *theModule) { FocalModule *tempFocus,*prevFocus, *nextFocus; bool found = false; bool currentFocusRemoved = false; /*====================================*/ /* Return NULL if there is nothing on */ /* the focus stack to remove. */ /*====================================*/ if (EngineData(theEnv)->CurrentFocus == NULL) return NULL; /*=============================================*/ /* Remove the first occurence of the specified */ /* module from the focus stack. */ /*=============================================*/ prevFocus = NULL; tempFocus = EngineData(theEnv)->CurrentFocus; while ((tempFocus != NULL) && (! found)) { if (tempFocus->theModule == theModule) { found = true; nextFocus = tempFocus->next; rtn_struct(theEnv,focalModule,tempFocus); tempFocus = nextFocus; if (prevFocus == NULL) { currentFocusRemoved = true; EngineData(theEnv)->CurrentFocus = tempFocus; } else { prevFocus->next = tempFocus; } } else { prevFocus = tempFocus; tempFocus = tempFocus->next; } } /*=========================================*/ /* If the given module is not in the focus */ /* stack, simply return the current focus */ /*=========================================*/ if (! found) return EngineData(theEnv)->CurrentFocus->theModule; /*========================================*/ /* If the current focus is being watched, */ /* then print an informational message. */ /*========================================*/ #if DEBUGGING_FUNCTIONS if (EngineData(theEnv)->WatchFocus && (! ConstructData(theEnv)->ClearReadyInProgress) && (! ConstructData(theEnv)->ClearInProgress)) { WriteString(theEnv,STDOUT,"<== Focus "); WriteString(theEnv,STDOUT,theModule->header.name->contents); if ((EngineData(theEnv)->CurrentFocus != NULL) && currentFocusRemoved) { WriteString(theEnv,STDOUT," to "); WriteString(theEnv,STDOUT,EngineData(theEnv)->CurrentFocus->theModule->header.name->contents); } WriteString(theEnv,STDOUT,"\n"); } #endif /*======================================================*/ /* Set the current module to the module associated with */ /* the current focus (if it changed) and set a boolean */ /* flag indicating that the focus has changed. */ /*======================================================*/ if ((EngineData(theEnv)->CurrentFocus != NULL) && currentFocusRemoved) { SetCurrentModule(theEnv,EngineData(theEnv)->CurrentFocus->theModule); } EngineData(theEnv)->FocusChanged = true; /*====================================*/ /* Return the module that was removed */ /* from the focus stack. */ /*====================================*/ return(theModule); } /**********************************************************/ /* PopFocus: C access routine for the pop-focus function. */ /**********************************************************/ Defmodule *PopFocus( Environment *theEnv) { if (EngineData(theEnv)->CurrentFocus == NULL) return NULL; return RemoveFocus(theEnv,EngineData(theEnv)->CurrentFocus->theModule); } /************************************************************/ /* GetNextFocus: Returns the next focus on the focus stack. */ /************************************************************/ FocalModule *GetNextFocus( Environment *theEnv, FocalModule *theFocus) { /*==================================================*/ /* If NULL is passed as an argument, return the top */ /* focus on the focus stack (the current focus). */ /*==================================================*/ if (theFocus == NULL) return EngineData(theEnv)->CurrentFocus; /*=======================================*/ /* Otherwise, return the focus following */ /* the focus passed as an argument. */ /*=======================================*/ return theFocus->next; } /*********************************************************/ /* FocalModuleName: Returns the name of the FocalModule. */ /*********************************************************/ const char *FocalModuleName( FocalModule *theFocus) { return theFocus->theModule->header.name->contents; } /****************************************************************/ /* FocalModuleModule: Returns the Defmodule of the FocalModule. */ /****************************************************************/ Defmodule *FocalModuleModule( FocalModule *theFocus) { return theFocus->theModule; } /***************************************************/ /* Focus: C access routine for the focus function. */ /***************************************************/ void Focus( Defmodule *theModule) { FocalModule *tempFocus; Environment *theEnv; if (theModule == NULL) return; theEnv = theModule->header.env; /*==================================================*/ /* Make the specified module be the current module. */ /* If the specified module is the current focus, */ /* then no further action is needed. */ /*==================================================*/ SetCurrentModule(theEnv,theModule); if (EngineData(theEnv)->CurrentFocus != NULL) { if (EngineData(theEnv)->CurrentFocus->theModule == theModule) return; } /*=====================================*/ /* If the focus is being watched, then */ /* print an information message. */ /*=====================================*/ #if DEBUGGING_FUNCTIONS if (EngineData(theEnv)->WatchFocus && (! ConstructData(theEnv)->ClearReadyInProgress) && (! ConstructData(theEnv)->ClearInProgress)) { WriteString(theEnv,STDOUT,"==> Focus "); WriteString(theEnv,STDOUT,theModule->header.name->contents); if (EngineData(theEnv)->CurrentFocus != NULL) { WriteString(theEnv,STDOUT," from "); WriteString(theEnv,STDOUT,EngineData(theEnv)->CurrentFocus->theModule->header.name->contents); } WriteString(theEnv,STDOUT,"\n"); } #endif /*=======================================*/ /* Add the new focus to the focus stack. */ /*=======================================*/ tempFocus = get_struct(theEnv,focalModule); tempFocus->theModule = theModule; tempFocus->theDefruleModule = GetDefruleModuleItem(theEnv,theModule); tempFocus->next = EngineData(theEnv)->CurrentFocus; EngineData(theEnv)->CurrentFocus = tempFocus; EngineData(theEnv)->FocusChanged = true; } /************************************************/ /* ClearFocusStackCommand: H/L access routine */ /* for the clear-focus-stack command. */ /************************************************/ void ClearFocusStackCommand( Environment *theEnv, UDFContext *context, UDFValue *returnValue) { ClearFocusStack(theEnv); } /****************************************/ /* ClearFocusStack: C access routine */ /* for the clear-focus-stack command. */ /****************************************/ void ClearFocusStack( Environment *theEnv) { while (EngineData(theEnv)->CurrentFocus != NULL) PopFocus(theEnv); EngineData(theEnv)->FocusChanged = true; } /**********************************************/ /* AddAfterRuleFiresFunction: Adds a function */ /* to the ListOfAfterRuleFiresFunctions. */ /**********************************************/ bool AddAfterRuleFiresFunction( Environment *theEnv, const char *name, RuleFiredFunction *functionPtr, int priority, void *context) { EngineData(theEnv)->ListOfAfterRuleFiresFunctions = AddRuleFiredFunctionToCallList(theEnv,name,priority,functionPtr, EngineData(theEnv)->ListOfAfterRuleFiresFunctions,context); return true; } /***********************************************/ /* AddBeforeRuleFiresFunction: Adds a function */ /* to the ListOfBeforeRuleFiresFunctions. */ /***********************************************/ bool AddBeforeRuleFiresFunction( Environment *theEnv, const char *name, RuleFiredFunction *functionPtr, int priority, void *context) { EngineData(theEnv)->ListOfBeforeRuleFiresFunctions = AddRuleFiredFunctionToCallList(theEnv,name,priority,functionPtr, EngineData(theEnv)->ListOfBeforeRuleFiresFunctions,context); return true; } /****************************************************/ /* RemoveAfterRuleFiresFunction: Removes a function */ /* from the ListOfAfterRuleFiresFunctions. */ /****************************************************/ bool RemoveAfterRuleFiresFunction( Environment *theEnv, const char *name) { bool found; EngineData(theEnv)->ListOfAfterRuleFiresFunctions = RemoveRuleFiredFunctionFromCallList(theEnv,name,EngineData(theEnv)->ListOfAfterRuleFiresFunctions,&found); return found; } /*****************************************************/ /* RemoveBeforeRuleFiresFunction: Removes a function */ /* from the ListOfBeforeRuleFiresFunctions. */ /*****************************************************/ bool RemoveBeforeRuleFiresFunction( Environment *theEnv, const char *name) { bool found; EngineData(theEnv)->ListOfBeforeRuleFiresFunctions = RemoveRuleFiredFunctionFromCallList(theEnv,name,EngineData(theEnv)->ListOfBeforeRuleFiresFunctions,&found); return found; } /***************************************************/ /* AddRuleFiredFunctionToCallList: Adds a function */ /* to a rule fired function list. */ /***************************************************/ RuleFiredFunctionItem *AddRuleFiredFunctionToCallList( Environment *theEnv, const char *name, int priority, RuleFiredFunction *func, RuleFiredFunctionItem *head, void *context) { RuleFiredFunctionItem *newPtr, *currentPtr, *lastPtr = NULL; char *nameCopy; newPtr = get_struct(theEnv,ruleFiredFunctionItem); nameCopy = (char *) genalloc(theEnv,strlen(name) + 1); genstrcpy(nameCopy,name); newPtr->name = nameCopy; newPtr->func = func; newPtr->priority = priority; newPtr->context = context; if (head == NULL) { newPtr->next = NULL; return newPtr; } currentPtr = head; while ((currentPtr != NULL) ? (priority < currentPtr->priority) : false) { lastPtr = currentPtr; currentPtr = currentPtr->next; } if (lastPtr == NULL) { newPtr->next = head; head = newPtr; } else { newPtr->next = currentPtr; lastPtr->next = newPtr; } return head; } /***********************************************************/ /* RemoveRuleFiredFunctionFromCallList: Removes a function */ /* from a rule fired function list. */ /***********************************************************/ RuleFiredFunctionItem *RemoveRuleFiredFunctionFromCallList( Environment *theEnv, const char *name, RuleFiredFunctionItem *head, bool *found) { RuleFiredFunctionItem *currentPtr, *lastPtr; *found = false; lastPtr = NULL; currentPtr = head; while (currentPtr != NULL) { if (strcmp(name,currentPtr->name) == 0) { *found = true; if (lastPtr == NULL) { head = currentPtr->next; } else { lastPtr->next = currentPtr->next; } genfree(theEnv,(void *) currentPtr->name,strlen(currentPtr->name) + 1); rtn_struct(theEnv,voidCallFunctionItem,currentPtr); return head; } lastPtr = currentPtr; currentPtr = currentPtr->next; } return head; } /******************************************************/ /* DeallocateRuleFiredCallList: Removes all functions */ /* from a list of rule fired functions. */ /******************************************************/ void DeallocateRuleFiredCallList( Environment *theEnv, RuleFiredFunctionItem *theList) { RuleFiredFunctionItem *tmpPtr, *nextPtr; tmpPtr = theList; while (tmpPtr != NULL) { nextPtr = tmpPtr->next; genfree(theEnv,(void *) tmpPtr->name,strlen(tmpPtr->name) + 1); rtn_struct(theEnv,ruleFiredFunctionItem,tmpPtr); tmpPtr = nextPtr; } } /*******************************************************/ /* RunCommand: H/L access routine for the run command. */ /*******************************************************/ void RunCommand( Environment *theEnv, UDFContext *context, UDFValue *returnValue) { unsigned int numArgs; long long runLimit = -1LL; UDFValue theArg; numArgs = UDFArgumentCount(context); if (numArgs == 0) { runLimit = -1LL; } else if (numArgs == 1) { if (! UDFFirstArgument(context,INTEGER_BIT,&theArg)) return; runLimit = theArg.integerValue->contents; } Run(theEnv,runLimit); } /***********************************************/ /* HaltCommand: Causes rule execution to halt. */ /***********************************************/ void HaltCommand( Environment *theEnv, UDFContext *context, UDFValue *returnValue) { Halt(theEnv); } /***************************/ /* Halt: C access routine */ /* for the halt command. */ /***************************/ void Halt( Environment *theEnv) { EngineData(theEnv)->HaltRules = true; } #if DEBUGGING_FUNCTIONS /********************************/ /* SetBreak: C access routine */ /* for the set-break command. */ /********************************/ void SetBreak( Defrule *theRule) { Defrule *thePtr; for (thePtr = theRule; thePtr != NULL; thePtr = thePtr->disjunct) { thePtr->afterBreakpoint = 1; } } /***********************************/ /* RemoveBreak: C access routine */ /* for the remove-break command. */ /***********************************/ bool RemoveBreak( Defrule *theRule) { Defrule *thePtr; bool rv = false; for (thePtr = theRule; thePtr != NULL; thePtr = thePtr->disjunct) { if (thePtr->afterBreakpoint == 1) { thePtr->afterBreakpoint = 0; rv = true; } } return rv; } /**************************************************/ /* RemoveAllBreakpoints: Removes all breakpoints. */ /**************************************************/ void RemoveAllBreakpoints( Environment *theEnv) { Defrule *theRule; Defmodule *theDefmodule = NULL; while ((theDefmodule = GetNextDefmodule(theEnv,theDefmodule)) != NULL) { theRule = NULL; while ((theRule = GetNextDefrule(theEnv,theRule)) != NULL) { RemoveBreak(theRule); } } } /**********************************/ /* ShowBreaks: C access routine */ /* for the show-breaks command. */ /**********************************/ void ShowBreaks( Environment *theEnv, const char *logicalName, Defmodule *theModule) { ListItemsDriver(theEnv,logicalName,theModule, NULL,NULL, (GetNextItemFunction *) GetNextDefrule, (const char *(*)(void *)) GetConstructNameString, NULL, (bool (*)(void *)) DefruleHasBreakpoint); } /***********************************************/ /* DefruleHasBreakpoint: Indicates whether the */ /* specified rule has a breakpoint set. */ /***********************************************/ bool DefruleHasBreakpoint( Defrule *theRule) { return theRule->afterBreakpoint; } /*****************************************/ /* SetBreakCommand: H/L access routine */ /* for the set-break command. */ /*****************************************/ void SetBreakCommand( Environment *theEnv, UDFContext *context, UDFValue *returnValue) { UDFValue theArg; const char *argument; Defrule *defrulePtr; if (! UDFFirstArgument(context,SYMBOL_BIT,&theArg)) return; argument = theArg.lexemeValue->contents; if ((defrulePtr = FindDefrule(theEnv,argument)) == NULL) { CantFindItemErrorMessage(theEnv,"defrule",argument,true); return; } SetBreak(defrulePtr); } /********************************************/ /* RemoveBreakCommand: H/L access routine */ /* for the remove-break command. */ /********************************************/ void RemoveBreakCommand( Environment *theEnv, UDFContext *context, UDFValue *returnValue) { UDFValue theArg; const char *argument; Defrule *defrulePtr; if (UDFArgumentCount(context) == 0) { RemoveAllBreakpoints(theEnv); return; } if (! UDFFirstArgument(context,SYMBOL_BIT,&theArg)) return; argument = theArg.lexemeValue->contents; if ((defrulePtr = FindDefrule(theEnv,argument)) == NULL) { CantFindItemErrorMessage(theEnv,"defrule",argument,true); return; } if (RemoveBreak(defrulePtr) == false) { WriteString(theEnv,STDERR,"Rule "); WriteString(theEnv,STDERR,argument); WriteString(theEnv,STDERR," does not have a breakpoint set.\n"); } } /*******************************************/ /* ShowBreaksCommand: H/L access routine */ /* for the show-breaks command. */ /*******************************************/ void ShowBreaksCommand( Environment *theEnv, UDFContext *context, UDFValue *returnValue) { unsigned int numArgs; bool error; Defmodule *theModule; numArgs = UDFArgumentCount(context); if (numArgs == 1) { theModule = GetModuleName(context,1,&error); if (error) return; } else { theModule = GetCurrentModule(theEnv); } ShowBreaks(theEnv,STDOUT,theModule); } /***********************************************/ /* ListFocusStackCommand: H/L access routine */ /* for the list-focus-stack command. */ /***********************************************/ void ListFocusStackCommand( Environment *theEnv, UDFContext *context, UDFValue *returnValue) { ListFocusStack(theEnv,STDOUT); } /***************************************/ /* ListFocusStack: C access routine */ /* for the list-focus-stack command. */ /***************************************/ void ListFocusStack( Environment *theEnv, const char *logicalName) { FocalModule *theFocus; for (theFocus = EngineData(theEnv)->CurrentFocus; theFocus != NULL; theFocus = theFocus->next) { WriteString(theEnv,logicalName,DefmoduleName(theFocus->theModule)); WriteString(theEnv,logicalName,"\n"); } } #endif /* DEBUGGING_FUNCTIONS */ /***********************************************/ /* GetFocusStackFunction: H/L access routine */ /* for the get-focus-stack function. */ /***********************************************/ void GetFocusStackFunction( Environment *theEnv, UDFContext *context, UDFValue *returnValue) { CLIPSValue result; GetFocusStack(theEnv,&result); CLIPSToUDFValue(&result,returnValue); } /***************************************/ /* GetFocusStack: C access routine */ /* for the get-focus-stack function. */ /***************************************/ void GetFocusStack( Environment *theEnv, CLIPSValue *returnValue) { FocalModule *theFocus; Multifield *theList; unsigned long count = 0; /*===========================================*/ /* If there is no current focus, then return */ /* a multifield value of length zero. */ /*===========================================*/ if (EngineData(theEnv)->CurrentFocus == NULL) { returnValue->value = CreateMultifield(theEnv,0L); return; } /*=====================================================*/ /* Determine the number of modules on the focus stack. */ /*=====================================================*/ for (theFocus = EngineData(theEnv)->CurrentFocus; theFocus != NULL; theFocus = theFocus->next) { count++; } /*=============================================*/ /* Create a multifield of the appropriate size */ /* in which to store the module names. */ /*=============================================*/ theList = CreateMultifield(theEnv,count); returnValue->multifieldValue = theList; /*=================================================*/ /* Store the module names in the multifield value. */ /*=================================================*/ for (theFocus = EngineData(theEnv)->CurrentFocus, count = 0; theFocus != NULL; theFocus = theFocus->next, count++) { theList->contents[count].value = theFocus->theModule->header.name; } } /******************************************/ /* PopFocusFunction: H/L access routine */ /* for the pop-focus function. */ /******************************************/ void PopFocusFunction( Environment *theEnv, UDFContext *context, UDFValue *returnValue) { Defmodule *theModule; theModule = PopFocus(theEnv); if (theModule == NULL) { returnValue->lexemeValue = FalseSymbol(theEnv); return; } returnValue->value = theModule->header.name; } /**************************************/ /* FocusCommand: H/L access routine */ /* for the focus function. */ /**************************************/ void FocusCommand( Environment *theEnv, UDFContext *context, UDFValue *returnValue) { UDFValue theArg; const char *argument; Defmodule *theModule; unsigned int argCount, i; /*===========================================*/ /* Focus on the specified defrule module(s). */ /*===========================================*/ argCount = UDFArgumentCount(context); for (i = argCount; i > 0; i--) { if (! UDFNthArgument(context,i,SYMBOL_BIT,&theArg)) { return; } argument = theArg.lexemeValue->contents; theModule = FindDefmodule(theEnv,argument); if (theModule == NULL) { CantFindItemErrorMessage(theEnv,"defmodule",argument,true); returnValue->lexemeValue = FalseSymbol(theEnv); return; } Focus(theModule); } /*===================================================*/ /* Return true to indicate success of focus command. */ /*===================================================*/ returnValue->lexemeValue = TrueSymbol(theEnv); } /********************************************************************/ /* GetFocusChanged: Returns the value of the variable FocusChanged. */ /********************************************************************/ bool GetFocusChanged( Environment *theEnv) { return EngineData(theEnv)->FocusChanged; } /*****************************************************************/ /* SetFocusChanged: Sets the value of the variable FocusChanged. */ /*****************************************************************/ void SetFocusChanged( Environment *theEnv, bool value) { EngineData(theEnv)->FocusChanged = value; } /******************************************/ /* SetHaltRules: Sets the HaltRules flag. */ /******************************************/ void SetHaltRules( Environment *theEnv, bool value) { EngineData(theEnv)->HaltRules = value; } /*************************************************/ /* GetHaltRules: Returns the HaltExecution flag. */ /*************************************************/ bool GetHaltRules( Environment *theEnv) { return(EngineData(theEnv)->HaltRules); } #endif /* DEFRULE_CONSTRUCT */