#include <stdio.h> #define trace if (traceon) printf extern FILE *popen(); extern char *getenv(); extern char *malloc(); /* HYpothesis Driven Expert */ struct ref { struct ref *next; struct defblk *this; char *text; }; struct defblk { struct defblk *next; char *name; int type; struct ref *definition; }; struct conc { int weight; struct hype *hypo; int vector; int num; struct fact *confirms[]; }; struct fact { struct fact *next; struct defblk *name; int truth; struct concr *setlist; }; struct concr { struct concr *next; struct conc *this; }; struct hype { struct hype *next; struct defblk *name; int confid; short asked; short action; struct ref *queries; char *text; char *explain; }; #define NHASH 255 struct defblk *hashtable[NHASH]; #define FACT 1 #define HYPO 2 #define STRING 3 #define ASK 1 #define SCAN 2 #define RUN 3 #define FUNKNOWN 0 #define FTRUE 1 #define FFALSE -1 #define HUNCERTAIN 10 #define HTRUE 100 #define HFALSE 0 struct hype *hypes; struct fact *facts; int tot_facts; int tot_hypes; int traceon = 0; FILE *kfile; #define SYMCHAR 1 #define WHITE 2 char ctype[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, WHITE, WHITE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WHITE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR, SYMCHAR,SYMCHAR,0, 0, 0, 0, 0, 0, 0,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR, SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR, SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR, SYMCHAR,SYMCHAR,SYMCHAR,0, 0, 0, 0, SYMCHAR, 0 ,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR, SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR, SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR,SYMCHAR, SYMCHAR,SYMCHAR,SYMCHAR,0, 0, 0, 0, 0 }; #define streq(x,y) (strcmp(x,y) == 0) struct defblk *getref(name,type,create) /* Keywords: symbol-table:50 internal-database:50 */ char *name; int type; int create; { int hash; char *np; struct defblk *defp; np = name; hash = 0; while (*np) hash += *np++; hash = hash %NHASH; defp = hashtable[hash]; while (defp && strcmp(name,defp->name)) defp = defp->next; if (defp == NULL) { struct fact *factp; struct hype *hypep; if (create == 0) return(NULL); defp = ((struct defblk *) malloc(sizeof(*defp))); defp->next = hashtable[hash]; hashtable[hash] = defp; defp->name = (char *) malloc(strlen(name)+1); strcpy(defp->name,name); defp->type = type; switch(type) { case STRING: defp->definition = NULL; break; case FACT: tot_facts++; defp->definition = (struct ref *) malloc(sizeof(*factp)); factp = (struct fact *) defp->definition; factp->name = defp; factp->next = facts; facts = factp; factp->truth = FUNKNOWN; factp->setlist = NULL; trace ("Defining fact %s\n",name); break; case HYPO: tot_hypes++; defp->definition = (struct ref *) malloc(sizeof(*hypep)); hypep = (struct hype *) defp->definition; hypep -> name = defp; hypep -> next = hypes; hypes = hypep; hypep -> asked = 0; hypep -> confid = HUNCERTAIN; hypep -> text = NULL; hypep -> explain = NULL; hypep -> queries = NULL; trace ("Defining hypothesis %s\n",name); break; } } else { if (type && (type != defp->type)) { fprintf(stderr,"Multiply defined symbol: %s\n",name); } } return(defp); } char expbuf[512]; char *expcom(oldcom) register char *oldcom; { register char *newcom; char symbuf[128]; char *symp; struct defblk *defp; struct fact *factp; int infalse; newcom = expbuf; while (*oldcom) { switch (*oldcom) { case '\\': *newcom++ = * ++oldcom; break; case '%': oldcom++; symp = symbuf; while ((ctype[*oldcom] & SYMCHAR)==0) oldcom++; while ((ctype[*oldcom] & SYMCHAR)) *symp++ = *oldcom++; *symp = 0; defp = getref(symbuf,0,0); if (defp ) switch(defp->type) { case STRING: symp = (char *) defp->definition; while (*symp) *newcom++ = *symp++; oldcom--; break; case FACT: factp = (struct fact *) defp->definition; while (*oldcom != '(' ) oldcom++; infalse = 0; oldcom++; while (1) { switch (*oldcom) { case 0: case ')': goto done; case ':': infalse++; break; case '\\': ++oldcom; default: if (((factp->truth == FTRUE) && (infalse == 0)) || ((factp->truth == FFALSE) && (infalse))) { *newcom++ = *oldcom; } break; } oldcom++; } } else { fprintf(stderr,"%s is undefined on expansion",symbuf); } done: break; default: *newcom++ = *oldcom; } oldcom++; } *newcom= 0; trace ("Expanded into %s\n",expbuf); return(expbuf); } struct hype * curhype() { struct hype *hype,*rehype; int maxhype; maxhype = HUNCERTAIN; /* Cut off any below this level */ rehype = NULL; for (hype = hypes; hype; hype = hype-> next) if ((hype->asked==0) && (hype->confid > maxhype)) { maxhype = hype->confid; rehype = hype; } trace ("Best hypothesis is %s\n",rehype->name->name); return(rehype); } reset() { struct fact *factp; struct hype *hypep; struct defblk *defp; int i; for (i = 0; i < NHASH; i++) { for (defp = hashtable[i]; defp; defp = defp->next) { if (defp->type == STRING) defp->definition = NULL; } } for (factp = facts; factp; factp = factp->next) factp->truth=FUNKNOWN; for (hypep = hypes; hypep; hypep = hypep->next) { hypep->confid = HUNCERTAIN; hypep->asked = 0; } defp = getref("start",FACT,1); setfact(defp->definition,FTRUE); /* Start up the inference engine */ } askhype(hype) struct hype *hype; { char buf[20]; struct defblk *defp; struct ref *refp; struct fact *factp; struct fact *factors[20]; int truth[20]; int nfact = 0; int i; char *bufp; FILE *fp; trace ("Asking about hypothesis %s\n",hype->name->name); refp = hype->queries; while (refp) { defp = refp->this; if (defp->type == STRING) { if (defp->definition) return(0); goto setup; } factp = (struct fact *) defp->definition; trace ("Sub-fact %s, state %d\n",factp->name->name,factp->truth); if (factp->truth == FUNKNOWN) { if (nfact == 0) { setup: switch (hype->action) { case ASK: printf ("%s\n\n",expcom(hype->text)); if (defp->type == STRING) { gets(buf); defp->definition = (struct ref *) malloc(strlen(buf)+1); strcpy(defp->definition,buf); return(1); } break; case SCAN: fp = fopen(hype->text,"r"); trace ("Scanning %s from hypothesis %s\n", hype->text,hype->name->name); fmatch(fp,hype->queries); fclose(fp); return(1); case RUN: fp = popen(expcom(hype->text),"r"); trace ("Running %s from hypothesis %s\n", hype->text,hype->name->name); fmatch(fp,hype->queries); pclose(fp); return(1); } } factors[nfact++] = factp; printf (" %d) %s\n",nfact,refp->text); } refp = refp->next; } if (nfact == 0) return(0); printf ("\n? "); again: gets(buf); for (i = 0; i < nfact; i++) truth[i] = FFALSE; for (bufp = buf; *bufp; bufp++) { if (*bufp == 't') { traceon = !traceon; continue; } if (*bufp == '!') { bufp++; if (*bufp == 0) { bufp = getenv("SHELL"); if ((bufp == NULL) || (*bufp == 0)) bufp= "/bin/sh"; } system (bufp); return(askhype(hype)); } if (*bufp == 'q') return(-1); if ((*bufp < '1') || (*bufp > '0'+nfact)) { if ((*bufp == ' ') || (*bufp == ',')) continue; printf ("Please type all of the numbers that apply\n"); goto again; } truth[*bufp-'1'] = FTRUE; } for (i = 0; i < nfact; i++) { setfact(factors[i],truth[i]); } return(1); } setfact(factp,truth) struct fact *factp; int truth; { struct hype *hype; struct conc *concp; struct concr *refp; int trigger; int i; factp->truth = truth; trace ("Setting fact %s to %d\n",factp->name->name,truth); for (refp = factp->setlist; refp; refp = refp ->next) { concp = refp->this; trigger = 1; for (i = 0;(trigger && (i < concp->num)); i++) { if (concp->confirms[i]->truth == FUNKNOWN) trigger = 0; else { if ((concp->vector>>i) & 1) { if (concp->confirms[i]->truth == FFALSE) trigger = 0; } else { if (concp->confirms[i]->truth == FTRUE) trigger = 0; } } } if (trigger) { sethype(concp->hypo,concp->weight); } } } sethype(hype,weight) struct hype *hype; int weight; { if (hype->name->type == FACT) { struct fact *factp; factp = (struct fact *) hype; if (factp-> truth != FUNKNOWN) return; if (weight == 0) { setfact(factp,FFALSE); } else { setfact(factp,FTRUE); } return; } if (weight < 0) hype->confid = HTRUE; else { hype->confid *= weight; hype->confid /= 10; } if (hype->confid > HTRUE) hype->confid = HTRUE; trace ("Adjusting hypothesis %s by %d gives %d\n",hype->name->name,weight,hype->confid); } dumplike() { struct hype *hypo; for (hypo = hypes; hypo; hypo = hypo->next) { hypo->asked = 0; } while (hypo = curhype()) { hypo->asked = 1; if (hypo->explain) { printf ("The following is a possible cause of your problem:\n\n%s\n",hypo->explain); printf ("\nThis explaination has a confidence factor of %d on a scale of %d to %d\n",hypo->confid,HFALSE,HTRUE); printf ("_________________________________________________________________________\n"); } } } cycle() { struct hype *hypo; int ret; #ifdef DIAGNOSE printf ("You will be asked to clarify your problem\n"); #endif printf ("For each question, please answer with all of the numbers\n"); printf ("of the statements that apply. For example 136\n"); printf ("if answers 1, 3, and 6 apply. If you would like to quit, type 'q'\n"); printf ("If you would like to escape to run a unix commmand, type '!'\n"); printf ("or '!command' in response to a question.\n\n"); while (hypo = curhype()) { if (ret= askhype(hypo)) { if (ret < 0) return; /* User quit */ if (hypo->explain == NULL) hypo->asked = 1; } else { hypo->asked = 1; if (hypo->explain) { #ifdef DIAGNOSE printf ("The following is a possible cause of your problem:\n\n%s\n",hypo->explain); printf ("\nThis explaination has a confidence factor of %d on a scale of %d to %d\n",hypo->confid,HFALSE,HTRUE); if (gyn("Would you like to continue to identify other possible causes") == 0) return; #else printf("%s\n",hypo->explain); return(0); #endif } } } } gyn(string) char *string; { char buf[100]; while (1) { printf ("%s\n",string); if (gets(buf) == NULL) return(0); if (buf[0] == 'y') return(1); if (buf[0] == 'n') return(0); printf ("Please answer with 'yes' or 'no'\n"); } } char * gstring() { char buf[512]; char *bufp; int c; c = nonblank(); if (c != '"') { fprintf("Missing character string argument\n"); ungetc(c,kfile); return; } bufp = buf; while (((c =getc(kfile)) != EOF) && (c != '"')) { if (c == '\\') c = getc(kfile); *bufp++ = c; } *bufp++ = 0; bufp = malloc(bufp-buf+1); strcpy(bufp,buf); trace ("Storing character string %s\n",bufp); return(bufp); } nonblank() { int c; while ((c = getc(kfile)) != EOF) { if ((ctype[c] & WHITE) == 0) return(c); } return(EOF); } int number(sp) /* Keywords: string-processing file-scanning:50 user-interface:10 */ char *sp; { int n; n = 0; while (*sp) n = n*10 + (*sp++) -'0'; return(n); } char symbuf[64]; char * symbol() { int c; char *symp; symp = symbuf; c = nonblank(); while ((c != EOF) && (ctype[c]&SYMCHAR)) { *symp++ = c; c = getc(kfile); } if (c == EOF) return(NULL); ungetc(c,kfile); *symp++ = 0; trace ("Reading symbol: %s\n",symbuf); return(symbuf); } /* Knowledge file formats: */ /* Hypothesis name: { */ /* Ask: "text for asking" */ /* Scan: "file(s) to scan" */ /* Run: "command to run" */ /* Replies: { */ /* factname: "description" */ /* } */ /* Read: string */ /* Explain: "explaination if terminal diagnosis" */ /* Infer hypothesis <with certaintity number> from { */ /* Conclude fact from { */ /* fact, or !fact */ /* } */ main(argc, argv) /* Keywords: user-interface command-line file-scanning:10 file-opening:10 */ int argc; char *argv []; { int i; int ktest = 0; for (i = 1; i < argc; i++) { if (streq(argv[i],"-t")) { traceon++; i++; } if (streq(argv[i],"-k")) { ktest++; i++; } if (argc>2) printf ("Loading file %s\n",argv[i]); parse(argv[i]); } if (ktest) { dumpknow(); } while (1) { reset(); cycle(); #ifdef DIAGNOSE printf ("I can offer no further help with this problem\n"); printf ("If you are still having trouble, contact ihnss!warren\n\n"); if (gyn("Would you like a summary of all likely causes?")) dumplike(); if (gyn("Would you like to try another diagnosis?")==0) break; #else if (gyn("Would you like to do something else?")==0) break; #endif } } dumpknow() { struct fact *factp; struct hype *hypep; struct ref *refp; struct conc *consp; struct concr *concrp; int header; reset(); header = 0; for (factp = facts; factp; factp= factp->next) { for (concrp = factp->setlist; concrp; concrp = concrp->next) { concrp->this->hypo->asked = 1; } } header = 0; for (hypep = hypes; hypep; hypep = hypep->next) { if ((hypep->text == NULL)&& (hypep->explain == NULL)) { if (header == 0) { header = 1; printf ("The following hypotheses are never defined:\n"); } printf (" %s\n",hypep->name->name); } for (refp = hypep->queries; refp; refp = refp->next) { ((struct fact *) refp->this->definition)->truth = FTRUE; } } header = 0; for (hypep = hypes; hypep; hypep = hypep->next) { if (hypep->asked == 0) { if (header == 0) { header = 1; printf ("The following hypotheses are not inferred by any facts:\n"); } printf (" %s\n",hypep->name->name); } } header = 0; for (factp = facts; factp; factp= factp->next) { if (factp->truth == FUNKNOWN) { if (header == 0) { header = 1; printf ("The following facts are never set:\n"); } printf (" %s\n",factp->name->name); } } header = 0; for (factp = facts; factp; factp= factp->next) { if (factp->setlist == NULL) { if (header == 0) { header = 1; printf ("The following facts are never used to infer anything:\n"); } printf (" %s\n",factp->name->name); } } reset(); } parse(np) /* Keywords: file-scanning file-opening:10 preprocessor:10 database-input:10 */ char *np; { register char *symp; char c; kfile = fopen(np,"r"); if (kfile == NULL) { fprintf(stderr,"Can't open %s\n",np); return; } while (symp = symbol()) { if (streq(symp,"Hypothesis")) addhype(); else if (streq(symp,"Infer")) addinfer(HYPO); else if (streq(symp,"Conclude")) addinfer(FACT); else { fprintf (stderr,"Bad keyword %s\n",symp); c = nonblank(); if (ctype[c] & SYMCHAR) ungetc(c,kfile); } } } addhype() { register char *sp; register struct hype *hypep; register struct defblk *defp; register struct fact *factp; struct ref *refp; char c; sp = symbol(); if (sp == NULL) { eof: fprintf(stderr,"Unexpected EOF in hypothesis\n"); return; } defp = getref(sp,HYPO,1); hypep = (struct hype *) defp->definition; c = nonblank(); if (c != ':') { fprintf(stderr,"Missing ':' after hypothesis name %s\n",sp); } c = nonblank(); if (c != '{') { fprintf (stderr,"Empty hypothesis body %s\n",sp); } while (1) { c = nonblank(); if ((c == EOF) || (c == '}')) return; ungetc(c,kfile); sp = symbol(); c = nonblank(); if (c != ':') { fprintf(stderr,"Missing ':' after hypothesis name %s\n",sp); } if (streq(sp,"Ask")) { hypep->text = gstring(); hypep->action = ASK; } else if (streq(sp,"Scan")) { hypep->text = gstring(); hypep->action = SCAN; } else if (streq(sp,"Run")) { hypep->text = gstring(); hypep->action = RUN; } else if (streq(sp,"Explain")) { hypep->explain = gstring(); } else if (streq(sp,"Read")) { sp = symbol(); defp = getref(sp,STRING,1); refp = (struct ref *) malloc(sizeof *refp); refp->next = hypep->queries; refp->this = defp; refp->text = NULL; hypep->queries = refp; } else if (streq(sp,"Replies")) { c = nonblank(); if (c != '{') { fprintf (stderr,"Empty replies body %s\n",sp); } while (1) { c = nonblank(); if (c == '}') break; ungetc(c,kfile); sp = symbol(); c = nonblank(); if (c != ':') { fprintf(stderr,"Missing ':' after reply name %s\n",sp); } if ((*sp >= 'A') && (*sp <= 'Z')) { /* Define a hypothesis and fact of the same name, and make an inference */ struct hype *infhype; struct conc *concp; struct concr *concrp; defp = getref(sp,HYPO,1); infhype = (struct hype *) defp->definition; *sp += 32; defp = getref(sp,FACT,1); factp = (struct fact *) defp->definition; if (factp->setlist) { /* Already exists, assume the infers part does to */ goto makeref; /* Join other branch to make this reference to it. */ } concrp = (struct concr *) malloc(sizeof *concrp); factp->setlist = concrp; concp = (struct conc *) malloc((sizeof *concp) + (sizeof factp)); concrp->this = concp; concrp->next = NULL; concp -> weight = -1; concp ->hypo = infhype; concp -> vector = 1; concp -> num = 1; concp ->confirms[0] = factp; } else { defp = getref(sp,FACT,1); factp = (struct fact *) defp->definition; } makeref: c = nonblank(); ungetc(c,kfile); refp = (struct ref *) malloc(sizeof *refp); refp -> next = hypep -> queries; hypep->queries = refp; refp->this = defp; if (c == '"') { refp->text = gstring(); } } } else { fprintf (stderr,"Bad Keyword %s\n",sp); } } } addinfer(type) int type; { struct hype *hypep; struct fact *factp; struct conc *concp; struct fact *facts[20]; struct concr *concrp; struct ref *refp; struct defblk *defp; register char *sp; int factor; int vector; int c; int numinf; int num; sp = symbol(); factor = -1; if (*sp == '!') { factor = 0; sp++; } defp = getref(sp,type,1); hypep = (struct hype *) defp->definition; sp = symbol(); if (streq(sp,"certainty")) { if (type != HYPO) fprintf(stderr,"Conclude clause can't have a certainty factor\n"); sp = symbol(); factor = number(sp); sp = symbol(); } if (streq(sp,"from")) { c = nonblank(); if (c == '{') { num=1000; } else { ungetc(c,kfile); num = 1; } vector = 0; numinf = 0; while (num--) { c = nonblank(); if (c == '}') break; vector = vector << 1; if (c != '!' ) { vector += 1; ungetc(c,kfile); } sp = symbol(); defp = getref(sp,FACT,1); facts[numinf++] = (struct fact *) defp->definition; } if (numinf) { int x; concp = (struct conc *) malloc((sizeof *concp) + numinf * (sizeof factp)); concp ->hypo = hypep; concp -> weight = factor; concp -> vector = vector; concp -> num = numinf; x = 0; while (--numinf >= 0) { factp = facts[numinf]; concrp = (struct concr *) malloc(sizeof *concrp); concrp ->next = factp->setlist; factp->setlist = concrp; concrp->this = concp; concp->confirms[x++] = factp; } } } } match(pat,targ) char *pat,*targ; { int first; register char *p, *t; if (*pat== '^') { first=1; pat++; } else first = 0; while ( *targ) { if (*pat == *targ) { p = pat; t = targ; while (*p) if (*p++ != *t++) goto next; return(1); } next: if (first) return(0); targ++; } return(0); } fmatch(fp,refp) FILE *fp; struct ref *refp; { struct fact *factp; struct ref *rp; char buf[512]; if (fp == NULL) return; while (fgets(buf,512,fp)) { for (rp = refp; rp != NULL; rp = rp->next) { if (rp->this->type == STRING) { buf[strlen(buf)-1] = 0; rp->this->definition = (struct ref *) malloc(strlen(buf)+1); strcpy(rp->this->definition,buf); } else { factp = (struct fact *) rp->this->definition; if ((factp->truth == FUNKNOWN) && match(rp->text,buf)) setfact(factp,FTRUE); } } } for (rp = refp; rp != NULL; rp = rp->next) { if (rp->this->type == FACT) { factp = (struct fact *) rp->this->definition; if (factp->truth == FUNKNOWN) setfact(factp,FFALSE); } } }