You are on page 1of 230

CONVERSATIONS WITH MY COMPUTER A HANDBOOK OF NATURAL LANGUAGE PROCESSING AND CONVERSATIONAL COMPUTING

Written by Matthew Probert

CONTENTS SLOT AND FRAME PARSING SCANNING SYNTACTICAL STRUCTURES HUMAN PARSING COMMON PHRASES COMPONENT PARSING QUALIFIERS AND AMBIGUITY UNDERSTANDING AND RESPONDING GEORGE INTELLIGENCE THROUGH LEARNING TOOLS APPENDIX NOTES FOR PROGRAMMERS

INTRODUCTION

While some were introduced to the idea of talking to computers through Arthur C. Clarke's book "2001", I was weaned on Star Trek. I make no apology for never having finished reading 2001. Nor for never sitting all the way through the film that followed, "2001, A Space Odyssey". The fact is I find them both rather tedious! However, an interesting point comes out. Both readers of 2001 and audiences attending the film made few criticisms of HAL the talking computer. It would appear that most people take the idea of understanding their natural language for granted. In her book "Artificial Intelligence and Natural Man" Margaret Boden is some what scathing of people's naivety in accepting the idea that a computer can understand human language, even with its subtle variations. But in fairness to the public, Margaret, how many people in 1977 had seen a computer? Let alone learnt how to program one. The reason people didn't wonder at HAL's quite extraordinary powers of comprehension was that they did not see anything strange in them. "If I can understand English, and a computer is an electronic brain, then of course it can understand." Might go the reasoning. Later audiences were treated to the idea of confusing computers. This idea was used in Star Trek to defeat several unpleasant computers whose logic had got the better of their human companions, and in Doctor Who where a certain computer (in "the Green Death") was particularly unpleasant. This latter computer was destroyed following being asked a paradoxical question. The public were awakening to the idea that computer's may be bright, but they're not brilliant! Enough of science fiction, what of the reality? The matter is that people are fascinated by the idea of "thinking machines". And what better sort of thinking machine than one that can converse in your own language? During the 1960s a lot of work was undertaken by the psychology and psychiatric professions in modelling neurosis with computer simulations. In 1962 K.M. Colby, a psychoanalyst, attempted to model free association in psychotherapy with a computer system he called a "simulation of a neurotic process". The computer modelled a woman who believes that her father has abandoned her, but cannot consciously accept that she hates him. The computer operator plays the role of the psychotherapist with this system. In his book "Experimental Treatment of Neurotic Computer Programs", K.M. Colby details the following interaction between the computer and the therapist; computer: FATHER IS INDIFFERENT TO ME. MOTHER SAYS FATHER ABANDONED US. FATHER PREFERRED SISTER.

I AVOID FATHER. SISTER ADMIRED FATHER. I FEEL TENSE NOW. therapist: YOU ADMIRE FATHER. computer: NO. I DO NOT ADMIRE FATHER. I ADMIRE AL. MOTHER OUGHT NOT TO ADMIRE AL. MOTHER DETESTS FATHER. Colby's hope in developing this computer model was that it would be of use in studying the ways in which belief systems and defensive habits of thought can be modified by particular conceptual interventions by, say, a therapist. I wonder? Another medical type artificial intelligence computer system was called ELIZA. ELIZA was developed by Joseph Weizenbaum to model a psychiatrist. The computer operator may then take the role of a patient and talk with the program. In his book "ELIZA", Weizenbaum details the following interaction between ELIZA and a human operator; operator: MEN ARE ALL ALIKE eliza: operator eliza: IN WHAT WAY THEY'RE ALWAYS BUGGING US ABOUT SOMETHING OR OTHER. CAN YOU THINK OF A SPECIFIC EXAMPLE

operator: WELL, MY BOYFRIEND MADE ME COME HERE. eliza: YOUR BOYFRIEND MADE YOU COME HERE

operator: HE SAYS I'M DEPRESSED MUCH OF THE TIME. eliza: I AM SORRY TO HEAR THAT YOU ARE DEPRESSED

operator: IT'S TRUE. I AM UNHAPPY. eliza: DO YOU THINK COMING HERE WILL HELP YOU NOT TO BE UNHAPPY

operator: I NEED SOME HELP, THAT MUCH SEEMS CERTAIN. eliza: WHAT WOULD IT MEAN TO YOU IF YOU GOT SOME HELP

operator: PERHAPS I COULD LEARN TO GET ALONG WITH MY MOTHER. eliza: TELL ME MORE ABOUT YOUR FAMILY

operator: MY MOTHER TAKES CARE OF ME. eliza: WHO ELSE IN YOUR FAMILY TAKES CARE OF YOU

operator: MY FATHER. eliza: YOUR FATHER

operator: YOU ARE LIKE MY FATHER IN SOME WAYS. eliza: WHAT RESEMBLANCE DO YOU SEE

operator: YOU ARE NOT VERY AGGRESSIVE BUT I THINK YOU DON'T WANT ME TO NOTICE THAT. eliza: WHAT MAKES YOU THINK I AM NOT VERY AGGRESSIVE

operator: YOU DON'T ARGUE WITH ME. eliza: WHY DO YOU THINK I DON'T ARGUE WITH YOU

operator: YOU ARE AFRAID OF ME. eliza: DOES IT PLEASE YOU TO THINK I AM AFRAID OF YOU

operator: MY FATHER IS AFRAID OF EVERYBODY. eliza: WHAT ELSE COMES TO MIND WHEN YOU THINK OF YOUR FATHER

operator: BULLIES. eliza: DOES THAT HAVE ANYTHING TO DO WITH THE FACT THAT YOUR BOYFRIEND MADE YOU COME HERE

Exponents of ELIZA point out that it can talk about any subject. Critics may notice that it does not always talk very intelligently! But ELIZA did trick a salesman at the computer company. Not a very difficult task perhaps, but here anyway is a transcript of the conversation that is reported to have taken place by teletype. man: DO YOU THINK THAT I CAN USE THE COMPUTER THIS MORNING?

teletype: WHY DO YOU ASK? man: I MIGHT BE ABLE TO MAKE SOME ADDITIONAL TELCOMP SALES.

teletype: WHY AREN'T YOU SURE? man: MY PROSPECTIVE CUSTOMERS HAVE NEVER SEEN A DEMONSTRATION OF THE SYSTEM.

teletype: DOES THIS MEAN VERY MUCH TO YOU? man: OF COURSE IT DOES.

teletype: YOU SEEM VERY POSITIVE ABOUT THAT. man: THIS IS RIDICULOUS.

teletype: WHAT DO YOU MEAN THIS IS RIDICULOUS? man: IS THERE ANY REASON WHY I CAN'T USE THE COMPUTER NOW?

teletype: TELL ME MORE ABOUT YOUR PROSPECTIVE CUSTOMERS. man: PLEASE DIAL ME UP ON 491-1850.

From the anger apparent in the salesman's responses, he may not have realised that he was talking to a computer. But he certainly knew that who ever he was talking to was fairly obtuse.

SLOT AND FRAME PARSING

The simplest way in which a computer program can respond to supplied natural language is with a slot-and-frame parser. At its most primitive level the slot-and-frame approach simply compares the supplied text with known phrases. This approach was used in early adventure game programs. Anyone who played any of the first adventure games will be familiar with typing in phrases like; "GO EAST" and "TAKE LAMP".

GET INPUT PHRASE \/ INITIALISE COUNTER \/ > COMPARE INPUT PHRASE WITH LIBRARY PHRASE \/ NO<MATCH?>YES /\ \/ \/ INCREMENT COUNTER PRINT RESPONSE \/ NOHAS COUNTER \/ REACHED LIMIT? \/ PRINT YES>I DON'T >STOP UNDERSTAND Flow diagram of a slot-and-frame parser

A slot-and-frame parser is simply implemented with two tables: one of known phrases, a second with corresponding responses. Phrase Response HELLO HELLO HOW ARE YOU? VERY WELL THANK YOU HOW'S THE WIFE? STILL BREATHING DO YOU WANT A BEER? IS THE POPE A CATHOLIC? DO YOU TAKE SUGAR? NO THANKS An example slot-and-frame phrase and response table The following program illustrates the idea of a slot-and-frame parser. The number of known phrases is very small, but may be expanded to the extent of the available computer's memory. --------Listing 1 --------/* Primitive Slot & Frame parser demonstration */ char *phrase[] = { "HELLO", "WHAT IS YOUR NAME", "HOW OLD ARE YOU", "DO YOU LIKE CRICKET", "HAVE YOU EVER BEEN TO SPAIN", "DO YOU LIKE CHIPS", "" }; char *response[] = { "Hello", "Billy", "Too Old!", "Yes I like cricket", "No I have never been to Spain", "Yes, I like chips", "" };

void RESPOND(char *text) { int n; for(n = 0; *phrase[n]; n++) { /* Case insensitive comparison */ if (stricmp(text,phrase[n]) == 0) { /* A precise match is found */ printf("%s",response[n]); return; } } printf("I Don't understand"); } main() { char text[250]; for(;;) { printf("\n?"); gets(text); if (*text) { RESPOND(text); } else break; } } The first problem encountered with the primitive slot-and-frame approach is that even the most irrelevant difference between the entered phrase, and the known phrase will not be understood. If the phrase "What is your name" is entered the demonstration program, a response of "Biily" will be printed. However, the inclusion of the name Billy to a question is enough to cause confusion. "How old are you Billy" is a seemingly valid question, but the program cannot find a matching known phrase and so the response is "I don't understand". The next generation of slot-and-frame parsers scanned the input phrase for known words or phrases, and responded accordingly. The early slot-and-frame parsers were written in early dialects of BASIC. BASIC still does not have

string handling facilities that are as powerful as those provided by C. The differences between this example second generation slot-and-frame parser and the primitive one are twofold. First, the input string is converted to upper case. Second, the C strstr() function is used to locate a known phrase within the input phrase. We have to convert the input phrase to the same case as the known phrases because strstr() is case sensitive. --------Listing 2 --------/* Second Generation Slot & Frame parser demonstration */ char *phrase[] = { "HELLO", "WHAT IS YOUR NAME", "HOW OLD ARE YOU", "DO YOU LIKE CRICKET", "HAVE YOU EVER BEEN TO SPAIN", "DO YOU LIKE CHIPS", "" }; char *response[] = { "Hello", "Billy", "Too Old!", "Yes I like cricket", "No I have never been to Spain", "Yes, I like chips", "" }; void RESPOND(char *text) { int n; for(n = 0; *phrase[n]; n++) { /* Search for the known phrase occurring within the text string */ if (strstr(text,phrase[n])) { /* The known phrase was located */ printf("%s",response[n]); return; } } /* No known phrases were located within the text string */ printf("I Don't understand"); }

main() { char text[250]; for(;;) { printf("\n?"); gets(text); if (*text) { strupr(text); RESPOND(text); } else break; } } With such a small development, the flexibility of the parser has grown enormously. Now phrases such as "How old are you?" and "How old are you, Billy?" generate a sensible response, where before they didn't. The third generation of slot-and-frame parsers search are sometimes called "verb and noun search parsers". The input phrase is searched for known verbs (action words), and nouns (objects). This type of parser is extensively used in adventure games where the input phrase will be a noun and a verb, with perhaps a smattering or prepositions. Something along the lines of; "TAKE THE BUCKET", or "OPEN THE DOOR" or "SMASH THE WINDOW WITH THE HAMMER". Let us use the phrase "SMASH THE WINDOW WITH THE HAMMER" for an example. The program may recognise the verb "SMASH", the object "WINDOW" but also require a qualification object "HAMMER". So long as the player enters a phrase containing these three words the parser will understand the command. So, "HAMMER WINDOW SMASH" and "SMASH HAMMER WINDOW" and even "SMASH THE HAMMER WITH THE WINDOW" will all be accepted. A verb and noun search parser is very lax on semantics, this doesn't matter too much in the adventure game domain. In conversational computing a lack of semantics can lead to erroneous deductions. One other point to remember about all the slot-and-frame computer parsers is that the computer program never understands what is being said. Rather it simply makes automated responses to standard phrases.

SCANNING

Scanning is a process of quick interpretation by ignoring minor words. When you scan a written document, you search very quickly for key facts. Within the context of natural language processing, the principle is the same. Minor words include most prepositions. A scan of the sentence, "the quick brown fox jumps over the lazy dog." Will reveal "quick brown fox jumps over lazy dog." The essence of the sentence remains. It is a frequent problem to computers that humans expand their speech with superfluous words. An enquirer as a train station may wish to know at what time the next train will depart for Manchester. A straight forward enquiry would be. "When does the next train leave for Manchester?" Is it necessary to specify which train? A scan of the question might return. "When train leave for Manchester?" It does not sound very natural to a human, but it conveys the same request as the initial sentence. Writing a computer program to respond to English sentences scanning might be used to good effect. The traditional approach, used by SQL and Data Ease, is to insist that the operator does most of the scanning. These programs may understand phrases such as: "For Customers; list records" or "modify records in inventory" Where as a human approach may be to command. "Print a list of all the records in the customers file." Or "modify every record in the inventory file." An imaginary application might be a plain English disk operating system which instead of accepting commands like "COPY *.C A:" Would understand commands like "COPY ALL .C FILES TO DRIVE A". The argument against computer interfaces that understand plain English has always been that the operator has to do too much typing. In the absence of reliable and cost-effective audio input devices there is little defence against this argument. Currently commands to a computer must be typed in or selected by pointing to them.

SYNTACTICAL STRUCTURES

Sentences, if constructed properly, are comprised of two parts: subject and predicate. For example. The sentence "many hands make light work." Splits into "many hands" and "make light work". The predicative qualifies or describes the subject. In some sentences either the subject or the predicate may be implied, rather than stated. For example in the sentence "come here!" The subject, "you", is implied. Of course this type of sentence can lead to misunderstandings! Within the subject, one word is the "subject word". In the sentence "many hands make light work.", the subject word is "hands" because it is a noun following an adjective. Whereas in the sentence, "most of the audience enjoyed the concert." The subject word is "most", denoted by an adjective phrase following it, "of the audience". Within a sentence there is also an object. The object is a noun, perhaps represented by a pronoun, upon which the sentence's verb acts. For example. In the sentence "the cat chased the mouse." The object is "mouse", because it is the noun acted upon by the verb, "chased". Often a sentence contains phrases and clauses. Clauses are sub-sections of the sentence with their own verb (action word), a phrase does not have a verb. For example. "I saw Mrs Brown drive her car along the motorway." This sentence contains the subject, "I" and the predicative "saw Mrs Brown drive her car along the motorway." The predicative is further divided into: "saw Mrs Brown", "drive her car" and "along the motorway." This breaking down of sentences can be taken further. Linguistics declares that a phrase can be broken down into a hierarchical structure in which groups of words appear as groups. If we analyse the sentence "I believe that the box fell off the kitchen table." First we can split it into the subject and predicate; Subject I believe Predicate that the box fell off the kitchen table

full syntactical analyse of the sentence is; S--------VP--------S-------S-------VP------PP------NP-------N---------N | | | | | | | | | NP V CONJ NP V P DET N N | | | | | | | | | I believe that | fell off the kitchen table +--+--+ | | DET N | | the box Key to diagram CONJ DET N NP P S VP V Conjunction Determiner Noun Noun Phrase Preposition Sentence Verb Phrase Verb

A predicate parser that extracts subjects and clauses from a sentence is a useful device for breaking a sentence down into small sections. --------Listing 3 --------/* PREDICATE PARSER */ /*---------------*/ /* Include files */ /*---------------*/ #include <stdio.h> #include <string.h> #include <alloc.h> #include <ctype.h>

/*-----------*/ /* Constants */ /*-----------*/ #define num_a #define num_q #define num_l #define num_c #define num_f #define num_qs

139 72 20 14 18 5

/*-----------*/ /* Lexicons */ /*-----------*/ /* Verbs and adverbs */ 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", "CROSS", "ENTER", "CLIMB", "TIE", "CROUCH","CRAWL", "BREATHE", "SNIFF", "SPIT" ,"GO","PUT","DRINK", "LOOK", "TURN", "BORN","LEAVE", "URINATE", "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","HAD","HAS" }; static char *questions[num_qs] = { "WHAT", "WHEN", "HOW", "WHY", "WHERE" };

/* Prepositions and conjunctions */ static char *fillers[num_f] = { "IS", "THE" , "A", "AN" , "ARE" ,"TOO","ALSO", "COME" , "FROM" , "ITS" , "THEIR" ,"WITH", "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","BIG", "SMALL", "FAT", "THIN", "TALL" ,"SHORT", "LARGE", "PETITE", "TINY" , "BLACK", "BLUE", "RED", "YELLOW", "ORANGE","GREEN","BROWN" , "GINGER" , "PURPLE","MAUVE", "SILVERY", "SILVER", "GOLD", "CYAN" ,"LAZY" ,"CARNIVOROUS" ,"PAST", "MY", "IN" , "ON", "BESIDE","OLD" }; static char *locations[num_l] = { "IN" , "ON", "BESIDE" , "EAST" , "WEST" , "NORTH" ,"SOUTH" , "UNDER" , "UP" , "BELOW", "ABOVE", "RIGHT", "LEFT", "OVER","INTO","AROUND" , "TO", "OFF", "THERE" }; typedef struct { char text[500]; char object[80]; char description[80]; char action[80]; } SUBJECT; SUBJECT SUBJECT SUBJECT SUBJECT SUBJECT subject; predicate; clause1; clause2; clause3;

/*---------------------*/ /* Function prototypes */ /*---------------------*/ int IS_COLOUR(char *); int IS_FILLER(char *); int IS_LOCATION(char *); int IS_VERB(char *); void strchg(char *, char *, char *); void truncstr(char *,int ); /*--------------------*/ /* Start of functions */ /*--------------------*/ static void GET_OBJECT(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) { 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 (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 void GET_ACTION(char *text , char *action) { /* Obtain action words from the phrase string */ char *p; char pre[50]; char copy[250]; strcpy(copy,text); strcpy(action,""); strcpy(pre,""); p = strtok(text," ,.;:?!"); while(p) { if (IS_VERB(p)) { if (stricmp(pre,"THE")) { if (*action) strcat(action," "); strcat(action,p); } } 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_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_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 *subject) { /* Splits a sentence into its two parts */ char char char char char *p; *q; word[40]; save; previous[40];

strcpy(predicate,data); strcpy(subject,""); 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(subject,p); return; } if (stricmp(word,"MOST") == 0) { save = *p; *p = 0; strcpy(predicate,data); *p = save; strcpy(subject,p); return; }

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(subject,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(subject,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 */ save = *q;

*q = 0; strcpy(predicate,data); *q = save; strcpy(subject,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 truncstr(char *p,int num) { /* Truncate string by losing last num characters */ if (num < strlen(p)) p[strlen(p) - num] = 0; } void main() { /* This is a test call */ char *p; FILE *fp; char text[250]; fp = fopen("response.out","w+");

do { printf("\nEnter a phrase\n"); gets(text); if (*text != 0) { strupr(text); GET_PREDICATE(text,subject.text,predicate.text); GET_ACTION(subject.text,subject.action); fprintf(fp,"\nSUBJECT [%s] PREDICATE [%s]",subject.text, predicate.text); GET_OBJECT(subject.text,subject.object); fprintf(fp,"\nOBJECT[%s]",subject.object); fprintf(fp,"\nACTION [%s]",subject.action); GET_DESCRIPTION(subject.text,subject.description); fprintf(fp,"\nDESCRIPTION [%s]",subject.description); fprintf(fp,"\nPREDICATE ANALYSIS"); GET_PREDICATE(predicate.text,text,clause1.text); if (*clause1.text) { p = strstr(predicate.text,clause1.text); *p = 0; } GET_ACTION(predicate.text,predicate.action); fprintf(fp,"\nACTION [%s]",predicate.action); GET_OBJECT(predicate.text,predicate.object); fprintf(fp,"\nOBJECT [%s]",predicate.object); GET_DESCRIPTION(predicate.text,predicate.description); fprintf(fp,"\nDESCRIPTION [%s]",predicate.description); GET_PREDICATE(clause1.text,text,clause2.text); if (*clause2.text) { p = strstr(clause1.text,clause2.text); *p = 0; } fprintf(fp,"\nFIRST CLAUSE ANALYSIS"); fprintf(fp,"\nCLAUSE [%s]",clause1.text); GET_ACTION(clause1.text,clause1.action); fprintf(fp,"\nACTION [%s]",clause1.action); GET_OBJECT(clause1.text,clause1.object); fprintf(fp,"\nOBJECT [%s]",clause1.object); GET_DESCRIPTION(clause1.text,clause1.description); fprintf(fp,"\nDESCRIPTION [%s]",clause1.description); GET_PREDICATE(clause2.text,text,clause3.text);

if (*clause3.text) { p = strstr(clause2.text,clause3.text); *p = 0; } fprintf(fp,"\nSECOND CLAUSE ANALYSIS"); fprintf(fp,"\nCLAUSE [%s]",clause2.text); GET_ACTION(clause2.text,clause2.action); fprintf(fp,"\nACTION [%s]",clause2.action); GET_OBJECT(clause2.text,clause2.object); fprintf(fp,"\nOBJECT [%s]",clause2.object); GET_DESCRIPTION(clause2.text,clause2.description); fprintf(fp,"\nDESCRIPTION [%s]",clause2.description); fprintf(fp,"\nTHIRD CLAUSE ANALYSIS"); fprintf(fp,"\nCLAUSE [%s]",clause3.text); GET_ACTION(clause3.text,clause3.action); fprintf(fp,"\nACTION [%s]",clause3.action); GET_OBJECT(clause3.text,clause3.object); fprintf(fp,"\nOBJECT [%s]",clause3.object); GET_DESCRIPTION(clause3.text,clause3.description); fprintf(fp,"\nDESCRIPTION [%s]",clause3.description); } } while(*text); } A type of parser which makes use of syntactical structures is the "augmented transition network", abbreviated to "ATN". ATN was developed by Thorne, Bratley and Dewar in 1968, and built upon by W.A. Woods. He published his work under the title "Transition network grammars for natural language analysis" in the communications of the association for computing machinery journal, volume 13 in 1970. The general idea behind ATN is that in the English language, verb phrases generally follow noun phrases. Thus an ATN seeks a noun phrase, and then seeks a verb phrase to associate with the noun phrase. This idea of using grammatical rules to parse sentences is illustrated in the following example of a "depth-first exhaustive search" syntactical parser.

--------Listing 4 --------/*---------------*/ /* Include Files */ /*---------------*/ #include <stdio.h> #include <stdlib.h> #include <string.h> The following data comprises the rules of grammar, and the lexicon for the parser. Other rules and words can be added as required. char *data[] = { "<SENTENCE>=<NOUN.PHRASE> <VERB.PHRASE-PREPOSITIONAL.PHRASES>", "<SENTENCE>=<SENTENCE> <CONJUNCTION> <SENTENCE>", "<NOUN.PHRASE>=<DETERMINER> <ADJECTIVES-NOUN>", "<ADJECTIVES-NOUN>=<ADJECTIVE> <ADJECTIVES-NOUN>", "<ADJECTIVES-NOUN>=<NOUN>", "<VERB.PHRASE-PREPOSITIONAL.PHRASES>= <VERB.PHRASE-PREPOSITIONAL.PHRASES> <PREPOSITIONAL.PHRASE>", "<VERB.PHRASE-PREPOSITIONAL.PHRASES>=<VERB.PHRASE>", "<VERB.PHRASE>=<VERB> <NOUN.PHRASE>", "<VERB.PHRASE-PREPOSITIONAL.PHRASES>=<VERB> <PREPOSITIONAL. PHRASE>", "<PREPOSITIONAL.PHRASE>=<PREPOSITION> <NOUN.PHRASE>", "<VERB.PHRASE>=<VERB>", "<DETERMINER>=A","AN","THE","THIS","THAT", "<ADJECTIVE>=GREEN","RED","BLUE","YELLOW","CYAN","STRONG", "BUILT", "<NOUN>=JACK","JOHN","ROSE","GRASS","SUN","HOUSE","TREE", "<VERB>=GREW","SHONE","MADE", "<PREPOSITION>=TO","OF","IN", "<CONJUNCTION>=AND","OR","IS", "" }; /*----------------*/ /* Work variables */ /*----------------*/ char char char char as[100]; h[250]; text[250]; gs[50][100];

char ws[50][100]; int ra[50]; int la[50]; int lb[50]; char temp[100]; int s; int c; int q; int r; int f; int ls; int c1; int c2; int c3; int c4; int counter; char *p; int i; int j; int k; void main() { s = 1; c = 0; q = 1; /* Clear the display */ clrscr(); /* Receive an input phrase from the operator */ printf("\nInput a sentence "); gets(text); /* Convert the input phrase to upper case */ strupr(text); /* Insert a space at the beginning of the input string */ memmove(&text[1],text,strlen(text) + 1); text[0] = 32; /* Append a space to the end of the input string */ strcat(text," ");

/* Preserve a copy of the input string */ strcpy(H,text); /* Load constant data */ counter = 0; r = 0; do { f = 0; strcpy(as,data[counter]); if (*as) { while(as[strlen(as) - 1] == 32) as[strlen(as) - 1] = 0; for(i = 0; i < strlen(as); i++) { if(as[i] == '=') { strncpy(gs[r],as,i); gs[r][i + 1] = 0; f = 1; strcpy(ws[r],&as[i+1]); i = strlen(as); } } if (f == 0) { strcpy(gs[r], gs[r - 1]); strcpy(ws[r],as); } } r = r + 1; counter++; } while(*as); /*----------------*/ /* The parse loop */ /*----------------*/ /* Clear the display */ clrscr(); printf("Syntactical parsing analysis of %s",text); printf("\n\n"); do

{ f = 0; s = 0; ls = strlen(text); c1 = 0; q = 0; for(i = q; i < ls; i++) { if (text[i] == ' ') { i = i + 1; c1 = c1 + 1; for(j = s; j <= r; j++) { p = &text[i]; strcpy(temp,ws[j]); strcat(temp," "); if (strncmp(temp,p,strlen(ws[j])+1) == 0) { c = c + 1; la[c] = i; lb[c] = ls - strlen(ws[j]) - i; p = &text[strlen(text) - lb[c]]; strcpy(temp,p); text[la[c]] = 0; strcat(text,gs[j]); strcat(text,temp); f = 1; ra[c] = j; printf("\n%d %s",c,text); c2 = c1 + 1; for(k = 0; k < strlen(ws[j]); k++) { if(ws[j][k] == ' ') c2++; } c3 = 0; c4 = 0; strcpy(as,""); for(k = 0; k < strlen(h); k++) { strcat(as," "); as[strlen(as) - 1] = h[k]; if (h[k] == ' ') { c3++;

c4++; if (c3 > c1 && c4 <= c2) { as[strlen(as) - 1] = 0; } if (c3 == c1) strcat(as,"("); if (c4 == c2) strcat(as,") "); } } strcpy(h,as); printf("\n%s",h); j = r; i = ls; } } } } p = &text[strlen(text) - 2]; if (c1 == 1 && c2 == 1 && strncmp(text," <",2) == 0 && strcmp(p,"> ") == 0) { printf("\n\nParsing complete"); exit(0); } } while(f == 1); printf("\nParsing complete"); } Here are some sample outputs of the syntactical parser; Syntactical parsing analysis of THIS IS THE HOUSE THAT JACK BUILT 1 <DETERMINER> IS THE HOUSE THAT JACK BUILT (THIS) IS THE HOUSE THAT JACK BUILT 2 <DETERMINER> <CONJUNCTION> THE HOUSE THAT JACK BUILT (THIS) (IS) THE HOUSE THAT JACK BUILT 3 <DETERMINER> <CONJUNCTION> <DETERMINER> HOUSE THAT JACK BUILT (THIS) (IS) (THE) HOUSE THAT JACK BUILT

4 <DETERMINER> <CONJUNCTION> <DETERMINER> <NOUN> THAT JACK BUILT (THIS) (IS) (THE) (HOUSE) THAT JACK BUILT 5 <DETERMINER> <CONJUNCTION> <DETERMINER> <ADJECTIVES-NOUN> THAT JACK BUILT (THIS) (IS) (THE) ((HOUSE)) THAT JACK BUILT 6 <DETERMINER> <CONJUNCTION> <NOUN.PHRASE> THAT JACK BUILT (THIS) (IS) ((THE)((HOUSE))) THAT JACK BUILT 7 <DETERMINER> <CONJUNCTION> <NOUN.PHRASE> <DETERMINER> JACK BUILT (THIS) (IS) ((THE)((HOUSE))) (THAT) JACK BUILT 8 <DETERMINER> <CONJUNCTION> <NOUN.PHRASE> <DETERMINER> <NOUN> BUILT (THIS) (IS) ((THE)((HOUSE))) (THAT) (JACK) BUILT 9 <DETERMINER> <CONJUNCTION> <NOUN.PHRASE> <DETERMINER> <ADJECTIVES-NOUN> BUILT (THIS) (IS) ((THE)((HOUSE))) (THAT) ((JACK)) BUILT 10 <DETERMINER> <CONJUNCTION> <NOUN.PHRASE> <NOUN.PHRASE> BUILT (THIS) (IS) ((THE)((HOUSE))) ((THAT)((JACK))) BUILT 11 <DETERMINER> <CONJUNCTION> <NOUN.PHRASE> <NOUN.PHRASE> <ADJECTIVE> (THIS) (IS) ((THE)((HOUSE))) ((THAT)((JACK))) (BUILT) Parsing complete

Syntactical parsing analysis of THE SUN SHONE AND THE GRASS GREW 1 <DETERMINER> SUN SHONE AND THE GRASS GREW (THE) SUN SHONE AND THE GRASS GREW 2 <DETERMINER> <NOUN> SHONE AND THE GRASS GREW (THE) (SUN) SHONE AND THE GRASS GREW 3 <DETERMINER> <ADJECTIVES-NOUN> SHONE AND THE GRASS GREW (THE) ((SUN)) SHONE AND THE GRASS GREW 4 <NOUN.PHRASE> SHONE AND THE GRASS GREW ((THE)((SUN))) SHONE AND THE GRASS GREW 5 <NOUN.PHRASE> <VERB> AND THE GRASS GREW ((THE)((SUN))) (SHONE) AND THE GRASS GREW

6 <NOUN.PHRASE> <VERB.PHRASE> AND THE GRASS GREW ((THE)((SUN))) ((SHONE)) AND THE GRASS GREW 7 <NOUN.PHRASE> <VERB.PHRASE-PREPOSITIONAL.PHRASES> AND THE GRASS GREW ((THE)((SUN))) (((SHONE))) AND THE GRASS GREW 8 <SENTENCE> AND THE GRASS GREW (((THE)((SUN)))(((SHONE)))) AND THE GRASS GREW 9 <SENTENCE> <CONJUNCTION> THE GRASS GREW (((THE)((SUN)))(((SHONE)))) (AND) THE GRASS GREW 10 <SENTENCE> <CONJUNCTION> <DETERMINER> GRASS GREW (((THE)((SUN)))(((SHONE)))) (AND) (THE) GRASS GREW 11 <SENTENCE> <CONJUNCTION> <DETERMINER> <NOUN> GREW (((THE)((SUN)))(((SHONE)))) (AND) (THE) (GRASS) GREW 12 <SENTENCE> <CONJUNCTION> <DETERMINER> <ADJECTIVES-NOUN> GREW (((THE)((SUN)))(((SHONE)))) (AND) (THE) ((GRASS)) GREW 13 <SENTENCE> <CONJUNCTION> <NOUN.PHRASE> GREW (((THE)((SUN)))(((SHONE)))) (AND) ((THE)((GRASS))) GREW 14 <SENTENCE> <CONJUNCTION> <NOUN.PHRASE> <VERB> (((THE)((SUN)))(((SHONE)))) (AND) ((THE)((GRASS))) (GREW) 15 <SENTENCE> <CONJUNCTION> <NOUN.PHRASE> <VERB.PHRASE> (((THE)((SUN)))(((SHONE)))) (AND) ((THE)((GRASS))) ((GREW)) 16 <SENTENCE> <CONJUNCTION> <NOUN.PHRASE> <VERB.PHRASE-PREPOSITIONAL.PHRASES> (((THE)((SUN)))(((SHONE)))) (AND) ((THE)((GRASS))) (((GREW))) 17 <SENTENCE> <CONJUNCTION> <SENTENCE> (((THE)((SUN)))(((SHONE)))) (AND) (((THE)((GRASS)))(((GREW)))) 18 <SENTENCE> ((((THE)((SUN)))(((SHONE))))(AND)(((THE)((GRASS)))(((GREW))))) Parsing complete

Syntactical parsing involves complex and very impressive algorithms. Too complex for the average human mind? I think so. I think that in order to understand natural language we must understand the human mind. In the next chapter I shall explore natural language parsing from the much simpler psychology angle. Too simple? Perhaps, but remember that a child can understand simple phrases. It takes a rare breed to comprehend linguistics!

HUMAN PARSING

To be able to program a computer to first understand how the human mind This book is not concerned with how that a computer program or a brain. happens to the data once it has got

understand natural language, one must handles data received in natural language. the data reaches the thought machinery, be Rather this book is concerned with what there.

Human's make use of a range of parsers when trying to understand a phrase. First a slot-and-frame parser is used to compare the phrase against a small library of "common phrases". These are generally exclamations, including greetings (HELLO, HI, NICE TO MEET YOU) and dismissals (PUSH OFF, GO AWAY) but also including warnings (LOOK OUT!). Some questions may also be stored, (HOW ARE YOU? WHAT IS YOUR NAME? WOULD YOU LIKE A BEER?) as well as instructions (SHUT THE DOOR, DON'T DO THAT!) and so on. Phrases which fail the slot-andframe parser are then passed through a more intricate combination of predicate parsers and component analysis. Finally, a verb and noun search may be carried out. Missing components will be filled in by the mind based upon experience both of the question and phrases previously received from the transmitter. In this way an individuals' poor grammar is more easily understood after talking with them for some time. For example, the rural Jamaicans use a peculiar language called "patoi". A favourite phrase of which is "soon come". This translates, into the Queen's English, as "I shall soon come to join you."

IS THE DATA A COMMON PHRASE? YESNO RETRIEVE STANDARD PARSE PHRASE RESPONSE FROM LIBRARY : : : : ............................:

: : PARSE PHRASE EXPAND APOSTROPHES EXTRACT COMPONENTS IS IT A QUESTION? YESNO DECIDE TYPE IS IT AN OF QUESTION INSTRUCTION? YESNO DECIDE TYPE IT'S A OF INSTRUCTION STATEMENT

Questions may be "interrogative", such as "are you married?". Or they may be requests. "Will you marry me?". Instructions encompass commands, requests, entreaties and wishes. An example of a command is "shut the door." A wish may be "I want to go to the pub!". Statements include exclamations. A statement might be "I went to the pub." Whereas an exclamation may be "I enjoyed that!"

COMMON PHRASES

These are phrases to which a reaction has become almost instinctive. They vary from one individual to the next, but may include the following: Exclamations such as: "Hello" and other informal greetings; "Hi", "Wotchya", etc. Commands such as "Go away" (and its vulgar counterparts), "Don't do that!" Questions such as: "How are you?" , "Who are you?" , "What's your name?" , "Fancy a beer?" and "Have you got a light?" Common phrases reduce much ambiguity in communication. For example, without a knowledge of the common phrase "Have you got a light?" Implying a request for a source of ignition, probably for a cigarette, it would be parsed. In parsing this phrase its whole meaning will change. Probably to an interrogation of whether "you" have a source of light energy. The way in which we, as humans, react to "common phrases" is still debated by psychologists. I believe that slot-and-frame parsing is the answer. It may not be, but it certainly provides a means to model the response to common phrases within a computer program.

COMPONENT PARSING

When a human receives a phrase, be it a sentence or paragraph. The brain breaks it down into manageable sized phrases and clauses. Much like the predicate parser. These phrases and clauses are then further broken down into component parts. These components may be summarised as; Question/Order (The nature of the phrase) Object Subject Qualifiers Size Colour Number Location Action Person Tense For example; "What is the matter with Mary Jane?" Breaks down into phrases and clauses thus; Subject [What] Predicate [is the matter with Mary Jane?] Object [is the matter] The predicate further breaks down into the phrase; Phrase [with Mary Jane?] (What the phrase refers to) (What the phrase says about the object) ) ) ) Descriptions qualifying the object and subject ) ) (All, one, two etc) (Where is the object) (Verbs) (You, me, us, them, they, him, her) (Past, present, future or indeterminable)

Taking the phrase as a whole it breaks down into these components;

The The The The The The The The The The The The

question is WHAT IS order is object is MATTER qualifiers are colours are sizes are subject is WITH MARY JANE person is number is action is location is tense is present

Similarly, "It's lovely rice pudding for dinner again!" Produces; Subject [It] Predicate [is lovely rice pudding for dinner again!] The predicate breaks down into; Object [is rice pudding] Description [lovely] And produces a sub-phrase; Phrase [for dinner again!] Analysing the phrase as a whole we get; The The The The The The The The The The The The question is order is object is qualifiers are LOVELY colours are sizes are subject is LOVELY RICE PUDDING FOR DINNER AGAIN person is number is action is location is tense is indeterminable

"This is the house that Jack built" produces the following analysis. Compare it with the output of the syntactical parser described in a previous chapter. Subject [This] Predicate [is the house that Jack built]

The predicate breaks down into an object and a sub-phrase; Object [is the house] Phrase [that jack built] The sub-phrase breaks down into an object and an action (verb); Action [built] Object [that Jack] Analysing the phrase as a whole we get the following summary; The question is The order is The object is HOUSE The object qualifiers are The object colours are The object sizes are The subject is JACK The person is The number is The action is BUILT The location is The tense is indeterminable Enter a phrase

A component parser is a fairly complex affair. But can be a useful addition to existing parsers. The component parser has some similarities to the predicate parser. The lexicon of verbs (actions) is the same for example. --------Listing 5 --------/* COMPONENT PARSER */ /*---------------*/ /* Include files */ /*---------------*/ #include <stdio.h> #include <string.h> #include <alloc.h> #include <ctype.h>

/*-----------*/ /* Constants */ /*-----------*/ #define num_a #define num_p #define num_q #define num_l #define num_n #define num_c #define num_f #define num_qs #define num_s #define num_o

140 12 41 20 37 14 17 19 9 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","CROSS", "ENTER", "CLIMB", "TIE", "BREATHE", "SNIFF", "SPIT" ,"GO","PUT","DRINK", "LOOK", "TURN", "BORN","LEAVE", "URINATE", "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", "CROUCH","CRAWL", "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", "FORTY", "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 */ /*------------*/ static char last_subject[80]; static char last_object[80]; /*---------------------*/ /* 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 *); 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 preceding 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 char char char char *p; n; word[50]; pre[50]; 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(char char char { /* Parses char *p; char *q;

*text, char *question, char *order, char *object, *object_qualify, char *subject, char *number, char *action, *location, char *colour, char *size, char *who, char *tense) a simple phrase and returns the component parts */

/* 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"); /* 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"); 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, object_qualify, subject); GET_COLOUR(text,colour); GET_SIZE(text,size); GET_NUMBER(text , number); GET_LOCATION(text,location); printf("\nThe printf("\nThe printf("\nThe printf("\nThe printf("\nThe printf("\nThe printf("\nThe printf("\nThe printf("\nThe printf("\nThe printf("\nThe printf("\nThe question is %s",question); order is %s",order); object is %s",object); object qualifiers are %s",object_qualify); object colours are %s",colour); object sizes are %s",size); subject is %s",subject); person is %s",who); number is %s",number); action is %s",action); location is %s",location); tense is ");

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); } } } switch(*tense) { case 1: printf("past"); break; case 2: printf("present"); break; case 3: printf("future"); break;

default: printf("indeterminable"); } } void main() { char a[80]; char b[80]; char c[80]; char d[80]; char e[80]; char f[80]; char g[80]; char h[80]; char i[80]; char j[80]; char k[80]; char tense; char text[100]; do { printf("\nEnter a phrase\n"); gets(text); if (*text != 0) { strupr(text); NLP(text,a,b,c,d,e,f,g,h,i,j,k,&tense); } } while(*text); } In trying to understand a phrase, extensive use of the component parts is made, with occasional referrals back to the phrase as a whole. The problems of "understanding", rather than "responding" are discussed in a later chapter. By breaking a phrase down into components understanding various forms of the same phrase becomes easier. Let's take the basic phrase, "I'M BORED". This can have variations such as "I AM BORED", "I AM VERY BORED", "I AM EXTREMELY BORED" and so on. But the basic message is the same, "I AM BORED". Without breaking the phrase into components we have to compare the phrase against a vast lexicon of known phrases. However, with components we only need to compare the object and subject, either of which will be set to "BORED" in all the previous examples of the same basic message.

/* /* /* /* /* /* /* /* /* /* /* /*

question */ order */ object/directive */ object qualifier */ subject */ number */ action */ location/direction */ Colour */ size */ who */ Tense, past present or future */

The component parser decides whether a phrase is a question, or not by extracting the first word of the phrase and comparing it against a lexicon of known questions; WHO, WHAT, WHERE, WHEN, WHY, WOULD, SHOULD, DO, DID, IS, ARE, WILL, CAN. If a match is found then the phrase is a question. For example; "Where is the cat?" The first word is extracted, "WHERE", and upon comparing it against the lexicon of questions a match is found. So this phrase is a question. "Do you think Arsenal will win the match?" The first word is extracted, "DO", and upon comparing it against the lexicon of questions a match is found. So this phrase is a question. You might recognise this technique from the slot-and-frame parsers described earlier. A further stage of component parsing is deciding the nature of a question or statement. questions are of two different types; request and interrogation. Although both request something, the interrogation requests further information. Human's tend to communicate ambiguously, as in the question; "Have you got a light?" Which, under some circumstances actually means; "Can you give me a source of ignition". Under other circumstances it means, "Can I have a source of light". Finally this phrase may actually be an interrogation asking if you have a source of light.

QUALIFIERS AND AMBIGUITY

Computer's are remarkably stupid machines, when compared to the human brain. A phrase that may appear straight forward and obvious to us, "Got a light, mate?" Will confuse any but the most sophisticated conversational computer programs. Why? If we ignore the idea of "common phrases", and limit our selves to syntactical parsing. Then we may start to understand the problems. Perhaps even to sympathise with the computer program trying to respond intelligently. Referring back to the dreadful phrase, "Have you got a light?". Let us assume this to be a straight forward interrogation asking if you have a source of light. The question is unclear. It may mean do you own a source of light, or it may be asking if you have a source of light in your possession. This lack of clarity is often used in comedy. So there you are, out on a dark night with a friend. You need the use of a torch and so you ask, "Have you got a light?". Your friend owns a torch and so replies quite truthfully, "Yes". Now you are starting to get agitated. You were actually requesting a torch, and so you say "Can I borrow it please?". Another unclear phrase. Your friend being a generous chap, replies "Yes, you can borrow my torch." You still have not received what you wanted, a torch, so you say "Give me your torch please", at last a clear statement. Your friend replies "I can't". And so it goes on until at lat you discover that the torch, which your friend owns, is not in his possession. We can illustrate this more clearly perhaps with an imaginary repetoir. The scene is a dark, moonless night. You are with a friend standing in a wood. The friend is obviously obtuse. He might even be an android, because the answers he gives to seemingly obvious questions reflect very little comprehension. you: (Requesting a torch) Have you got a light?

friend: Yes. you: Can I borrow it, please?

friend: Yes, you can borrow my torch. you: Give me your torch! Please!

friend: I can't. you: Why not?

friend: Why not what?

you:

Why can't you give me your torch?

friend: Because I haven't got it with me. you: Where is it?

friend: Where is what? you: The torch!

friend: Which torch? you: The torch I want to borrow.

friend: What about the torch you want to borrow? you: Where is your torch?

friend: My torch is at home. This is the sort of problem that pursues you when trying to create a conversational computer program. Unlike a human, a computer does not automatically cross reference between current and previous phrases. The simplest example of this the use of the word "it". We use "it" a lot. For example. "Have you got the book I lent you?" "Yes" "Can I have it back please" You understand that in the last sentence, "Can I have it back please", that the word "it" refers to "the book I lent you".

UNDERSTANDING AND RESPONDING

At first glance, if a computer program gives an appropriate reply to an input, then the program has understood what was required of it. This would be an erroneous deduction to make. Responding in an appropriate manner does not imply an understanding. Rather it may simply be that a programmed response was given to a recognisable input. Slot-and-frame parsing again. Search one table for the input, and give a corresponding entry from another table. Understanding requires intelligence. Consider a dialogue between two people. John walks into his local pub one evening. At the bar he observes his friend Martin and so he walks up to him. Following the ordering of a drink John turns to Martin. John: Evening Martin!

Martin: Hello John. Alright? John: A bit tired.

Martin: A hard day at work? John: No my mother went into hospital last night.

Martin: Nothing serious I hope? John: A stroke.

Martin: I'm sorry. John: That's okay. It's not your fault. Still, did you see the football last night?

Martin: Who was playing? And so it continues. If we look at the intercourse that took place we will learn quite a lot about "understanding". The exchange starts with "Evening Martin". Martin recognises this as a common phrase, and responds with one of the possible standard responses. Perhaps John looks tired, or may be Martin is just being polite, but he asks after John's health and well being. John knows that the interrogation, "alright?" does not require an indepth reply. He knows this because of the circumstances of the intercourse taking place. If he was talking to his doctor, in a consultation he might give an in-depth reply. But in this circumstance he gives a short answer. Martin's response to John's answer, "a bit tired", is again a standard response to a common phrase. He

doesn't care if he gets an answer or not. The reply that John's mother has gone into hospital generates an intelligent response from Martin. Martin understands the phrase, and has been taught that; a) Mother's give pleasure. b) People go into hospital if they are sick. c) Being sick is not pleasant, it is suffering. d) Someone who gives pleasure suffering passes on suffering to the person to whom they give pleasure. Therefore, by John's mother going into hospital John is also suffering. Martin's intelligent response is none-the-less a standard phrase for these circumstances. However, it is selected because it is appropriate for the circumstances. John is taking a passive role in the conversation. He answers Martin's questions simply and honestly. He knows from experience that a stroke is a serious ailment. He knows that the common phrase "nothing serious I hope?" Requires different answers. If the ailment is not serious, then a simple "no", perhaps with qualification is required. If the condition is serious, then the condition itself is described. Martin, upon receiving the answer "stroke" remembers what was being talked about. That John's mother had gone into hospital, and therefore deduced that "stroke" applied to her condition. Martin has been taught that a stroke is a dangerous ailment, and replies with a standard phrase expressing emotion that he hopes will help John. Why does Martin wish to help John? Perhaps because if John is suffering then he, Martin, as a friend will also suffer. And suffering is not nice. John also replies with an intelligent response in the same vein to stop causing Martin any suffering. He may know that Martin enjoys watching football. Or he may deduce that as Martin is a male adult, and that male adults enjoy watching football so does Martin. Within this exchange we notice that the understanding process requires recalling to memory what has been learnt about a vast range of subjects in the past. We also notice prediction occurring. Both parties attempts to predict what the response will be if they supply supply certain stimuli. These stimuli being phrases. This prediction is based upon their memories of responses to phrases, and also what their own response would be. If Martin's mother has never gone into hospital with a stroke. If Martin has never known of anyone else ever going into hospital with a stroke, he can still make judgements based upon similar circumstances.

Currently available computers do not have the memory capacity, or the speed to retain sufficient facts and be able to cross reference them as the human mind does. With CD-ROMs the memory limitations may be overcome, the speed problem will remain I believe until optical circuitry is implemented, with light energy taking the place of DC current. In the meantime the talking computer is limited to responding without very much understanding.

GEORGE

George is an example of how a computer program can be written that will converse with a human operator. As we have already established, a computer cannot currently converse very intelligently. However, George makes use of a few simple techniques to ask the operator questions and respond to the answers given. The program is in two parts. The first part is the component parser listing already described. The second is the listing that follows. You should be able to easily add more prompts and develop your own personality slant for the program. George is not just a demonstration. It can also be used to develop ideas for creative writing and may even help you sort out difficult decisions! George echoes the entire conversation to a disk file entitled "george.con", which is destroyed each time george is run, so make sure you make a copy if you want to keep a particular conversation! --------Listing 6 --------/*-----------------------------------------------*/ /* Demonstration conversational computer program */ /*-----------------------------------------------*/ #include <stdio.h> #include <dos.h> #include <conio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <stdarg.h> char data[1000]; char last_question; char user[30]; char *family[] = { "MOTHER", "FATHER", "BROTHER", "SISTER", "SON", "DAUGHTER", "COUSIN", "AUNTY", "UNCLE" , "" };

char *names[42] = { "ALAN", "ANNE", "ANN", "BRIAN", "JAMES", "JIM", "SUE", "SUSAN", "ROGER", "PETE", "PETER", "MATT", "MATTHEW", "MATHEW", "IAN", "HARRY", "TONY", "ALISON", "TANYA", "RICK", "RICHARD", "MICHAEL", "MARTIN", "MIKE","TIM", "TIMOTHY", "GEORGE","HOWARD", "CAROLINE", "SARA", "SARAH", "KATE", "KATY", "TOM", "THOMAS", "JON", "JONATHAN", "CHARLES", "VINCE", "VINCENT","BILL","WILLIAM" }; char *subjects[14] = { "THE GOVERNMENT", "FRENCH FARMERS", "THE SUN NEWSPAPER", "THE SUNDAY SPORT", "INFLATION", "ABORTION", "MARRIAGE", "CHILDREN", "CHANNEL FOUR", "BBC2", "MICHAEL JACKSON", "WOOPI GOLDBERG", "REAL ALE","THE ENVIRONMENT" }; char *authors[11] = { "FREUD", "ENID BLYTON", "JUNG", "LAING", "SHAKESPEAR", "DICKENS", "W.E. JOHNS", "JACK HIGGINS" , "SCIENCE FICTION" , "TOLSTOY" , "TOLKEIN" }; /*------------------------------------*/ /* Data we have learnt about the user */ /*------------------------------------*/ char known[50][100]; int fkn; FILE *echo; char query; char fc; int ft; char cf; char more; /*---------------------*/ /* Function prototypes */ /*---------------------*/ int DO_YOU_KNOW(void); void NEED(void); void NLP(char *, char *, char *, char *,char *, char *, char *, char *, char *, char *, char *, char *, char *); int READ(void); void REQUIRE(void); void WANT(void); int WHAT_ABOUT(void); void cdecl xprintf(char *, ...); char *strftos(char *); char *strrstr(char *, char *); void strwhy(char *);

void FEED(void); void BORROW(void); void cdecl xprintf(char *format, ...) { /* This function echoes the output to the screen and to a disk file */ va_list arg_ptr; static char output[1000]; va_start(arg_ptr, format); vsprintf(output, format, arg_ptr); va_end(arg_ptr); fputs(output,echo); printf("%s",output); } void BORROW() { /* Personality code */ /* What do they want to borrow ? */ char *p; p = strrstr(data,"BORROW"); if (strcmp(p,"A QUID") == 0 || strcmp(p,"A FIVER") == 0 || strcmp(p,"A POUND") == 0) { xprintf("HOW DO I KNOW I'LL GET IT BACK?"); } else xprintf("NOT BLOODY LIKELY!"); }

void FEED() { /* Prompts the human operator with a randomly selected question */ /* This keeps the conversation flowing, rather than stagnating into */ /* an infuriating 'doctor-patient' question and answer routine */ int n; int stop; char *p; stop = 0; if(last_question) { switch(last_question) { case 1: xprintf("TELL ME MORE ABOUT YOUR FAMILY."); query = 0; break; case 5: xprintf("WHAT DO YOU LIKE ABOUT IT?"); last_question = 0; break; case 7: xprintf("NICE TO MEET YOU %s.",user); last_question = 0; break; } last_question = 0; stop = 1; } else { if (fc == 1) { xprintf("WHAT'S YOUR FAVOURITE COLOUR?"); query = 1; fc = 2; stop = 1; }

else if (ft == 0) { xprintf("DO YOU LIKE PLAYING FOOTBALL?"); query = 1; ft = 1; last_question = 5; stop = 1; } else if (ft == 1) { xprintf("DO YOU LIKE WATCHING FOOTBALL?"); query = 1; ft = 2; last_question = 5; stop = 1; } else if (cf == 0 && !random(20)) { xprintf("DO YOU LIKE CHINESE FOOD?"); cf = 1; query = 1; last_question = 5; stop = 1; } else if (cf == 1 && !random(20)) { xprintf("DO YOU LIKE INDIAN FOOD?"); cf = 2; query = 1; last_question = 5; stop = 1; } else if (cf == 2 && !random(20)) { xprintf("HAVE YOU EVER TRIED JAMAICAN CUISINE?"); cf = 3; query = 1; stop = 1; }

else if (!query && fkn > 1) { n = random(fkn); if (strncmp("I ",known[n],2) == 0) { p = &known[n][2]; xprintf("YOU %s",p); stop = 1; } } else if (!random(15)) stop = DO_YOU_KNOW(); if (!stop) { if (!random(15)) stop = WHAT_ABOUT(); } if (!stop) { if (!random(10)) stop = READ(); } if (!stop) { if (!random(3)) xprintf("HMMMMM."); else xprintf("I SEE."); } } } void WANT() { char *p; p = strrstr(data,"I"); xprintf("WHY DO YOU %s?",p); query = 1; }

void NEED() { /* Respond to a 'I need' statement */ char *p; p = strrstr(data,"I NEED"); xprintf("HOW WILL YOU FEEL IF YOU GET %s?",p); query = 1; } void REQUIRE() { /* Respond to a 'I require' statement */ char *p; p = strrstr(data,"I REQUIRE"); xprintf("HOW WILL YOU FEEL IF YOU GET %s?",p); query = 1; } int DO_YOU_KNOW() { /* Generates a random 'DO YOU KNOW.....' question */ /* Once a name has been used it is destroyed to prevent the same */ /* question being asked again */ int n; n = random(42); if (*names[n]) { printf("DO YOU KNOW %s?",names[n]); query = 1; strcpy(names[n],""); return(1); } return(0); }

int WHAT_ABOUT() { /* Generates a random 'WHAT DO YOU THINK ABOUT.....' question */ /* Once a subject has been used it is destroyed to prevent the same */ /* question being asked again */ int n; n = random(14); if (*subjects[n]) { printf("WHAT DO YOU THINK ABOUT %s?",subjects[n]); strcpy(subjects[n],""); query = 1; return(1); } else return(0); } int READ() { /* Generates a random 'HAVE YOU READ ANY.....' question */ /* Once an author has been used it is destroyed to prevent the same */ /* question being asked again */ int n; n = random(11); if (*authors[n]) { printf("HAVE YOU READ ANY %s?",authors[n]); strcpy(authors[n],""); query = 1; return(1); } return(0); }

void RESPOND(char *data, char *question, char *order, char *object, char *object_qualify, char *subject, char *number, char *action, char *location, char *colour, char *size, char *who, char *tense) { int done; int n; char *p; /* The 'done' flag indicates when a response has been given */ done = 0; /* Remove trailing punctuation from data */ if (strchr("!?.",data[strlen(data) - 1])) data[strlen(data) - 1] = 0; /* Remove trailing spaces */ p = &data[strlen(data) - 1]; while(*p == 32 && p >= data) *p-- = 0; /* Personality code */ /* The operator has mentioned colours */ if (*colour && fc == 0) fc = 1; /* Personality code */ /* Learn about the operator */ if (strstr(who,"I") && fkn < 50) { fkn++; strcpy(known[fkn],data); } /* Personality code */ /* Is the operator talking about their family? */ if (!last_question) { for(n = 0; *family[n]; n++) { if(strstr(data,family[n])) { last_question = 3; break; } } }

/* Bad phrase */ if (*data == 0) { done = 1; xprintf("I DON'T UNDERSTAND"); } /* Automatic responses to standard phrases */ if (strcmp(data,"THAT IS OKAY") == 0 && !done) { done = 1; FEED(); } if (strcmp(data,"WHERE IS SPAIN") == 0 && !done) { done = 1; xprintf("IN EUROPE."); } if (strcmp(object,"HELLO") == 0 || strcmp(object,"HI") == 0 || last_question == 6 && !done) { if(strcmp(who,"I") == 0) { p = strrstr(data,"AM"); if(p) { done = 1; if (!*user) { strcpy(user,p); xprintf("PLEASED TO MEET YOU %s.",user); } else xprintf("I DO REMEMBER YOU NAME, %.",user); if (last_question == 6 || last_question == 7) last_question = 0; } } }

if (strcmp(question,"WHAT IS") == 0) { if (strcmp(data,"THE WEATHER LIKE THERE") == 0 || strcmp(data,"THE WEATHER LIKE") == 0) { done = 1; xprintf("QUITE WARM AND SUNNY."); } else if (strcmp(object,"MATTER") == 0) { done = 1; if (!*subject) xprintf("WHAT MAKES YOU THINK SOMETHING'S else { p = strrstr(data,"THE MATTER"); xprintf("WHAT MAKES YOU THINK SOMETHING'S } query = 1; } else if (strcmp(object,"WRONG") == 0) { done = 1; if (!*subject) xprintf("WHAT MAKES YOU THINK SOMETHING'S else { p = strrstr(data,"WRONG"); xprintf("WHAT MAKES YOU THINK SOMETHING'S } query = 1; } }

THE MATTER?");

THE MATTER %s?",p);

WRONG?");

WRONG %s?",p);

if (strncmp("I BORROW ",data,9) == 0 && strcmp(question,"CAN") == 0) { done = 1; BORROW(); }

if (strcmp(data,"I HAVE ALREADY SAID") == 0 || strcmp(data,"I HAVE ALREADY TOLD YOU") == 0 || strcmp(data,"I HAVE ALREADY TOLD YOU THAT") == 0 || strcmp(data,"I HAVE ALREADY SAID THAT") == 0) { done = 1; if (query) { xprintf("SORRY. MY MEMORY'S NOT WHAT IT USED TO BE."); query = 0; } else { p = strstr(data,"YOU"); if (p) { memmove(p,p+1,strlen(p) + 1); strncpy(p,"ME ",2); } p = data; memmove(p+2,p,strlen(p) + 1); strncpy(data,"YOU ",3); p = strstr(data,"THAT"); if (p) { *p = 'W'; strcat(data,"?"); } else strcat(data," WHAT?"); query = 1; xprintf("%s",data); } } if (strcmp(data,"I HAVE") == 0) { done = 1; xprintf("YOU HAVE WHAT?"); query = 1; last_question = 8; }

if (strcmp(object,"BORED") == 0 || strcmp(subject,"BORED") == 0 && strcmp(who,"I") == 0) { /* Personality code */ done = 1; xprintf("JOIN THE CLUB!"); } if(strncmp("YOU HAVE ALREADY ASKED ME",data,25) == 0) { if (query) { xprintf("HAVE I? SORRY!"); done = 1; query = 0; } else { xprintf("I DIDN'T THINK I'D ASKED YOU ANYTHING"); done = 1; } } if (strcmp(question,"WHAT") == 0 && !done) { /* Standard what questions */ if (strcmp(data,"SHALL WE TALK ABOUT") == 0) { done = 1; xprintf("I DON'T KNOW. YOU CHOOSE A SUBJECT."); } } if (strcmp(question,"WHAT DO") { if (strcmp(data,"YOU THINK { done = 1; xprintf("IT'S IN A BIT } else if (strcmp(data,"YOU THINK { done = 1; xprintf("IT'S IN A BIT } == 0 && !done) ABOUT THE ECONOMY") == 0) OF A MESS."); ABOUT OF THE ECONOMY") == 0) OF A MESS.");

else if (strcmp(data,"YOU THINK ABOUT THE STATE OF THE ECONOMY") { done = 1; xprintf("IT'S IN A BIT OF A MESS."); } else if (strcmp(data,"YOU THINK ABOUT THE STATE OF THE COUNTRY") { done = 1; xprintf("IT'S IN A BIT OF A MESS."); } else if (strcmp(data,"YOU THINK ABOUT THE ENGLISH CRICKET TEAM") { done = 1; xprintf("IT'S IN A BIT OF A MESS."); } else if (strcmp(data,"YOU THINK OF THE ENGLISH CRICKET TEAM") == { done = 1; xprintf("IT'S PRETTY HOPELESS!"); } else if (strcmp(data,"YOU THINK ABOUT RUGBY") == 0) { done = 1; xprintf("IT'S A GREAT GAME."); } else if (strcmp(data,"YOU THINK ABOUT ENGLISH RUGBY") == 0) { done = 1; xprintf("IT'S REALLY IMPROVING."); } } if (strstr(data,"I WAS ONLY ASKING") && !done) { done = 1; xprintf("OKAY."); }

== 0)

== 0)

== 0)

0)

if (strcmp(question,"HOW IS") == 0 && strcmp(object,"LIFE") == 0 && !done) { done = 1; xprintf("FINE THANKS.\n"); FEED(); } /* Personality code */ /* Operator has replied NO to a question */ if (query && strcmp(data,"NO") == 0 && !done) { done = 1; if (last_question == 5) { last_question = 0; xprintf("NOR DO I.\nI PREFER RUGBY.\n"); FEED(); query = 0; } else FEED(); } /* Parsed data. Operator is introducing them self */ if (strcmp(object,"NAME") == 0 && strcmp(who,"MY") == 0 && *subject) { done = 1; if (!*user) { strcpy(user,subject); xprintf("PLEASED TO MEET YOU %s.\n",user); } else xprintf("I DO REMEMBER YOUR NAME, %.\n",user); FEED(); }

if (strncmp("I AM ",data,4) == 0 && !done) { /* Personality code */ /* Operator has made a statement about them self */ if (!last_question) { if (*object_qualify) { xprintf("WHY ARE YOU %s?",object_qualify); query = 1; done = 1; } else { if (ft == -1 && !random(10)) ft = 0; done = 1; FEED(); } } else if (*object_qualify) { xprintf("WHY DO YOU THINK THAT IS?"); query = 1; done = 1; } else xprintf("HMMMM"); } /* Automatic response to standard phrase */ if (strcmp(data,"I DO NOT KNOW") == 0) { done = 1; if (query && last_question == 4) { xprintf("LET'S NOT GET METAPHYSICAL!"); last_question = 0; query = 0; }

else if (query) xprintf("WHAT DO YOU KNOW?"); else { xprintf("WHAT DON'T YOU KNOW?"); last_question = 4; } query = 1; } if (strncmp("BECAUSE ",data,8) == 0 && !done) { if (query || *action) { done = 1; if (strcmp(action,"SEE") == 0) FEED(); else if (!random(3)) xprintf("AND YOU'RE SURE THAT'S WHY?"); else xprintf("ARE YOU SURE?"); } else { done = 1; xprintf("I DON'T UNDERSTAND YOU."); } } if (strcmp(object,"WANT") == 0 && *who == 'I' && !done) { done = 1; WANT(); } if (strcmp(object,"NEED") == 0 && *who == 'I' && !done) { done = 1; NEED(); }

if (strcmp(object,"REQUIRE") == 0 && *who == 'I' && !done) { done = 1; REQUIRE(); } /* Semi-automatic reaction to strong phrase */ if (strstr(action,"HATE") && strstr(data,"HATE ")) { done = 1; if (strstr(data,"BECAUSE")) xprintf("ARE YOU SURE?"); else { data = strstr(data,"HATE "); data = strchr(data,32); data++; xprintf("WHY DO YOU FEEL SO STRONGLY ABOUT %s?",data); } query = 1; } if (strstr(action,"LOVE") && strstr(data,"LOVE ")) { done = 1; if (strstr(data,"BECAUSE")) xprintf("ARE YOU SURE?"); else { data = strstr(data,"LOVE "); data = strchr(data,32); data++; xprintf("WHY DO YOU FEEL SO STRONGLY ABOUT %s?",data); } query = 1; } /* Parsed decision requested */ if (strstr(subject," OR ") && !done) { done = 1; p = strchr(action,','); if (p) *p = 0; data = strstr(data,action); p = strstr(data," OR "); *p = 0;

/* replace any self references */ if (strftos(data) == NULL) { printf("\nNERVOUS BREAKDOWN!"); exit(0); } xprintf("WHAT DO YOU THINK WILL HAPPEN IF YOU %s?",data); query = 1; } if (*action && strcmp(who,"I") == 0 && *object && !*subject && !*question && !done) { done = 1; data = strchr(data,32); data++; if (strncmp("I ",data,2) == 0) data++; while(*data == 32) data++; p = &action[strlen(action) - 2]; if (strcmp(p,"ED") == 0) *p = 0; if (strcmp(object,"ONLY") == 0) xprintf("WHY DO YOU %s?",action); else if (*p) { /* Catch silly phrases! */ if (strncmp(action,"SHOULD",6) == 0) xprintf("WHY DO YOU THINK YOU %s?",data); else xprintf("WHY DO YOU %s?",data); } else xprintf("OH, REALLY?"); query = 1; }

if (strcmp(order,"TELL") == 0 && !done) { done = 1; xprintf("WHY IS IT SO IMPORTANT TO YOU?"); query = 1; } if (*action && strcmp(who,"I") == 0 && *object && *subject && !done) { done = 1; if (strcmp(object,"DON'T") == 0 || strcmp(object,"NOT") == 0) { p = strstr(data,"DO NOT"); if (p) p += 5; else { p = strstr(data,"DON'T"); p += 4; } data = strchr(p,32); data++; xprintf("WHY DON'T YOU %s?",data); query = 1; } else { data = strchr(data,32); data++; if (strncmp(action,"WENT",4) == 0) { /* catch past tense and trun around for questioning */ data = strrstr(data,"WENT"); xprintf("WHY DID YOU GO %s?",data); } else xprintf("WHY DO YOU %s?",data); query = 1; } }

if (*question == 0 && *order == 0 && strchr(data,',') == NULL && !done) { /* Statement */ if ((strstr(subject,"THOUGHT") || strstr(subject,"THINKS")) && *who != 'I') { xprintf("AND WHAT DO YOU THINK?"); done = 1; query = 1; return; } if (*number == 0) { if (strstr(data," ARE ")) { if (!*object && *object_qualify) strcpy(object,object_qualify); else if (!*subject && *object_qualify) strcpy(subject,object_qualify); done = 1; if (strcmp(who,"YOU")) { if (*subject) { if (*who == 0) xprintf("WHY DO YOU THINK %s ARE %s?", object,subject); else { if (*object_qualify) xprintf("WHAT'S SO %s ABOUT IT?", object_qualify); else xprintf("WHY DO YOU THINK THAT?"); } } else xprintf("WHAT MAKES YOU THINK THEY ARE %s?",object); }

else { p = strstr(data,object); memmove(data,p,strlen(p) + 1); while(*p && !strchr(",.;:!?",*p)) p++; *p = 0; xprintf("WHY DO YOU THINK I'M %s?",data); } query = 1; } else if (strstr(data," IS A ")) { if (!*object && *object_qualify) strcpy(object,object_qualify); else if (!*subject && *object_qualify) strcpy(subject,object_qualify); done = 1; if (strncmp("A ",data,2) == 0) xprintf("WHY DO YOU THINK A %s IS A %s?",object,subject); else if (strncmp("THE ",data,4) == 0) xprintf("WHY DO YOU THINK THE %s IS A %s?",object,subject); else xprintf("WHY DO YOU THINK %s IS A %s?",object,subject); query = 1; } else if (strstr(data," IS ")) { if (!*object && *object_qualify) strcpy(object,object_qualify); else if (!*subject && *object_qualify) strcpy(subject,object_qualify); done = 1;

if (*who == 0) { if (strcmp(subject,object)) { if (strstr(data,"IT IS NOT")) xprintf("NO?"); else xprintf("WHY DO YOU THINK %s IS %s?",object, subject); } else done = 0; } else { if (strcmp(who,"MY") == 0) { p = strstr(data,object); p += strlen(object); while (*p == 32) p++; if (strncmp("IS ",p,3) == 0) p += 3; while (*p == 32) p++; xprintf("WHY DO YOU THINK YOUR %s IS %s?",object,p); } else if (strcmp(who,"YOUR") == 0) xprintf("WHY DO YOU THINK MY %s IS %s?",object,subject); else { xprintf("I SEE.\n"); FEED(); } } query = 1; } }

else { if (strstr(data," ARE ")) { if (!*object && *object_qualify) strcpy(object,object_qualify); else if (!*subject && *object_qualify) strcpy(subject,object_qualify); done = 1; xprintf("WHY DO YOU THINK %s %s ARE %s?",number,object,subject); query = 1; } else if (strstr(data," IS A ")) { if (!*object && *object_qualify) strcpy(object,object_qualify); else if (!*subject && *object_qualify) strcpy(subject,object_qualify); done = 1; xprintf("WHY DO YOU THINK %s?",data); query = 1; } else if (strstr(data," IS ")) { if (!*object && *object_qualify) strcpy(object,object_qualify); else if (!*subject && *object_qualify) strcpy(subject,object_qualify); done = 1; xprintf("WHY DO YOU THINK %s %s IS %s?",number,object,subject); query = 1; } } }

if (strncmp("I USED TO THINK ",data,16) == 0 && !done) { done = 1; xprintf("WHAT MADE YOU CHANGE YOUR MIND?"); query = 1; } if (strncmp("I USED TO BE ",data,13) == 0 && !done) { done = 1; xprintf("BUT YOU'RE NOT ANY MORE?"); query = 1; } if (strcmp(action,"DIED") == 0 && strcmp(who,"MY") == 0 && *object && !done) { done = 1; if (*subject) xprintf("I AM SORRY TO HEAR THAT YOUR %s %s %s.",object, subject,action); else xprintf("I AM SORRY TO HEAR THAT YOUR %s %s.",object,action); xprintf("\nWERE YOU CLOSE?"); query = 1; } if (*who && *object && !*subject && !*action && !*question && !done) { done = 1; xprintf("PARDON?"); query = 0; }

if (*question && strcmp(who,"YOU") == 0 && !done) { /* Question asked of the computer */ /* Determine the type of question */ if (strncmp("WHAT",question,4) == 0) { /* The question is a WHAT type */ if (strcmp(action,"LIKE") == 0) { /* The operator has asked what something is like */ done = 1; if (strcmp(object,"WEATHER") == 0) { xprintf("QUITE WARM AND SUNNY."); } else xprintf("I'M NOT SURE."); } } } if (!done && *action && !*question && strcmp(who,"YOU") == 0) { done = 1; xprintf("YOU TELL ME!"); query = 0; } if (strcmp(who,"I,YOU") == 0 && *action && !done) { if (strftos(data) == NULL) { printf("\nNERVOUS BREAKDOWN!"); exit(0); } done = 1; strwhy(data); xprintf("%s",data); query = 1; } if (*action && *object && strcmp(who,"ME") == 0 && !done) { done = 1; xprintf("WHY DO YOU THINK %s %s YOU?",object,action); query = 1; }

if (strcmp(question,"WHY DO") == 0 && strstr(who,"YOU") && strstr(action,"ASK") && !done) { done = 1; xprintf("DOES IT ANGER YOU?"); query = 1; } if (*question && strstr(who,"YOU") && !done) { done = 1; /* Personality code */ n = random(10); switch(n) { case 0 : case 1 : xprintf("DOES IT MATTER?"); query = 1; break; case 2 : xprintf("THAT'S MY BUSINESS!"); query = 0; break; case 3 : xprintf("I'D RATHER NOT SAY."); query = 0; break; case 4 : xprintf("I DON'T WANT TO SAY."); query = 0; break; case case case case 5 6 7 8 : : : : xprintf("WHY DO YOU ASK?"); query = 1; break;

case 9 : xprintf("I GUESS THAT'S JUST ONE OF LIFES' LITTLE MYSTERIES."); query = 0; break; } }

if (!done && *question) { done = 1; if (*object_qualify) xprintf("WHAT'S SO %s ABOUT IT?",object_qualify); else { if (strcmp(who,"I") == 0) { if (*user && strcmp(question,"WHO AM") == 0) { xprintf("WELL, YOU TOLD ME YOU WERE %s.",user); query = 0; } else { xprintf("DON'T YOU KNOW?"); query = 1; } } else { xprintf("I DON'T KNOW."); query = 0; done = 1; } } } /* Personality code */ if (strcmp(object,"GOT") == 0 && strcmp(who,"I") == 0 && !done) { xprintf("BIG DEAL!"); done = 1; }

if (!done && (*action || *object_qualify)) { done = 1; if (strcmp(action,"ADMIRE") == 0) { if(strcmp(who,"I,MY") == 0) { xprintf("WHAT IS IT THAT YOU %s ABOUT YOUR %s?",action,object); } else xprintf("WHAT IS IT THAT YOU %s ABOUT %s?",action,object); } else { if (*action) { /* Change 1st person to 2nd person */ if(strftos(data) == NULL) { printf("\nNERVOUS BREAKDOWN!"); exit(0); } xprintf("%s?",data); } else xprintf("WHY DO YOU SAY THAT?"); } query = 1; } if (strcmp(data,"OKAY") == 0) { done = 1; xprintf("YES THANKS. HOW ABOUT YOU?"); query = 1; }

if (strcmp(who,"I") == 0 && *object_qualify && !done) { query = 1; done = 1; if (strcmp(object_qualify,"DRUNK") == 0) { xprintf("A BIT OF A PISS HEAD ARE YOU?"); } else xprintf("WHY DO YOU THINK YOU'RE %s",object_qualify); } if (strcmp(who,"I") == 0 && *size && !done) { done = 1; query = 1; if (strcmp(size,"FAT") == 0) xprintf("WHAT'S WRONG WITH BEING FAT?"); else xprintf("WHY DO YOU THINK YOU'RE %s",size); } if (strcmp(data,"YES") == 0 && !done) { done = 1; FEED(); } if (!done) { done = 1; if (strcmp(object,"THINK") == 0) { xprintf("WHY ARE YOU UNCERTAIN?"); query = 1; } else if (!query && fkn > 1) { n = random(fkn); if (strncmp("I ",known[n],2) == 0) { p = &known[n][2]; xprintf("YOU %s",p); } }

else { if (strcmp(object,"HELLO") == 0 || strcmp(object,"HI") == 0) { if(!*user) { last_question = 6; done = 1; if (!more) { xprintf("WHO ARE YOU?"); query = 1; } } else xprintf("HELLO %s",user); } else { if (last_question == 6) { strcpy(user,data); xprintf("%s. IS THAT YOUR NAME?",user); last_question = 7; query = 1; done = 1; } else { if (last_question == 8) { last_question = 0; xprintf("OH YES."); } else xprintf("I SEE."); query = 0; } } } } }

void main() { char copy[100]; char a[80]; char b[80]; char c[80]; char d[80]; char e[80]; char f[80]; char g[80]; char h[80]; char i[80]; char j[80]; char k[80]; char tense; char *p; char quit;

/* /* /* /* /* /* /* /* /* /* /* /*

question */ order */ object/directive */ object qualifier */ subject */ number */ action */ location/direction */ Colour */ size */ who */ Tense, past present or future */

echo = fopen("george.con","w+"); query = 0; fc = 0; ft = -1; fkn = -1; cf = 0; randomize(); clrscr(); printf("Servile Software. Demonstration of the Probert Parser\n\n"); xprintf("HI! I'M GEORGE...."); do { quit = 0; xprintf("\n?"); gets(data); if (*data == 0) quit = 1; else { strupr(data); fprintf(echo,"%s\n",data); p = &data[strlen(data) - 1]; while(strchr(" ?.,!",*p)) *p-- = 0;

do { more = 0; strcpy(copy,data); p = strpbrk(data,"?.,"); if (p) { *p = 0; more = 1; } NLP(data,a,b,c,d,e,f,g,h,i,j,k,&tense); RESPOND(data,a,b,c,d,e,f,g,h,i,j,k,&tense); if (p) { strcpy(data,copy); p++; memmove(data,p,strlen(p) + 1); p = data; while(*p == 32) p++; memmove(data,p,strlen(p) + 1); } } while(more && !query); } } while(!quit); } George follows a fairly simple path in determing how to respond to an entry. First, the entry is received from the operator. The entry is then parsed into components by function NLP(). This includes expanding common abbreviations. The parsed entry data is then passed to the response function, RESPOND(). The response function removes any trailing punctuation from the entry string. A long list of tests then occurrs on the entry text. These tests determine the nature of the entered text. It may be a question, a statement or a response to a question asked by George. George's personality comes from programmed responses to particular subjects. The demonstration listing checks for references to family members, and responds accordingly. It would be easy to add checks for references to other subjects and generate feeder responses accordingly. A small degree of intelligence is built into George by the 'query' and 'last_question' variables. These record that a question has been asked of the

human operator, and which question it was. Further sophistication at this level would improve the quality of dialogue that George produces. Here is a transcript of a conversation with George; computer: HI! I'M GEORGE.... human: HI GEORGE, I'M MATTHEW

computer: PLEASED TO MEET YOU MATTHEW. human: FANCY A BEER?

computer: I SEE. human: WELL, DO YOU?

computer: I SEE.DOES IT MATTER? human: I ONLY ASKED

computer: WHY DO YOU ASK? human: BECAUSE I WANT A BEER

computer: ARE YOU SURE? human: YES

computer: I SEE. human: HOW ABOUT A CUP OF TEA?

computer: I DON'T KNOW. human: I FEEL AWFUL

computer: WHY DO YOU FEEL AWFUL? human: I WENT TO THE PUB LAST NIGHT.

computer: WHY DID YOU GO TO THE PUB LAST NIGHT? human: TO RELAX

computer: I SEE.

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 subbranch 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

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 <stdio.h> #include <string.h> #include <stdlib.h> char text[250]; char f_last[250]; /* User input */ /* 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 SUBJECT SUBJECT SUBJECT SUBJECT SUBJECT psubj; predicate; clause1; clause2; clause3; 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 <stdio.h> #include <string.h> extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern char text[]; char *facts[]; int fact_ptr; 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;

/* 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 <string.h> #include <alloc.h> typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern void void void void char void SUBJECT psubj; SUBJECT predicate; SUBJECT clause1; SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; tense;

HOW_YOU(void); NLP(void); say(char *, ...); strins(char *, char *); *strrstr(char *, char *); 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 <string.h> #include <alloc.h> typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern void void void void char void SUBJECT psubj; SUBJECT predicate; SUBJECT clause1; SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; tense;

NLP(void); WHERE_YOU(void); say(char *, ...); strins(char *, char *); *strrstr(char *, char *); 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 <string.h> #include <alloc.h> typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern void void void void char void SUBJECT psubj; SUBJECT predicate; SUBJECT clause1; SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; tense;

NLP(void); WHEN_YOU(void); say(char *, ...); strins(char *, char *); *strrstr(char *, char *); 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 <string.h> #include <alloc.h> typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern void char void void void char void SUBJECT psubj; SUBJECT predicate; SUBJECT clause1; SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; tense;

WHY_DO_YOU(void); GET_FATT(char *, char *); NLP(void); say(char *, ...); strins(char *, char *); *strrstr(char *, char *); 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 <string.h> #include <alloc.h> typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern void char void void void char void SUBJECT psubj; SUBJECT predicate; SUBJECT clause1; SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; tense;

HAVE_YOU(void); GET_FATT(char *, char *); NLP(void); say(char *, ...); strins(char *, char *); *strrstr(char *, char *); 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 <string.h> #include <alloc.h> typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern SUBJECT psubj; SUBJECT predicate; SUBJECT clause1; SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; char fflags[]; int fact_ptr; char stk[50][40]; int stp; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; 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 <string.h> #include <alloc.h> typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern void void void void char SUBJECT psubj; SUBJECT predicate; SUBJECT clause1; SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; tense;

NLP(void); TOPAST(char *); say(char *, ...); strins(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 <string.h> #include <alloc.h> 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 extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern

SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; 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 { say("YOU return; } if (yes == 1 { say("YOU return; } if (yes == 1 { say("YOU return; } if (yes == 0 { say("YOU return; } && attitude == 1) LIKE %s AND %s.",object,subject); && attitude == -1) LIKE %s.",object); && attitude == 0) LIKE %s, BUT YOU DON'T LIKE %s.",object,subject); && attitude == 1) LIKE %s, BUT YOU DON'T LIKE %s.",subject,object);

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 #include #include #include IS() */ <stdio.h> <string.h> <stdlib.h>

typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern char stk[50][40]; extern int stp; extern extern extern extern extern extern SUBJECT SUBJECT SUBJECT SUBJECT SUBJECT SUBJECT psubj; predicate; clause1; clause2; clause3; clause4;

extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern

char text[]; char *facts[]; char fflags[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; 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 <string.h> #include <alloc.h> typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern void char void void void char void SUBJECT psubj; SUBJECT predicate; SUBJECT clause1; SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; tense;

WILL_YOU(void); GET_FATT(char *, char *); NLP(void); say(char *, ...); strins(char *, char *); *strrstr(char *, char *); 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 <string.h> #include <alloc.h> static char negative; typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern SUBJECT psubj; SUBJECT predicate; SUBJECT clause1; SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; 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 void void char void void

WANT(void); WANTGO(void); say(char *, ...); *strftos(char *); strins(char *, char *); 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") == { WANT(); return; } if (strncmp(psubj.text,"LIKE",4) { ISTATE(); return; } if (strncmp(psubj.text,"LOVE",4) { ISTATE(); return; } if (strncmp(psubj.text,"HATE",4) { ISTATE(); return; } } 0)

== 0)

== 0)

== 0)

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); } ARE GOING %s?",string); 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 <string.h> #include <alloc.h> static char negative; typedef struct { char text[240]; char object[80]; char description[80]; char action[80]; } SUBJECT; extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern SUBJECT psubj; SUBJECT predicate; SUBJECT clause1; SUBJECT clause2; SUBJECT clause3; SUBJECT clause4; char text[]; char *facts[]; int fact_ptr; char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; 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 void void void char void void

YOU_SHOULD(void); YOU_WANT(void); YOU_WANT_GO(void); say(char *, ...); *strftos(char *); strins(char *, char *); 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 <string.h> #include <alloc.h> extern char text[]; extern char *facts[]; extern int fact_ptr; extern extern extern extern extern extern extern extern extern extern extern extern char char char char char char char char char char char char question[80]; order[80]; object[80]; description[80]; subject[80]; number[80]; action[80]; location[80]; colour[80]; size[80]; who[80]; 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 <stdio.h> #include <string.h> #include <alloc.h> #include <ctype.h> /*-----------*/ /* Constants */ /*-----------*/ #define num_a #define num_p #define num_q #define num_l #define num_n #define num_c #define num_f #define num_qs #define num_s #define num_o

141 12 41 20 37 14 17 19 9 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 extern extern extern extern extern extern extern extern extern extern extern extern extern extern char text[]; 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; SUBJECT psubj; SUBJECT predicate;

extern extern extern extern

SUBJECT SUBJECT SUBJECT SUBJECT

clause1; clause2; clause3; 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 char char char char *p; n; word[50]; pre[50]; 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 { 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 { 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; } }

== 32)

== 32)

"); ");

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 = &copy[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 if (stricmp(p,"CAN") == 0 || stricmp(p,"MAY") == 0 || stricmp(p,"SHALL") == 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);

such as "CAN" and "MAY" and "DO" */ stricmp(p,"WILL") == 0 || stricmp(p,"DO") == 0 || || stricmp(p,"SHOULD") == 0)

} 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 char char char char *p; *q; word[40]; save; 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); }

TOOLS When working with natural language, there are some operations which are required time and again. Conversational computing requires the ability to convert a phrase between the first and second person, as in; "I am going to Italy for my holidays" and its response "Why are you going to Italy for your holidays?". Sometimes you will need to convert a phrase into a question. In doing so you may encounter the problem of "DO NOT", as in; "I do not like fish", which as an interrogative response is "Why do you not like fish?" Notice that the first person "I" has been changed to the second person "you". Also the "I do not" phrase has been rearranged to "Why do you not". Include files for an NLP toolbox; #include #include #include #include <stdio.h> <alloc.h> <ctype.h> <string.h>

Truncating a string by a specified length; void truncstr(char *p,int num) { /* Truncate string by losing last num characters */ if (num < strlen(p)) p[strlen(p) - num] = 0; } Changing a string's length by inserting or deleting characters; 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); } }

Inserting one string into another string; void strins(char *p, char *q) { /* Insert string q into p */ strlench(p,strlen(q)); strncpy(p,q,strlen(q)); } Replacing all occurences of one substring within a string with another substring; 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); } Switching between first and second person references in a string;

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 char char char *copy; *p; *q; *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 = &copy[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); /* Transfer copy to data */ strcpy(data,copy); /* Free memory */ free(copy); return data; } Correcting gramatical errors which may occur when converting phrases; 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); } }

Converting a statement into a 'WHY?' question; 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,"? "); } Locate a substring within a string, and return a pointer to the first word following that substring; 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); }

A demonstration function which calls the toolbox functions; main() { char data[80]; char *p; clrscr(); strcpy(data,"My name is Michael Caine!"); p = strftos(data); if (p == NULL) printf("\nUnable to change string"); else printf("\n%s",data); strcpy(data,"My boyfriend made me come to see you."); p = strftos(data); if (p == NULL) printf("\nUnable to change string"); else printf("\n%s",data); strcpy(data,"I used to think that I was ugly, but now I am not so sure"); p = strftos(data); if (p == NULL) printf("\nUnable to change string"); else printf("\n%s",data); strcpy(data,"I think you are a bit of a wally!"); p = strftos(data); if (p == NULL) printf("\nUnable to change string"); else printf("\n%s",data); strcpy(data,"MY FRIEND MADE ME COME TO SEE YOU"); p = strftos(data);

if (p == NULL) printf("\nUnable to change string"); else printf("\n%s",data); strcpy(data,"WHY I DO NOT LIKE YOU ANYMORE"); p = strftos(data); if (p == NULL) printf("\nUnable to change string"); else { strcor(data); printf("\n%s",data); } strcpy(data,"I DO NOT LIKE YOU, ANYMORE, you big prat!"); p = strftos(data); if (p == NULL) printf("\nUnable to change string"); else { strwhy(data); printf("\n%s",data); } strcpy(data,"WHAT IS THE MATTER WITH THE ECONOMY?"); printf("\n%s",strrstr(data,"THE MATTER")); }

APPENDIX NOTES FOR PROGRAMMERS

The programming examples contained in this book are all written in Borland's Turbo C. They are ninety-nine percent compatible with the ANSI standard. However, the clrscr() function is not standard C. All it does within the listed examples is clear the display.

REFERENCES

"A dictionary of modern English usage", H. W. Fowler & Sir Ernest Gowers, Oxford University press (2nd edition 1965) "Artficial Intelligence An introduction", Alan Garnham, Routledge & Kegan Paul (1988) "Artificial intelligence and Natural Man", Margaret Boden, Harvester Press (1977) "Artificial Intelligence through search", Christopher James Thornton & Benedict du Boulay, Intellect (1992) "Chaos", James Gleick, Cardinal (1987) "Cassell English usage", Tim Storrie, & James Matson, Cassell (1991) "Games people play", Eric Berne, Penguin (1968) "Knowledge systems design", John K. Debenham, Prentice Hall (1989) "More than words", Richard Dimbleby & Graeme Burton, Methuen (1985) "New adventure systems for the Spectrum", S. Robert Speel, Fontana (1984) "Psychology", Douglas A. Bernstein, Edward J. Roy, Thomas K. Srull & Christopher D. Wickens, Houghton Mifflin (1988) "Speech and language-based interaction with machines", John A. Waterworth & Mike Talbot, Ellis Horwood (1987) "The hitch-hiker's guide to artificial intelligence", Richard Forsyth & Chris Naylor, Chapman and Hall/Methuen (1985)

You might also like