INTELLIGENCE THROUGH LEARNING

Putting the techniques already described together, and building upon them we can start to look to the future. A future where intelligence is evident in computer programs.

FRANK

FRANK is a demonstration of a computer program which learns by accepting input from the operator, and uses what it knows to make intelligent responses to questions. The program is offered as an example skeleton of how certain AI techniques can be used to create intelligent computer programs. It is not intended to be viewed as a finished program.

Frank receives an input from the operator, and then branches to one of many possible sub-modules depending upon the nature of the input.

                                      INPUT
                                        |
  +----|-----|------|----|----|----|--|-+-|-----|-----|----|----|---|-----+
  |    |     |      |    |    |    |  |   |     |     |    |    |   |     |
WHAT  HOW  WHERE  WHEN  WHY  HAVE  DO |  DID  SHOULD  IS  WILL  I  YOU  OTHER
                                      |
                                     CAN

                      Fig 1. First level of decision tree

Each branch of the descision tree branches in turn to other more specific inputs. For example. The WHAT branch sub-divides depending upon the target subject: You, Your, I or something else. So the input "WHAT IS YOUR NAME?" will pass first to the WHAT branch. From there it will pass to the YOUR sub- branch of the WHAT branch. This branch can have a multitude of sub-branches and leaves. Such as: NAME, AGE, SEX, CREED, BREEDING, FATHER'S, MOTHER'S etc.
                                     WHAT
                                       |
                          +-------+----+-|---------+
                          |       |      |         |
                         YOU     YOUR    I       OTHER


                      Fig 2. WHAT decision branch level one

The sub-branches may sub-divide themselves in to infinite recusions of the same sub-branch structure as illustrated in fig 3.
									YOUR
                                      |
              +-----|----|-----|-------+---------|-----------+
              |     |    |     |       |         |           |
             NAME  AGE  SEX  CREED  BREEDING   FATHER'S   MOTHER'S
                                                  |
                                                  |
              +-----|----|-----|-------|----------+-----------+
              |     |    |     |       |          |           |
             NAME  AGE  SEX  CREED  BREEDING   FATHER'S   MOTHER'S
                                                  |
                                                  |
                                                 etc


                    Fig 3. WHAT....YOUR decision sub-branch

Facts about FRANK are hard coded, or can be entered, but must start "FRANK ". Other knowledge can be stored in a file called "frank.dat". By amending the knowledge available to FRANK, you can see how learning can occur.
Listing 1. Frank Main program

/*-----------------------------------------------------------------*/
/*                             FRANK                               */
/*                 Conversational Computer Model                   */
/*-----------------------------------------------------------------*/


#include 
#include 
#include 

char text[250];        /* User input */
char f_last[250];    /* Frank's last statement */

/* Parser variables */
char question[80];
char order[80];
char object[80];
char description[80];
char subject[80];
char number[80];
char action[80];
char location[80];
char colour[80];
char size[80];
char who[80];
char tense;

/* Record of statements */
/* Statements about frank MUST commence FRANK */
char *facts[2000] = { "FRANK DOES LIKE CATS BECAUSE THEY'RE INDEPENDANT." ,
                      "FRANK DOES NOT LIKE DOGS BECAUSE THEY'RE DUMB." ,
                      "FRANK DOES NOT SMOKE BECAUSE I'M NOT ALLOWED TO!" ,
                      "FRANK HAS BEEN TO MORROCO",
                      "FRANK HAS BEEN TO AMERICA",
                      "FRANK HAS NEVER BEEN TO GERMANY"
                    };

/* Index into facts table */
int fact_ptr;
char fflags[2000];

char stk[50][40];
int stp;


typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

SUBJECT psubj;
SUBJECT predicate;
SUBJECT clause1;
SUBJECT clause2;
SUBJECT clause3;
SUBJECT clause4;

/*---------------------*/
/* Function prototypes */
/*---------------------*/
void DID(void);
void DO(void);
void EXPAND(void);
void GET_DATA(void);
void HAVE(void);
void HOW(void);
void I(void);
void INITIALISE(void);
void IS(void);
void RESPOND(void);
void SHOULD(void);
void STATEMENT(void);
void WHAT(void);
void WHEN(void);
void WHERE(void);
void WHY(void);
void WILL(void);
void YOU(void);
void strchg(char *, char *, char *);
void strins(char *, char *);
void strlench(char *,int);
void truncstr(char *,int);

/*---------------*/
/* Start of code */
/*---------------*/

void cdecl say(char *format, ...)
{
    /*----------------------*/
    /* Frank says something */
    /*----------------------*/

    va_list arg_ptr;
    static char output[1000];

    va_start(arg_ptr, format);
    vsprintf(output, format, arg_ptr);
    va_end(arg_ptr);

    if (*output)
        strcpy(f_last,output);

    printf("%s",f_last);
}

void TOPAST(char *word)
{
    /*------------------------------*/
    /* Convert a word to past tense */
    /*------------------------------*/

    if (word[strlen(word) - 1] == 'E')
        strcat(word,"D");
    else
    if (word[strlen(word) - 1] == 'Y')
    {
        /* Irregular words! */
        if (stricmp(word,"PAY") == 0)
            strcpy(word,"PAID");
        else
        if (stricmp(word,"PLAY") == 0)
            strcpy(word,"PLAYED");
        else
        if (stricmp(word,"FLY") == 0)
            strcpy(word,"FLEW");
        else
        {
            word[strlen(word) - 1] = 0;
            strcat(word,"IED");
        }
    }
    else
        strcat(word,"ED");
}

void EXPAND()
{
    /*--------------------------*/
    /* Expand abbreviated words */
    /*--------------------------*/

    /* DON'T must be protected from expansion */
    strchg(text,"DON'T","DON|T");

    /* Expand "'RE" */
    strchg(text,"'RE"," ARE");

    /* Expand "'S" */
    strchg(text,"'S"," IS");

    /* Expand I'M */
    strchg(text,"I'M","I AM");

    /* Expand "N'T" */
    strchg(text,"N'T"," NOT");

    /* Expand "I'VE" */
    strchg(text,"I'VE","I HAVE");

    /* DON'T must be protected from expansion */
    strchg(text,"DON|T","DON'T");
}

char GET_ATT(char *s1, char *s2)
{
    /*---------------------------------------------*/
    /* Determine the operators attitude towards s2 */
    /*---------------------------------------------*/
    char string[100];

    strcpy(string,"I DON'T LIKE ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(0);

    strcpy(string,"I DON'T LOVE ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(0);

    strcpy(string,"I DON'T ENJOY ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(0);

    strcpy(string,"I DON'T DISLIKE ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(1);

    strcpy(string,"I HATE ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(0);

    strcpy(string,"I DISLIKE ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(0);

    strcpy(string,"I LIKE ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(1);

    strcpy(string,"I LOVE ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(1);

    strcpy(string,"I ENJOY ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(1);

    strcpy(string,"I DO ENJOY ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(1);

    return(1);
}

char GET_FATT(char *s1, char *s2)
{
    /*----------------------------------------------*/
    /* Determine the computer's attitude towards s2 */
    /*----------------------------------------------*/
    char string[100];

    strcpy(string,"FRANK DOES NOT LIKE ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(0);

    strcpy(string,"FRANK DOES NOT ENJOY ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(0);

    strcpy(string,"FRANK DOES NOT ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(0);

    strcpy(string,"FRANK DOES LIKE ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(1);

    strcpy(string,"FRANK DOES ENJOY ");
    strcat(string,s2);
    if (strstr(s1,string))
        return(1);

    return(1);
}


void RESPOND()
{
    /*------------------------------*/
    /* Root of the response process */
    /*------------------------------*/

    char *p;
    char key_word[40];

    /*---------------------------*/
    /* Get first word from input */
    /*---------------------------*/
    p = strchr(text,32);
    if (p)
    {
        *p = 0;
        p++;
        strcpy(key_word,text);
        memmove(text,p,strlen(p)+1);
    }
    else
    {
        strcpy(key_word,text);
        *text = 0;
    }

    /*-------------------------------*/
    /* Branch on first word of input */
    /*-------------------------------*/
    if (strcmp(key_word,"WHAT") == 0)
    {
        WHAT();
        return;
    }

    if (strcmp(key_word,"HOW") == 0)
    {
        HOW();
        return;
    }

    if (strcmp(key_word,"WHERE") == 0)
    {
        WHERE();
        return;
    }

    if (strcmp(key_word,"WHEN") == 0)
    {
        WHEN();
        return;
    }

    if (strcmp(key_word,"WHY") == 0)
    {
        WHY();
        return;
    }

    if (strcmp(key_word,"HAVE") == 0)
    {
        HAVE();
        return;
    }

    if (strcmp(key_word,"DO") == 0 || strcmp(key_word,"DOES") == 0)
    {
        DO();
        return;
    }

    if (strcmp(key_word,"CAN") == 0)
    {
        DO();
        return;
    }

    if (strcmp(key_word,"DID") == 0)
    {
        DID();
        return;
    }

    if (strcmp(key_word,"SHOULD") == 0)
    {
        SHOULD();
        return;
    }

    if (strcmp(key_word,"IS") == 0)
    {
        IS();
        return;
    }

    if (strcmp(key_word,"ARE") == 0)
    {
        IS();
        return;
    }

    if (strcmp(key_word,"WILL") == 0)
    {
        WILL();
        return;
    }

    if (strcmp(key_word,"I") == 0)
    {
        I();
        return;
    }

    if (strcmp(key_word,"YOU") == 0)
    {
        YOU();
        return;
    }

    /* Put key_word back at start of text */
    strcat(key_word," ");
    strins(text,key_word);
    STATEMENT();
}

void INITIALISE()
{
    /* Set variables */

    strcpy(f_last,"I didn't say anything.");

    /* Find end of facts table */
    fact_ptr = 0;
    while(facts[fact_ptr])
        fflags[fact_ptr++] = 0;
}

void GET_DATA()
{
    char *p;
    char temp[1000];
    FILE *fp;
    fp = fopen("frank.dat","r");
    if (fp == NULL)
        return;

    for(;;)
    {
        p = fgets(temp,999,fp);
        if (p == NULL)
            break;
        truncstr(temp,1);
        facts[fact_ptr] = malloc(strlen(temp));
        if (facts[fact_ptr] == NULL)
        {
            printf("\nOut of memory");
            exit(0);
        }
        strcpy(facts[fact_ptr++],temp);
    }
    fclose(fp);
}



void main()
{
    char *p;

    clrscr();

    INITIALISE();

    GET_DATA();

    for(;;)
    {
        printf("\n?");
        gets(text);

        /* Remove leading spaces */
        p = text;
        while(*p == 32)
            p++;
        memmove(text,p,strlen(p) + 1);

        /* Remove trailing puntuation */
        p = &text[strlen(text) - 1];

        while(strchr("?.;:!,",*p) && p >= text)
            *p-- = 0;

        /* If an empty string has been entered, abort */
        if (*text == 0)
            break;

        /* Convert text to upper case */
        strupr(text);

        /* Expand */
        EXPAND();

        RESPOND();

    }
}

Listing 2. Frank WHAT sub-module

/* Frank WHAT() */
#include 
#include 

extern char text[];
extern char *facts[];
extern int fact_ptr;
extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

/* Function prototypes */
void NLP(void);
void say(char *, ...);
char *strrstr(char *,char *);
void WCOMPUTER(void);
void WCOMPUTERS(void);
void WHUMAN(void);
void WOTHER(void);

void WHAT()
{
    /* Start of the branch for all WHAT questions */

    /* Straight WHAT */
    if (*text == 0)
    {
        say("");
        return;
    }

    /* Parse text */
    NLP();

    /* Possible branches depending upon the target */

    if (strcmp(who,"YOU") == 0 || strcmp(who,"YOU,") == 0)
    {
        /* Reference made to 'FRANK' */
        WCOMPUTER();
        return;
    }

    if (strcmp(who,"YOUR") == 0 || strcmp(who,"YOUR,") == 0)
    {
        /* Reference made to something of 'FRANK' */
        WCOMPUTERS();
        return;
    }

    if (strcmp(who,"I") == 0 || strcmp(who,"I,") == 0)
    {
        /* Reference made to the operator */
        WHUMAN();
        return;
    }

    /* No recognisable reference made */
    WOTHER();
    return;
}

void WCOMPUTER()
{
    /* WHAT DO YOU.... */
    int n;
    char *p;

    if (strcmp(action,"THINK") == 0)
    {
        p = strstr(text,object);
        memmove(text,p,strlen(text));
        for(n = 0; n < fact_ptr; n++)
        {
            if (strncmp("FRANK",facts[n],5) == 0 && strstr(facts[n],text))
            {
                /* This fact does apply to FRANK */
                p = strrstr(facts[n],"DOES");
                if (p)
                {
                    say("I DO %s",p);
                    return;
                }
            }
        }
        say("I HAVE NO FEELINGS ABOUT %s.",text);
        return;
    }
    say("I DON'T KNOW!");
    return;
}

void WCOMPUTERS()
{
    /* WHAT....YOUR.... */
    if (strcmp(object,"NAME") == 0)
    {
        if (tense == 1)
            say("MY NAME WAS, AND STILL IS FRANK.");
        else
        if (tense == 2)
            say("MY NAME'S FRANK.");
        else
            say("MY NAME WILL STILL BE FRANK.");
        return;
    }
}

void WHUMAN()
{
    /* WHAT....I.... */
    say("I DON'T KNOW!");
    return;
}

void WOTHER()
{
    int n;
    char *p;

    if (strcmp(question,"IS") == 0 || strcmp(question,"ARE") == 0 ||
        strcmp(question,"DOES") == 0)
    {
        /* WHAT IS.... or WHAT ARE.... */

        /* Search facts table for the object */
        for(n = 0; n < fact_ptr; n++)
        {
            if ((p = strstr(facts[n],object)) != NULL)
            {
                p += strlen(object);
                if (strchr(".,;: ", *p) || *p == 0)
                {
                    say(facts[n]);
                    return;
                }
            }
        }
    }
    say("I HAVE NO IDEA!");
}

Listing 3. Frank HOW sub-module.

/* Frank HOW() */
#include 
#include 

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

void HOW_YOU(void);
void NLP(void);
void say(char *, ...);
void strins(char *, char *);
char *strrstr(char *, char *);
void strlench(char *,int );

void HOW()
{
    /*---------*/
    /* HOW.... */
    /*---------*/
    int n;
    char string[250];
    char *p;

    NLP();

    /*--------------*/
    /* HOW...YOU... */
    /*--------------*/
    if (strcmp(who,"YOU") == 0 || strncmp(who,"YOU,",4) == 0)
    {
        HOW_YOU();
        return;
    }
    say("I HAVE NO IDEA!");
}

void HOW_YOU()
{
    /*----------------*/
    /* HOW....YOU.... */
    /*----------------*/
    if (strcmp(question,"DO") == 0 && strcmp(text,"YOU DO") == 0)
    {
        /* How do you do? */
        say("VERY WELL, THANKS!");
        return;
    }

    if (strcmp(question,"ARE") == 0 && strcmp(text,"YOU") == 0)
    {
        /* How are you? */
        say("VERY WELL, THANKS!");
        return;
    }

    say("I DON'T KNOW!");
}

Listing 4. Frank WHERE sub-module

/* Frank WHERE() */
#include 
#include 

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

void NLP(void);
void WHERE_YOU(void);
void say(char *, ...);
void strins(char *, char *);
char *strrstr(char *, char *);
void strlench(char *,int );

void WHERE()
{
    /*-----------*/
    /* WHERE.... */
    /*-----------*/
    int n;
    char string[250];
    char *p;

    NLP();

    /*----------------*/
    /* WHERE...YOU... */
    /*----------------*/
    if (strcmp(who,"YOU") == 0 || strncmp(who,"YOU,",4) == 0)
    {
        WHERE_YOU();
        return;
    }
    say("I HAVE NO IDEA!");
}

void WHERE_YOU()
{
    /*------------------*/
    /* WHERE....YOU.... */
    /*------------------*/
    if (strcmp(action,"GOING") == 0)
    {
        /* Where are you going? */
        say("I'M NOT GOING ANY WHERE!");
    }

    say("I DON'T KNOW!");
}

Listing 5. Frank WHEN sub-module.

/* Frank WHEN() */
#include 
#include 

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

void NLP(void);
void WHEN_YOU(void);
void say(char *, ...);
void strins(char *, char *);
char *strrstr(char *, char *);
void strlench(char *,int );

void WHEN()
{
    /*-----------*/
    /* WHEN.... */
    /*-----------*/
    int n;
    char string[250];
    char *p;

    NLP();

    /*---------------*/
    /* WHEN...YOU... */
    /*---------------*/
    if (strcmp(who,"YOU") == 0 || strncmp(who,"YOU,",4) == 0)
    {
        WHEN_YOU();
        return;
    }
    say("I HAVE NO IDEA!");
}

void WHEN_YOU()
{
    /*-----------------*/
    /* WHEN....YOU.... */
    /*-----------------*/
    if (strcmp(action,"GOING") == 0)
    {
        /* When are you going? */
        say("I'M NOT GOING ANY WHERE!");
        return;
    }

    say("I DON'T KNOW!");
}

Listing 6. Frank WHY sub-module.

/* Frank WHY() */
#include 
#include 

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

void WHY_DO_YOU(void);
char GET_FATT(char *, char *);
void NLP(void);
void say(char *, ...);
void strins(char *, char *);
char *strrstr(char *, char *);
void strlench(char *,int );


void WHY()
{
    /* WHY.... */

    NLP();

    if ((strcmp(who,"YOU") == 0 || strcmp(who,"YOU,") == 0) &&
         (strcmp(question,"DO") == 0 || strcmp(object,"DON'T") == 0))
    {
        WHY_DO_YOU();
        return;
    }

    say("I HAVE NO IDEA!");
}

void WHY_DO_YOU()
{
    /* WHY DO YOU.... */
    char attitude;
    char negative;
    int n;
    char *p;

    if (strcmp(object,"DON'T"))
    {
        memmove(text,&text[4],strlen(text));
        negative = 0;
        if (strcmp(object,"NOT") == 0)
        {
            strcpy(object,subject);
            p = strstr(text,"NOT");
            if (p)
                strlench(p,-4);
            negative = 1;
        }
    }
    else
    {
        memmove(text,&text[10],strlen(text));
        negative = 1;
    }

    for(n = 0; n < fact_ptr; n++)
    {
        if (strncmp("FRANK",facts[n],5) == 0 && strstr(facts[n],text))
        {
            /* This fact does apply to FRANK */
            if (strcmp(action,"LIKE") == 0)
            {
                p = strstr(facts[n],"BECAUSE");
                attitude = GET_FATT(facts[n],object);
                if (negative)
                {
                    if (attitude == 1)
                        say("NO I %s",text);
                    else
                        say("%s",p);
                }
                else
                {
                    if (attitude == 1)
                        say("%s",p);
                    else
                        say("I DO NOT %s",text);
                }
                return;
            }
            else
            {
                attitude = GET_FATT(facts[n],text);
                p = strstr(facts[n],"BECAUSE");
                if (negative)
                {
                    if (attitude == 1)
                        say("BUT I DO %s",text);
                    else
                        say("%s",p);
                    return;
                }
                if (attitude == 1)
                    say("%s",p);
                else
                    say("BUT I DON'T %s",text);
                return;
            }
        }
    }
    say("I DON'T KNOW");
    return;
}

Listing 7. Frank HAVE sub-module.

/* Frank HAVE() */
#include 
#include 

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

void HAVE_YOU(void);
char GET_FATT(char *, char *);
void NLP(void);
void say(char *, ...);
void strins(char *, char *);
char *strrstr(char *, char *);
void strlench(char *,int );

void HAVE()
{
    /* HAVE.... */
    int n;
    char string[250];
    char *p;

    NLP();

    if (strcmp(who,"YOU") == 0 || strncmp(who,"YOU,",4) == 0)
    {
        HAVE_YOU();
        return;
    }

    for(n = 0; n < fact_ptr; n++)
    {
        if(strstr(facts[n],text))
        {
            say("I BELIEVE SO");
            return;
        }
    }
    strcpy(string,text);
    p = strrstr(string,object);
    if (p)
    {
        strins(p,"DON'T ");
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                say("I DON'T THINK SO.");
                return;
            }
        }
        strcpy(string,text);
        p = strrstr(string,object);
        strins(p,"DO ");
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                say("I THINK SO.");
                return;
            }
        }
        strcpy(string,text);
        p = strrstr(string,object);
        strins(p,"DO NOT ");
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                say("I DO NOT THINK SO.");
                return;
            }
        }
    }
    say("I HAVE NO IDEA!");
}

void HAVE_YOU()
{
    /* HAVE YOU.... */
    int n;
    char string[250];

    memmove(text,&text[4],strlen(text));

    /* Remove 'EVER' as it's superflous */
    if (strncmp(text,"EVER ",5) == 0)
        memmove(text,&text[5],strlen(text));

    strcpy(string,"NOT ");
    strcat(string,text);
    for(n = 0; n < fact_ptr; n++)
    {
        if (strncmp("FRANK",facts[n],5) == 0 && strstr(facts[n],text))
        {
            /* This fact does apply to FRANK */

            /* Check for negative response */
            strcpy(string,"NOT ");
            strcat(string,text);
            if (strstr(facts[n],string))
                say("NO, I HAVE %s",string);
            else
            {
                strcpy(string,"NEVER ");
                strcat(string,text);
                if (strstr(facts[n],string))
                    say("NO, I HAVE %s",string);
                else
                    say("YES, I HAVE %s",text);
            }
            return;
        }
    }
    say("I DON'T KNOW");
    return;
}

Listing 8. Frank DO sub-module.

/* Frank DO() */
#include 
#include 

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern char fflags[];
extern int fact_ptr;
extern char stk[50][40];
extern int stp;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

int DOES_OBJECT(char *);
void DO_YOU(void);
char GET_FATT(char *, char *);
void NLP(void);
void say(char *, ...);
void strins(char *, char *);
char *strrstr(char *, char *);
void strlench(char *,int );

void DO()
{
    /* DO.... */
    int n;

    NLP();

    if (strcmp(who,"YOU") == 0 || strncmp(who,"YOU,",4) == 0)
    {
        DO_YOU();
        return;
    }

    n = DOES_OBJECT(text);
    if (n == 0)
        say("I DON'T THINK SO!");
    else
    if (n == 1)
        say("YES! I THINK SO.");
    else
        say("I HAVE NO IDEA");

    return;
}

void DO_YOU()
{
    /* DO YOU.... */
    char attitude;
    char negative;
    int n;
    char *p;
    char string[250];

    memmove(text,&text[4],strlen(text));

    negative = 0;
    if (strcmp(object,"NOT") == 0)
    {
        strcpy(object,subject);
        p = strstr(text,"NOT");
        if (p)
            strlench(p,-4);
        negative = 1;
    }

    for(n = 0; n < fact_ptr; n++)
    {
        if (strncmp("FRANK",facts[n],5) == 0 && strstr(facts[n],text))
        {
            /* This fact does apply to FRANK */
            if (strcmp(action,"LIKE") == 0)
            {
                attitude = GET_FATT(facts[n],object);
                if (negative)
                {
                    if (attitude == 1)
                        say("NO I %s",text);
                    else
                        say("NO I DO NOT %s",text);
                }
                else
                {
                    if (attitude == 1)
                        say("YES I %s",text);
                    else
                        say("NO I DO NOT %s",text);
                }
                return;
            }
            else
            {
                strcpy(string,"NOT ");
                strcat(string,text);
                if (strstr(facts[n],text))
                    say("NO, I DO %s",string);
                else
                {
                    strcpy(string,"NEVER ");
                    strcat(string,text);
                    if (strstr(facts[n],text))
                        say("NO, I HAVE %s",string);
                    else
                        say("YES, I %s",text);
                }
                return;
            }
        }
    }
    say("I DON'T KNOW");
    return;
}

int DOES_OBJECT(char *s1)
{
    char string[250];
    char this_object[80];
    char act[80];
    int n;
    char known;

    /* Reset fflags[] */
    for(n = 0; n < fact_ptr; n++)
        fflags[n] = 0;

    strcpy(this_object,object);
    stp = -1;

    strcpy(text,s1);
    NLP();
    if (*subject)
        strcpy(text,subject);
    else
        strcpy(text,object);

    strcpy(object,this_object);
    strcpy(act,action);
    if (*act)
        strcat(act," ");

    do
    {
        known = 0;
        /* Check for object does not do action period! */
        strcpy(string,object);
        strcat(string," DOES NOT ");
        strcat(string,act);
        truncstr(string,1);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strcmp(facts[n],string) == 0)
            {
                return(0);
            }
        }
        strcpy(string,object);
        strcat(string," DO NOT ");
        strcat(string,act);
        truncstr(string,1);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strcmp(facts[n],string) == 0)
            {
                return(0);
            }
        }

        /* Check for object does do action period! */
        strcpy(string,object);
        strcat(string," DOES ");
        strcat(string,act);
        truncstr(string,1);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strcmp(facts[n],string) == 0)
            {
                return(1);
            }
        }
        strcpy(string,object);
        strcat(string," DO ");
        strcat(string,act);
        truncstr(string,1);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strcmp(facts[n],string) == 0)
            {
                return(1);
            }
        }
        strcpy(string,object);
        strcat(string," ");
        strcat(string,act);
        truncstr(string,1);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strcmp(facts[n],string) == 0)
            {
                return(1);
            }
        }

        /* Specific checks */
        strcpy(string,object);
        strcat(string," DOES NOT ");
        strcat(string,act);
        strcat(string,"A ");
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(0);
            }
        }
        strcpy(string,object);
        strcat(string," DO NOT ");
        strcat(string,act);
        strcat(string,"AN ");
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(0);
            }
        }
        strcpy(string,object);
        strcat(string," DOES NOT ");
        strcat(string,act);
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(0);
            }
        }

        strcpy(string,object);
        strcat(string," DO NOT ");
        strcat(string,act);
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(0);
            }
        }
        strcpy(string,object);
        strcat(string," DOES ");
        strcat(string,act);
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(1);
            }
        }
        strcpy(string,object);
        strcat(string," DO ");
        strcat(string,act);
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(1);
            }
        }

        strcpy(string,object);
        strcat(string," DO ");
        strcat(string,act);
        strcat(string,"A ");
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(1);
            }
        }

        strcpy(string,object);
        strcat(string," DO ");
        strcat(string,act);
        strcat(string,"AN ");
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(1);
            }
        }

        /* There may be multiple references for the object */
        /* This will check each in turn */
        for(;;)
        {
            strcpy(string,object);
            strcat(string," IS ");
            for (n = 0; n < fact_ptr; n++)
            {
                if (strstr(facts[n],string) && !fflags[n])
                {
                    stp++;
                    if (stp == 50)
                    {
                        printf("STACK OVERFLOW!");
                        exit(0);
                    }
                    strcpy(stk[stp],object);
                    strcpy(this_object,text);
                    fflags[n] = 1;
                    known = 1;
                    strcpy(text,facts[n]);
                    NLP();
                    if (*subject)
                        strcpy(object,subject);

                    strcpy(text,this_object);
                    break;
                }
            }
            if (!known)
            {
                strcpy(string,object);
                strcat(string," ARE ");
                for (n = 0; n < fact_ptr; n++)
                {
                    if (strstr(facts[n],string) && !fflags[n])
                    {
                        stp++;
                        if (stp == 50)
                        {
                            printf("STACK OVERFLOW!");
                            exit(0);
                        }
                        strcpy(stk[stp],object);
                        strcpy(this_object,text);
                        fflags[n] = 1;
                        known = 1;
                        strcpy(text,facts[n]);
                        NLP();
                        if (*subject)
                            strcpy(object,subject);

                        strcpy(text,this_object);
                        break;
                    }
                }
            }
            if (!known)
            {
                if(stp > -1)
                {
                    /* Backtrack down tree to original object */
                    /* and try the next path */

                    strcpy(object,stk[stp--]);
                }
                else
                    break;
            }
            else
                break;
        }
    }
    while(known);

    /* Don't know */
    return(-1);
}

Listing 9. Frank DID sub-module.

/* Frank DID() */
#include 
#include 

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

void NLP(void);
void TOPAST(char *);
void say(char *, ...);
void strins(char *, char *);
char *strrstr(char *, char *);

void DID()
{
    /* DID.... */
    int n;
    char string[250];
    char *p;
    char *q;

    NLP();

    if (strcmp(who,"YOU") == 0 || strcmp(who,"YOU,") == 0)
    {
        /* DID YOU.... */
        say("I DON'T KNOW");
        return;
    }

    for(n = 0; n < fact_ptr; n++)
    {
        if(strstr(facts[n],text))
        {
            say("I BELIEVE SO");
            return;
        }
    }
    strcpy(string,text);
    p = strrstr(string,object);
    if (p)
    {
        strins(p,"NOT ");
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                say("I DON'T THINK SO.");
                return;
            }
        }
        strcpy(string,text);
        p = strrstr(string,object);
        strins(p,"DID ");
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                say("I THINK SO.");
                return;
            }
        }
    }

    /* If there is a verb, convert it to past tense and try again */
    strcpy(string,text);
    p = strstr(string,action);
    if(p)
    {
        q = p;
        p += strlen(action);
        memmove(q,p,strlen(p)+1);
    }

    TOPAST(action);

    strins(q,action);

    for(n = 0; n < fact_ptr; n++)
    {
        if(strstr(facts[n],string))
        {
            say("I BELIEVE SO");
            return;
        }
    }
    strcpy(string,text);
    p = strrstr(string,object);
    if (p)
    {
        strins(p,"NOT ");
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                say("I DON'T THINK SO.");
                return;
            }
        }
        strcpy(string,text);
        p = strrstr(string,object);
        strins(p,"DID ");
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                say("I THINK SO.");
                return;
            }
        }
    }

    say("I HAVE NO IDEA!");
}
The SHOULD module illustrates how Frank makes decisions based upon the information already given to it. In deciding whether or not the operator should do something, Frank extracts the action from the input, and scans its knowledge base for a value judgement on that action. If a value judgment is found, the object of the judgement is compared with the object of the input. If the two correspond, or the value judgement knowledge does not have a qualifying object, then an affirmative encouragement can be made to the operator.

"HARRY IS A MAN"
"A MAN IS NOT MEAT"

For example. If Frank is told;

"IT IS GOOD TO EAT MEAT"

and is then asked;

"SHOULD I EAT MEAT?"

Frank will encourage the operator to "EAT MEAT", because Frank has been told that it is good to do so. Hovever, if Frank is asked;

"SHOULD I EAT HARRY?"

Frank will find that "EAT" is a good thing to do, when applied to meat. Frank does not know what "HARRY" is, and so will sit on the fence rather, and say that it depends on whether or not "HARRY" is "MEAT".

Listing 10. Frank SHOULD sub-module.

/* Frank SHOULD() */
#include 
#include 

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
147
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

char GET_ATT(char *, char *);
int IS_OBJECT(char *);
void PREDPAR(void);
void SHOULD_I(void);
void SHOULD_I_GO(void);
void NLP(void);
void say(char *, ...);
void strins(char *, char *);
char *strrstr(char *, char *);
void strsing(char *);
void strlench(char *,int );
void trim(char *);

void SHOULD()
{
    /* SHOULD.... */

    NLP();

    if (strcmp(who,"I") == 0 || strncmp(who,"I,",2) == 0)
    {
        SHOULD_I();
        return;
    }

    say("I HAVE NO IDEA!");
}

void SHOULD_I()
{
    /* SHOULD I.... */
    char negative;
    int n;
    int known;
    char *p;
    char save_text[250];

    memmove(text,&text[2],strlen(text));

    negative = 0;
    if (strcmp(object,"NOT") == 0)
    {
        strcpy(object,subject);
        p = strstr(text,"NOT");
        if (p)
            strlench(p,-4);
        negative = 1;
    }

    /*---------------------*/
    /* Many possibilities! */
    /*---------------------*/

    if (*action == 0)
    {
        /* Parse text */
        strcpy(save_text,text);
        PREDPAR();
        strcpy(text,save_text);
        trim(psubj.text);
    }
    else
        strcpy(psubj.text,action);

    /* psubj.text holds the suggested 'action' */
    if (strcmp(psubj.text,"GO") == 0)
    {
        SHOULD_I_GO();
        return;
    }

    /* Search knowledge base for a value judgement of the action */
    strcpy(save_text,"GOOD TO ");
    strcat(save_text,psubj.text);

    for(n = 0; n < fact_ptr; n++)
    {
        if (strstr(facts[n],save_text))
        {
            p = strrstr(facts[n],save_text);
            if (!negative)
            {
                if (*p == 0)
                    say("YES! %s",facts[n]);
                else
                if (strcmp(p,object) == 0)
                    say("YES! %s",facts[n]);
                else
                {
                    strsing(p);
                    known = IS_OBJECT(p);
                    if (known == 0)
                        say("NO! %s IS NOT A %s",object,p);
                    else
                    if (known == 1)
                        say("YES! %s IS A %s",object,p);
                    else
                        say("THAT DEPENDS ON WHETHER OR NOT %s IS A %s",
                             object,p);
                }
            }
            else
            {
                if (*p == 0)
                    say("NO! %s",facts[n]);
                else
                if (strcmp(p,object) == 0)
                    say("NO! %s",facts[n]);
                else
                {
                    strsing(p);
                    known = IS_OBJECT(p);
                    if (known == 0)
                        say("NO! %s IS NOT A %s",object,p);
                    else
                    if (known == 1)
                        say("YES! %s IS A %s",object,p);
                    else
                        say("THAT DEPENDS ON WHETHER OR NOT %s IS A %s",
                             object,p);
                }
            }
            return;
        }
    }

    strcpy(save_text,"BAD TO ");
    strcat(save_text,psubj.text);

    for(n = 0; n < fact_ptr; n++)
    {
        if (strstr(facts[n],save_text))
        {
            if (!negative)
            {
                p = strrstr(facts[n],save_text);
                if (*p == 0)
                    say("NO! %s",facts[n]);
                else
                if (strcmp(p,object) == 0)
                    say("NO! %s",facts[n]);
                else
                {
                    strsing(p);
                    known = IS_OBJECT(p);
                    if (known == 0)
                        say("YES! %s IS NOT A %s",object,p);
                    else
                    if (known == 1)
                        say("NO! %s IS A %s",object,p);
                    else
                        say("THAT DEPENDS ON WHETHER OR NOT %s IS A %s",
                             object,p);
                }
            }
            else
            {
                if (*p == 0)
                    say("YES! %s",facts[n]);
                else
                if (strcmp(p,object) == 0)
                    say("YES! %s",facts[n]);
                else
                {
                    strsing(p);
                    known = IS_OBJECT(p);
                    if (known == 0)
                        say("NO! %s IS NOT A %s",object,p);
                    else
                    if (known == 1)
                        say("YES! %s IS A %s",object,p);
                    else
                        say("THAT DEPENDS ON WHETHER OR NOT %s IS A %s",
                             object,p);
                }
            }
            return;
        }
    }

    /* No value judgement was found on the action */
    /* therefore no opinion can be given */
    say("I DON'T KNOW!");

    return;
}

void SHOULD_I_GO()
{
    /* SHOULD I GO.... */

    int n;
    signed char yes;
    signed char attitude;

    attitude = -1;
    yes = -1;

    /* Determine the operator's attitude towards the object */
    for(n = 0; n < fact_ptr; n++)
    {
        if (strstr(facts[n],object))
        {
            yes = GET_ATT(facts[n],object);
            break;
        }
    }

    if (*subject)
    {
        /* Determine the operator's attitude towards the subject */
        for(n = 0; n < fact_ptr; n++)
        {
            if (strstr(facts[n],object))
            {
                attitude = GET_ATT(facts[n],object);
                break;
            }
        }
    }
    if (yes == 1 && attitude == 1)
    {
        say("YOU LIKE %s AND %s.",object,subject);
        return;
    }
    if (yes == 1 && attitude == -1)
    {
        say("YOU LIKE %s.",object);
        return;
    }
    if (yes == 1 && attitude == 0)
    {
        say("YOU LIKE %s, BUT YOU DON'T LIKE %s.",object,subject);
        return;
    }
    if (yes == 0 && attitude == 1)
    {
        say("YOU LIKE %s, BUT YOU DON'T LIKE %s.",subject,object);
        return;
    }

    if (yes == 0 && attitude == 0)
    {
        say("YOU DON'T LIKE %s OR %s.",object,subject);
        return;
    }
    if (yes == 0 && attitude == -1)
    {
        say("YOU DON'T LIKE %s.",object);
        return;
    }
    say("I HAVE NO IDEA!");
}
The IS() module includes the interesting function IS_OBJECT(). IS_OBJECT() is interesting because it carries out deductive reasoning. Given an object and a subject, for example "CAT" and "LIVING" it will search its knowledge to deduce whether the object is as described by the subject, or is not.

Given the knowledge:

"A CAT IS A CARNIVORE"
"A CARNIVORE IS AN ANIMAL"
"AN ANIMAL IS LIVING"

IS_OBJECT() will find that there is no direct correlation between "CAT" and "LIVING". It then searches for any indirect reference. This it finds in "A CAT IS A CARNIVORE". The search continues with "CAT" replaced with "CARNIVORE" until a match is made with the subject, "LIVING", or no further indirect references occur. When there are no further indirect references IS_OBJECT() back tracks down the search tree until it finds another indirect reference, and so it continues. The back tracking is achieved by the stack "STK[]". IS_OBJECT() is prevented from searching the same path more than once by the flags table fflags[].

Listing 11. Frank IS sub-module.

/* Frank IS() */
#include 
#include 
#include 

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern char stk[50][40];
extern int stp;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern char fflags[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

int IS_OBJECT(char *);
void NLP(void);
void say(char *, ...);
void strins(char *, char *);
char *strrstr(char *, char *);

void IS()
{
    /* IS.... or ARE.... */
    int n;

    NLP();

    if (strcmp(who,"YOU") == 0 || strcmp(who,"YOU,") == 0)
    {
        /* IS/ARE YOU.... */
        say("I DON'T KNOW");
        return;
    }

    n = IS_OBJECT(text);

    if (n == 1)
    {
        say("I BELIEVE SO");
        return;
    }
    if (n == 0)
    {
        say("I DON'T THINK SO.");
        return;
    }
    say("I HAVE NO IDEA!");
}


int IS_OBJECT(char *s1)
{
    char string[250];
    char this_object[80];
    char loc[80];
    int n;
    char known;

    /* Reset fflags[] */
    for(n = 0; n < fact_ptr; n++)
        fflags[n] = 0;

    strcpy(this_object,object);
    stp = -1;

    strcpy(text,s1);
    NLP();
    if (*subject)
        strcpy(text,subject);
    else
        strcpy(text,object);

    strcpy(object,this_object);
    strcpy(loc,location);
    if (*loc)
        strcat(loc," ");

    do
    {
        known = 0;
        strcpy(string,object);
        strcat(string," IS NOT ");
        strcat(string,loc);
        strcat(string,"A ");
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(0);
            }
        }
        strcpy(string,object);
        strcat(string," IS NOT ");
        strcat(string,loc);
        strcat(string,"AN ");
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(0);
            }
        }
        strcpy(string,object);
        strcat(string," IS NOT ");
        strcat(string,loc);
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(0);
            }
        }

        strcpy(string,object);
        strcat(string," ARE NOT ");
        strcat(string,loc);
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(0);
            }
        }
        strcpy(string,object);
        strcat(string," IS ");
        strcat(string,loc);
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(1);
            }
        }
        strcpy(string,object);
        strcat(string," ARE ");
        strcat(string,loc);
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(1);
            }
        }

        strcpy(string,object);
        strcat(string," IS ");
        strcat(string,loc);
        strcat(string,"A ");
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(1);
            }
        }

        strcpy(string,object);
        strcat(string," IS ");
        strcat(string,loc);
        strcat(string,"AN ");
        strcat(string,text);
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                return(1);
            }
        }

        /* There may be multiple references for the object */
        /* This will check each in turn */
        for(;;)
        {
            strcpy(string,object);
            strcat(string," IS ");
            for (n = 0; n < fact_ptr; n++)
            {
                if (strstr(facts[n],string) && !fflags[n])
                {
                    stp++;
                    if (stp == 50)
                    {
                        printf("STACK OVERFLOW!");
                        exit(0);
                    }
                    strcpy(stk[stp],object);
                    strcpy(this_object,text);
                    fflags[n] = 1;
                    known = 1;
                    strcpy(text,facts[n]);
                    NLP();
                    if (*subject)
                        strcpy(object,subject);

                    strcpy(text,this_object);
                    break;
                }
            }
            if (!known)
            {
                strcpy(string,object);
                strcat(string," ARE ");
                for (n = 0; n < fact_ptr; n++)
                {
                    if (strstr(facts[n],string) && !fflags[n])
                    {
                        stp++;
                        if (stp == 50)
                        {
                            printf("STACK OVERFLOW!");
                            exit(0);
                        }
                        strcpy(stk[stp],object);
                        strcpy(this_object,text);
                        fflags[n] = 1;
                        known = 1;
                        strcpy(text,facts[n]);
                        NLP();
                        if (*subject)
                            strcpy(object,subject);

                        strcpy(text,this_object);
                        break;
                    }
                }
            }
            if (!known)
            {
                if(stp > -1)
                {
                    /* Backtrack down tree to original object */
                    /* and try the next path */

                    strcpy(object,stk[stp--]);
                }
                else
                    break;
            }
            else
                break;
        }
    }
    while(known);

    /* Don't know */
    return(-1);
}

Listing 12. Frank WILL sub-module.

/* Frank WILL() */
#include 
#include 

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

void WILL_YOU(void);
char GET_FATT(char *, char *);
void NLP(void);
void say(char *, ...);
void strins(char *, char *);
char *strrstr(char *, char *);
void strlench(char *,int );

void WILL()
{
    /* WILL.... */
    int n;
    char string[250];
    char *p;

    NLP();

    /*--------------*/
    /* WILL YOU.... */
    /*--------------*/
    if (strcmp(who,"YOU") == 0 || strncmp(who,"YOU,",4) == 0)
    {
        WILL_YOU();
        return;
    }

    /*---------------*/
    /* Other WILL....*/
    /*---------------*/
    strcpy(string,text);
    p = strrstr(string,object);
    strins(p,"WILL ");
    for(n = 0; n < fact_ptr; n++)
    {
        if(strstr(facts[n],string) || strcmp(facts[n],string) == 0)
        {
            say("YOU THINK SO");
            return;
        }
    }
    strcpy(string,text);
    p = strrstr(string,object);
    if (p)
    {
        strins(p,"WILL NOT ");
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                say("YOU DON'T THINK SO.");
                return;
            }
        }
    }

    strcpy(string,text);
    p = strrstr(string,object);
    if (p)
    {
        strins(p,"WONT ");
        for(n = 0; n < fact_ptr; n++)
        {
            if(strstr(facts[n],string))
            {
                say("YOU DON'T THINK SO.");
                return;
            }
        }
    }
    say("I HAVE NO IDEA!");
}

void WILL_YOU()
{
    /*--------------*/
    /* WILL YOU.... */
    /*--------------*/

    say("I DON'T KNOW");
    return;
}

Listing 13. Frank I sub-module.

/* Frank I() */
#include 
#include 

static char negative;

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

/* Function prototypes */
char GET_ATT(char *, char *);
void IDO(void);
void IDONT(void);
void ISTATE(void);
void NLP(void);
void PREDPAR(void);
void STATEMENT(void);
void WANT(void);
void WANTGO(void);
void say(char *, ...);
char *strftos(char *);
void strins(char *, char *);
void trim(char *);

void I()
{
    char save_text[250];
    if (*text == 0)
    {
        say("I DON'T UNDERSTAND.");
        return;
    }

    /* Parse text */
    strcpy(save_text,text);
    PREDPAR();
    strcpy(text,save_text);
    trim(psubj.text);

    if (strcmp(psubj.text,"DON'T") == 0)
    {
        IDONT();
        return;
    }
    if (strncmp(psubj.text,"DON'T ",6) == 0)
    {
        strcpy(predicate.text,&psubj.text[6]);
        psubj.text[6] = 0;
        IDONT();
        return;
    }
    IDO();

}

void IDONT()
{
    /* I DON'T.... */

    negative = 1;

    trim(predicate.text);
    if (strcmp(predicate.text,"WANT") == 0)
    {
        WANT();
        return;
    }
    if (strncmp(predicate.text,"LIKE",4) == 0)
    {
        ISTATE();
        return;
    }
    if (strncmp(predicate.text,"LOVE",4) == 0)
    {
        ISTATE();
        return;
    }
    if (strncmp(predicate.text,"HATE",4) == 0)
    {
        ISTATE();
        return;
    }
}

void IDO()
{
    /* I DO.... */
    /* The 'DO' may be implied */

    negative = 0;
    if (strcmp(psubj.text,"WANT") == 0)
    {
        WANT();
        return;
    }
    if (strncmp(psubj.text,"LIKE",4) == 0)
    {
        ISTATE();
        return;
    }
    if (strncmp(psubj.text,"LOVE",4) == 0)
    {
        ISTATE();
        return;
    }
    if (strncmp(psubj.text,"HATE",4) == 0)
    {
        ISTATE();
        return;
    }
}

void WANT()
{
    /* I WANT.... I DON'T WANT.... */
    char string[250];

    if (negative)
    {
        /* Consider clause 1 etc */
        strcpy(string,clause1.text);
    }
    else
        strcpy(string,predicate.text);

    trim(string);
    if (strncmp(string,"TO GO ",6) == 0)
    {
        if (negative)
            strcpy(clause2.text,&string[6]);
        else
            strcpy(clause1.text,&string[6]);
        string[6] = 0;
        trim(string);
    }

    if (strcmp(string,"TO GO") == 0)
    {
        WANTGO();
        return;
    }
}

void WANTGO()
{
    /* I WANT TO GO.... I DON'T WANT TO GO.... */
    char string[250];
    char destination[80];
    char found_fact;
    char attitude;
    int n;

    if (negative)
    {
        /* Consider clause 2 etc */
        strcpy(string,clause2.text);
        strcpy(destination,clause2.object);
    }
    else
    {
        strcpy(string,clause1.text);
        strcpy(destination,clause1.object);
    }
    else
        say("WHY DO YOU WANT TO GO %s?",string);
    return;
}

void ISTATE()
{
    /* I LIKE.... or I DON'T LIKE */

    char temp[5];

    /* Treat this as a statement */
    strcpy(temp,"I ");
    strins(text,temp);
    STATEMENT();
}

Listing 14. Frank YOU sub-module.

/* Frank YOU() */
#include 
#include 

static char negative;

typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;
extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

/* Function prototypes */
char GET_ATT(char *, char *);
void YOU_DO(void);
void YOU_DONT(void);
void YOU_STATE(void);
void NLP(void);
void PREDPAR(void);
void STATEMENT(void);
void YOU_SHOULD(void);
void YOU_WANT(void);
void YOU_WANT_GO(void);
void say(char *, ...);
char *strftos(char *);
void strins(char *, char *);
void trim(char *);

void YOU()
{
    char save_text[250];
    if (*text == 0)
    {
        say("I DON'T UNDERSTAND.");
        return;
    }

    /* Parse text */
    strcpy(save_text,text);
    PREDPAR();
    strcpy(text,save_text);
    trim(psubj.text);

    if (strcmp(psubj.text,"DON'T") == 0)
    {
        YOU_DONT();
        return;
    }
    if (strncmp(psubj.text,"DON'T ",6) == 0)
    {
        strcpy(predicate.text,&psubj.text[6]);
        psubj.text[6] = 0;
        YOU_DONT();
        return;
    }
    YOU_DO();
}

void YOU_DONT()
{
    /* YOU DON'T.... */

    negative = 1;

    trim(predicate.text);
    if (strcmp(predicate.text,"WANT") == 0)
    {
        YOU_WANT();
        return;
    }
    if (strncmp(predicate.text,"LIKE",4) == 0)
    {
        YOU_STATE();
        return;
    }
    if (strncmp(predicate.text,"LOVE",4) == 0)
    {
        YOU_STATE();
        return;
    }
    if (strncmp(predicate.text,"HATE",4) == 0)
    {
        YOU_STATE();
        return;
    }
}

void YOU_DO()
{
    /* YOU DO.... */
    /* The 'DO' may be implied */

    negative = 0;
    if (strcmp(psubj.text,"WANT") == 0)
    {
        YOU_WANT();
        return;
    }
    if (strncmp(psubj.text,"SHOULD ",7) == 0)
    {
        memmove(psubj.text,&psubj.text[7],strlen(psubj.text));
        YOU_SHOULD();
        return;
    }
    if (strncmp(psubj.text,"LIKE",4) == 0)
    {
        YOU_STATE();
        return;
    }
    if (strncmp(psubj.text,"LOVE",4) == 0)
    {
        YOU_STATE();
        return;
    }
    if (strncmp(psubj.text,"HATE",4) == 0)
    {
        YOU_STATE();
        return;
    }
}

void YOU_WANT()
{
    /* YOU WANT.... YOU DON'T WANT.... */
    char string[250];

    if (negative)
    {
        /* Consider clause 1 etc */
        strcpy(string,clause1.text);
    }
    else
        strcpy(string,predicate.text);

    trim(string);
    if (strncmp(string,"TO GO ",6) == 0)
    {
        if (negative)
            strcpy(clause2.text,&string[6]);
        else
            strcpy(clause1.text,&string[6]);
        string[6] = 0;
        trim(string);
    }

    if (strcmp(string,"TO GO") == 0)
    {
        YOU_WANT_GO();
        return;
    }
}

void YOU_SHOULD()
{
    /* YOU SHOULD.... YOU SHOULD NOT.... */

    if (strcmp(psubj.text,"GO TO") == 0)
    {
        YOU_WANT_GO();
        return;
    }

    if (strcmp(psubj.text,"NOT") == 0)
    {
        negative = 1;
        if (strcmp(predicate.text,"GO ") == 0)
        {
            strins(clause1.text,"GO ");
            strcpy(clause2.text,clause1.text);
            strcpy(clause2.object,clause1.object);
            YOU_WANT_GO();
        }
    }
}


void YOU_WANT_GO()
{
    /* YOU WANT TO GO.... YOU DON'T WANT TO GO.... */
    char string[250];
    char destination[80];
    char found_fact;
    char attitude;
    int n;

    if (negative)
    {
        /* Consider clause 2 etc */
        strcpy(string,clause2.text);
        strcpy(destination,clause2.object);
    }
    else
    {
        strcpy(string,clause1.text);
        strcpy(destination,clause1.object);
    }

    trim(string);
    if (*string == 0)
    {
        /* No destination specified */
        say("WHAT MAKES YOU THINK I'VE GOT TO GO?");
        return;
    }

    /* The operator has stated that FRANK do/donot want to go to */
    /* a destination */
    /* Frank should check the facts table to determine a possible reason */
    /* for the operators desire to go to the specified destination */

    found_fact = 0;
    for(n = 0; n < fact_ptr; n++)
    {
        if (strstr(facts[n],destination))
        {
            found_fact = 1;
            break;
        }
    }

    if(found_fact)
    {
        /* Determine the operators attitude towards the destination */
        attitude = GET_ATT(facts[n],destination);

        strcpy(string,facts[n]);
        strftos(string);

        /* Check for paradoxs */
        /* EG: I don't like chelsea */
        /*     You want to go to chelsea */

        if (attitude != negative)
            say("BECAUSE %s?",string);
        else
            say("BUT YOU SAID %s!",string);
        return;
    }

    if (negative)
        say("WHAT MAKES YOU THINK I AM ARE GOING %s?",string);
    else
        say("WHY DO YOU THINK I SHOULD GO %s?",string);
    return;
}

void YOU_STATE()
{
    /* YOU LIKE.... or YOU DON'T LIKE */

    char temp[5];

    /* Treat this as a statement */
    strcpy(temp,"YOU ");
    strins(text,temp);
    STATEMENT();
}

Listing 15. Frank other statements sub-module.

/* Frank STATEMENT() */
#include 
#include 

extern char text[];
extern char *facts[];
extern int fact_ptr;

extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;

/* Function prototypes */
void NLP(void);
void say(char *);


void STATEMENT()
{
    /* The operator has made a statement */
    /* record it for posperity */

    if (fact_ptr < 2000)
    {
        facts[fact_ptr] = malloc(strlen(text)+1);
        if (facts[fact_ptr])
        {
            strcpy(facts[fact_ptr],text);
            fact_ptr++;
        }
    }
    say("If you say so.");
}

Listing 16. Frank parser module.


/*---------------*/
/* Include files */
/*---------------*/
#include 
#include 
#include 
#include 

/*-----------*/
/* Constants */
/*-----------*/
#define num_a    141
#define num_p    12
#define num_q    41
#define num_l    20
#define num_n    37
#define num_c    14
#define num_f    17
#define num_qs    19
#define num_s    9
#define num_o    3

/*-----------*/
/* Lexicons  */
/*-----------*/
static char *actions[num_a] = { "LIKE" , "DISLIKE", "LOVE" , "HATE" , "SLEEP" ,
                               "PUMP", "MAKE" , "WEAR" , "RENOUNCE" , "SHORTEN",
                               "KEEP", "FEED", "DESCEND" , "CONVERT", "KEEP",
                               "OPEN" , "EAT" , "READ" , "DRY" , "KILL" ,
                               "MEND",    "CAUSE", "SIT" , "SAT" , "CLOSE",
                               "TAKE", "RUN" , "JUMP", "SKIP", "HOP", "BREAK",
                               "INVENT", "DISCOVER", "EXPLORE", "PUNCH","KICK",
                               "IGNITE", "CHAT", "WRITE", "TALK","LISTEN",
                               "WHISTLE","SHOUT","SCREAM","DRINK","BATHE",
                               "WASH", "FLY" , "SWIM",
                               "CROSS", "ENTER", "CLIMB", "TIE", "CROUCH",
                               "CRAWL",
                               "BREATHE", "SNIFF", "SPIT" ,"GO","PUT","DRINK",
                               "LOOK", "TURN", "BORN","LEAVE",
                               "SMOKE", "LIGHT", "DESIGN" , "DRAW" ,"PAINT",
                               "BOIL", "STRAIN", "POUR","TELEPHONE","PHONE",
                               "CALL" ,"DRESS", "CUT", "SAW", "GONE","STUDY",
                               "FOLD" , "PLAY", "PAY", "EARN","MEET", "MET",
                               "LIVE","DWELL", "BEEN", "HEAR" , "HEARD",
                               "SING" , "SUNG", "DEVELOP", "STIR","SEE",
                               "SEEN", "RING", "FEEL" , "FELT", "DIED",
                               "HUNT",
                               "ASK", "STAY" ,"KISS", "CUDDLE", "GIVE","BUY",
                               "BOUGHT", "LICK" , "TASTE", "ADMIRE" ,
                               "PROVIDE",
                               "COME", "BUILD", "BUILT", "REFERENCE" ,"WENT",
                               "CONSTRUCT" ,"LEAN","WALK","CARRY","RIDE",
                               "ORBIT",
                               "DRIVE" , "DROVE" , "DOWN", "ALONG", "SHALL",
                               "SHOULD" , "THINK", "THOUGHT","GOT","GET",
                               "LEND",
                               "LENT","HAVE", "HAS" , "HAD","SUGGEST" };

static char *questions[num_qs] = { "WHAT", "WHEN", "HOW", "WHY", "WHERE",
                                   "WHO","DO", "DOES" ,"ARE", "IS", "WAS",
                                   "CAN" ,"AM" ,"WHICH","HAVE" , "WILL" ,
                                   "DID" , "HAS", "SHOULD"};

static char *orders[num_o] = { "USE" , "TELL", "QUIT" };

static char *fillers[num_f] = { "IS", "THE" , "A", "AN" , "ARE" ,"TOO","ALSO",
                            "OF" , "COME" , "FROM" , "ITS" , "THEIR" ,
                            "THAT" , "ABOUT", "THING" , "THIS" , "BECAUSE" };

static char *qualifiers[num_q] = {"FAST", "SLOW", "ROUND", "SQUARE",
                        "HAIRY", "BALD", "FRIGHTENING","INTERESTED",
                        "SHY", "NICE", "HORRIBLE" , "LOVELY","INTERESTING",
                        "PRETTY" , "BEAUTIFUL" , "BLONDE" , "BRUNETTE" ,
                        "SEXY" , "SHINY" , "DULL" , "BORING" , "CAREFUL" ,
                        "HARD" , "SOFT" , "GENTLE" , "QUICK" , "LIGHT",
                        "HEAVY", "RADIOACTIVE", "METALLIC","TIRED",
                        "GASEOUS", "BRIGHT", "SWEET", "SOUR" , "BITTER",
                        "GOOD","BAD","DRUNK","STUPID","MUTUAL" };


static char *sizes[num_s] = {"BIG", "SMALL", "FAT", "THIN", "TALL" ,"SHORT",
                             "LARGE", "PETITE", "TINY" };

static char *colours[num_c] = { "BLACK", "BLUE", "RED", "YELLOW", "ORANGE",
                             "GREEN","BROWN" , "GINGER" , "PURPLE",
                             "MAUVE", "SILVERY", "SILVER", "GOLD", "CYAN" };

static char *numbers[num_n] = { "ALL" , "SOME" , "FEW" , "MANY" , "MOST" ,
                           "COUPLE","NEXT","PREVIOUS","ONE", "TWO", "THREE" ,
                            "FOUR", "EVERY", "LAST",
                           "FIVE", "SIX", "SEVEN","EIGHT", "NINE", "TEN",
                           "ELEVEN","TWELVE", "THIRTEEN","FOURTEEN",
                           "FIFTEEN", "SIXTEEN", "SEVENTEEN", "EIGHTEEN",
                           "NINETEEN","TWENTY", "THIRTY", "FOURTY",
                           "FIFTY", "SIXTY","SEVENTY", "EIGHTY",
                           "NINETY" };


static char *pronouns[num_p] = { "THEM", "THEY", "WE", "HIM", "HER" ,"US" ,
                                 "HE", "I", "YOU" ,"MY", "YOUR" , "ME"};

static char *locations[num_l] = { "IN" , "ON", "BESIDE" , "EAST" , "WEST" ,
                                  "NORTH" ,"SOUTH" , "UNDER" , "UP" ,
                                  "DOWN", "BELOW", "ABOVE", "RIGHT", "LEFT",
                                  "OVER","INTO","AROUND" , "TO", "OFF",
                                  "THERE" };

/*------------*/
/* Work areas */
/*------------*/
char last_subject[80];
char last_object[80];


/* Global variables */
typedef struct {
    char text[240];
    char object[80];
    char description[80];
    char action[80];
}
SUBJECT;

extern char text[];
extern char question[80];
extern char order[80];
extern char object[80];
extern char description[80];
extern char subject[80];
extern char number[80];
extern char action[80];
extern char location[80];
extern char colour[80];
extern char size[80];
extern char who[80];
extern char tense;
extern SUBJECT psubj;
extern SUBJECT predicate;
extern SUBJECT clause1;
extern SUBJECT clause2;
extern SUBJECT clause3;
extern SUBJECT clause4;



/*---------------------*/
/* Function prototypes */
/*---------------------*/
int IS_COLOUR(char *);
int IS_FILLER(char *);
int IS_LOCATION(char *);
int IS_NUMBER(char *);
int IS_PRONOUN(char *);
int IS_SIZE(char *);
int IS_VERB(char *);
void strchg(char *, char *, char *);
void truncstr(char *,int );

/*--------------------*/
/* Start of functions */
/*--------------------*/
static void GET_QUESTION(char *text, char *question)
{
    /* Extract a question from the phrase string */

    char *p;
    char first_word[40];
    char save;
    int n;
    int more;

    strcpy(question,"");

    do
    {
        more = 0;
        p = strpbrk(text," ,.;:?!");
        save = *p;
        *p = 0;
        strcpy(first_word,text);
        *p = save;

        for(n = 0; n < num_qs; n++)
        {
            if (stricmp(first_word,questions[n]) == 0)
            {
                /* store question */
                if (*question)
                    strcat(question," ");
                strcat(question,first_word);
                /* remove question from text */
                if (p)
                {
                    while(strchr(" ,.:;?!",*p) && *p)
                        p++;
                    if (*p)
                        memmove(text,p,strlen(p) + 1);
                }
                else
                    *text = 0;
                more = 1;
            }
        }
    }
    while(more);
}

static void GET_ORDER(char *text, char *order)
{
    /* Extract an order from the phrase string */

    char *p;
    char first_word[40];
    char save;
    int n;

    strcpy(order,"");
    p = strpbrk(text," ,.;:?!");
    save = *p;
    *p = 0;
    strcpy(first_word,text);
    *p = save;

    for(n = 0; n < num_o; n++)
    {
        if (stricmp(first_word,orders[n]) == 0)
        {
            /* store order */
            strcpy(order,first_word);
            /* remove order from text */
            while(strchr(" ,.:;?!",*p))
                p++;
            memmove(text,p,strlen(p) + 1);
            return;
        }
    }
}

static void GET_OBJECT(char *text, char *object, char *action)
{
    /* Obtain object from phrase string */

    char *p;
    int n;
    int ignore;
    char temp[30];
    char copy[250];

    strcpy(copy,text);
    p = strtok(text," ,.;:?!");

    while(p)
    {
        ignore = 0;

        if (stricmp(p,action) == 0)
            ignore = 1;

        if (!ignore)
            ignore = IS_FILLER(p);

        if (!ignore)
            ignore = IS_NUMBER(p);

        if (!ignore)
            ignore = IS_COLOUR(p);

        if (!ignore)
            ignore = IS_SIZE(p);

        if (!ignore)
            ignore = IS_LOCATION(p);

        if (!ignore)
            ignore = IS_PRONOUN(p);

        if (!ignore)
        {
            for (n = 0; n < num_q; n++)
            {
                if (stricmp(p,qualifiers[n]) == 0)
                    ignore = 1;
                else
                {
                    strcpy(temp,qualifiers[n]);
                    if (temp[strlen(temp) - 1] == 'E')
                    {
                        temp[strlen(temp) - 1] = 0;
                        if (temp[strlen(temp) - 1] == 'L')
                            temp[strlen(temp) - 1] = 0;
                    }
                    strcat(temp,"LY");
                    if (stricmp(p,temp) == 0)
                    {
                        ignore = 1;
                        break;
                    }
                }
            }
        }
        if (!ignore)
        {
            for(n = 0; n < num_qs; n++)
            {
                if (stricmp(p,questions[n]) == 0)
                {
                    ignore = 1;
                    break;
                }
            }
        }

        if (!ignore)
        {
            if (stricmp(p,"IT") == 0)
                strcpy(object,last_object);
            else
                strcpy(object,p);
            strcpy(text,copy);
            p = strstr(text,object);
            strcpy(last_object,"");
            if (p)
            {
                /* Capture preceeding preposition */
                p--;
                while(*p == 32)
                    p--;
                while(*p != 32 && p >= text)
                    p--;
                if (*p == 32)
                    p++;
                if (strncmp("THE ",p,4) == 0)
                    strcpy(last_object,"THE ");
                else
                if (strncmp("the ",p,4) == 0)
                    strcpy(last_object,"the ");
            }

            strcat(last_object,object);
            return;
        }

        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);
    strcpy(object,"");
}

static void GET_SUBJECT(char *text, char *subject, char *object,
                        char *action)
{
    /* Obtain subject from phrase string */

    char *p;
    int ignore;
    char copy[250];

    strcpy(copy,text);
    strcpy(last_subject,subject);
    strcpy(subject,"");

    /* Restrict phrase to that part following the object */
    p = strstr(text,object);
    if (p == NULL)
        p = strstr(text,"IT ");
    memmove(text,p,strlen(p) + 1);


    p = strtok(text," ,.;:?!");

    while(p)
    {
        ignore = 0;
        if (stricmp(p,object) == 0)
            ignore = 1;

        if (!ignore)
            ignore = IS_FILLER(p);

        if (!ignore)
            ignore = IS_LOCATION(p);

        if (!ignore)
            ignore = IS_PRONOUN(p);

        if (!ignore)
            ignore = IS_COLOUR(p);

        if (!ignore)
            ignore = IS_SIZE(p);

        if (!ignore)
            ignore = IS_NUMBER(p);

        if (stricmp(p,action) == 0)
            ignore = 1;

        if (!ignore)
        {
            if (stricmp(p,"IT"))
            {
                if (*subject)
                    strcat(subject," ");
                strcat(subject,p);
            }
        }
        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);
}

static void GET_OBJECT_QUALIFY(char *text, char *object_qualify, char *subject)
{
    /* Obtain qualifying statements from phrase string */

    char *p;
    int n;
    int ignore;
    char oq[78];
    char copy[250];
    char temp[30];

    strcpy(copy,text);
    strcpy(object_qualify,"");

    p = strtok(text," ,.;:?!");

    while(p)
    {
        for(n = 0; n < num_q; n++)
        {
            if (stricmp(p,qualifiers[n]) == 0)
            {
                if (*object_qualify)
                    strcat(object_qualify,",");
                strcat(object_qualify,p);
                break;
            }
            else
            {
                strcpy(temp,qualifiers[n]);
                if (temp[strlen(temp) - 1] == 'E')
                {
                    temp[strlen(temp) - 1] = 0;
                    if (temp[strlen(temp) - 1] == 'L')
                        temp[strlen(temp) - 1] = 0;
                }
                strcat(temp,"LY");
                if (stricmp(p,temp) == 0)
                {
                    if (*object_qualify)
                    strcat(object_qualify,",");
                    strcat(object_qualify,p);
                    break;
                }
            }
        }
        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);

    ignore = 0;

    strcpy(oq,object_qualify);

    p = strtok(object_qualify,",");
    while(p)
    {
        if (stricmp(p,subject) == 0)
        {
            ignore = 1;
            break;
        }
        p = strtok(NULL,",");
    }

    strcpy(object_qualify,oq);
    if (!ignore)
    for (n = 0; n < num_q; n++)
    {
        if (stricmp(subject,qualifiers[n]) == 0)
        {
            if (*object_qualify)
                strcat(object_qualify,",");
            strcat(object_qualify,subject);
            strcpy(subject,"");
            break;
        }
    }
}

static void GET_NUMBER(char *text, char *number)
{
    /* Obtain number words from phrase string */

    char *p;
    char copy[250];
    int n;

    strcpy(copy,text);
    strcpy(number,"");

    p = strtok(text," ,.;:?!");

    while(p)
    {
        for(n = 0; n < num_n; n++)
        {
            if (stricmp(p,numbers[n]) == 0)
            {
                if (*number)
                    strcat(number,",");
                strcat(number,p);
                break;
            }
        }
        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);
}

static void GET_WHO(char *text, char *who)
{
    /* Obtain first or second person references from phrase string */

    char *p;
    char copy[250];
    int n;

    strcpy(copy,text);
    strcpy(who,"");

    p = strtok(text," ,.;:?!");

    while(p)
    {
        for(n = 0; n < num_p; n++)
        {
            if (stricmp(p,pronouns[n]) == 0)
            {
                if (*who)
                    strcat(who,",");
                strcat(who,p);
                break;
            }
        }
        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);
}

static void GET_COLOUR(char *text, char *colour)
{
    /* Obtain colour words from the phrase string */

    char *p;
    char copy[250];
    int n;

    strcpy(copy,text);
    strcpy(colour,"");

    p = strtok(text," ,.;:?!");

    while(p)
    {
        for(n = 0; n < num_c; n++)
        {
            if (stricmp(p,colours[n]) == 0)
            {
                if (*colour)
                    strcat(colour,",");
                strcat(colour,p);
                break;
            }
        }
        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);
}

static void GET_SIZE(char *text, char *size)
{
    /* Obtain size description words from the phrase string */

    char *p;
    char copy[250];
    int n;

    strcpy(copy,text);
    strcpy(size,"");

    p = strtok(text," ,.;:?!");

    while(p)
    {
        for(n = 0; n < num_s; n++)
        {
            if (stricmp(p,sizes[n]) == 0)
            {
                if (*size)
                    strcat(size,",");
                strcat(size,p);
                break;
            }
        }
        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);
}

static void GET_LOCATION(char *text, char *location)
{
    /* Obtain location description words from the phrase string */

    char *p;
    char copy[250];
    int n;

    strcpy(copy,text);
    strcpy(location,"");

    p = strtok(text," ,.;:?!");

    while(p)
    {
        for(n = 0; n < num_l; n++)
        {
            if (stricmp(p,locations[n]) == 0)
            {
                if (*location)
                    strcat(location,",");
                strcat(location,p);
                break;
            }
        }
        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);
}

static void GET_ACTION(char *text , char *action)
{
    /* Obtain action words from the phrase string */

    char *p;
    char n;
    char word[50];
    char pre[50];
    char copy[250];

    strcpy(copy,text);
    strcpy(action,"");
    strcpy(pre,"");

    p = strtok(text," ,.;:?!");

    while(p)
    {
        for(n = 0; n < num_a; n++)
        {
            strcpy(word,actions[n]);
            if (stricmp(p,word) == 0)
            {
                if (stricmp(pre,"THE"))
                {
                    if (*action)
                        strcat(action,",");
                    strcat(action,p);
                }
                break;
            }
            else
            {
                /* Past tense */
                if (word[strlen(word) - 1] == 'E')
                    strcat(word,"D");
                else
                if (word[strlen(word) - 1] == 'Y')
                {
                    /* Irregular words! */
                    if (stricmp(word,"PAY") == 0)
                        strcpy(word,"PAID");
                    else
                    if (stricmp(word,"PLAY") == 0)
                        strcpy(word,"PLAYED");
                    else
                    {
                        word[strlen(word) - 1] = 0;
                        strcat(word,"IED");
                    }
                }
                else
                    strcat(word,"ED");
                if (stricmp(p,word) == 0)
                {
                    if (*action)
                        strcat(action,",");
                    strcat(action,p);
                    break;
                }
                else
                {
                    /* Plural */
                    strcpy(word,actions[n]);
                    strcat(word,"S");
                    if (stricmp(p,word) == 0)
                    {
                        if (stricmp(pre,"THE"))
                        {
                            if (*action)
                                strcat(action,",");
                            strcat(action,p);
                            break;
                        }
                    }
                    else
                    {
                        /* Active */
                        strcpy(word,actions[n]);
                        strcat(word,"ING");
                        if (stricmp(p,word) == 0)
                        {
                            if (stricmp(pre,"THE"))
                            {
                                if (*action)
                                    strcat(action,",");
                                strcat(action,p);
                                break;
                            }
                        }
                    }
                }
            }
        }
        strcpy(pre,p);
        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);
}

static int IS_FILLER(char *p)
{
    /* Return 1 if the word is a filler word */

    char n;

    for(n = 0; n < num_f; n++)
    {
        if (stricmp(p,fillers[n]) == 0)
            return(1);
    }

    return(0);
}

static int IS_NUMBER(char *p)
{
    /* Return 1 if the word is a number */

    int n;

    for(n = 0; n < num_n; n++)
    {
        if (stricmp(p,numbers[n]) == 0)
            return(1);
    }
    return(0);
}

static int IS_COLOUR(char *p)
{
    /* Return 1 if the word is a colour */

    char n;

    for(n = 0; n < num_c; n++)
    {
        if (stricmp(p,colours[n]) == 0)
            return(1);
    }
    return(0);
}

static int IS_SIZE(char *p)
{
    /* Return 1 if the word is a size description word */

    char n;

    for(n = 0; n < num_s; n++)
    {
        if (stricmp(p,sizes[n]) == 0)
            return(1);
    }
    return(0);
}

static int IS_LOCATION(char *p)
{
    /* Return 1 if the word is a location description */

    char n;

    for(n = 0; n < num_l; n++)
    {
        if (stricmp(p,locations[n]) == 0)
            return(1);
    }
    return(0);
}


static int IS_PRONOUN(char *p)
{
    /* Return 1 if the word is a first or second person reference */

    char n;

    for(n = 0; n < num_p; n++)
    {
        if (stricmp(p,pronouns[n]) == 0)
            return(1);
    }
    return(0);
}


void NLP()
{
    /* Parses a simple phrase and returns the component parts */

    char *p;
    char *q;

    GET_QUESTION(text, question);
    GET_ACTION(text , action);
    GET_ORDER(text,order);
    GET_OBJECT(text,object,action);
    GET_SUBJECT(text, subject, object, action);
    GET_WHO(text,who);
    GET_OBJECT_QUALIFY(text, description, subject);
    GET_COLOUR(text,colour);
    GET_SIZE(text,size);
    GET_NUMBER(text , number);
    GET_LOCATION(text,location);

    if (strstr(question,"WAS"))
        tense = 1;
    else
    if (strstr(question,"IS"))
        tense = 2;
    else
    if (strstr(question,"DO"))
        tense = 2;
    else
    if (strstr(question,"IS"))
        tense = 2;
    else
    if (strstr(question,"DID"))
        tense = 1;
    else
    if (strstr(text,"GOT "))
        tense = 2;
    else
    if (strstr(question,"WILL"))
        tense = 3;
    else
        tense = 0;

    if (tense == 0)
    {
        /* Perhaps action gives a clue */
        if (*action)
        {
            if (strchr(action,',') == NULL)
            {
                p = &action[strlen(action) - 2];
                if (stricmp(p,"ED") == 0)
                    tense = 1;
            }
            else
            {
                q = action;
                do
                {
                    p = strchr(q,',');
                    q = p - 1;
                    if (*q == 'S')
                    {
                        tense = 2;
                        break;
                    }
                    q--;
                    if (stricmp(q,"ED") == 0)
                    {
                        tense = 1;
                        break;
                    }
                    q = p + 1;
                }
                while(p);
            }
        }
    }
}

/* --------------------------------------------------------- */
/* Toolbox functions for working with conversational strings */
/* --------------------------------------------------------- */
void truncstr(char *p,int num)
{
    /* Truncate string by losing last num characters */
    if (num < strlen(p))
        p[strlen(p) - num] = 0;
}

void trim(char *text)
{
    /* remove trailing spaces */
    char *p;

    p = &text[strlen(text) - 1];
    while(*p == 32 && p >= text)
        *p-- = 0;
}

void strlench(char *p,int num)
{
    /* Change length of string by adding or deleting characters */

    if (num > 0)
        memmove(p + num,p,strlen(p) + 1);
    else
    {
        num = 0 - num;
        memmove(p,p + num,strlen(p) + 1);
    }
}

void strins(char *p, char *q)
{
    /* Insert string q into p */
    strlench(p,strlen(q));
    strncpy(p,q,strlen(q));
}

void strchg(char *data, char *s1, char *s2)
{
    /* Replace all occurences of s1 with s2 */
    char *p;
    char changed;

    do
    {
        changed = 0;
        p = strstr(data,s1);
        if (p)
        {
            /* Delete original string */
            strlench(p,0 - strlen(s1));

            /* Insert replacement string */
            strins(p,s2);
            changed = 1;
        }
    }
    while(changed);
}

char *strftos(char *data)
{
    /* convert first person to second person */
    /* I ---------> YOU */
    /* ME --------> YOU */
    /* I AM ------> YOU ARE */
    /* YOUR ------> MY */
    /* MY --------> YOUR */
    /* YOU ARE ---> I AM */
    /* YOU -------> I */
    /* I WAS -----> YOU WERE */
    /* YOU WERE --> I WAS */

    char *copy;
    char *p;
    char *q;
    char *r;

    strcat(data," ");

    /* Allocate space for a copy of the string */
    copy = malloc(strlen(data) * 2);
    if (copy == NULL)
        return NULL;
    strcpy(copy,"");

    /* Check first phrase */
    p = data;
    do
    {
        if (strncmp(p,"I AM",4) == 0)
        {
            q = &p[4];
            if (ispunct(*q) || *q == 0 || *q == 32)
            {
                strcat(copy,"YOU ARE ");
                copy[strlen(copy) - 1] = *q;
            }
            p += 5;
        }
        else
        if (strncmp(p,"I am",4) == 0)
        {
            q = &p[4];
            if (ispunct(*q) || *q == 0 || *q == 32)
            {
                if (p == data)
                    strcat(copy,"You are ");
                else
                {
                    p--;
                    if (*p == '.')
                        strcat(copy,"You are ");
                    else
                        strcat(copy,"you are ");
                    p++;
                }
                copy[strlen(copy) - 1] = *q;
                p += 5;
            }
        }
        else
        if (strncmp(p,"I WAS ",6) == 0)
        {
            strcat(copy,"YOU WERE ");
            p += 6;
        }
        else
        if (strncmp(p,"I was ",6) == 0)
        {
            if (p == data)
                strcat(copy,"You were ");
            else
            {
                p--;
                if (*p == '.')
                    strcat(copy,"You were ");
                else
                    strcat(copy,"you were ");
                p++;
            }
            p += 6;
        }
        else
        if (strncmp(p,"YOU ARE",7) == 0)
        {
            q = &p[7];
            if (ispunct(*q) || *q == 0 || *q == 32)
            {
                strcat(copy,"I AM ");
                copy[strlen(copy) - 1] = *q;
                p += 8;
            }
        }
        else
        if (strncmp(p,"You are",7) == 0)
        {
            q = &p[7];
            if (ispunct(*q) || *q == 0 || *q == 32)
            {
                strcat(copy,"I am ");
                copy[strlen(copy) - 1] = *q;
                p += 8;
            }
        }
        else
        if (strncmp(p,"you are",7) == 0)
        {
            q = &p[7];
            if (ispunct(*q) || *q == 0 || *q == 32)
            {
                strcat(copy,"I am ");
                copy[strlen(copy) - 1] = *q;
                p += 8;
            }
        }
        else
        if (strncmp(p,"YOU WERE ",9) == 0)
        {
            strcat(copy,"I WAS ");
            p += 9;
        }
        else
        if (strncmp(p,"You were ",9) == 0)
        {
            strcat(copy,"I was ");
            p += 9;
        }
        else
        if (strncmp(p,"you were ",9) == 0)
        {
            strcat(copy,"I was ");
            p += 9;
        }
        else
        if (strncmp(p,"I ",2) == 0)
        {
            /* Is the next word uppercase ? */
            if (p == data)
            {
                q = p;
                q++;
                while(*q == 32)
                    q++;
                if(islower(*q))
                    strcat(copy,"You ");
                else
                    strcat(copy,"YOU ");
            }
            else
            {
                q = p;
                q++;
                while(*q == 32)
                    q++;
                if(islower(*q))
                    strcat(copy,"you ");
                else
                    strcat(copy,"YOU ");
            }
            p += 2;
        }
        else
        if (strncmp(p,"YOU",3) == 0)
        {
            r = &p[3];
            if (ispunct(*r) || *r == 0 || *r == 32)
            {
                if (p == data)
                    strcat(copy,"I ");
                else
                {
                    q = p;
                    while(*q != '.' && q >= data)
                    {
                        q--;
                        if (*q != 32)
                            break;
                    }
                    if (q == data || *q == '.')
                        strcat(copy,"I ");
                    else
                        strcat(copy,"ME ");
                }
                copy[strlen(copy) - 1] = *r;
                p += 4;
            }
        }
        else
        if (strncmp(p,"You",3) == 0)
        {
            r = &p[3];
            if (ispunct(*r) || *r == 0 || *r == 32)
            {
                strcat(copy,"I ");
                p += 4;
                copy[strlen(copy) -1] = *r;
            }
        }
        else
        if (strncmp(p,"you",3) == 0)
        {
            r = &p[3];
            if (ispunct(*r) || *r == 0 || *r == 32)
            {
                strcat(copy,"me ");
                p += 4;
                copy[strlen(copy) -1] = *r;
            }
        }
        else
        if (strncmp(p,"ME ",3) == 0)
        {
            strcat(copy,"YOU ");
            p += 3;
        }
        else
        if (strncmp(p,"Me ",3) == 0)
        {
            strcat(copy,"You ");
            p += 3;
        }
        else
        if (strncmp(p,"me ",2) == 0)
        {
            strcat(copy,"you ");
            p += 3;
        }
        else
        if (strncmp(p,"YOUR ",5) == 0)
        {
            strcat(copy,"MY ");
            p += 5;
        }
        else
        if (strncmp(p,"Your ",5) == 0)
        {
            strcat(copy,"My ");
            p += 5;
        }
        else
        if (strncmp(p,"your ",5) == 0)
        {
            strcat(copy,"my ");
            p += 5;
        }
        else
        if (strncmp(p,"MY ",3) == 0)
        {
            strcat(copy,"YOUR ");
            p += 3;
        }
        else
        if (strncmp(p,"My ",3) == 0)
        {
            strcat(copy,"Your ");
            p += 3;
        }
        else
        if (strncmp(p,"my ",3) == 0)
        {
            strcat(copy,"your ");
            p += 3;
        }
        else
        if (strncmp(p,"ME.",3) == 0)
        {
            strcat(copy,"YOU.");
            p += 3;
        }
        else
        if (strncmp(p,"Me.",3) == 0)
        {
            strcat(copy,"You.");
            p += 3;
        }
        else
        if (strncmp(p,"me.",2) == 0)
        {
            strcat(copy,"you.");
            p += 3;
        }
        else
        if (strncmp(p,"YOU.",4) == 0)
        {
            strcat(copy,"ME.");
            p += 4;
        }
        else
        if (strncmp(p,"You.",4) == 0)
        {
            strcat(copy,"Me.");
            p += 4;
        }
        else
        if (strncmp(p,"you.",4) == 0)
        {
            strcat(copy,"me.");
            p += 4;
        }

        q = ©[strlen(copy)];
        /* Step to next word */
        while(*p && *p != 32)
            *q++ = *p++;

        while(strchr(" ,.:;?!",*p) && *p)
            *q++ = *p++;
        *q = 0;
    }
    while(*p);

    /* Remove trailing space */
    truncstr(copy,1);

    p = strrchr(copy,32);
    if (p)
    {
        p++;
        if (stricmp(p,"I") == 0)
        {
            truncstr(copy,1);
            if (isupper(copy[2]))
                strcat(copy,"ME");
            else
                strcat(copy,"me");
        }
    }
    /* Transfer copy to data */
    strcpy(data,copy);

    /* Free memory */
    free(copy);
    return data;
}

void strcor(char *data)
{
    /* Correct common gramatical errors */
    char *p;

    p = strstr(data,"WHY YOU DO NOT");
    if (p)
        strncpy(p,"WHY DO YOU NOT ",14);

    p = strstr(data,"WHY ME DO NOT");
    if (p)
    {
        strlench(p,-1);
        strncpy(p,"WHY DO I NOT ",13);
    }
}

void strwhy(char *data)
{
    /* Change data into a question */
    char *p;

    p = data;
    strins(data,"WHY ");
    strcor(data);

    /* Remove any text following the first part of the string */
    p = strpbrk(data,",.:;!");
    if (p)
        *p = 0;

    p = data;
    while(*p && *p != '.' && *p != '!')
        p++;
    if (*p)
        *p = '?';
    else
        strins(p,"? ");
}

char *strrstr(char *s1, char *s2)
{
    /* find occurence of s2 in s1, and point to first word after it */
    char *p;

    p = strstr(s1,s2);
    if (p)
    {
        p += strlen(s2);
        while(*p == 32)
            p++;
    }
    return(p);
}

void strsing(char *word)
{
    char *p;

    /* Convert plurals to singular */

    if (strcmp(word,"GAS") == 0)
        return;
    if (strcmp(word,"GASSES") == 0)
    {
        strcpy(word,"GAS");
        return;
    }

    if (word[strlen(word) - 1] == 'S' && word[strlen(word) - 2] != 'S')
    {
        word[strlen(word) - 1] = 0;

        /* Words ending IES, convert to Y in the singular */
        p = &word[strlen(word) - 2];
        if (strcmp(p,"IE") == 0)
            strcpy(p,"Y");
        else
        /* Words ending OUS are NOT plurals! */
        if (strcmp(p,"OU") == 0)
            strcat(word,"S");
        else
        {
            /* Words ending MEN are MAN in the singular */
            p--;
            if (strcmp(p,"MEN") == 0)
                strcpy(p,"MAN");
        }
    }
}


/* PREDICATE CLAUSE ANALYSIS */
/*--------------------*/
/* Start of functions */
/*--------------------*/
static void GET_POBJECT(char *text, char *object)
{
    /* Copy NOUN into object */

    char *p;
    int n;
    int ignore;
    char temp[30];
    char copy[250];
    char previous[50];

    strcpy(copy,text);
    strcpy(object,"");
    strcpy(previous,"");
    p = strtok(text," ,.;:?!");

    while(p)
    {
        ignore = IS_LOCATION(p);

        if (!ignore)
            ignore = IS_FILLER(p);

        if (!ignore)
        {
            for (n = 0; n < num_q; n++)
            {
                if (stricmp(p,qualifiers[n]) == 0)
                    ignore = 1;
                else
                {
                    strcpy(temp,qualifiers[n]);
                    if (temp[strlen(temp) - 1] == 'E')
                    {
                        temp[strlen(temp) - 1] = 0;
                        if (temp[strlen(temp) - 1] == 'L')
                            temp[strlen(temp) - 1] = 0;
                    }
                    strcat(temp,"LY");
                    if (stricmp(p,temp) == 0)
                    {
                        ignore = 1;
                        break;
                    }
                }
            }
        }
        if (!ignore)
        {
            for(n = 0; n < num_qs; n++)
            {
                if (stricmp(p,questions[n]) == 0)
                {
                    ignore = 1;
                    break;
                }
            }
            /* We must also ignore words such as "CAN" and "MAY" and "DO" */
            if (stricmp(p,questions[n]) == 0)
                {
                    ignore = 1;
                    break;
                }
        }
        if (!ignore)
        {
            /* We must also ignore words such as "CAN" and "MAY" and "DO" */
            if (stricmp(p,"CAN") == 0 || stricmp(p,"WILL") == 0 ||
                stricmp(p,"MAY") == 0 || stricmp(p,"DO") == 0 ||
                 stricmp(p,"SHALL") == 0 || stricmp(p,"SHOULD") == 0)
                    ignore = 1;
        }
        if (!ignore)
            if (IS_VERB(p))
                ignore = 1;

        if (stricmp(p,"OF") == 0)
        {
            if (*object)
                strcat(object," ");
            strcat(object,"OF");
            strcat(object,previous);
            strcpy(previous,"");
        }
        else
        if(!ignore)
        {
            if (*object)
                strcat(object," ");
            strcat(object,p);
        }

        if (ignore && stricmp(p,"OF") != 0)
            strcpy(previous,p);
        else
            strcpy(previous,"");

        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);
}

static void GET_DESCRIPTION(char *text, char *object_qualify)
{
    /* Obtain qualifying statements from phrase string */

    char *p;
    int n;
    char copy[250];
    char temp[50];
    char previous[50];

    strcpy(copy,text);
    strcpy(object_qualify,"");
    strcpy(previous,"");

    p = strtok(text," ,.;:?!");

    while(p)
    {
        for(n = 0; n < num_q; n++)
        {
            if (stricmp(p,qualifiers[n]) == 0)
            {
                if (*object_qualify)
                    strcat(object_qualify," ");
                strcat(object_qualify,p);
                break;
            }
            else
            {
                strcpy(temp,qualifiers[n]);
                if (temp[strlen(temp) - 1] == 'E')
                {
                    temp[strlen(temp) - 1] = 0;
                    if (temp[strlen(temp) - 1] == 'L')
                        temp[strlen(temp) - 1] = 0;
                }
                strcat(temp,"LY");
                if (stricmp(p,temp) == 0)
                {
                    if (*object_qualify)
                        strcat(object_qualify," ");
                    strcat(object_qualify,p);
                    break;
                }
            }
        }
        if (stricmp(previous,"THE") == 0 || stricmp(previous,"A") == 0 ||
            stricmp(previous,"AN") == 0 || stricmp(previous,"OF") == 0)
        {
            if (IS_VERB(p))
            {
                if (*object_qualify)
                    strcat(object_qualify," ");
                strcat(object_qualify,p);
            }
        }
        strcpy(previous,p);
        p = strtok(NULL," ,.;:?!");
    }
    strcpy(text,copy);
}

static int IS_DESCRIPTION(char *p)
{
    int n;

    for(n = 0; n < num_q; n++)
    {
        if (stricmp(p,qualifiers[n]) == 0)
            return 1;
    }
    return 0;
}


void GET_PREDICATE(char *data,char *predicate,char *psubj)
{
    /* Splits a sentence into its two parts */

    char *p;
    char *q;
    char word[40];
    char save;
    char previous[40];

    strcpy(predicate,data);
    strcpy(psubj,"");


    p = data;
    q = p;

    do
    {
        p = strpbrk(p," .,;:?!");
        if (p)
        {
            save = *p;
            *p = 0;
            strcpy(word,q);
            *p = save;
            p++;
            if (stricmp(word,"IT") == 0)
            {
                save = *p;
                *p = 0;
                strcpy(predicate,data);
                *p = save;
                strcpy(psubj,p);
                return;
            }
            if (stricmp(word,"ARE") == 0 && q != data)
            {
                save = *q;
                *q = 0;
                strcpy(predicate,data);
                strcpy(predicate,data);
                *q = save;
                strcpy(psubj,q);
                return;
            }
            if (stricmp(word,"MOST") == 0)
            {
                save = *p;
                *p = 0;
                strcpy(predicate,data);
                *p = save;
                strcpy(psubj,p);
                return;
            }

            if ((stricmp(word,"IS") == 0 || stricmp(word,"CAN") == 0 ||
            stricmp(word,"WILL") == 0 || stricmp(word,"MAY") == 0 ||
            stricmp(word,"THAT") == 0 || stricmp(word,"WAS") == 0 ||
            stricmp(word,"DID") == 0  || stricmp(word,"SHALL") == 0 ||
            stricmp(word,"SHOULD") == 0 || stricmp(word,"DO") == 0) &&
            q == data)
            {
                /* Situations such as IS A LION A CAT? */

                /* Skip the next word */
                p = strpbrk(p," .,;:?!");
                if (p)
                {
                    while(*p == 32)
                        p++;
                    q = p;

                    do
                    {
                        p = strpbrk(p," .,;:?!");
                        if (p)
                        {
                            save = *p;
                            *p = 0;
                            strcpy(word,q);
                            /* Is this word a filler? */
                            if(IS_FILLER(word) || IS_VERB(word))
                            {
                                *p = save;
                                save = *q;
                                *q = 0;
                                strcpy(predicate,data);
                                *q = save;
                                strcpy(psubj,q);
                                return;
                            }
                            *p = save;
                            while(*p == 32)
                                p++;
                            q = p;
                        }
                    }
                    while(p);
                }
            }

            if (stricmp(word,"IS") == 0 || stricmp(word,"CAN") == 0 ||
            stricmp(word,"WILL") == 0 || stricmp(word,"AM") == 0 ||
            stricmp(word,"TO") == 0 || stricmp(word,"FOR") == 0 ||
            stricmp(word,"MAY") == 0 || stricmp(word,"THAT") == 0 ||
            stricmp(word,"WAS") == 0 || stricmp(word,"DID") == 0  ||
            stricmp(word,"DIDN'T") == 0 || stricmp(word,"IN") == 0 ||
            stricmp(word,"WITH") == 0 || stricmp(word,"SHALL") == 0 ||
            stricmp(word,"SHOULD") == 0)
            {
                if (q != data)
                {
                    save = *q;
                    *q = 0;
                    strcpy(predicate,data);
                    *q = save;
                    strcpy(psubj,q);
                    return;
                }
            }

            if (stricmp(previous,"THE") && stricmp(previous,"A") &&
                stricmp(previous,"AN") && stricmp(previous,"OF") &&
                stricmp(previous,"SHALL") && stricmp(word,"SHOULD") &&
                stricmp(previous,"WILL"))
            {
                if (q != data)
                {
                    if(!IS_FILLER(word) && !IS_LOCATION(word) &&
                       !IS_DESCRIPTION(word) && !IS_VERB(word) &&
                       !IS_FILLER(previous) && !IS_LOCATION(previous) &&
                       !IS_DESCRIPTION(previous) && !IS_VERB(previous))
                    {
                        save = *q;
                        *q = 0;
                        strcpy(predicate,data);
                        *q = save;
                        strcpy(psubj,q);
                        return;
                    }

                    if(IS_VERB(word))
                    {
                        /* Ignore verbs starting a clause/phrase */
                        /* If the previous word is a noun */
                        /* and we are scanning the predicate */
                        /* return from the previous word */
                        /* Ignore the verb if the previous word is an adverb */

                        if (stricmp(previous,"DO") && stricmp(previous,"NOT") &&
                            stricmp(previous,"DON'T") &&
                            stricmp(previous,"TO"))
                        {
                            save = *q;
                            *q = 0;
                            strcpy(predicate,data);
                            *q = save;
                            strcpy(psubj,q);
                            return;
                        }
                    }
                }
            }
        }
        save = *p;
        *p = 0;
        strcpy(previous,q);
        q = &previous[strlen(previous)-1];
        while(strchr(" .,;:!?",*q))
            *q-- = 0;

        *p = save;
        q = p;
    }
    while(p);
}

int IS_VERB(char *word)
{
    int n;
    char verb[40];

    for(n = 0; n < num_a; n++)
    {
        strcpy(verb,actions[n]);
        if (stricmp(word,verb) == 0)
        {
            return 1;
        }
        /* Past tense */
        if (verb[strlen(verb) - 1] == 'E')
            strcat(verb,"D");
        else
        if (verb[strlen(verb) - 1] == 'Y')
        {
            /* Irregular words! */
            if (stricmp(verb,"PAY") == 0)
                strcpy(verb,"PAID");
            else
            if (stricmp(verb,"PLAY") == 0)
                strcpy(verb,"PLAYED");
            else
            {
                verb[strlen(verb) - 1] = 0;
                strcat(verb,"IED");
            }
        }
        else
            strcat(verb,"ED");
        if (stricmp(word,verb) == 0)
        {
            return 1;
        }
        /* Plurals */
        strcpy(verb,actions[n]);
        strcat(verb,"S");
        if (stricmp(word,verb) == 0)
        {
            return 1;
        }
        /* ING */
        strcpy(verb,actions[n]);
        if (verb[strlen(verb) - 1] == 'E')
            truncstr(verb,1);
        strcat(verb,"ING");
        if (stricmp(word,verb) == 0)
        {
            return 1;
        }
    }
    return 0;
}

void PREDPAR()
{
    char *p;

    GET_PREDICATE(text,psubj.text,predicate.text);
    GET_ACTION(psubj.text,psubj.action);
    GET_POBJECT(psubj.text,psubj.object);
    GET_DESCRIPTION(psubj.text,psubj.description);
    GET_PREDICATE(predicate.text,text,clause1.text);
    if (*clause1.text)
    {
        p = strstr(predicate.text,clause1.text);
        *p = 0;
    }
    GET_ACTION(predicate.text,predicate.action);
    GET_POBJECT(predicate.text,predicate.object);
    GET_DESCRIPTION(predicate.text,predicate.description);
    GET_PREDICATE(clause1.text,text,clause2.text);
    if (*clause2.text)
    {
        p = strstr(clause1.text,clause2.text);
        *p = 0;
    }
    GET_ACTION(clause1.text,clause1.action);
    GET_POBJECT(clause1.text,clause1.object);
    GET_DESCRIPTION(clause1.text,clause1.description);
    GET_PREDICATE(clause2.text,text,clause3.text);
    if (*clause3.text)
    {
        p = strstr(clause2.text,clause3.text);
        *p = 0;
    }
    GET_ACTION(clause2.text,clause2.action);
    GET_POBJECT(clause2.text,clause2.object);
    GET_DESCRIPTION(clause2.text,clause2.description);
    GET_PREDICATE(clause3.text,text,clause4.text);
    if (*clause4.text)
    {
        p = strstr(clause3.text,clause4.text);
        *p = 0;
    }
    GET_ACTION(clause3.text,clause3.action);
    GET_POBJECT(clause3.text,clause3.object);
    GET_DESCRIPTION(clause3.text,clause3.description);
    GET_ACTION(clause4.text,clause4.action);
    GET_POBJECT(clause4.text,clause4.object);
    GET_DESCRIPTION(clause4.text,clause4.description);
}