; NOTE: ; McSAM must be read in before McELI, so that the readmacro for "?" ; will be defined. ; The sentences and their parses for McELI (SETQ STORY-TEXT '( (JACK WENT TO THE STORE) (HE GOT A KITE) (HE WENT HOME] ; "He" is ignored since McSAM has no provision for ; matching "male person" against Jack. This means that ; "He got a kite" is parsed into just the ATRANS of a kite. ; The missing ACTOR, TO and FROM slots are filled in by McSAM ;********************************************************************** ; THE TOP-LEVEL STORY UNDERSTANDER ; McSAM and McELI -- together at last ;********************************************************************** ; DO-STORY takes a list of sentences in list form, such as STORY-TEXT. ; For each sentence, it calls McELI to get a conceptual analysis, ; then it calls McSAM to process the analysis. It pauses before each ; phase. Type GO when asked. At the end, the instantiated ; script form is printed. (DE DO-STORY (STORY) (CLEAR-SCRIPTS) (LOOP [INITIAL SENTENCE NIL CONCEPT NIL] [WHILE (SETQ SENTENCE (POP STORY] [DO (MSG T T "Type GO to start McELI ") (READ) (MSG T "Parsing " SENTENCE) (SETQ CONCEPT (PARSE SENTENCE)) (MSG T T "Final concept") (SPRINT CONCEPT 1) (MSG T T "Type GO to start McSAM ") (READ) (PROCESS-LINE CONCEPT) ] ) (ADD-STM *CURRENT-SCRIPT*) (MSG T T "Story done -- the data base is") (SPRINT *DATA-BASE* 4] ;********************************************************************** ; McELI: THE ENGLISH LANGUAGE INTERPRETER ;********************************************************************** ; The heart of McELI is the variable *STACK*. *STACK* is a list of ; packets of things that McELI is prepared to do. For example, ; after McELI has analyzed the verb "go" into PTRANS, it prepares for ; filling in the TO slot by putting a packet on *STACK* that says ; "look for 'to ' OR look for 'home'". Notice that a packet ; is a list of alternative situateions that may arise. An alternative ; is called a REQUEST and has this format: ; ((TEST predicate) ; (ASSIGN variable1 expression1 ; variable2 expression2 ...) ; (NEXT-PACKET request1 request2...)) ; -- all three fields are optional ; -- the dictionary near the end of this file shows how words are ; defined with packets of requests ; The flow of control during analysis is: ; 1) read a word and put its packet on the front of *STACK* ; 2) take the first packet in *STACK* ; take the first request in it whose test evaluates to true ; if there are none, go to step 3 ; otherwise, remove the packet from *STACK* ; execute the assignments in the request ; save the request in the list TRIGGERED ; go to step 2 ; 3) take each request saved in TRIGGERED and if it has a ; NEXT-PACKET clause then add the requests specified to *STACK* ; go to step 1 ; Note that only the first packet in *STACK* is checked. If no ; request in it is triggered, then no more packets are checked and the ; next word is read. Also note that new packets are added in front of ; the pending ones. *STACK* is a true "stack" or LIFO (last in, first ; out) data-control structure. The first element in the list *STACK* ; is called the "top" of the stack. ; The following variables are used by McELI: ; *SENTENCE* -- the sentence being analyzed ; *WORD* -- the current word being analyzed ; *CONCEPT* -- the CD form for the whole sentence ; *PREDICATES* -- the list of predicates describing the tokens built ; *STACK* -- the list of pending packets ; The following variables are English-oriented -- they are used only ; by the dictionary entries, not by the central McELI functions -- the ; pseudo-word *START* (see the dictionary) clears them at the start of ; a sentence: ; *PART-OF-SPEECH* -- the current part of speech ; *CD-FORM* -- the current conceptual dependency form ; *SUBJECT* -- the CD form for the subject of the sentence ; (SPECIAL *SENTENCE* *WORD* *PART-OF-SPEECH* *CD-FORM* ; *CONCEPT* *SUBJECT* *PREDICATES* *STACK*) ;********************************************************************** ; DATA STRUCTURES ;********************************************************************** ; McELI uses a stack for control -- the top of the stack is the first ; element in the list (DE TOP-OF (STACK) (CAR STACK] ; ADD-STACK puts a packet at the front of the list of pending packets (DE ADD-STACK (PACKET) (AND PACKET (SETQ *STACK* (CONS PACKET *STACK*))) PACKET] ; Word definitions are stored under the property DEFINITION. ; LOAD-DEF adds a word's request packet to the stack. (DE LOAD-DEF () (LET (PACKET (GET *WORD* 'DEFINITION)) (COND (PACKET (ADD-STACK PACKET)) ( T (MSG " -- not in the dictionary"] ; REQ-CLAUSE gets clauses from the format ; ((TEST ...) (ASSIGN ...) (NEXT-PACKET ...)) ; -- NOTE: this definition depends on CDR NIL = NIL (DE REQ-CLAUSE (KEY L) (CDR (ASSOC KEY L] ;********************************************************************** ; PROGRAM ;********************************************************************** ; PARSE takes a sentence in list form -- e.g., (JACK WENT TO THE STORE) ; -- and returns the conceptual analysis for it. It sets *SENTENCE* ; to the input sentence (e.g., (JACK WENT TO THE STORE)) with ; the atom *START* stuck in front. *START* is a pseudo-word in the ; dictionary with information useful for starting the analysis. ; PARSE takes *SENTENCE* one word at a time, loads the packet ; for that word (if any), and then checks the top packet on the stack ; to see if any request in it has a true test. ; During the analysis, the variable *CONCEPT* will be set to the ; main concept of the sentence (usually it will be set by the main ; verb's requests). Since McELI builds CD forms with variables in them, ; McELI has to remove these variables when the sentence is finished, so ; that McSAM can use the CD form produced. It uses the function ; REMOVE-ELI-VARIABLES to do this. ; When noun group tokens are built, predications like ; (PERSON (OBJECT JACK1)) are saved in *PREDICATES*. Hence the ; analysis is really the union of *PREDICATES* and *CONCEPT*. (DE PARSE (SENTENCE) (SETQ *CONCEPT* NIL) (SETQ *PREDICATES* NIL) (SETQ *STACK* NIL) (LOOP [INITIAL *WORD* NIL *SENTENCE* (CONS '*START* SENTENCE) ] [WHILE (SETQ *WORD* (POP *SENTENCE*] [DO (MSG T T "Processing " *WORD*) (LOAD-DEF) (RUN-STACK) ] [RESULT (APPEND1 *PREDICATES* (REMOVE-ELI-VARIABLES *CONCEPT*] ] ; RUN-STACK: ; As long as some request in the expectation packet on top of ; the stack can be triggered, the whole packet is removed from the ; stack, and that request is executed and saved. ; When the top packet does not contain any triggerable requests, ; the packets in the requests that were executed and saved (if ; any) are added to the stack (DE RUN-STACK () (LOOP [INITIAL REQUEST NIL TRIGGERED NIL] [WHILE (SETQ REQUEST (CHECK-TOP *STACK*] [DO (POP *STACK*) (DO-ASSIGNS REQUEST) (SETQ TRIGGERED (CONS REQUEST TRIGGERED)) ] [RESULT (ADD-PACKETS TRIGGERED] ] ; CHECK-TOP gets the first request in the packet on top of the stack ; with a true test (if any) (DE CHECK-TOP (STACK) (COND (STACK (LOOP [INITIAL REQUEST NIL PACKET (TOP-OF STACK] [WHILE (SETQ REQUEST (POP PACKET] [UNTIL (IS-TRIGGERED REQUEST] [RESULT REQUEST] ] ; IS-TRIGGERED returns true if a request has no test at all or if the ; test evaluates to true (DE IS-TRIGGERED (REQUEST) (LET (TEST (REQ-CLAUSE 'TEST REQUEST)) (OR (NULL TEST) (EVAL (CAR TEST] ; DO-ASSIGNS sets the variables given in the ASSIGN clause ; -- the first POP gets a variable and the second POP gets the value ; following it. (DE DO-ASSIGNS (REQUEST) (LOOP [INITIAL ASSIGNMENTS (REQ-CLAUSE 'ASSIGN REQUEST] [WHILE ASSIGNMENTS] [DO (REASSIGN (POP ASSIGNMENTS) (POP ASSIGNMENTS)] ] ; REASSIGN set VAR to the value of VAL and prints a message saying ; it did it (DE REASSIGN (VAR VAL) (COND ((SET VAR (EVAL VAL)) (MSG T " " VAR " = ") (SPRINT (EVAL VAR) (POSN] ; ADD-PACKETS takes a list of requests and adds the packets ; attached to them to the stack (DE ADD-PACKETS (REQUESTS) (LOOP [INITIAL REQUEST NIL] [WHILE (SETQ REQUEST (POP REQUESTS] [DO (ADD-STACK (REQ-CLAUSE 'NEXT-PACKET REQUEST] ] ; REMOVE-ELI-VARIABLES removes all the parser variables from a CD pattern ; -- the function EVAL gets the bindings of McELI's variables (DE REMOVE-ELI-VARIABLES (CD-FORM) (REMOVE-VARIABLES CD-FORM 'EVAL] ;********************************************************************** ; TOKEN BUILDING FUNCTIONS ;********************************************************************** ; MAKE-TOKEN returns a new token from NAME, adding the given ; predications to *PREDICATES* ; -- for example "a man" would call (MAKE-TOKEN '(PERSON) 'MAN) ; which would return MAN1, saving the fact that MAN1 is a PERSON (DE MAKE-TOKEN (PREDICATES NAME) (SAVE-PREDICATES (NEW-NAME NAME) PREDICATES] ; GET-TOKEN is like MAKE-TOKEN, but it reuses the last new token ; generated for NAME -- for example,"the man" would call ; (GET-TOKEN '(PERSON) 'MAN), returning MAN1 as generated above ; -- if McELI didn't do this, then every time it parsed "the man" ; it would get a new token (DE GET-TOKEN (PREDICATES NAME) (SAVE-PREDICATES (OLD-NAME NAME) PREDICATES] ; SAVE-PREDICATES saves the predications of a token on the list ; *PREDICATES* -- for example, (SAVE-PREDICATES 'KITE1 (KITE RED)) ; would save (KITE (OBJECT KITE1)) and (RED (OBJECT KITE1)) on the ; list *PREDICATES* -- TOKEN is returned (DE SAVE-PREDICATES (TOKEN PREDICATES) (LOOP [INITIAL PREDICATE NIL] [WHILE (SETQ PREDICATE (POP PREDICATES] [DO (SETQ *PREDICATES* (CONS (LIST PREDICATE (LIST 'OBJECT TOKEN)) *PREDICATES*] [RESULT TOKEN] ] ;********************************************************************** ; NAME GENERATING FUNCTIONS ;********************************************************************** ; NEW-NAME increments the counter for generating a name (DE NEW-NAME (NAME) (MAKE-NAME NAME (ADD1 (OR (GET NAME 'NAME-COUNT) 0] ; OLD-NAME uses the current counter for generating a name (DE OLD-NAME (NAME) (MAKE-NAME NAME (OR (GET NAME 'NAME-COUNT) 1] ; MAKE-NAME concatenates an atom and a number, and saves the ; number under the atom -- in Rutgers LISP, *NOPOINT must be set to ; T to avoid decimal points in the number (DE MAKE-NAME (NAME COUNT) (INTERN (STRCONS NAME COUNT] ; The original definition in Rutgers LISP is as follows: ;(DE MAKE-NAME (NAME COUNT) ; (LET (*NOPOINT T) ; (READLIST (APPEND (EXPLODE NAME) ; (EXPLODE (PUTPROP NAME COUNT 'NAME-COUNT] (DE SPRINT (S COL) % Pretty-Print a symbolic expression , (PPAUX S COL 0 (LESSP COL (POSN))) ,,,) ;********************************************************************** ; THE DICTIONARY ;********************************************************************** ; (DEF-WORD name request1 request2...) stores a definition ; under a word consisting of the list (request1 request2...) (DF DEF-WORD (L) (PUTPROP (CAR L) (CDR L) 'DEFINITION) (CAR L] ; HE is a noun phrase that produces an empty CD form (DEF-WORD HE ((ASSIGN *PART-OF-SPEECH* 'NOUN-PHRASE *CD-FORM* NIL] ; JACK is a noun phrase that means a person named Jack (DEF-WORD JACK ((ASSIGN *CD-FORM* (GET-TOKEN '(PERSON) *WORD*) *PART-OF-SPEECH* 'NOUN-PHRASE] ; GOT is a verb that means someone ATRANSed something to the subject. ; GOT looks for a noun phrase to fill the object slot. (DEF-WORD GOT ((ASSIGN *PART-OF-SPEECH* 'VERB *CD-FORM* '(ATRANS (ACTOR ?GET-VAR3) (OBJECT ?GET-VAR2) (TO ?GET-VAR1) (FROM ?GET-VAR3)) GET-VAR1 *SUBJECT* GET-VAR2 NIL GET-VAR3 NIL) (NEXT-PACKET ((TEST (EQUAL *PART-OF-SPEECH* 'NOUN-PHRASE)) (ASSIGN GET-VAR2 *CD-FORM*] ; WENT is a verb that means someone (the subject) PTRANSed himself to ; somewhere from elsewhere. WENT looks for "to " or ; "home" to fill the TO slot. (DEF-WORD WENT ((ASSIGN *PART-OF-SPEECH* 'VERB *CD-FORM* '(PTRANS (ACTOR ?GO-VAR1) (OBJECT ?GO-VAR1) (TO ?GO-VAR2) (FROM ?GO-VAR3)) GO-VAR1 *SUBJECT* GO-VAR2 NIL GO-VAR3 NIL) (NEXT-PACKET ((TEST (EQUAL *WORD* 'TO)) (NEXT-PACKET ((TEST (EQUAL *PART-OF-SPEECH* 'NOUN-PHRASE)) (ASSIGN GO-VAR2 *CD-FORM*)))) ((TEST (EQUAL *WORD* 'HOME)) (ASSIGN GO-VAR2 (GET-TOKEN '(HOUSE) *WORD*] ; A looks for a noun to build a noun phrase with a new token name (DEF-WORD A ((TEST (EQUAL *PART-OF-SPEECH* 'NOUN)) (ASSIGN *PART-OF-SPEECH* 'NOUN-PHRASE *CD-FORM* (MAKE-TOKEN *CD-FORM* *WORD*] ; THE looks for a noun to build a noun phrase with a new token name (DEF-WORD THE ((TEST (EQUAL *PART-OF-SPEECH* 'NOUN)) (ASSIGN *PART-OF-SPEECH* 'NOUN-PHRASE *CD-FORM* (GET-TOKEN *CD-FORM* *WORD*] ; KITE is a noun that builds the concept KITE (DEF-WORD KITE ((ASSIGN *PART-OF-SPEECH* 'NOUN *CD-FORM* '(KITE] ; STORE is a noun that builds the concept STORE (DEF-WORD STORE ((ASSIGN *PART-OF-SPEECH* 'NOUN *CD-FORM* '(STORE] ; *START* is loaded at the start of each sentence. It looks for ; a noun phrase (the subject) followed by a verb (the main concept) (DEF-WORD *START* ((ASSIGN *PART-OF-SPEECH* NIL *CD-FORM* NIL) (NEXT-PACKET ((TEST (EQUAL *PART-OF-SPEECH* 'NOUN-PHRASE)) (ASSIGN *SUBJECT* *CD-FORM*) (NEXT-PACKET ((TEST (EQUAL *PART-OF-SPEECH* 'VERB)) (ASSIGN *CONCEPT* *CD-FORM*]