Table of ContentsSection 7 I/O Router SystemThe I/O router system provided in CLIPS is quite flexible and will allow a wide variety of interfaces to be developed and easily attached to CLIPS. The system is relatively easy to use and is explained fully in sections 7.1 through 7.4. The CLIPS I/O functions for using the router system are described in sections 7.5 and 7.6, and finally, in appendix B, some examples are included which show how I/O routing could be used for simple interfaces. 7.1 IntroductionThe problem that originally inspired the idea of I/O routing will be considered as an introduction to I/O routing. Because CLIPS was designed with portability as a major goal, it was not possible to build a sophisticated user interface that would support many of the features found in the interfaces of commercial expert system building tools. A prototype was built of a semi portable interface for CLIPS using the CURSES screen management package. Many problems were encountered during this effort involving both portability concerns and CLIPS internal features. For example, every statement in the source code which used the C print function, printf, for printing to the terminal had to be replaced by the CURSES function, wprintw, which would print to a window on the terminal. In addition to changing function call names, different types of I/O had to be directed to different windows. The tracing information was to be sent to one window, the command prompt was to appear in another window, and output from printout statements was to be sent to yet another window.
7.2 Logical NamesOne of the key concepts of I/O routing is the use of logical names. An analogy will be useful in explaining this concept. Consider the Acme company which has two computers: computers X and Y. The Acme company stores three data sets on these two computers: a personnel data set, an accounting data set, and a documentation data set. One of the employees, Joe, wishes to update the payroll information in the accounting data set. If the payroll information was located in directory A on computer Y, Joe's command would be
documentation = X:[C] personnel = Y:[B]
stdin The default for all user inputs. The read and readline functions read from stdin if t is specified as the logical name.
7.3 RoutersThe use of logical names has solved two problems. Logical names make it easy to create generic I/O functions, and they allow the specification of different sources and destinations for I/O. The use of logical names allows CLIPS to ignore the specifics of an I/O request. However, such requests must still be specified at some level. I/O routers are provided to handle the specific details of a request.
7.4 Router PrioritiesEach I/O router has a priority. Priority determines which routers are queried first when determining the router that will handle an I/O request. Routers with high priorities are queried before routers with low priorities. Priorities are assigned as integer values (the higher the integer, the higher the priority). Priorities are important because more than one router can handle an I/O request for a single logical name, and they enable the user to define a custom interface for CLIPS. For example, the user could build a custom router which handles all logical names normally handled by the default router associated with the standard interface. The user adds the custom router with a priority higher than the priority of the router for the standard interface. The custom router will then intercept all I/O requests intended for the standard interface and specially process those requests to the custom interface.
50 Any router that uses “unique” logical names and does not want to share I/O with catch-all routers.
7.5 Internal I/O FunctionsThe following functions are called internally by CLIPS. These functions search the list of active routers and determine which router should handle an I/O request. Some routers may wish to deactivate themselves and call one of these functions to allow the next router to process an I/O request. Prototypes for these functions can be included by using the clips.h header file or the router.h header file. 7.5.1 ExitRoutervoid ExitRouter(exitCode); int exitCode;
7.5.2 GetcRouterint GetcRouter(logicalName); char *logicalName;
7.5.3 PrintRouterint PrintRouter(logicalName,str); char *logicalName, *str;
2) The string that is to be printed.
7.5.4 UngetcRouterint UngetcRouter(ch,logicalName); int ch; char *logicalName;
2) The logical name associated with the ungetc character I/O request.
7.6 Router Handling FunctionsThe following functions are used for creating, deleting, and handling I/O routers. They are intended for use within user defined functions. Prototypes for these functions can be included by using the clips.h header file or the router.h header file. 7.6.1 ActivateRouter
char *routerName;
7.6.2 AddRouter
2) The priority of the I/O router. I/O routers are queried in descending order of priorities. 3) A pointer to the query function associated with this router. This query function should accept a single argument, a logical name, and return either TRUE (1) or FALSE (0) depending upon whether the router recognizes the logical name. 4) A pointer to the print function associated with this router. This print function should accept two arguments: a logical name and a character string. The return value of the print function is not meaningful. 5) A pointer to the get character function associated with this router. The get character function should accept a single argument, a logical name. The return value of the get character function should be an integer which represents the character or end of file (EOF) read from the source represented by logical name. 6) A pointer to the ungetc character function associated with this router. The ungetc character function accepts two arguments: a logical name and a character. The return value of the unget character function should be an integer which represents the character which was passed to it as an argument if the ungetc is successful or end of file (EOF) is the ungetc is not successful. 7) A pointer to the exit function associated with this router. The exit function should accept a single argument: the exit code represented by num.
7.6.3 DeactivateRouterint DeactivateRouter(routerName); char *routerName;
7.6.4 DeleteRouterint DeleteRouter(routerName); char *routerName;
Section 8 Memory ManagementEfficient use of memory is a very important aspect of an expert system tool. Expert systems are highly memory intensive and require comparatively large amounts of memory. To optimize both storage and processing speed, CLIPS does much of its own memory management. Section 8.1 describes the basic memory management scheme used in CLIPS. Section 8.2 describes some functions that may be used to monitor/ control memory usage. 8.1 How CLIPS Uses MemoryThe CLIPS internal data structures used to represent constructs and other data entities require the allocation of dynamic memory to create and execute. Memory can also be released as these data structures are no longer needed and are removed. All requests, either to allocate memory or to free memory, are routed through the CLIPS memory management functions. These functions request memory from the operating system and store previously used memory for reuse. By providing its own memory management, CLIPS is able to reduce the number of malloc calls to the operating system. This is very important since malloc calls are handled differently on each machine, and some implementations of malloc are very inefficient.
*** MEMORY DEALLOCATED ***
8.2 Standard Memory FunctionsCLIPS currently provides a few functions that can be used to monitor and control memory usage. Prototypes for these functions can be included by using the clips.h header file or the memalloc.h header file. 8.2.1 GetConserveMemory;int GetConserveMemory();
8.2.2 MemRequestslong int MemRequests();
8.2.3 MemUsedlong int MemUsed();
8.2.4 ReleaseMem
long int ReleaseMem(howMuch, printMessage); int printMessage;
2) A non-zero value causes a memory deallocation message to be printed when this function is called.
8.2.5 SetConserveMemoryint SetConserveMemory(value); int value;
8.2.6 SetOutOfMemoryFunctionint (*SetOutOfMemoryFunction(outOfMemoryFunction))(); int (*outOfMemoryFunction)();
int size;
Section 9 EnvironmentsCLIPS provides the ability to create multiple environments into which programs can be loaded. Each environment maintains its own set of data structures and can be run independently of the other environments. 9.1 Creating, selecting, and destroying environmentsIf you have no need for multiple CLIPS programs loaded concurrently, there is no need to use any of the environment functions described in this section. The call to InitializeEnvironment automatically creates an environment for you and any subsequent calls to CLIPS functions will be applied to that environment. Environments can also be created using the CreateEnvironment function. The return value of the CreateEnvironment function is an anonymous (void *) pointer to an environmentData data structure. Environments created using the CreateEnvironment function are automatically initialized, so there is no need to call the InitializeEnvironment function.
Environments Using Environment Embedded Calls
{ void *theEnv1, *theEnv2; theEnv1 = CreateEnvironment(); theEnv2 = CreateEnvironment(); EnvLoad(theEnv1,“program1.clp”); EnvLoad(theEnv2,“program2.clp”); EnvReset(theEnv1); EnvReset(theEnv2); EnvRun(theEnv1,-1); EnvRun(theEnv2,-1);
DestroyEnvironment(theEnv2); } Environments Using Standard Embedded Calls
{ void *theEnv1, *theEnv2; theEnv1 = CreateEnvironment(); theEnv2 = CreateEnvironment(); SetCurrentEnvironment(theEnv1); Load(“program1.clp”); Reset(); Run(-1);
Load(“program2.clp”); Reset(); Run(-1); DestroyEnvironment(theEnv1); DestroyEnvironment(theEnv2); } 9.2 Environment Companion FunctionsWith a few exceptions, all of the CLIPS embedded function calls described in sections 3 through 8 have a companion function of the same name preceded with “Env”. The first argument to these companion functions is a generic pointer to an environment data structure and the remaining arguments are the same as the standard embedded function. For example, the standard embedded function call for Run is defined as follows:
int AddClearFunction(clearItemName,clearFunction,priority); char *clearItemName; void (*clearFunction)(); int priority;
char *clearItemName; void (*clearFunction)(); int priority;
9.3 Standard Environment FunctionsThe following functions are used to create and manipulate environments. Prototypes for these functions can be included by using the clips.h header file or the envrnmnt.h header file. 9.3.1 AddEnvironmentCleanupFunction;int AddEnvironmentCleanupFunction(theEnv,theName,theFunction,priority); struct environmentData *theEnv; char *theName; void (*)(void *theFunction); int priority;
2) The name associated with the environment cleanup function. 3) A pointer to the environment cleanup function which is to be called when the environment is deleted. When called, the function is passed a generic pointer to the environment being destroyed. 4) The priority of the environment cleanup function which determines the order in which cleanup functions are called (higher priority items are called first). The values -2000 to 2000 are reserved for CLIPS system defined run items and should not be used for user defined run items.
9.3.2 AllocateEnvironmentDataint AllocateEnvironmentData(theEnv,position,size,cleanupFunction); void *theEnv; unsigned int position; unsigned long size; void (*)(void *cleanupFunction);
2) The integer position index used to reference the data. 3) The amount of environment data that needs to be allocated. 4) A pointer to a cleanup function that is called when the environment is destroyed. When called, the function is passed a generic pointer to the environment being destroyed. CLIPS automatically handles the allocation and deallocation of the base environment data created by this function (the amount of data specified by the size argument). You do not need to supply a cleanup function for this purpose and can supply NULL as this argument. If your base environment data contains pointers to memory that you allocate, then you need to either supply a cleanup function as this argument or add a cleanup function using AddEnvironmentCleanupFunction.
9.3.3 CreateEnvironment;void *CreateEnvironment();
9.3.4 DestroyEnvironment;int DestroyEnvironment(theEnv); void *theEnv;
9.3.5 GetCurrentEnvironment;void *GetCurrentEnvironment();
9.3.6 GetEnvironmentData;void *GetEnvironmentData(position); unsigned int position;
9.3.7 GetEnvironmentIndex;unsigned long GetEnvironmentIndex(theEnv); void *theEnv;
9.3.8 SetCurrentEnvironment;void SetCurrentEnvironment(theEnv); void *theEnv;
9.3.9 SetCurrentEnvironmentByIndex;int SetCurrentEnvironmentByIndex(envIndex); unsigned long envIndex;
9.4 Environment Aware User-Defined FunctionsIn order to support all environment features fully, any user-defined functions that you create must be environment aware. To be environment aware, user-defined function must satisfy the following conditions:
2) You should register your functions from within EnvUserFunctions instead of UserFunctions. EnvUserFunctions is located in main.c and its single argument is a generic pointer to an environment. This pointer should be passed into your calls to either EnvDefineFunction or EnvDefineFunction2. The macro identifier PTIEF can be placed in front of a function name to cast it as a pointer to a function which accepts a generic pointer (the environment) as its single argument and returns an integer. This macro is analogous to the PTIF macro with the addition of the generic pointer to theenvironment. 3) Your user-defined function should accept an additional argument as its first argument: a generic pointer to an environment. 4) Your user-defined function should use the environment companion functions where required to be environment aware. 5) If your user-defined functions (or other extensions) make use of global data that could differ for each environment, you should allocate this data with the AllocateEnvironmentData function (see section 9.5). Example The following example shows the necessary modifications to the code from section 3.4 in order for the user-defined function to be environment aware.
void *theEnv) { EnvDefineFunction2(theEnv,“triple”,'u',PTIEF TripleNumber, “TripleNumber”, “11n”); }
void *theEnv, DATA_OBJECT_PTR returnValuePtr) { void *value; long longValue; double doubleValue;
/* If illegal arguments are passed, return zero. */ /*===============================================*/
{ SetpType(returnValuePtr,INTEGER); SetpValue(returnValuePtr,EnvAddLong(theEnv,0L)); return; }
{ SetpType(returnValuePtr,INTEGER); SetpValue(returnValuePtr,EnvAddLong(theEnv,0L)); return; }
/* Triple the number. */ /*====================*/
{ value = GetpValue(returnValuePtr); longValue = 3 * ValueToLong(value); SetpValue(returnValuePtr,EnvAddLong(theEnv,longValue)); } else /* the type must be FLOAT */ { value = GetpValue(returnValuePtr); doubleValue = 3.0 * ValueToDouble(value); SetpValue(returnValuePtr,EnvAddDouble(theEnv,doubleValue)); }
} 9.5 Allocating Environment DataIf your user-defined functions (or other extensions) make use of global data that could differ for each environment, you should allocate this data with the AllocateEnvironmentData function. A call to this function has four arguments. The first is a generic pointer to the environment to which the data is being added.
1 CLIPS> (get-index) 2 CLIPS> (get-index) 3 CLIPS>
{ long index; }; #define IndexData(theEnv) \ ((struct indexData *) GetEnvironmentData(theEnv,INDEX_DATA))
void *theEnv) { if (! AllocateEnvironmentData(theEnv,INDEX_DATA, sizeof(struct indexData),NULL)) { printf(“Error allocating environment data for INDEX_DATA\n”); exit(EXIT_FAILURE); }
“00”); }
void *theEnv) { if (EnvArgCountCheck(theEnv,“get-index”,EXACTLY,0) == -1) { return(0); } return(IndexData(theEnv)→index++); }
9.6 Environment GlobalsThe only global variables in the C source code for CLIPS are used to keep track of the current environment and the environment indices. If it is desired to remove these global variables, the ALLOW_ENVIRONMENT_GLOBALS compiler directive can be disabled. If disabled, you can no longer use the following functions: GetCurrentEnvironment, GetEnvironmentIndex, SetCurrentEnvironment, and SetCurrentEnvironmentByIndex. In addition, if disabled the ENVIRONMENT_API_ONLY compiler directive is enabled and the EMACS_EDITOR compiler directive is disabled. 9.7 Other ConsiderationsThe mechanism for loading run-time program has changed with the introduction of environments. See section 5 for more details.
Appendix A Language Integration ListingsThis appendix includes listings for various language interface packages described in section 6. The portability of these routines varies. Most of the code listed in the interface packages defined in sections A.1 and A.2 should be fairly portable. However, the string conversion routine in section A.3 is not as portable. For example, the Ada function Convert_to_C_String is probably portable to any Ada machine, yet the C function MakeStringDsc listed here is very specific to the DEC VMS. These functions should be typed in exactly as shown below. A.1 Ada Interface Package for CLIPSThe following listings are an Ada package specification and body for some of the CLIPS functions used in embedded CLIPS systems. The code is specific to the DEC Ada compiler because of the PRAGMA IMPORT_PROCEDURE. Other Ada compilers may provide a similar capability, and this package specification could be modified. CLIPS Package Specification
– Initializes the CLIPS environment upon program startup.
----------------------------------------------------------------- – Resets the CLIPS environment.
----------------------------------------------------------------- – Loads a set of constructs into the CLIPS database. If there are syntactic – error in the constructs, xLoad will still attempt to read the – entire file, and error notices will be sent to werror. – Returns: an integer, zero if an error occurs.
----------------------------------------------------------------- – Allows Run_Limit rules to fire (execute). – -1 allows rules to fire until the agenda is empty. – Returns: Number of rules that were fired.
----------------------------------------------------------------- – Lists the facts in the fact-list.
Module_Ptr : in integer; First : in integer; Last : in integer; Max : in integer); ----------------------------------------------------------------- – Turns the watch facilities of CLIPS on.
----------------------------------------------------------------- – Turns the watch facilities of CLIPS off.
----------------------------------------------------------------- – Asserts a fact into the CLIPS fact-list. The function version – returns the Fact_Pointer required by xRetractFact.
----------------------------------------------------------------- – Causes a fact asserted by the ASSERT_FACT function to be retracted. – Returns: false if fact has already been retracted, else true. – Input of any value not returned by ASSERT_FACT will – cause CLIPS to abort.
----------------------------------------------------------------- – Queries all active routers until it finds a router that – recognizes the logical name associated with this I/O request – to print a string. It then calls the print function associated – with that router.
Str : in string) return integer; ----------------------------------------------------------------- – Removes a rule from CLIPS. – Returns: false if rule not found, else true.
----------------------------------------------------------------- -----------------------------------------------------------------
pragma IMPORT_PROCEDURE (INTERNAL ⇒ xInitializeEnvironment, EXTERNAL ⇒ InitializeEnvironment);
pragma IMPORT_PROCEDURE (INTERNAL ⇒ xReset, EXTERNAL ⇒ Reset);
pragma INTERFACE (C, cLoad); pragma IMPORT_FUNCTION (INTERNAL ⇒ cLoad, EXTERNAL ⇒ Load, MECHANISM ⇒ REFERENCE);
pragma IMPORT_FUNCTION (INTERNAL ⇒ xRun, EXTERNAL ⇒ Run, MECHANISM ⇒ VALUE);
Module_Ptr : in integer; First : in integer; Last : in integer; Max : in integer); pragma INTERFACE (C, cFacts); pragma IMPORT_PROCEDURE (INTERNAL ⇒ cFacts, EXTERNAL ⇒ Facts, MECHANISM ⇒ (REFERENCE, VALUE, VALUE, VALUE, VALUE));
pragma INTERFACE (C, cWatch); pragma IMPORT_FUNCTION (INTERNAL ⇒ cWatch, EXTERNAL ⇒ Watch, MECHANISM ⇒ REFERENCE);
pragma INTERFACE (C, cUnwatch); pragma IMPORT_FUNCTION (INTERNAL ⇒ cUnwatch, EXTERNAL ⇒ Unwatch, MECHANISM ⇒ REFERENCE);
pragma INTERFACE (C, cAssertString); pragma IMPORT_FUNCTION (INTERNAL ⇒ cAssertString, EXTERNAL ⇒ AssertString, MECHANISM ⇒ REFERENCE);
pragma INTERFACE (C, cRetract); pragma IMPORT_FUNCTION (INTERNAL ⇒ cRetract, EXTERNAL ⇒ Retract, MECHANISM ⇒ VALUE);
Str : in string) return integer; pragma INTERFACE (C, cPrintRouter); pragma IMPORT_FUNCTION (INTERNAL ⇒ cPrintRouter, EXTERNAL ⇒ PrintRouter, MECHANISM ⇒ REFERENCE);
pragma INTERFACE (C, cUndefrule); pragma IMPORT_FUNCTION (INTERNAL ⇒ cUndefrule, EXTERNAL ⇒ Undefrule, MECHANISM ⇒ REFERENCE);
return string is
for I in Input_String'RANGE loop if (Input_String (I) in ' ' .. '~' or Input_String (I) = ASCII.Cr or Input_String (I) = ASCII.Lf ) then Out_String (I) := Input_String (I); else Out_String (I) := ASCII.Nul; end if; end loop; Out_String (Out_String'LAST) := ASCII.Nul; return Out_String; end ADA_TO_C_STRING;
return cLoad (ADA_TO_C_STRING (File_Name)); end xLoad;
Module_Ptr : in integer; First : in integer; Last : in integer; Max : in integer) is
cFacts (ADA_TO_C_STRING (Logical_Name),Module_Ptr,First,Last,Max);
return cWatch (ADA_TO_C_STRING (Watch_Item));
return cUnwatch (ADA_TO_C_STRING (Watch_Item));
return cAssertString (ADA_TO_C_STRING (Pattern)); end xAssertString;
return cRetract (Fact_Pointer); end xRetract;
Str : in string ) return integer is
return cPrintRouter (ADA_TO_C_STRING (Log_Name), ADA_TO_C_STRING (Str)); end xPrintRouter;
return cUndefrule (ADA_TO_C_STRING (Rule_Name)); end xUndefrule;
A.2 FORTRAN Interface Package for VAX VMSThe following pages are listings of the FORTRAN interface functions for the VAX VMS plus the internal functions used to convert FORTRAN character strings to C character strings and vice versa. Many of these functions may work with minor modifications on other machines; note, however, the use of the VMS argument passing modifier, %VAL, in some functions.
C————————————————————– C SUBROUTINE xInitializeEnvironment
RETURN END C C————————————————————– C SUBROUTINE xReset
RETURN END C C————————————————————– C INTEGER FUNCTION xLoad (FILE_NAME)
CHARACTER *80 C_FILE_NAME INTEGER C_FILE_NAME_POINTER, Load EQUIVALENCE (C_FILE_NAME, C_FILE_NAME_POINTER)
xLoad = Load (C_FILE_NAME_POINTER) RETURN END C C————————————————————– C INTEGER FUNCTION xRun (RUN_LIMIT)
RETURN END C C————————————————————– C SUBROUTINE xFacts (LOGICAL_NAME, MODULE, BEGIN, END, MAX)
INTEGER MODULE, BEGIN, END, MAX CHARACTER *80 C_LOGICAL_NAME INTEGER C_LOGICAL_NAME_POINTER EQUIVALENCE (C_LOGICAL_NAME, C_LOGICAL_NAME_POINTER)
CALL Facts(C_LOGICAL_NAME_POINTER,%VAL (MODULE), * %VAL (BEGIN),%VAL (END),%VAL (MAX)) RETURN END C C————————————————————– C INTEGER FUNCTION xWatch (WATCH_ITEM)
CHARACTER *80 C_WATCH_ITEM INTEGER C_WATCH_ITEM_POINTER, Watch EQUIVALENCE (C_WATCH_ITEM, C_WATCH_ITEM_POINTER)
xWatch = Watch (C_WATCH_ITEM_POINTER) RETURN END C C————————————————————– C INTEGER FUNCTION xUnwatch (WATCH_ITEM)
CHARACTER *80 C_WATCH_ITEM INTEGER C_WATCH_ITEM_POINTER, Unwatch EQUIVALENCE (C_WATCH_ITEM, C_WATCH_ITEM_POINTER)
xUnwatch = Unwatch (C_WATCH_ITEM_POINTER) RETURN END C C————————————————————– C INTEGER FUNCTION xAssertString (PATTERN) C CHARACTER * (*) PATTERN CHARACTER *80 C_PATTERN INTEGER C_PATTERN_POINTER, AssertString EQUIVALENCE (C_PATTERN, C_PATTERN_POINTER) C CALL CONVERT_TO_C_STRING (PATTERN, C_PATTERN) xAssertString = AssertString (C_PATTERN_POINTER) RETURN END C C————————————————————– C INTEGER FUNCTION xRetract (FACT_ADDRESS)
RETURN END C C————————————————————– C INTEGER FUNCTION xPrintRouter (LOG_NAME, PRINT_LINE)
CHARACTER * (*) PRINT_LINE CHARACTER *80 C_LOG_NAME CHARACTER *80 C_PRINT_LINE INTEGER C_PRINT_LINE_POINTER, C_LOG_NAME_POINTER EQUIVALENCE (C_PRINT_LINE, C_PRINT_LINE_POINTER), * (C_LOG_NAME , C_LOG_NAME_POINTER )
CALL CONVERT_TO_C_STRING (LOG_NAME, C_LOG_NAME) xPrintRouter = PrintRouter (C_LOG_NAME_POINTER, * C_PRINT_LINE_POINTER) RETURN END C C————————————————————– C INTEGER FUNCTION xFindDefrule (RULE_NAME)
CHARACTER *80 C_RULE_NAME INTEGER C_RULE_NAME_POINTER, Undefrule EQUIVALENCE (C_RULE_NAME, C_RULE_NAME_POINTER)
xFindDefrule = FindDefrule (C_RULE_NAME_POINTER) RETURN END C C————————————————————– C INTEGER FUNCTION xUndefrule (RULE_NAME)
CHARACTER *80 C_RULE_NAME INTEGER C_RULE_NAME_POINTER, Undefrule EQUIVALENCE (C_RULE_NAME, C_RULE_NAME_POINTER)
xUndefrule = Undefrule (C_RULE_NAME_POINTER) RETURN END C C————————————————————– C SUBROUTINE CONVERT_TO_C_STRING (F_STRING, C_STRING) CHARACTER * (*) F_STRING, C_STRING
DO 100 I = 1,K C_STRING (I:I) = F_STRING (I:I) 100 CONTINUE K = K + 1 C_STRING (K:K) = CHAR (0) RETURN END C C————————————————————– C INTEGER FUNCTION LENGTH (STRING) C CHARACTER * (*) STRING C K = LEN (STRING) DO 100 I=K,1,-1 IF(STRING(I:I) .NE. ' ') GO TO 150 C 100 CONTINUE 150 CONTINUE LENGTH = I RETURN END A.3 Function to Convert C Strings for VMS Ada or FORTRANThis function converts a C string to an Ada string. The MakeStringDsc function normally is stored in the same file together with the UserFunctions definition and any C interface subroutines. The function is not portable and is specific to the VAX VMS environment. The definition of Ada string descriptors is implementation dependent, and access to those definitions from C also is implementation dependent. However, a very similar function could be written for any environment that supports Ada and C. C Function: MakeStringDsc (Note:This function definition is VAX VMS specific)
Appendix B I/O Router ExamplesThe following examples demonstrate the use of the I/O router system. These examples show the necessary C code for implementing the basic capabilities described. B.1 Dribble SystemWrite the necessary functions that will divert all tracing information to the trace file named “trace.txt”.
First of all, we need a file pointer to the dribble file which will contain the tracing information. */
#include “clips.h”
We want to recognize any output that is sent to the logical name “wtrace” because all tracing information is sent to this logical name. The recognizer function for our router is defined below. */
char *logicalName) { if (strcmp(logicalName,“wtrace”) == 0) return(TRUE);
}
We now need to define a function which will print the tracing information to our trace file. The print function for our router is defined below. */
char *str) fprintf(TraceFP,“%s”,str); }
When we exit CLIPS the trace file needs to be closed. The exit function for our router is defined below. */
fclose(TraceFP); }
There is no need to define a get character or ungetc character function since this router does not handle input.
*/
{ if (TraceFP == NULL) return(FALSE); } else { return(FALSE); }
20, /* Priority */ FindTrace, /* Query function */ PrintTrace, /* Print function */ NULL, /* Getc function */ NULL, /* Ungetc function */ ExitTrace); /* Exit function */
}
A function to turn the trace mode off needs to be defined. This function will check if the trace file is already closed. If the file is already closed, then nothing will happen. Otherwise, the trace router will be deleted and the trace file will be closed. The trace off function is defined below. */
{ if (TraceFP != NULL) { DeleteRouter(“trace”);
{ TraceFP = NULL; return(TRUE); } }
return(FALSE); }
Now add the definitions for these functions to the UserFunctions function in file “main.c”. */
DefineFunction(“troff”,'b',TraceOff, “TraceOff”);
Compile and link the appropriate files. The trace functions should now be accessible within CLIPS as external functions. For Example CLIPS>(tron) • • • CLIPS>(troff) */ B.2 Better Dribble SystemModify example 1 so the tracing information is sent to the terminal as well as to the trace dribble file.
This example requires a modification of the PrintTrace function. After the trace string is printed to the file, the trace router must be deactivated. The trace string can then be sent through the PrintRouter function so that the next router in line can handle the output. After this is done, then the trace router can be reactivated. */
char *logicalName, char *str) { fprintf(TraceFP,“%s”,str); DeactivateRouter(“trace”); PrintRouter(logicalName,str); ActivateRouter(“trace”); }
The TraceOn function must also be modified. The priority of the router should be 40 instead of 20 since the router passes the output along to other routers. */
{ if (TraceFP == NULL) { TraceFP = fopen(“trace.txt”,“w”); if (TraceFP == NULL) return(FALSE); } else { return(FALSE); }
40, /* Priority */ FindTrace, /* Query function */ PrintTrace, /* Print function */ NULL, /* Getc function */ NULL, /* Ungetc function */ ExitTrace); /* Exit function */
} B.3 Batch SystemWrite the necessary functions that will allow batch input from the file “batch.txt” to the CLIPS top level interface.
First of all, we need a file pointer to the batch file which will contain the batch command information. */
#include “clips.h”
We want to recognize any input requested from the logical name “stdin” because all user input is received from this logical name. The recognizer function for our router is defined below. */
char *logicalName) { if (strcmp(logicalName,“stdin”) == 0) return(TRUE); return(FALSE); }
We now need to define a function which will get and unget characters from our batch file. The get and ungetc character functions for our router are defined below. */
static int BatchLocation = 0;
char *logicalName) { int rv;
{ DeleteRouter(“mybatch”); fclose(BatchFP); return(GetcRouter(logicalName)); }
BatchLocation++; BatchBuffer[BatchLocation] = EOS;
{ PrintRouter(“wprompt”,BatchBuffer); BatchLocation = 0; }
}
int ch, char *logicalName) /* unused */ { if (BatchLocation > 0) BatchLocation–; BatchBuffer[BatchLocation] = EOS; return(ungetc(ch,BatchFP)); }
When we exit CLIPS the batch file needs to be closed. The exit function for our router is defined below. */
int exitCode) /* unused */ { fclose(BatchFP); }
There is no need to define a print function since this router does not handle output except for echoing the command line. Now we define a function that turns the batch mode on. */
{ BatchFP = fopen(“batch.txt”,“r”);
20, /* Priority */ FindMybatch, /* Query function */ NULL, /* Print function */ GetcMybatch, /* Getc function */ UngetcMybatch, /* Ungetc function */ ExitMybatch); /* Exit function */
}
Now add the definition for this function to the UserFunctions function in file “main.c”. */
Compile and link the appropriate files. The batch function should now be accessible within CLIPS as external function. For Example CLIPS> (mybatch) */ B.4 Simple Window SystemWrite the necessary functions using CURSES (a screen management function available in UNIX) that will allow a top/bottom split screen interface. Output sent to the logical name top will be printed in the upper window. All other screen I/O should go to the lower window. (NOTE: Use of CURSES may require linking with special libraries.)
First of all, we need some pointers to the windows and a flag to indicate that the windows have been initialized. */
#include <curses> #include “clips.h”
int WindowInitialized = FALSE;
We want to intercept any I/O requests that the standard interface would handle. In addition, we also need to handle requests for the logical name top. The recognizer function for our router is defined below. */
char *logicalName) { if ((strcmp(logicalName,“stdout”) == 0) || (strcmp(logicalName,“stdin”) == 0) || (strcmp(logicalName,“wprompt”) == 0) || (strcmp(logicalName,“wdisplay”) == 0) || (strcmp(logicalName,“wdialog”) == 0) || (strcmp(logicalName,“werror”) == 0) || (strcmp(logicalName,“wwarning”) == 0) || (strcmp(logicalName,“wtrace”) == 0) || (strcmp(logicalName,“top”) == 0) ) { return(TRUE); }
}
We now need to define a function which will print strings to the two windows. The print function for our router is defined below. */
char *logicalName, char *str) { if (strcmp(logicalName,“top”) == 0) { wprintw(UpperWindow,“%s”,str); wrefresh(UpperWindow); } else { wprintw(LowerWindow,“%s”,str); wrefresh(LowerWindow); } }
We now need to define a function which will get and unget characters from the lower window. CURSES uses unbuffered input so we will simulate buffered input for CLIPS. The get and ungetc character functions for our router are defined below. */
static int SaveChar; static int SendReturn = TRUE;
static int CharLocation = 0;
char *logicalName) { int rv;
{ UseSave = FALSE; return(SaveChar); }
{ if (SendReturn == FALSE) { SendReturn = TRUE; return('\n'); }
CharLocation = 0; } rv = StrBuff[CharLocation]; if (rv == '\0') return('\n'); CharLocation++; SendReturn = FALSE; return(rv); }
char ch, char *logicalName) { UseSave = TRUE; SaveChar = ch; return(ch); }
When we exit CLIPS CURSES needs to be deactivated. The exit function for our router is defined below. */
int num) /* unused */ { endwin(); }
Now define a function that turns the screen mode on. */
{ int halfLines, i;
else WindowInitialized = TRUE;
echo();
10, /* Priority */ FindScreen, /* Query function */ PrintScreen, /* Print function */ GetcScreen, /* Getc function */ UngetcScreen, /* Ungetc function */ ExitScreen); /* Exit function */
UpperWindow = newwin(halfLines,COLS,0,0); LowerWindow = newwin(halfLines - 1,COLS,halfLines + 1,0);
scrollok(LowerWindow,TRUE);
{ mvaddch(halfLines,i,'-'); } refresh();
wclear(LowerWindow); wmove(LowerWindow, 0,0);
}
Now define a function that turns the screen mode off. */
{ /* Is CURSES already deactivated? */
endwin();
}
Now add the definitions for these functions to the UserFunctions function in file “main.c”. */
DefineFunction(“screen-off”,'b',ScreenOff, “ScreenOff”);
Compile and link the appropriate files. The screen functions should now be accessible within CLIPS as external functions. For Example CLIPS> (screen-on) • • • CLIPS> (screen-off) */
Appendix C Update Release NotesThe following sections denote the changes and bug fixes for CLIPS versions 6.05, 6.1, and 6.2. C.1 Version 6.2• Environments – It is now possible in an embedded application to create multiple environments into which programs can be loaded (see section 9).
SetClassDefaultsMode (see section 4.12.19)
envrnmnt.h C.2 Version 6.1• C++ Compatible – The CLIPS source code can now be compiled using either an ANSI C or C++ compiler. Minimally, non-ANSI C compilers must support full ANSI style function prototypes and the void data type in order to compile CLIPS.
ExitCLIPS (use ExitRouter instead) GetcCLIPS (use GetcRouter instead) InitializeCLIPS (use InitializeEnvironment instead) PrintCLIPS (use PrintRouter instead) UngetcCLIPS (use UngetcRouter instead)
CLIPS_TRUE (use TRUE instead) CLIPSFalseSymbol (use FalseSymbol instead) CLIPSTrueSymbol (use TrueSymbol instead) WCLIPS (use WPROMPT instead)
memory.c (use memalloc.c instead)
parsefun.h proflfun.c proflfun.h sortfun.c sortfun.h C.3 Version 6.05• Compiler Directives The CLP_EDIT flag in the file setup.h has been renamed to EMACS_EDITOR.
Build (see section 4.1.24) Eval (see section 4.1.25) FactExistp (see section 4.4.20) FactSlotNames (see section 4.4.21) GetFactList (see section 4.4.22) LoadFactsFromString (see section 4.4.23) LoadInstancesFromString (see section 4.13.26) RestoreInstancesFromString (see section 4.13.27) |