C Inference (Part 2 of 4)

TOOLS tools at raybed2.UUCP
Tue Jan 28 03:11:06 AEST 1986


As requested by the author we are reposting the source and documentation of
a C inference engine. Address all questions to George Hageman at:
UUCP: {asgb!benish}!hageman
MAIL:	George W. Hageman
	P.O. Box 11234
	Boulder, Colorado  80301
NOTE: RAYTHEON Inc. is not reponsible for the contents and/or consequences
of use of this software. This software is totally the work of George Hageman
and is being reposted as per his request (see following message). Address all
questions, comments, etc. to him.
=========================================================================
>From linus!decvax!seismo!hao!asgb!benish!hageman Fri Jan 24 07:31:04 1986
>Subject: Re:  C inference engine
>
>	... it seems that
>	the probability of getting somthing out to net.and is
>	inversely proportional to the number of hops it has to go.
>
>	I'll send you all of the shars (inference rulecompiler and 
>	the storm expert).  If you could make sure that they are
>	available at your site either by reposting them from your
>	end or by some other means it would be appreciated.
>
>	Thanks,
>
>George [Hageman]
=========================================================================


_____CUT for inference.sh_____
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	expert.h
#	infer.h
#	inference.h
#	keywords.h
#	routine.h
#	gettruth.c
#	inference.c
#	remante.c
#	runrouti.c
#	verify.c
#	verifytr.c
#	weknow.c
#	inference.str
#	makeinfe
#	makercom
#	README
# This archive created: Sun Jan 12 16:12:08 1986
export PATH; PATH=/bin:$PATH
if test -f 'expert.h'
then
	echo shar: will not over-write existing file "'expert.h'"
else
cat << \SHAR_EOF > 'expert.h'

/*************************************************************************
**									**
**	The software contained in this distribution is copyright (C)	**
**	by George Hageman 1985 and is released into the public 		**
**	domain with the following restrictions:	 			**
**									**
**		(1)  This software is intended for non-commertial	**
**			usage.						**
**		(2)  I am held save from damages resulting from		**
**			its use, and					**
**		(3)  The following concepts and legal jargon are	**
**			agreed to by the user of this software.		**
**									**
**	User-supported software concept: 				**
**									**	
**	IF    you find use for this software				**
**	ANDIF it saves you some development time			**
**	THEN        send me $10.00					**
**	ANDTHENHYP  you will feel good!					**
**									**
**									**
**	This source code is provided on an "as is" basis without	**
**	warranty of any kind, expressed or implied, including but	**
**	not limited to the implied warranties of merchantability	**
**	and fitness for a particular purpose.  The entire risk as	**
**	to quality and performance of this software is with you. 	**
**	Should the software prove defective, you assume the entire	**
**	cost of all necessary repair, servicing, or correction.  In	**
**	no event will the author be liable to you for any damages,	**
**	including any lost profits, lost savings, or other		**
**	incidental or consequential damages arising out of the  use	**
**	of inability to use this software.  In short my friends, I	**
**	have done  a reasonable ammount of work in debugging this	**
**	software and I think it is pretty good but, as you know,	**
**	there is always some chance that a bug is still lurking 	**
**	around. If you should happen to be lucky enough to  find one,	**
**	please let me know so I	can make an attempt to fix it.		**
**									**
**				Thanks,					**
**									**
**				George Hageman				**
**				P.O. Box 11234				**
**				Boulder, Colorado 80302			**
**									**
*************************************************************************/


/*
**	These are the structures of the rulebase which will
**	be used to compile the rules into.  
*/

#define FALSE 			0 
#define TRUE  			-1 
#define MAX_STRING_BUFF 	5000
#define MAX_STR_LEN		100
#define MAX_RULE_STATEMENTS 	500
#define MAX_HYPS		250 
#define ANTECEDENT 		1
#define CONSEQUENT 		2
#define COMMENT_CHAR		'!'
#define	BLANK 			0x20
#define EOL			0x0a

#define KEY_EOF			-2 
#define LINE_ERROR		-3
#define KEY_WORD_ERROR		-4
#define ERROR		 	-5	
#define STR_LEN_ERROR		-6

/*
**	Other definitions of key words
*/

#define	AND_N       	 0
#define	ANDIF_N     	 1
#define	ANDIFRUN_N  	 2
#define	ANDNOT_N    	 3	
#define	ANDNOTRUN_N 	 4
#define	ANDRUN_N    	 5
#define	ANDTHEN_N   	 6 
#define ANDTHENHYP       7
#define	ANDTHENRUN_N	 8
#define	ANDTHENRUNHYP_N  9
#define	IF_N        	10
#define	IFNOT_N     	11
#define	IFNOTRUN_N  	12
#define	IFRUN_N     	13
#define	THEN_N      	14
#define	THENHYP_N     	15  
#define THENRUN_N	16
#define THENRUNHYP_N    17
		
/*
**	Flag definitions:
*/

#define	STRING_TRUE	 1
#define	STRING_FALSE	 2
#define	ROUTINE_TRUE	 3
#define ROUTINE_FALSE	 4
#define STRING_TRUE_HYP  5
#define ROUTINE_TRUE_HYP 6

#define NUM_KEYWORDS	18

struct	rule_statement_r
		{
		int flag ;   /* logical flag for inference engine */
		int string ; /* offset into string buffer */
		};


/* 
**	rules are compiled into the array rules in the folloiwng form:
**
**	antecedent-group consequent-group 
**	... 
**	antecedent-group consequent-group
**	end-group
**
**	Each group of consequences and antecedents
**	are compiled in a group like the following:
**
**	flag pointer flag pointer ... flag pointer 0-flag 0-pointer
**
**	The end-group is merely:
**
**	0-flag 0-pointer 0-flag 0-pointer 0-flag 0-pointer
**
**	flags are used by the inference engine to determine what to
**	do with the following string pointer.  
**	string pointers are merely offsets into the string array.
**	The pointers may either point
**	to a string which is a rule statement such as "the animal has wings"
**	or is a UNIX pathname for a particular routine which is to be
**	executed such as "/g1/hageman/Diagnostics/Disk1diag".   This
**	routine will then be executed and will return either a true or
**	false indication.  Latter versions of the inference engin may be
**	capable of returning more than this via some pipe-line mechanism or
**	other.
**
**	Once an anicedent whether string or routine is verified it is placed
**	in either a known-true or known-false stack for later verification 
**	in other rules which use them.  In short they only have to be verified
**	once.
**
**	Examples of a rule structure are:
**
**	IFNOT the animal is a bird
**	AND the animal has wings
**	ANDNOT the animal lives in caves
**	AND the animal is nocternal
**	THEN the animal is a bat
**	IF the animal is a bat
**	ANDRUN /g1/hageman/Src/Expert/speed_of_bat
**	THENHYP the bat is out of hell
**	IF the animal is a bat
**	ANDNOTRUN /g1/hageman/Src/Expert/speed_of_bat
**	THENHYP the bat is out of cave
**
*/
SHAR_EOF
fi # end of overwriting check
if test -f 'infer.h'
then
	echo shar: will not over-write existing file "'infer.h'"
else
cat << \SHAR_EOF > 'infer.h'


/*****************************************************************
**								**
**	  Inference -- (C) Copyright 1985 George Hageman	**
**								**
**	    user-supported software:				**
**								**
**		    George Hageman				**
**		    P.O. Box 11234				**
**		    Boulder, Colorado 80302			**
**								**
*****************************************************************/

/*
**	the following are the global common variables which
**	are used in the inference engine...
**
**	all routines except inference.c should have this
**	file included.
*/

#define	MAX_KNOWN	500

int	numHypot, hypStack[MAX_HYPS],strBuffOfst ;
char	strBuff[MAX_STRING_BUFF] ;
int	ruleBuffOfst ;
int	knownTrue[MAX_KNOWN], knownFalse[MAX_KNOWN] ;
int	numTrue, numFalse ;
struct  rule_statement_r ruleBuff[MAX_RULE_STATEMENTS] ;

SHAR_EOF
fi # end of overwriting check
if test -f 'inference.h'
then
	echo shar: will not over-write existing file "'inference.h'"
else
cat << \SHAR_EOF > 'inference.h'


/*****************************************************************
**								**
**	  Inference -- (C) Copyright 1985 George Hageman	**
**								**
**	    user-supported software:				**
**								**
**		    George Hageman				**
**		    P.O. Box 11234				**
**		    Boulder, Colorado 80302			**
**								**
*****************************************************************/

/*************************************************************************
**									**
**	The software contained in this distribution is copyright (C)	**
**	by George Hageman 1985 and is released into the public 		**
**	domain with the following restrictions:	 			**
**									**
**		(1)  This software is intended for non-commertial	**
**			usage.						**
**		(2)  I am held save from damages resulting from		**
**			its use, and					**
**		(3)  The following concepts and legal jargon are	**
**			agreed to by the user of this software.		**
**									**
**	User-supported software concept: 				**
**									**	
**	IF    you find use for this software				**
**	ANDIF it saves you some development time			**
**	THEN        send me $10.00					**
**	ANDTHENHYP  you will feel good!					**
**									**
**									**
**	This source code is provided on an "as is" basis without	**
**	warranty of any kind, expressed or implied, including but	**
**	not limited to the implied warranties of merchantability	**
**	and fitness for a particular purpose.  The entire risk as	**
**	to quality and performance of this software is with you. 	**
**	Should the software prove defective, you assume the entire	**
**	cost of all necessary repair, servicing, or correction.  In	**
**	no event will the author be liable to you for any damages,	**
**	including any lost profits, lost savings, or other		**
**	incidental or consequential damages arising out of the  use	**
**	of inability to use this software.  In short my friends, I	**
**	have done  a reasonable ammount of work in debugging this	**
**	software and I think it is pretty good but, as you know,	**
**	there is always some chance that a bug is still lurking 	**
**	around. If you should happen to be lucky enough to  find one,	**
**	please let me know so I	can make an attempt to fix it.		**
**									**
**				Thanks,					**
**									**
**				George Hageman				**
**				P.O. Box 11234				**
**				Boulder, Colorado 80302			**
**									**
*************************************************************************/


/*
Expert system inference engine

This inference engine is backwards-chaining only and features the
running of binary files if:

	1) they are antecedents associated with a particuar consequent
	   being proved, or
	2) they are consequents which have been proven true by verify().
	   their actual predicate value will be determined by their returned
	   result after running.

This inference engine is designed with diagnostics in mind and so will probably
be best suited for this application.  Later revisons will include 
forward-chaining so that the user will have the opportunity to give pre-existant
conditons, or that these my be supplied by the calling process.

(If you cant tell by the above description -- I am very excited about the 
possibilties presented in this form of computer control of the diagnostic
process... Geo.)

See structure design for details of operation,  but basically the 

inference reads in all of the compiled information as produced by the
    rule compiler.
    
it proceeds to attempt to prove each consequent by proving the truth
	or falseness of any antecedent associated with this consequent.
if any antecedent of a consequent turns out to be a consequent itself, then
        the inference engine will recursively attempt to prove this consequent.

the process is complete when all of the predicate values of the consequents has
	been determined.
	
Additional features which I may put in at a later time is the abiltiy for
the inference engine to expalin itself to the user when the user asks why
the inference engine needs to know the predicate value of a particular
antecedent.   This will be done by forward and backward chaining of predicat
clauses, the truth of each will be displayed.
*/
#define	MAX_ANTECEDENTS		25

extern 	int	numHypot, hypStack[],strBuffOfst ;
extern 	char	strBuff[] ;
extern	int	ruleBuffOfst ;
extern	int	knownTrue[], knownFalse[] ;
extern	int	numTrue, numFalse ;
extern	struct  rule_statement_r ruleBuff[] ;
SHAR_EOF
fi # end of overwriting check
if test -f 'keywords.h'
then
	echo shar: will not over-write existing file "'keywords.h'"
else
cat << \SHAR_EOF > 'keywords.h'

char	*keyWords[NUM_KEYWORDS] =
	{
	"AND           ",
	"ANDIF         ",
	"ANDIFRUN      ",
	"ANDNOT        ",
	"ANDNOTRUN     ",
	"ANDRUN        ",
	"ANDTHEN       ", 
	"ANDTHENHYP    ",
	"ANDTHENRUN    ",
	"ANDTHENRUNHYP ",
	"IF            ",
	"IFNOT         ",
	"IFNOTRUN      ",
	"IFRUN         ",
	"THEN          ",
	"THENHYP       ",
	"THENRUN       ",
	"THENRUNHYP    "
	} ;

SHAR_EOF
fi # end of overwriting check
if test -f 'routine.h'
then
	echo shar: will not over-write existing file "'routine.h'"
else
cat << \SHAR_EOF > 'routine.h'


/*****************************************************************
**								**
**	  Inference -- (C) Copyright 1985 George Hageman	**
**								**
**	    user-supported software:				**
**								**
**		    George Hageman				**
**		    P.O. Box 11234				**
**		    Boulder, Colorado 80302			**
**								**
*****************************************************************/

/*
**	these are the two return values
**	which must be returned as the exit
**	value.  Any other will result in
**	an assumption that the result is true
**	or that some sort of error occured.
*/

#define RETURN_ROUTINE_TRUE	254
#define RETURN_ROUTINE_FALSE	255

SHAR_EOF
fi # end of overwriting check
if test -f 'gettruth.c'
then
	echo shar: will not over-write existing file "'gettruth.c'"
else
cat << \SHAR_EOF > 'gettruth.c'


/*****************************************************************
**								**
**	  Inference -- (C) Copyright 1985 George Hageman	**
**								**
**	    user-supported software:				**
**								**
**		    George Hageman				**
**		    P.O. Box 11234				**
**		    Boulder, Colorado 80302			**
**								**
*****************************************************************/

/*************************************************
**
**	getTruth(antecedent) 
**
**	asks user for the truth of a string or
**	returns --
**		TRUE if user says the statement is TRUE
**		FALSE if the user says the statement is FALSE
**
*************************************************/

#include <stdio.h>

#ifdef MSDOS
#include <conio.h>
#endif

#include "expert.h"
#include "inference.h"

int getTruth(cnsquent)
	int	cnsquent ;
{
int done,c ;
done = FALSE ;

while(!done)
	{
	printf("\n Is the following statement True?  (T/F,Y/N)\n\n ") ;
	printf("%s    ?",&strBuff[ruleBuff[cnsquent].string]) ;
#ifdef MSDOS
	c = getche() ;
#endif
#ifdef UNIXSV
	c = getchar() ;	
	getchar() ;
#endif
	switch(c) 
		{
		case 'y' :
		case 'Y' :
		case 't' :
		case 'T' :
			printf("\n\n") ;
			return(TRUE) ;
		case 'n' :
		case 'N' :
		case 'f' :
		case 'F' :
			printf("\n\n") ;
			return(FALSE) ;
		case 'w' :
		case 'W' :
			printf("\n Why is not implemented \n") ;
		default :
			printf("\n Please try again \"T\" or \"F\" \n ") ;
		}
	}
}




SHAR_EOF
fi # end of overwriting check
if test -f 'inference.c'
then
	echo shar: will not over-write existing file "'inference.c'"
else
cat << \SHAR_EOF > 'inference.c'
/*****************************************************************
**								**
**	  Inference -- (C) Copyright 1985 George Hageman	**
**								**
**	    user-supported software:				**
**								**
**		    George Hageman				**
**		    P.O. Box 11234				**
**		    Boulder, Colorado 80302			**
**								**
*****************************************************************/

/*******************************************************************
**
**	inference engine main routine
**
*******************************************************************/

#include <stdio.h>
#include <string.h>

#include "expert.h"
#include "routine.h"

#define	MAX_KNOWN	100

int	numHypot, hypStack[MAX_HYPS],strBuffOfst ;
char	strBuff[MAX_STRING_BUFF] ;
int	ruleBuffOfst ;
int	knownTrue[MAX_KNOWN], knownFalse[MAX_KNOWN] ;
int	numTrue, numFalse ;
struct  rule_statement_r ruleBuff[MAX_RULE_STATEMENTS] ;

int	main(argc,argv)
int argc ;
char **argv ;

{
int	i,consequent ;
int	proved ;
int	p_value ;
FILE	*infile ;

for( proved = 0; proved < MAX_STRING_BUFF; proved ++ )
	strBuff[proved] = 0 ;

proved = FALSE ;

#ifdef	DEBUG
	fprintf(stdout,"\nDEBUG-RULECOMP argc=%d ",argc) ;
#endif

if(argc != 2 )
	{
	fprintf(stdout, "\n\n Usage: inference in.file \n " ) ;
	exit(RETURN_ROUTINE_FALSE) ;
	}
infile = fopen( argv[1], "rb" );
if(infile == NULL)
	{
	fprintf(stdout,"\n\n Cannot open input file %s \n ", argv[1]);
	exit(RETURN_ROUTINE_FALSE) ;
	}
for(i=0;i<MAX_KNOWN;i++)
	knownTrue[i]=knownFalse[i]=0 ;
/*
**
**	read in the compiled rules
**
*/

fread(&numHypot,sizeof(int),1,infile) ;
fread(hypStack,sizeof(int),numHypot,infile) ;
fread(&ruleBuffOfst,sizeof(int),1,infile) ;
fread(ruleBuff,2*sizeof(int),ruleBuffOfst,infile) ;
fread(&strBuffOfst,sizeof(int),1,infile) ;
fread(strBuff,1,strBuffOfst,infile) ;

#ifdef	DEBUG
	
printf("\nDEBUG-main, numHypot = %d",numHypot) ;
for(i=0;i<numHypot;i++)
	{
	printf("\nDEBUG-main, consequent[%d]= %d, string=%s",i,hypStack[i],
			&strBuff[(ruleBuff[hypStack[i]].string)] ) ;
	}
printf("\nDEBUG-main, number of rules = %d",ruleBuffOfst) ;
printf("\nDEBUG-main, number of bytes in stringbuffer=%d",strBuffOfst) ;
for(i=0;i<ruleBuffOfst;i++)
	{
	if(ruleBuff[i].flag !=0)
		{
		printf("\nDEBUG-main, rule[%d].flag=%d, rule[%d].string= %d->%s"
        		  ,i,ruleBuff[i].flag,i,ruleBuff[i].string,&strBuff[ruleBuff[i].string]) ;
		}
	}
printf("\n") ;

#endif

for( i=0 ; i < numHypot ; i++ )

	{
	consequent = hypStack[i] ;

#ifdef DEBUG
	printf("\nDEBUG-main, consequent = %d ",consequent ) ;
#endif

	if(weKnow(consequent,&p_value) == TRUE)
		{
#ifdef DEBUG
	printf("\nDEBUG-main, we knew this consequent was ") ;
	if(p_value == TRUE)
		printf("-- TRUE") ;
	else
		printf("-- FALSE") ;
#endif
		continue ;
		}
		
	if ( verify(consequent) == TRUE )
		{
		
#ifdef DEBUG
			printf("\nDEBUG-main, consequent verified TRUE " ) ;
#endif
		
		if( (ruleBuff[consequent].flag == ROUTINE_TRUE) ||
			(ruleBuff[consequent].flag == ROUTINE_TRUE_HYP) )
			{
			if(weKnow(consequent,&p_value) == TRUE)
				continue ;
#ifdef DEBUG
			printf("\nRuning Routine %s",&strBuff[ruleBuff[consequent].string]) ;
#endif
			if (runRoutine(consequent) == TRUE)
				{
				knownTrue[numTrue++]=ruleBuff[consequent].string ;
#ifdef DEBUG
				printf(" -- TRUE\n") ;
#endif
				if(ruleBuff[consequent].flag == ROUTINE_TRUE_HYP)
					{
					printf("\nCONCLUSION\n") ;
					exit(RETURN_ROUTINE_TRUE) ;
					}
				}
			else
				{
				knownFalse[numFalse++]=ruleBuff[consequent].string ;
#ifdef DEBUG
				printf(" -- FALSE\n") ;
#endif
				}
			}
		else
			{
			knownTrue[numTrue++]=ruleBuff[consequent].string ;
			proved = TRUE ;
			printf("\nI infer that : %s\n",&strBuff[ruleBuff[consequent].string]) ;
			if(ruleBuff[consequent].flag == STRING_TRUE_HYP)
				{
				printf("\nCONCLUSION\n") ;
				exit(RETURN_ROUTINE_TRUE) ;
				}
			}
		}
	else
		{
#ifdef DEBUG
			printf("\nDEBUG-main, consequent not proved " ) ;
#endif
		}
	}
if(proved == FALSE)
	{
	printf("\n I can't prove anything\n") ;
	exit(RETURN_ROUTINE_FALSE) ;
	}
exit(RETURN_ROUTINE_TRUE) ;
}

/* --<<hidden tof */

/******************************************************
**
**	known(hypot,knownFile,numKnown)
**
**	checks to see if hypot is in the stack knownFile of length
**		numKnown
**
**	returns true if known, false otherwise
**
******************************************************/



known(hypot,knownFile,numKnown)
	int 	hypot,numKnown ;
	int	knownFile[] ;
{
int i ;
for (i = 0 ; i < numKnown ; i++ )
	if (ruleBuff[hypot].string == knownFile[i] )
		return(TRUE) ;
return(FALSE) ;
}


SHAR_EOF
fi # end of overwriting check
if test -f 'remante.c'
then
	echo shar: will not over-write existing file "'remante.c'"
else
cat << \SHAR_EOF > 'remante.c'


/*****************************************************************
**								**
**	  Inference -- (C) Copyright 1985 George Hageman	**
**								**
**	    user-supported software:				**
**								**
**		    George Hageman				**
**		    P.O. Box 11234				**
**		    Boulder, Colorado 80302			**
**								**
*****************************************************************/

/***********************************************
**
**	remAnte(antecedent)
**
**	returns the truth value of the fact to be remembered
**	similar to verify except that the fact is a known antecedent
**	and is not a consequent of any rule.  And therefore can be remembered
**	false as well as true.
**
************************************************/

#include <stdio.h>
#include "expert.h"
#include "inference.h"


int	remAnte(antecedent)
	int	antecedent ;
{
int p_value ;

switch(ruleBuff[antecedent].flag)
	{
	case STRING_TRUE :
	case STRING_TRUE_HYP:
		knownTrue[numTrue++]=ruleBuff[antecedent].string ;
		return(TRUE) ;
	case STRING_FALSE :
		knownTrue[numTrue++]=ruleBuff[antecedent].string ;
		return(FALSE) ;
	default:  /* routine to run */
		if(weKnow(antecedent,&p_value) == TRUE)
			return(p_value) ;
		if( runRoutine(antecedent) == TRUE )
			{
			knownTrue[numTrue++]=ruleBuff[antecedent].string ;
			if((ruleBuff[antecedent].flag == ROUTINE_TRUE) ||
				(ruleBuff[antecedent].flag == ROUTINE_TRUE_HYP))
				{
				return(TRUE) ;
				}
			else
				{
				return(FALSE) ;
				}
			}
		else
			{
			knownFalse[numFalse++]=ruleBuff[antecedent].string ;
			if(ruleBuff[antecedent].flag == ROUTINE_FALSE)
				{
				return(TRUE) ;
				}
			else
				{
				return(FALSE) ;
				}
			}
	}
}		 
SHAR_EOF
fi # end of overwriting check
if test -f 'runrouti.c'
then
	echo shar: will not over-write existing file "'runrouti.c'"
else
cat << \SHAR_EOF > 'runrouti.c'


/*****************************************************************
**								**
**	  Inference -- (C) Copyright 1985 George Hageman	**
**								**
**	    user-supported software:				**
**								**
**		    George Hageman				**
**		    P.O. Box 11234				**
**		    Boulder, Colorado 80302			**
**								**
*****************************************************************/

/*****************************************************************
**
**	runRoutine(antecedent)
**
**	spawns the process with the path name specified in
**
**	ruleBuff[antecedent].string
**
**	and returns the value as TRUE or FALSE  depending on
**	the value returned from the exit() command from the spawnd routine.
**
**	the routine returns TRUE if there is any problem with spawning
**	the specified executable.
**
******************************************************************/
#define	MAX_ARGS	20

#include <stdio.h>

#ifdef MSDOS
#include <process.h>
#endif

#include <errno.h>

#include "expert.h"
#include "inference.h"
#include "routine.h"

int	runRoutine(cnsquent)
	int	cnsquent ;
{
extern int errno ;

#ifdef UNIXSV
int	pnid ;
#endif

int	i,argc,p_value,numChars ;
char	buffer[MAX_STR_LEN], *string_p;
char	*argv[MAX_ARGS] ;

/*
** copy string to buffer to get parameters
*/

string_p = &strBuff[ruleBuff[cnsquent].string] ;

for (numChars = 0 ; numChars < MAX_STR_LEN ; numChars++)
	{
	buffer[numChars]= *( string_p + numChars ) ; 
	if(buffer[numChars] == NULL)
		break ;
	}
	
#ifdef DEBUG
printf("\nDEBUG -- runroutine -- copied string is %s\n",buffer) ;
#endif

/*
** set the argv(alues) to the proper location within the buffer
*/

argc = 1 ;
argv[0] = buffer ;

for(i=0;i<numChars;i++)
	{
#ifdef DEBUG
printf("\n DEBUG -- runroutine parameters i = %d, char = %c \n",i,buffer[i]) ;
#endif
	if(buffer[i] == NULL)
		{
		break ;
		}
	if(buffer[i] == BLANK)
		{
		buffer[i] = NULL ;
		while( buffer[++i] == BLANK ) ;
		if(buffer[i] == NULL)
			{
			break ;
			}
		argv[argc++] = &buffer[i] ;
		if(argc == MAX_ARGS)
			{
			printf("\n maximum arguments exceeded for %s\n",string_p);
			argc -= 1 ;
			break ;
			}
		}
	}
argv[argc]=NULL ;
#ifdef DEBUG
for( i = 0 ; i < argc ; i++)
	{
	printf("\n argv[%d] = %s\n",i,argv[i]) ;
	}

printf("\nRunning Routine %s ",argv[0] ) ;
#endif

#ifdef MSDOS
p_value=spawnv(P_WAIT,argv[0],argv ) ;
#endif


#ifdef UNIXSV
pnid = fork() ;
if(pnid == -1)
	{
	printf("\nFork failure! Running %s -- returning TRUE \n",argv[0]) ;
	return(TRUE) ;
	}
if(pnid != 0 ) 		/* parent */
	{
	wait(&p_value) ;
	if(p_value != -1)
		p_value = (p_value >> 8 ) & 0x0ff ;
	}
else			/* child */
	{
	execv(argv[0],argv) ;
	}
#endif

/*
**	The return value is set by the routine having an exit(X)  where
**	X is the value to be returned...
**	There is a problem since the return value can also provide an
**	indication that there was some problem with the attempt as follows:
*/

if(p_value == RETURN_ROUTINE_TRUE)
	{
#ifdef DEBUG
	printf(" -- TRUE\n") ;
#endif
	return(TRUE) ;
	}
if(p_value == RETURN_ROUTINE_FALSE)
	{
#ifdef DEBUG
	printf(" -- FALSE \n") ;
#endif
	return(FALSE) ;
	}
if(p_value)
	switch(errno)
		{
		case ENOENT :
			printf("\n Executable file %s not found assumed TRUE\n",argv[0]) ;
			return(TRUE) ;
		case ENOEXEC :
			printf("\n File %s is not executable assumed TRUE\n",argv[0]) ;
			return(TRUE) ;
		case ENOMEM :
			printf("\ Not enough memory to run -- assumed TRUE\n") ;
			return(TRUE) ;
		}	
printf("\n Routine did not return either ROUTINE_TRUE or ROUTINE_FALSE assumed TRUE\n") ;
return(TRUE) ;
}

SHAR_EOF
fi # end of overwriting check
if test -f 'verify.c'
then
	echo shar: will not over-write existing file "'verify.c'"
else
cat << \SHAR_EOF > 'verify.c'

/*****************************************************************
**								**
**	  Inference -- (C) Copyright 1985 George Hageman	**
**								**
**	    user-supported software:				**
**								**
**		    George Hageman				**
**		    P.O. Box 11234				**
**		    Boulder, Colorado 80302			**
**								**
*****************************************************************/

/********************************************************
**
**	verify(consequent)
**
**	finds the truth about the consequent -- returns
**	TRUE if consequent is proved (all antecedents are TRUE)
**	FALSE if any of the antecedents are FALSE
**
********************************************************/

#include <stdio.h>
#include "expert.h"
#include "inference.h"

int	verify(cnsquent) 
	int	cnsquent ;

{
int 	i,j,k,m,n ;
int	antecedent[MAX_ANTECEDENTS] ;
int	consequent[MAX_ANTECEDENTS] ;
int	p_value ;			/* predicate value */

#ifdef DEBUG
	int di ;
	for(di=0;di<numTrue;di++)
		printf("\nDEBUG-verify knownTrueFact = %d",knownTrue[di]) ;
	for(di=0;di<numFalse;di++)
		printf("\nDEBUG-verify knownFalseFact = %d",knownFalse[di]) ;

#endif

/*
**
**	get all of the antecedents for the consequent
**
**	First locate the antecedents -- should be directly in front
**	of the consequent, delimited by a consequent with a flag of zero
*/

#ifdef DEBUG
	printf("\nDEBUG -- verify consequent = %d",cnsquent ) ;
#endif

j = 0 ;

for(i=cnsquent; i>=0 ; i--)
	{
	if(ruleBuff[i].flag == 0 )
		break ;
	}

if(i == -1)
	{
	printf("\n Bad Consequent, has no antecedents! returning TRUE value \n ") ;
	return(TRUE);
	}

for(i = i-1 ;i>=0; i--)
	{
	if(ruleBuff[i].flag != 0)
		antecedent[j++]=i ;
	else
		break ;
	}

if(j==0)
	{
	printf("\n Bad Consequent, has no antecedents! returning TRUE value \n ") ;
	return(TRUE);
	}
	
#ifdef DEBUG
	printf("\nDEBUG -- verify antecedents = ") ;
	for(di=0; di < j; di++)
		printf("\n 	%d -- %d",di,antecedent[di]) ;
#endif

/*
**	at this point we should have some antecedents in the antecedent
**	stack so now lets see if each can be proved, and if there are
**	any consequents which will need verification.
*/

/*
**	main verificaiton loop:
*/

for(i=j-1 ; i >= 0 ; i--) /* for all of the antecedents in our stack */ 
	{

/*
**	determine if the antecedent is itself a consequent
**	
**	compare value of the string pointer of the antecedent needs to
**	be compaired with the value of the string pointer of all of the
**	consequents, if there is a match then the antecedent is a consequent
**
**	There may be more
**	than one consequent and this is handled below since all of these
**	must be false before we give up hope that one will be verified--
**	REMEMBER -- consequents are not remembered FALSE they can only be
**	remembered when they are true -- FALSEness for a consequent merely
**	means that this particular rule did not verify it , some other one
**	might.
**
*/

	for(k=0; k< numHypot; k++ )
		if(ruleBuff[hypStack[k]].string == ruleBuff[antecedent[i]].string)
			break ;

	if(k != numHypot)  /* we have an antecedent which is a consequent */
		{

#ifdef DEBUG
	printf("\nDEBUG -- verify antecedent %d is consequent %d",i, antecedent[i] ) ;
#endif

/*
**	What we have is an antecedent which is a consequent of another
**	rule or rules.  What is needed then is to place each of these consequents
**	into a stack and prove them one at a time.  We will return truth
**	if any of these are proven true and return false only when all of
**	them are false.   We can use veriry recursively in this case to
**	verify each one.
**
**	get all consequents matching hypStack[k].string into a stack
**	look for truth of any consequent.
**	if consequent is true (according to flag) return true.
**	if all consequents are false then return false.
*/
		n = 0 ;
		for( m = 0 ; m < numHypot ; m++)
			{
			if(ruleBuff[antecedent[i]].string == ruleBuff[hypStack[m]].string)
				{
#ifdef DEBUG
				printf("\nDEBUG--consequent stack(%d) = %d",n,hypStack[m]) ;
#endif
				consequent[n++] = hypStack[m] ;
				}
			}
/*
**	verify each consequent
*/

		p_value = FALSE ;
		for(m = 0 ; m < n ; m++)		
			{

#ifdef DEBUG
	printf("\nDEBUG -- verify(2) consequent = %d",consequent[m] ) ;
#endif
			if( verify(consequent[m]) == TRUE )
				{
				p_value = TRUE ;
				if(known(consequent[m],knownTrue,numTrue) == FALSE)
					{
					printf("\nI infer that : %s\n",&strBuff[ruleBuff[consequent[m]].string]) ;
					knownTrue[numTrue++]=ruleBuff[consequent[m]].string ;
					}
				if(remAnte(antecedent[i]) == TRUE)
					{
					break ;
					}
				else
					{
					return(FALSE) ;
					}
				}
			}
		if(p_value == FALSE) /* all of the consequents were not proved */
			{
			switch(ruleBuff[antecedent[i]].flag)
				{
				case STRING_FALSE :
				case ROUTINE_FALSE :
					continue ;
				case STRING_TRUE :
				case STRING_TRUE_HYP :
				case ROUTINE_TRUE :
				case ROUTINE_TRUE_HYP :
					return(FALSE) ;
				}
			}
		else			/* at least one was known */
			{
			switch(ruleBuff[antecedent[i]].flag)
				{
				case STRING_TRUE :
				case STRING_TRUE_HYP :
				case ROUTINE_TRUE:
				case ROUTINE_TRUE_HYP:
					continue ;
				case STRING_FALSE :
				case ROUTINE_FALSE :
					return(FALSE) ;
				}
			}
		}
		
	else	/* we have a plane old string or routine antecedent */
		
		{
		
		if(weKnow(antecedent[i],&p_value) == TRUE)
			{
			if(p_value == TRUE)
				continue ;
			else
				return(FALSE) ;
			}
/*
**	Things arent known and are simple consequents so prove them
**	either by asking the user or by running the routine
*/
		if(verifyTruth(antecedent[i]) == TRUE)
			continue ;
		else
			return(FALSE) ;
		}
	}
/*
**	Everything was TRUE in the big and statement so return it
*/
return(TRUE) ;
}

SHAR_EOF
fi # end of overwriting check
if test -f 'verifytr.c'
then
	echo shar: will not over-write existing file "'verifytr.c'"
else
cat << \SHAR_EOF > 'verifytr.c'


/*****************************************************************
**								**
**	  Inference -- (C) Copyright 1985 George Hageman	**
**								**
**	    user-supported software:				**
**								**
**		    George Hageman				**
**		    P.O. Box 11234				**
**		    Boulder, Colorado 80302			**
**								**
*****************************************************************/

/*****************************************************
**
**	verifyTruth(antecedent) ;
**
**	This routine verifies the truth of a string or routine which
**	is not a consequnt of a rule (ie an antecedent)
**
**	The routine remembers the final state of the antecedent in
**	the knownTrue, or knownFalse stacks
**
**	accomplishes task either by running the routine or by
**	asking the user for the specific truth of a statement
**
**	returns -- 
**		the "true" predicate value of the antecedent
**		according to its .flag:
**
**			TRUE if the antecedent phrase is TRUE and
**			   the antecedent .flag indicates positive,
**			FALSE if the antecedent phrase is TRUE and
**			   the antecedent .flag indicates negative.
**			etc.
**
*******************************************************/

#include <stdio.h>
#include "expert.h"
#include "inference.h"

int	verifyTruth(antecedent) 
	int antecedent ;
{
switch(ruleBuff[antecedent].flag)
	{
	case STRING_TRUE :
	case STRING_TRUE_HYP:
		if( getTruth(antecedent) == TRUE) 
			{
			knownTrue[numTrue++] = ruleBuff[antecedent].string ;
			return(TRUE) ;
			}
		else
			{
			knownFalse[numFalse++] = ruleBuff[antecedent].string ;
			return(FALSE) ;
			}
	case STRING_FALSE :
		if( getTruth(antecedent) == TRUE) 
			{
			knownTrue[numTrue++] = ruleBuff[antecedent].string ;
			return(FALSE) ;
			}
		else
			{
			knownFalse[numFalse++] = ruleBuff[antecedent].string ;
			return(TRUE) ;
			}
	case ROUTINE_TRUE :
	case ROUTINE_TRUE_HYP:
		if( runRoutine(antecedent) == TRUE) 
			{
			knownTrue[numTrue++] = ruleBuff[antecedent].string ;
			return(TRUE) ;
			}
		else
			{
			knownFalse[numFalse++] = ruleBuff[antecedent].string ;
			return(FALSE) ;
			}
	case ROUTINE_FALSE :
		if (runRoutine(antecedent) == TRUE)
			{
			knownTrue[numTrue++] = ruleBuff[antecedent].string ;
			return(FALSE) ;
			}
		else
			{
			knownFalse[numFalse++] = ruleBuff[antecedent].string ;
			return(TRUE) ;
			}
		default:
			printf("\major problem # 0001 -- llegal antecedent flag\n") ;
			return(TRUE) ;
		}
}

SHAR_EOF
fi # end of overwriting check
if test -f 'weknow.c'
then
	echo shar: will not over-write existing file "'weknow.c'"
else
cat << \SHAR_EOF > 'weknow.c'


/*****************************************************************
**								**
**	  Inference -- (C) Copyright 1985 George Hageman	**
**								**
**	    user-supported software:				**
**								**
**		    George Hageman				**
**		    P.O. Box 11234				**
**		    Boulder, Colorado 80302			**
**								**
*****************************************************************/

/*****************************************************************
**	
**	weKnow(antecedent,&predicate) 
**
**	routines searches both knownTrue and knownFalse
**	stacks to determine whether the antecedent is known.
**
**	the return value of the routine is TRUE if known and FALSE otherwise
**
**	the predicate is set to the "true" value of the antecedent according
**	to whether its phase is TRUE or FALSE and its .flag indicator.
**
*****************************************************************/

#include "expert.h"
#include "inference.h"

int	weKnow(antecedent,p_value)
	int antecedent,*p_value ;
{
if(known(antecedent,knownTrue,numTrue) == TRUE )
	switch(ruleBuff[antecedent].flag)
		{
		case STRING_TRUE:
		case STRING_TRUE_HYP:
		case ROUTINE_TRUE:
		case ROUTINE_TRUE_HYP:
			*p_value = TRUE ;
			return(TRUE)  ;
		case STRING_FALSE:
		case ROUTINE_FALSE :
			*p_value = FALSE ;
			return(TRUE) ;
		}
if(known(antecedent,knownFalse,numFalse) == TRUE )
	switch(ruleBuff[antecedent].flag)
		{
		case STRING_TRUE:
		case STRING_TRUE_HYP:
		case ROUTINE_TRUE:
		case ROUTINE_TRUE_HYP :
			*p_value = FALSE ;
			return(TRUE) ;
		case STRING_FALSE:
		case ROUTINE_FALSE:
			*p_value = TRUE ;
			return(TRUE)  ;
		}
return(FALSE) ;
}

SHAR_EOF
fi # end of overwriting check
if test -f 'inference.str'
then
	echo shar: will not over-write existing file "'inference.str'"
else
cat << \SHAR_EOF > 'inference.str'
/*
**
**	This is the inference engine for which the rule compiler
**	has the task of compiling the rules into a knowledge base
**	This knowledge base has the following parts:

		hypstack consisting of a stack of integers, each
		of which points to the offset into the rule base
		which is a hypothesis.
		
		rule base consisting of an orderd set of flags and
		pointers, each comprising a single rule statement.
		Each flag identifies the type of rule and the pointer
		points to the specific string within the string buffer
		which accompanied the rule keyword.
		
		the keywords are defined in the rule compiler documentation
		so look there for it.
		
		
**
**
**
*/

/*********************INFERENCE ENGINE STRUCTURE******************/

/*
**   MAIN VERIFICATION DRIVER
**

MAIN(ARGC,ARGV)
{
READ ALL DATA FROM COMPILED RULES INTO INTERNAL BUFFERS
CLEAR THE KNOWN TRUE AND KNOWN FALSE STACKS
WHILE(ALL CONSEQUENT NOT DONE)
	{
	POP A CONSEQUENT OFF OF THE CONSEQUENT STACK (INVERSE ORDER)
	IF((CONSEQUENT IS KNOWN TRUE) OR (CONSEQUENT IS KNOWN FALSE))
		{
		CONTINUE
		}
	VERIFY(CONSEQUENT) 
	IF(CONSEQUENT IS TRUE)
		{
		IF(CONSEQUENT.FLAG == ROUTINE_TRUE)
			{
			IF(CONSEQUENT IS NOT KNOWN TRUE OR 
			     CONSEQUENT IS NOT KNOWN FALSE)
				{
				PRINT RUNNING CONSEQUENT (CONSEQUENT)
				RESULT = RUN ROUTINE (CONSEQUENT)
				TELL USER RESULT OF RUN
				}
			}
		ELSE
			{
			TELL USER THE TRUTH AND OR CONCLUSION
			}
		}
	}
IF(NO CONSEQUENT WAS PROVEN)
	{
	TELL USER "CANNOT PROVE ANYTHING"
	}
}
**
**
*/

/*
**
**	VERIFY A PARTICULAR CONSEQUENT
**

VERIFY(CONSEQUENT)  (RECURSIVE VERIFY OF THE CONSEQUENT)
{
GET ALL ANTECEDENTS FOR CONSEQUENT ONTO STACK
IF(THERE ARE NO ANTECEDENTS FOR THE CONSEQUENT)
	{
	PRINT "BAD CONSEQUENT -- HAS NO ANTECEDENTS"
	RETURN(TRUE)
	}
WHILE(ALL ANTECEDENTS FOR CONSEQUENT NOT PROVED)
	{
	POP ANTECEDENT OFF OF STACK
	IF(ANTECEDENT IS CONSEQUENT)
		{
		IF(CONSEQUENT IS KNOWN FALSE)
			{
			RETURN(FALSE)
			}
		IF(CONSEQUENT IS KNOWN TRUE)
			{
			CONTINUE
			}
		VERIFY(CONSEQUENT)
		IF(CONSEQUENT IS TRUE)
			{
			IF(CONSEQUENT.FLAG == ROUTINE_TRUE)
				{
				PRINT RUNNING CONSEQUENT (CONSEQUENT)
				RESULT = RUN ROUTINE (CONSEQUENT)
				IF (RESULT == FALSE)
					{
					RETURN(FALSE)
					}
				}
			ELSE
				{
				PUT CONSEQUENT ON KNOWN TRUE STACK
				}
			CONTINUE
			}
		ELSE
			{
			PUT CONSEQUENT ON KNOWN FALSE STACK
			RETURN(FALSE)
			}
		}
	ELSE
		{
		IF(ANTECEDENT IS KNOWN TRUE)
			{
			CONTINUE
			}
		IF(ANTECEDENT IS KNOWN FALSE)
			{
			RETURN (FALSE)
			}
		SWITCH(ANTECEDENT.FLAG):
			{
			CASE (STRING_TRUE) :
				RESULT = GET TRUTH FOR STATEMNT (ANTECEDENT)
				IF(RESULT == FALSE)
					{
					RETETURN (FALSE)
					}
				BREAK ;
			CASE (STRING_FALSE) :
				RESULT = GET TRUTH FOR STATEMNT (ANTECEDENT)
				IF(RESULT == TRUE)
					{
					RETURN(FALSE)
					}
				BREAK ;
			CASE (ROUTINE_TRUE) : 
				PRINT "RUNNING ROUTINE --"
				PRINT STRING POINTED TO BY ANTECEDENT.STRING
				RESULT = RUN ROUTINE (ANTECEDENT)
				IF(RESULT == FALSE)
					{
					RETURN (FALSE)
					}
				BREAK ;
			CASE (ROUTINE_FALSE) : 
				PRINT "RUNNING ROUTINE --"
				PRINT STRING POINTED TO BY ANTECEDENT.STRING
				RESULT = RUN ROUTINE(ANTECEDENT)
				IF(TRUE)
					{
					RETURN(FALSE)
					}
				BREAK ;
			DEFAULT:
				'DIS IS SOME PROBLEM MAMA!
			}
		}
	}
RETURN(TRUE)
}

**
**
*/

/*
**
**	ROUTINE FOR GETTING TRUTH OUT OF USER
**	PRINTS STRING AND ASKS FOR WHETHER IT IS TRUE OR FALSE
**

GET TRUTH FOR STATEMNT(ANTECEDENT)
{
PRINT "IS THE FOLLOWING TRUE?"
PRINT STATEMENT AT ANTECEDENT.STRING
GET USER RESPONSE (YES, WHY OR NO)
DO FOREVER
	{
	IF(USER RESPONSE IS YES)
		{
		PLACE ANTECEDENT IN KNOWN TRUE STACK
		RETURN(TRUE)
		}
	IF(USER RESPONSE IS NO)
		{
		PLACE ANTECEDENT IN KNOW FALSE STACK
		RETETURN (FALSE)
		}
	IF(USER RESPONSE IS WHY)
		{
		PRINT OUT CONSEQUENT/ANTECEDENT STACK IN A NICE FORMAT
		}
	}
}
**
**
*/

/*
**
**	ROUTINE TO TEST THE TRUTH OF A ROUTINE
**


RUN ROUTINE (ANTECEDENT)
{
SPAWN THE ROUTINE NAMED IN THE STRING POINTED TO BY ANTECEDENT.STRING
WAIT FOR ROUTINE TO FINISH
GET RESULT OF THE ROUTINE
IF(RESULT OF ROUTINE IS TRUE)
	{
	PLACE ANTECEDENT IN KNOWN TRUE STACK
	RETURN(TRUE)
	}
ELSE
	{
	PLACE ANTECEDENT IN KNOWN FALSE STACK
	RETURN (FALSE)
	}
}

**
**
**
*/


SHAR_EOF
fi # end of overwriting check
if test -f 'makeinfe'
then
	echo shar: will not over-write existing file "'makeinfe'"
else
cat << \SHAR_EOF > 'makeinfe'
#
# 	Makefile 
# 

# debug the file and do a 80286 model

#CFLAGS = /DDEBUG /DMSDOS /G2 /Zd /Od /Fc

CFLAGS = /DMSDOS 

# All object modules

inference.obj : inference.c expert.h 
	msc $(CFLAGS) inference.c ;

verify.obj : verify.c expert.h inference.h expert.h
	msc $(CFLAGS) verify.c ;
	lib inference -verify +verify ;

runrouti.obj : runrouti.c inference.h expert.h routine.h
	msc $(CFLAGS) runrouti.c ;
	lib inference -runrouti +runrouti ;

verifytr.obj : verifytr.c inference.h expert.h
	msc $(CFLAGS) verifytr.c ;
	lib inference -verifytr +verifytr ;

weknow.obj : weknow.c inference.h expert.h
	msc $(CFLAGS) weknow.c ;
	lib inference -weknow +weknow ;

remante.obj : remante.c inference.h expert.h routine.h
	msc $(CFLAGS) remante.c ;
	lib inference -remante +remante ;

gettruth.obj : gettruth.c inference.h expert.h	
	msc $(CFLAGS) gettruth.c ;
	lib inference -gettruth +gettruth ;
	


# 	inference.exe -- main target
#
inference.exe : inference.obj inference.lib 
#	link inference.obj ,inference.exe, inference.map, inference.lib /MAP/LINE/STACK:9000 ;
 	link inference.obj ,inference.exe, inference.map, inference.lib /STACK:9000 ;
#
#	mapsym inference
#
SHAR_EOF
fi # end of overwriting check
if test -f 'makercom'
then
	echo shar: will not over-write existing file "'makercom'"
else
cat << \SHAR_EOF > 'makercom'
#
# 	Makefile 
# 

# debug the file and do a 80286 model

#CFLAGS = /DDEBUG /G0 /Zd /Od /Fc

CFLAGS = /G0

# All object modules

rulecomp.obj : rulecomp.c expert.h keywords.h
	msc $(CFLAGS) rulecomp.c ;

getkeywo.obj : getkeywo.c expert.h
	msc $(CFLAGS) getkeywo.c ;

putstrin.obj : putstrin.c expert.h
	msc $(CFLAGS) putstrin.c ;


# 	rulecomp.exe -- main target
#
rulecomp.exe : rulecomp.obj getkeywo.obj putstrin.obj 
	link rulecomp.obj getkeywo.obj putstrin.obj ,rulecomp.exe,/STACK:15000 ;
#	link rulecomp.obj getkeywo.obj putstrin.obj ,rulecomp.exe,/MAP/LINE/STACK:15000 ;
#
#	mapsym rulecomp
#
SHAR_EOF
fi # end of overwriting check
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
To compile:

cc *.c -DUNIXSV -o inference


There is no need for a make but the one included is for the
Microsoft make command which comes whith their 4.0 Macro-assembler.

Geo.
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0



More information about the Comp.sources.unix mailing list