Files
elizagen.org/1965_Weizenbaum_MAD-SLIP/ELIZA_transcription_annotated_20220216.txt
jeffshrager 2f31df515e no message
2022-02-21 14:40:00 -08:00

909 lines
54 KiB
Plaintext

;
; ELIZA by Joseph Weizenbaum.
;
; Any line beginning with a semicolon is commentary and was not part of
; the original ELIZA code. Each commentary block generally refers to the
; code just above it.
;
CHANGE MAD
EXTERNAL FUNCTION (KEY,MYTRAN) 000010
NORMAL MODE IS INTEGER 000020
ENTRY TO CHANGE. 000030
LIST.(INPUT) 000040
V'S G(1)=$TYPE$,$SUBST$,$APPEND$,$ADD$, 000050
1$START$,$RANK$,$DISPLA$ 000060
V'S SNUMB = $ I3 *$ 000070
FIT=0 000080
CHANGE PRINT COMMENT $PLEASE INSTRUCT ME$ 001400
LISTRD.(MTLIST.(INPUT),0) 001410
JOB=POPTOP.(INPUT) 001420
T'H IDENT, FOR J=1,1, J.G. 7 001430
IDENT W'R G(J) .E. JOB, T'O THEMA 001440
PRINT COMMENT $CHANGE NOT RECOGNIZED$ 001450
T'O CHANGE 001460
THEMA W'R J .E. 5, F'N IRALST.(INPUT) 001470
W'R J .E. 7 001480
T'H DISPLA, FOR I=0,1, I .G. 32 001490
W'R LISTMT.(KEY(I)) .E. 0, T'O DISPLA 001500
S=SEQRDR.(KEY(I)) 001510
READ(7) NEXT=SEQLR.(S,F) 001520
W'R F .G. 0, T'O DISPLA 001530
PRINT COMMENT $*$ 001540
TPRINT.(NEXT,0) 001550
PRINT FORMAT SNUMB,I 001560
PRINT COMENT $ $ 001570
T'O READ(7) 001580
DISPLA CONTINUE 001590
PRINT COMMENT $ $ 001600
PRINT COMMENT $MEMORY LIST FOLLOWS$ 001610
PRINT COMMENT $ $ 001620
T'H MEMLIST, FOR I=1 , 1, I .G. 4 001630
MEMLST TXTPRT.(MYTRAN(I),0) 001640
T'O CHANGE 001650
E'L 001660
THEME=POPTOP.(INPUT) 001670
SUBJECT=KEY(HASH.(THEME,5)) 001680
S=SEQRDR.(SUBJECT) 001690
LOOK TERM=SEQLR.(S,F) 001700
W'R F .G. 0, T'O FAIL 001710
W'R TOP.(TERM) .E. THEME, T'O FOUND 001720
T'O LOOK 001730
FOUND T'O DELTA(J) 001740
DELTA(1) TPRINT.(TERM,0) 001750
T'O CHANGE 001760
FAIL PRINT COMMENT $LIST NOT FOUND$ 001770
T'O CHANGE 001780
DELTA(2) S=SEQRDR.(TERM) 001790
OLD=POPTOP.(INPUT) 001800
READ(1) OBJCT=SEQLR.(S,F) 001810
W'R F .G. 0, T'O FAIL 001820
W'R F .NE. 0, T'O READ(1) 001830
INSIDE=SEQRDR.(OBJECT) 001840
READ(2) IT=SEQLR.(INSIDE,F) 001850
W'R F .G. 0, T'O READ(1) 001860
SIT=SEQRDR.(IT) 001870
SOLD=SEQRDR.(OLD) 001880
ITOLD TOLD=SEQLR.(SOLD,FOLD) 001890
DIT=SEQLR.(SIT,FIT) 001900
W'R TOLD .E. DIT .AND. FOLD .LE. 0,T'O ITOLD 001910
W'R FOLD .G. 0, T'O OK(J) 001920
T'O READ(2) 001930
OK(2) SUBST.(POPTOP.(INPUT),LSPNTR.(INSIDE)) 001940
T'O CHANGE 001950
OK(3) NEWBOT.(POPTOP.(INPUT),OBJCT) 001960
T'O CHANGE 001970
DELTA(3) T'O DELTA(2) 001980
DELTA(4) W'R NAMTST.(BOT.(TERM)) .E. 0 001990
BOTTOM=POPBOT.(TERM) 002000
NEWBOT.(POPTOP.(INPUT),TERM) 002010
NEWBOT.(BOTTOM,TERM) 002020
O'E 002030
NEWBOT.(POPTOP.(INPUT),TERM) 002040
E'L 002050
T'O CHANGE 002060
DELTA(6) S=SEQRDR.(TERM) 002070
READ(6) OBJCT=SEQLR.(S,F) 002080
W'R F .G. 0, T'O FAIL 002090
W'R F .NE. 0, T'O READ(6) 002100
OBJCT=SEQLL.(S,F) 002110
W'R LNKLL.(OBJECT) .E. 0 002120
SUBST.(POPTOP.(INPUT),LSPNTR.(S)) 002130
O'E 002140
NEWTOP.(POPTOP.(INPUT),LSPNTR.(S)) 002150
E'L 002160
T'O CHANGE 002170
R* * * * * * * * * * END OF MODIFICATION ROUTINE 002180
E'N 002200
TPRINT MAD
EXTERNAL FUNCTION (LST) 000010
NORMAL MODE IS INTEGER 000020
ENTRY TO TPRINT. 000030
SA=SEQRDR.(LST) 000040
LIST.(OUT) 000050
READ NEXT=SEQLR.(SA,FA) 000060
W'R FA .G. 0, T'O P 000070
W'R FA .E. 0, T'O B 000080
POINT=NEWBOT.(NEXT,OUT) 000100
W'R SA .L. 0, MRKNEG.(POINT) 000110
T'O READ 000120
B TXTPRT.(OUT,0) 000130
SEQLL.(SA,FA) 000140
MORE NEXT=SEQLR.(SA,FA) 000150
W'R TOP.(NEXT) .E. $=$ 000160
TXTPRT.(NEXT,0) 000170
T'O MORE 000180
E'L 000190
W'R FA .G. 0, T'O DONE 000200
PRINT COMMENT $ $ 000210
SB=SEQRDR.(NEXT) 000220
MEHR TERM=SEQLR.(SB,FB) 000230
W'R FB .L.0 000240
PRINT ON LINE FORMAT NUMBER, TERM 000250
V'S NUMBER = $I3 *$ 000260
T'O MEHR 000270
E'L 000280
W'R FB .G. 0, T'O MORE 000290
TXTPRT.(TERM,0) 000300
T'O MEHR 000310
P TXTPRT.(OUT,0) 000320
DONE IRALST.(OUT) 000330
F'N 000340
E'N 000350
LPRINT MAD
EXTERNAL FUNCTION (LST,TAPE) 006340
NORMAL MODE IS INTEGER 006350
ENTRY TO LPRINT. 006360
BLANK = $ $ 006370
EXECUTE PLACE.(TAPE,0) 006380
LEFTP = 606074606060K 006390
RIGHTP= 606034606060K 006400
BOTH = 607460603460K 006410
EXECUTE NEWTOP.(SEQRDR.(LST),LIST.(STACK)) 006420
S=POPTOP.(STACK) 006430
BEGIN EXECUTE PLACE.(LEFTP,1) 006440
NEXT WORD=SEQLR.(S,FLAG) 006450
W'R FLAG .L. 0 006460
EXECUTE PLACE.(WORD,1) 006470
W'R S .G. 0, PLACE.(BLANK,1) 006480
T'O NEXT 006490
OR W'R FLAG .G. 0 006500
EXECUTE PLACE.(RIGHTP,1) 006510
W'R LISTMT.(STACK) .E. 0, T'O DONE 006520
S=POPTOP.(STACK) 006530
T'O NEXT 006540
OTHERWISE 006550
W'R LISTMT.(WORD) .E. 0 006560
EXECUTE PLACE.(BOTH,1) 006570
T'O NEXT 006580
OTHERWISE 006590
EXECUTE NEWTOP.(S,STACK) 006600
S=SEQRDR.(WORD) 006610
T'O BEGIN 006620
E'L 006630
E'L 006640
DONE EXECUTE PLACE.(0,-1) 006650
EXECUTE IRALST.(STACK) 006660
FUNCTION RETURN LST 006670
END OF FUNCTION 006680
;
; TESTS(CAND, S) return a sequence reader if the keyword matches the user's
; input text, otherwise return 0.
;
; CAND is the keyword candidate transformation rule
; S is the sequence reader for the user INPUT text
;
; This function has 3 tasks
;
; 1. Test whether the whole candidate keyword matches the whole word
; in the user's input text.
; 2. If the words do match, make any keyword substitution specified
; in the candidate transformation rule.
; 3. Position the candidate reader past the substitution keyword, if any.
;
; SLIP packs 6 6-bit characters into each 36-bit IBM 7094 machine word.
; If a word has more than 6 characters it is continued into the next SLIP
; cell, with the first cell having its sign bit set. ???
;
; This code abstracts this full-word matching and has the side-effect
; of modifying the user's input text with the substitution word, if
; specified.
;
TESTS MAD
EXTERNAL FUNCTION(CAND,S) 000010
NORMAL MODE IS INTEGER 000020
DIMENSION FIRST(5),SECOND(5) 000030
ENTRY TO TESTS. 000040
STORE=S 000050
READER=SEQRDR.(CAND) 000060
T'H ONE, FOR I=0,1, I .G. 100 000070
FIRST(I)=SEQLR.(READER,FR) 000080
ONE W'R READER .G. 0, T'O ENDONE 000090
;
; Copy all 6-character chunks of the candidate keyword to the FIRST array.
;
; [As the loop termination condition is I .G. 100 (000070), this code will
; write past the end of the FIRST array if the keyword is longer than 36
; characters (because the first 36 characters will be copied to
; FIRST(0) .. FIRST(5), and any further characters will be written to
; machine words past FIRST(5)).]
;
ENDONE SEQLL.(S,F) 000100
T'H TWO, FOR J=0,1, J .G. 100 000110
SECOND(J)=SEQLR.(S,F) 000120
TWO W'R S .G. 0, T'O ENDTWO 000130
;
; Copy all 6-character chunks of the user input word to the SECOND array.
; [May write past the end of SECOND.]
;
ENDTWO W'R I .NE. J, F'N 0 000140
;
; If the keyword in FIRST has a different number of 6-character chunks to
; the word in SECOND the two words cannot be the same, so return the value 0,
; signifying no match.
;
; W'R is an abbreviation of WHENEVER
; .NE. means not equal
; F'N is an abbreviation of FUNCTION RETURN
;
T'H LOOK, FOR K=0,1, K.G. J 000150
LOOK W'R FIRST(K) .NE. SECOND(K), F'N 0 000170
;
; Compare each 6-character chunk of the keyword with the corresponding chunk
; of the user input word. If any are different, return 0, signifying no match.
;
EQL=SEQLR.(READER,FR) 000180
W'R EQL .NE. $=$ 000190
SEQLL.(READER,FR) 000200
F'N READER 000210
O'E 000220
;
; At this point we know that the keyword matches the user's word.
; Check whether the transformation rules specify a simple word substitution,
; signified by the presence of an "=".
;
; If it is not an "=", reposition the reader back before the element and
; return the reader, signifying a successful match.
;
POINT=LNKL.(STORE) 000230
T'H DELETE , FOR K=0,1, K .G. J 000240
REMOVE.(LSPNTR.(STORE)) 000250
DELETE SEQLR.(STORE,F) 000260
INSRT NEW=SEQLR.(READER,FR) 000270
POINT=NEWTOP.(NEW,POINT) 000280
MRKNEG.(POINT) 000290
W'R READER .L. 0, T'O INSRT 000300
MRKPOS.(POINT) 000310
F'N READER 000320
E'L 000330
E'N 000340
;
; An "=" was present in the transformation rule. E.g. a script
; transformation rule may begin
;
; (YOUR = MY
; ((0 MY 0)
; (WHY ARE YOU CONCERNED OVER MY 3)
; (WHAT ABOUT YOUR OWN 3)
; :
;
; Say at this point the keyword YOUR has been found in the user's input text
; and we know that in the transformation rule the keyword (YOUR) is followed
; by an "=". So we're now going to replace the YOUR in the input text with
; the word following the "=" in the transformation rule (MY, in this case).
;
; First delete all the 6-character chunks that comprise this word, then
; insert all the 6-character chunks that comprise the replacement word.
;
; Finally, return the reader, signifying a successful match.
;
DOCBCD MAD
EXTERNAL FUNCTION (A,B) 000010
NORMAL MODE IS INTEGER 000020
ENTRY TO FRBCD. 000030
W'R LNKL.(A) .E. 0, T'O NUMBER 000040
B=A 000050
F'N 0 000060
NUMBER K=A*262144 000070
B=BCDIT.(K) 000080
F'N 0 000090
E'N 000100
;
; ELIZA entry point.
;
ELIZA MAD
NORMAL MODE IS INTEGER 000010
DIMENSION KEY(32),MYTRAN(4) 000020
;
; KEY - A hashmap used to record keywords.
; KEY(0)..KEY(31) is the keyword->transformation rule hashmap
; KEY(32) is the "NONE" transformation rule
;
; MYTRAN - A hashmap used to record the MEMORY rules.
; MYTRAN(1)..MYTRAN(4) contain the four MEMORY rules.
;
; A note on MAD arrays: DIMENSION D(N) allocates N+1 machine-words of
; core memory, which are accessed using indexes 0..N.
;
INITAS.(0) 000030
;
; INITAS must be the first executable statement in any program using SLIP.
; Its purpose is to create the List of Available Space from all unused
; core memory. It does not require an argument, but here is given 0.
;
PRINT COMMENT $WHICH SCRIPT DO YOU WISH TO PLAY$ 000060
READ FORMAT SNUMB,SCRIPT 000070
;
; Display the message "WHICH SCRIPT DO YOU WISH TO PLAY".
;
; Note that the IBM 7090/7094 character set doesn't include a question
; mark glyph. Also $ is used to delimit character strings.
;
; SNUMB is the FORTRAN format string " I3 *", defined previously, which
; expects the user to enter up to 3 decimal digits. This number is assigned
; to the variable SCRIPT and will be used as the tape drive unit number
; where the ELIZA script is expected to reside.
;
LIST.(TEST) 000080
LIST.(INPUT) 000090
LIST.(OUTPUT) 000100
LIST.(JUNK) 000110
;
; Initialise four lists. These are:
; TEST - Used to store the parts of the user's text matching a
; decomposition rule.
; INPUT - During ELIZA startup the selected script is read into this list,
; one round-bracketed list at a time.
; During the conversation phase the text entered by the user is
; read into this list.
; OUTPUT - ELIZA's response sentence is constructed in this list.
; JUNK - A list used for temporary storage for several different purposes.
;
LIMIT=1 000120
;
; When Weizenbaum talks in the January 1966 CACM paper of a "certain counting
; mechanism", it is this to which he is referring. LIMIT has the value 1..4,
; in order, and then restarts at 1. The value changes to the next in the
; sequence at each user input. More on LIMIT below.
;
LSSCPY.(THREAD.(INPUT,SCRIPT),JUNK) 000130
MTLIST.(INPUT) 000140
;
; The THREAD function reads text from the tape unit specified by the integer
; SCRIPT into the INPUT list. The LSSCPY function copies the first list in
; that INPUT to the list named JUNK.
;
; The first list in an ELIZA script must be the hello message, e.g.
; (HOW DO YOU DO. PLEASE TELL ME YOUR PROBLEM).)
;
T'H MLST, FOR I=1,1, I .G. 4 000150
MLST LIST.(MYTRAN(I)) 000160
;
; Initialise each of the four MYTRAN array entries as a new list.
;
; T'H is an abbreviation for THROUGH
; .G. is the Boolean grater than operator
;
; Set I to 1, if I is greater than 4 stop looping, otherwise execute the code
; up to and including the statement labelled MLST. Then add 1 to I and return
; to the top of the loop at the point of the test to see if I is greater than
; 4 and repeat.
;
; for I in 1..4 {
; call function LIST with argument a reference
; to the Ith entry in the MYTRAN array
; }
;
MINE=0 000170
LIST.(MYLIST) 000180
;
; MINE - Set to 0 and is never changed. It's referenced once below. ???
; MYLIST - As memories are made using the MYTRAN MEMORY rules they are
; recorded in MYLIST. Here MYLIST is being initialised as a new
; empty list.
;
T'H KEYLST, FOR I=0,1, I .G. 32 000220
KEYLST LIST.(KEY(I)) 000230
;
; Initialise each of KEY(0) .. KEY(32) array entries as a new list.
; for I in 0..32 {
; call function LIST with argument a reference
; to the Ith entry in the KEY array
; }
;
R* * * * * * * * * * READ NEW SCRIPT 000240
BEGIN MTLIST.(INPUT) 000250
NODLST.(INPUT) 000260
LISTRD.(INPUT,SCRIPT) 000270
;
; Empty the INPUT list. Remove the description list??? from INPUT (NODLST).
; Read the next round-bracket-delimited list from tape unit id SCRIPT.
;
W'R LISTMT.(INPUT) .E. 0 000280
TXTPRT.(JUNK,0) 000290
MTLIST.(JUNK) 000300
T'O START 000310
E'L 000320
;
; W'R is an abbreviation of WHENEVER.
; .E. means equals.
; T'O is an abbreviation for TRANSFER TO.
; E'L is an abbreviation of END OF CONDITIONAL.
;
; An empty list signals the end of the ELIZA script. (Which is presumably
; why there is () on the last line of the published DOCTOR script.)
;
; if INPUT is the empty list {
; (the whole ELIZA script has now been read and processed)
; print the value of JUNK, e.g. "HOW DO YOU DO. PLEASE TELL ME YOUR PROBLEM"
; clear the JUNK list
; goto the START label
; }
;
W'R TOP.(INPUT) .E. $NONE$ 000330
NEWTOP.(LSSCPY.(INPUT,LIST.(9)),KEY(32)) 000340
T'O BEGIN 000350
;
; If this list is the special "NONE" list, just copy it unchanged into KEY(32)
; and then goto BEGIN to read the next list in the script.
;
; Recall that the NONE list in the DOCTOR script is:
; (NONE
; ((0)
; (I AM NOT SURE I UNDERSTAND YOU FULLY)
; (PLEASE GO ON)
; (WHAT DOES THAT SUGGEST TO YOU)
; (DO YOU FEEL STRONGLY ABOUT DISCUSSING SUCH THINGS)))
;
OR W'R TOP.(INPUT) .E. $MEMORY$ 000360
POPTOP.(INPUT) 000370
MEMORY=POPTOP.(INPUT) 000380
T'H MEM, FOR I=1,1, I .G. 4 000390
MEM LSSCPY.(POPTOP.(INPUT),MYTRAN(I)) 000400
T'O BEGIN 000410
;
; Otherwise, if this list is the special "MEMORY" list, process it into the
; four MYTRAN lists. Recall that the MEMORY list looks like this and is
; required to have exactly four transformation patterns:
; (MEMORY MY
; (0 YOUR 0 = LETS DISCUSS FURTHER WHY YOUR 3)
; (0 YOUR 0 = EARLIER YOU SAID YOUR 3)
; (0 YOUR 0 = BUT YOUR 3)
; (0 YOUR 0 = DOES THAT HAVE ANYTHING TO DO WITH THE FACT THAT YOUR 3))
;
; else if the first word in INPUT is "MEMORY" {
; assign the memory keyword (e.g. "MY") to the MEMORY variable
; for I in 1..4 {
; copy the Ith MEMORY pattern/reconstruction to MYTRAN(I)
; }
; goto the BEGIN label (continue reading the ELIZA script)
; }
;
O'E 000420
NEWBOT.(LSSCPY.(INPUT,LIST.(9)),KEY(HASH. 000430
1 (TOP.(INPUT),5))) 000440
T'O BEGIN 000450
E'L 000460
;
; Otherwise, the first word in the INPUT list is expected to be a keyword.
; Insert this keyword into the KEY hashtable, so that
; KEY(HASH(keyword)) -> list of transformation rules for keywords
; that hash to this entry in KEY (i.e. more than
; one keyword may hash to the same entry in KEY,
; so each entry in KEY may have zero, one or many
; keyword transformation rules associated with it.)
;
; (1 in column 11 signifies a continuation of the previous line.)
;
; The HASH function takes a word and a number (N) and returns a deterministic
; value between 0 and (2 to the power N)-1, in this case 0..31.
;
; else {
; HASH the keyword and append this transformation rule to the
; entry in KEY with that index
; goto the BEGIN label (continue reading the ELIZA script)
; }
;
; This is the end of the script reading code. When the script has been
; read and processed the script reader explicitly jumps to the START label
; to begin the user conversation.
;
R* * * * * * * * * * BEGIN MAJOR LOOP 000470
START TREAD.(MTLIST.(INPUT),0) 000480
;
; Wait for the user to type a sentence and read it into the INPUT list,
; which is first cleared. Presumably, tape unit 0 is the console.
;
; TREAD is the SLIP system text read function.
;
KEYWRD=0 000490
PREDNC=0 000500
;
; KEYWRD - This will be the keyword found to have the highest precedence.
; PREDNC - The precedence of the keyword. Precedence is specified in the
; ELIZA script. E.g. (DREAMS = DREAM 3 (=DREAM)), the keyword
; DREAMS is given the precedence value 3.
;
LIMIT=LIMIT+1 000510
W'R LIMIT .E. 5, LIMIT=1 000520
;
; Increment the value of LIMIT. If it then equals 5, set it back to 1.
; If we just read the very first user input, LIMIT will now have the value 2.
;
W'R LISTMT.(INPUT) .E. 0, T'O ENDPLA 000530
;
; If the user input is a blank line, goto the ENDPLA label.
; A blank user input tells ELIZA the conversation is over.
;
IT=0 000540
;
; IT - On exit from the scanning loop IT will either be the sequence
; reader for the selected transformation rule, or it will be 0
; indicating that no keyword was detected in the user's INPUT.
;
W'R TOP.(INPUT) .E. $+$ 000550
CHANGE.(KEY,MYTRAN) 000560
T'O START 000570
E'L 000580
;
; If first word of the user input is a "+" character, call the CHANGE
; function defined higher up in this code. This function allows the user
; to modify the current ELIZA script with the commands TYPE, SUBST,
; APPEND, ADD, START, RANK and DISPLA.
; After making any changes, return to the START label and carry on the
; conversation.
;
W'R TOP.(INPUT) .E. $*$, T'O NEWLST 000590
;
; If first word of the user input is a "*" character, goto the NEWLST label.
; NEWLST is defined later in this code. It inserts a new transformation rule,
; which the user will have given after the "*", into the current in-memory
; script and then returns to the START label to carry on the conversation.
;
S=SEQRDR.(INPUT) 000600
;
; Create the Slip sequence reader, S, for the user's INPUT list.
;
NOTYET W'R S .L. 0 000610
SEQLR.(S,F) 000620
T'O NOTYET 000630
;
; ???
;
O'E 000640
WORD=SEQLR.(S,F) 000650
W'R WORD .E. $.$ .OR. WORD .E. $,$ .OR. WORD .E. $BUT$ 000660
W'R IT .E. 0 000670
NULSTL.(INPUT,LSPNTR.(S),JUNK) 000680
MTLIST.(JUNK) 000690
T'O NOTYET 000700
O'E 000710
NULSTR.(INPUT,LSPNTR.(S),JUNK) 000720
MTLIST.(JUNK) 000730
T'O ENDTXT 000740
E'L 000750
E'L 000760
E'L 000770
;
; Set the variable WORD to the next word in the user's INPUT list. Then
; test that word to see if it's a delimiter.
;
; Note that in Weizenbaum's 1966 CACM paper, only comma and period were
; listed as delimiters. And yet the example conversation given in that
; paper could not be reproduced unless BUT is also a delimiter.
;
; Note that WORD is a 36-bit integer. Weizenbaum developed ELIZA between
; 1964 and 1966 on an IBM 7094, which has a 36-bit word and uses a 6-bit
; character encoding. Characters were packed 6 to a word. In Slip, character
; strings longer than six characters are stored in successive list cells.
; In this case WORD=SEQLR.(S,F) is assigning the first six characters of
; the next word in the user's INPUT text to the integer variable WORD.
; If the word had fewer than six characters they would be left justified
; with space characters padding to the right.
;
;
; else {
; if WORD is one of the delimiters ".", "," or "BUT" {
; if we have found no keywords in the INPUT so far (IT .E. 0) {
; discard all words in INPUT to the left of, and including, this
; delimiter
; goto NOTYET and continue scanning the rest of the user INPUT
; for keywords
; }
; else {
; discard all words in INPUT to the right of, and including, this
; delimiter
; goto ENDTXT; scanning of the user INPUT is now complete
; }
; }
; }
;
W'R F .G. 0, T'O ENDTXT 000780
;
; If there were no more words to read in the user INPUT list, goto the
; ENDTXT label; scanning of the user INPUT is now complete.
; (F will be 1 when the sequence reader has traversed the whole INPUT
; list and is back at the list header.)
;
I=HASH.(WORD,5) 000790
SCANER=SEQRDR.(KEY(I)) 000800
SF=0 000810
T'H SEARCH, FOR J=0,0, SF .G. 0 000820
CAND= SEQLR.(SCANER,SF) 000830
W'R SF .G. 0, T'O NOTYET 000840
SEARCH W'R TOP.(CAND) .E. WORD, T'O KEYFND 000850
;
; Is WORD a keyword? Try to locate it in the KEY hashmap.
;
; Recall that more than one keyword may hash to the same entry in KEY,
; so each entry in KEY is a list that may have zero, one or many keyword
; transformation rules associated with it. We need to look through this
; list to see if it contains a keyword that exactly matches WORD.
;
; HASH the WORD to get the index I in the KEY table where this word
; would have been stored, if it is a keyword
; loop {
; try to read the next candidate list from the hashmap entry KEY(I)
; if there isn't another candidate list {
; WORD didn't match any entries so it's not a keyword
; goto NOTYET to continue scanning the user's input text
; }
; if WORD is the same as the first entry in this candidate list {
; WORD is a keyword and CAND is the transformation rule for
; this keyword, so goto KEYFND
; }
; }
;
KEYFND READER=TESTS.(CAND,S) 000860
W'R READER .E. 0, T'O NOTYET 000870
;
; Call the TESTS function, defined higher up in this code.
;
; TESTS checks that the whole keyword matches the whole user INPUT word. It
; also performs any keyword substitution in the user INPUT. (e.g. (MY = YOUR))
;
; If TESTS returns 0 it means the keyword is not identical to the word in
; the user input, so goto NOTYET to continue scanning the user INPUT.
;
; [This suggests that keywords must differ in the first six characters.
; (Because TESTS is called only for the first keyword candidate in
; the KEY hashmap that matches the first six characters of the user's
; input word).]
;
W'R LSTNAM.(CAND) .NE. 0 000880
DL=LSTNAM.(CAND) 000890
SEQ W'R S .L. 0 000900
SEQLR.(S,F) 000910
T'O SEQ 000920
O'E 000930
NEWTOP.(DL,LSPNTR.(S)) 000940
E'L 000950
O'E 000960
E'L 000970
;
; ???
;
NEXT=SEQLR.(READER,FR) 000980
W'R FR .G. 0, T'O NOTYET 000990
;
; Read the next element in the rules associated with this keyword.
; If we are back at the rules header, the rules list was empty, so goto
; NOTYET to continue scanning the user INPUT.
;
W'R IT .E. 0 .AND. FR .E. 0 001000
PLCKEY IT=READER 001010
KEYWRD=WORD 001020
;
; 001000 If this is the first keyword we've encountered in the user's INPUT
; (IT .E. 0), and the first entry in the associated rules is a list
; rather than a value (FR .E. 0)???, i.e. there is no precedence associated
; with this keyword, then record the associated rules reader in IT and
; the found keyword in KEYWRD. Then goto NOTYET (001100) to continue
; scanning the user's input.
;
OR W'R FR .L. 0 .AND. NEXT .G. PREDNC 001030
PREDNC=NEXT 001040
NEXT=SEQLR.(READER,FR) 001050
T'O PLCKEY 001060
0'E 001070
T'O NOTYET 001080
E'L 001090
T'O NOTYET 001100
R* * * * * * * * * * END OF MAJOR LOOP 001110
;
; 001030 Otherwise, if the first entry in the associated rules is a value???
; (FR .L. 0), i.e. the precedence of this keyword, and that value is greater
; than the precedence of the previously found highest precedence keyword
; (NEXT .G. PREDNC), then record the new highest precedence value in PREDNC
; and move the rule reader past the precedence value, then goto PLCKEY to
; also record the reader in IT and the found keyword in KEYWRD. Finally, goto
; NOTYET (001100) to continue scanning the user's input.
;
; Note that this differs from Weizenbaum's CACM paper, where it says that
; keywords of higher precedence are added to the top of a keyword stack and
; keywords of lower precedence are added to the bottom of this stack. This
; also means this code does not support the "NEWKEY" functionality he
; describes.
;
; [Note that this code implies that keywords in the script should never
; specify a precedence value of 0. If they do they would never be used
; (because NEXT .G. PREDNC will never be true).]
;
; 001080 Otherwise, ignore this keyword and return to NOTYET to continue
; scanning the user's INPUT.
;
ENDTXT W'R IT .E. 0 001120
W'R LIMIT .E. 4 .AND. LISTMT.(MYLIST) .NE. 0 001130
OUT=POPTOP.(MYLIST) 001140
TXTPRT.(OUT,0) 001150
IRALST.(OUT) 001160
T'O START 001170
O'E 001180
ES=BOT.(TOP.(KEY(32))) 001190
T'O TRY 001200
E'L 001210
;
; 001120 If IT is 0 it means we did not find any keywords in the user's
; input, so we cannot construct a response from the user's input combined
; with any of the transformation rules in the script.
;
; Instead we do one of two things: either print one of the memories we
; previously recorded in MYLIST, if any, or we use one of the messages
; from the NONE list (which is recorded in KEY(32)).
;
; 001130 This is the mysterious "when a certain counting mechanism is in a
; particular state": recall a memory only if the memory list (MYLIST) isn't
; empty and LIMIT happens to have the value 4.
;
OR W'R KEYWRD .E. MEMORY 001220
I=HASH.(BOT.(INPUT),2)+1 001230
NEWBOT.(REGEL.(MYTRAN(I),INPUT,LIST.(MINE)),MYLIST) 001240
SEQLL.(IT,FR) 001250
T'O MATCH 001260
;
; Otherwise, we did find a keyword (IT .E. 0 is false). If the keyword is
; the MEMORY keyword ("MY" in the DOCTOR script), then add a new memory to
; MYLIST before we carry on processing the transformation rules associated
; with the matched keyword.
;
; In the 1966 CACM paper, Weizenbaum says the selection of one of the
; transformations on the MEMORY list is random. The code shows that the
; selection is determined by the HASH value of the last word in the user's
; input. This means ELIZA conversations are repeatable, not random. If we
; have the HASH algorithm we should be able to reproduce the exact
; conversation. (The HASH function is part of the SLIP system.)
;
O'E 001270
SEQLL.(IT,FR) 001280
;
; Otherwise, the keyword we found isn't the MEMORY keyword, so just position
; the transformation rule sequence reader past the keyword and fall through
; to the matching code.
;
R* * * * * * * * * * MATCHING ROUTINE 001290
MATCH ES=SEQLR.(IT,FR) 001300
W'R TOP.(ES) .E. $=$ 001310
S=SEQRDR.(ES) 001320
SEQLR.(S,F) 001330
WORD=SEQLR.(S,F) 001340
I=HASH.(WORD,5) 001350
SCANER=SEQRDR.(KEY(I)) 001360
SCAN ITS=SEQLR.(SCANER,F) 001370
W'R F .G. 0, T'O NOMATCH(LIMIT) 001380
W'R WORD .E. TOP.(ITS) 001390
S=SEQRDR.(ITS) 001400
SCANI ES=SEQLR.(S,F) 001410
W'R F .NE.0, T'O SCANI 001420
IT=S 001430
T'O TRY 001440
O'E 001450
T'O SCAN 001460
E'L 001470
E'L 001480
W'R FR .G. 0, T'O NOMATCH(LIMIT) 001490
;
; If this keyword is a link to another keyword, switch to that keyword.
;
; An ELIZA script rule may have the form (HOW (=WHAT)). If the keyword
; HOW appears in the user's input and this transformation rule is selected,
; ELIZA will use the transformation rule associated with the keyword WHAT
; to generate its response.
;
; read the next decomposition rule from the selected transformation rule
; if the decomposition rule starts with an "=" symbol {
; assign the word after the "=" to WORD
; lookup WORD in the KEY hashmap
; if WORD doesn't exist in the KEY hashmap {
; (presumably this indicates a logical inconsistency in the script)
; goto one of the NOMATCH(1) .. NOMATCH(4) labels to print
; a message such as "HMMM" and back to the main conversation loop
; which NOMATCH label is selected is determined by the value LIMIT
; happens to have at this time
; }
; else {
; position IT at first decomposition rule for the linked keyword
; goto the TRY label to try to apply the decomposition rule
; }
; }
; else if there were no (or no more) decomposition rules (FR .G. 0) {
; (does this indicate an incorrectly formed script?)
; goto one of the NOMATCH(1) .. NOMATCH(4) labels
; }
;
TRY W'R YMATCH.(TOP.(ES),INPUT,MTLIST.(TEST)) .E. 0,T'O MATCH 001500
;
; Attempt to match the current decomposition rule (TOP.(ES)) to the user's
; INPUT.
;
; If it doesn't match (YMATCH returns 0), goto MATCH to try the next
; decomposition rule in the current transformation rule set.
;
; If it does match, the list TEST will contain the decomposed matching parts
; of the INPUT text ready for reassembly. E.g. ???
;
; The YMATCH function is part of the SLIP system.
;
ESRDR=SEQRDR.(ES) 001510
SEQLR.(ESRDR,ESF) 001520
POINT=SEQLR.(ESRDR,ESF) 001530
POINTR=LSPNTR.(ESRDR) 001540
W'R ESF .E. 0 001550
NEWBOT.(1,POINTR) 001560
TRANS=POINT 001570
T'O HIT 001580
O'E 001590
T'H FNDHIT,FOR I=0,1, I .G. POINT 001600
FNDHIT TRANS=SEQLR.(ESRDR,ESF) 001610
W'R ESF .G. 0 001620
SEQLR.(ESRDR,ESF) 001630
SEQLR.(ESRDR,ESF) 001640
TRANS=SEQLR.(ESRDR,ESF) 001650
SUBST.(1,POINTR) 001660
T'O HIT 001670
0'E 001680
SUBST.(POINT+1,POINTR) 001690
T'O HIT 001700
E'L 001710
E'L 001720
;
; Select one of the reassembly rules associated with this decomposition rule.
;
; Reassembly rules are used in turn. This code adds a counter (001560) to
; the rules and uses it to record which reassembly rule was used last (001690).
; When all reassembly rules have been used (001620) the counter is returned
; to 1 (001660) and the first rule is used again.
;
HIT TXTPRT.(ASSMBL.(TRANS,TEST,MTLIST.(OUTPUT)),0) 001730
T'O START 001740
E'L 001750
;
; Finally, use the selected reassembly rule (TRANS) and list of decomposition
; parts (TEST) to assemble a response (in the list OUTPUT) and print it. Then
; goto the START label to await the next user input and continue the
; conversation.
;
; The ASSMBL function is part of the SLIP system.
;
; The E'L (END OF CONDITIONAL) on line 001750 closes the O'E (OTHERWISE)
; on line 001270. ???
;
R* * * * * * * * * * INSERT NEW KEYWORD LIST 001760
NEWLST POPTOP.(INPUT) 001770
NEWBOT.(LSSCPY.(INPUT,LIST.(9)),KEY(HASH. 001780
1(TOP.(INPUT),5))) 001790
T'O START 001800
R* * * * * * * * * * DUMP REVISED SCRIPT 001810
ENDPLA PRINT COMMENT $WHAT IS TO BE THE NUMBER OF THE NEW SCRIPT$ 001820
READ FORMAT SNUMB,SCRIPT 001830
LPRINT.(INPUT,SCRIPT) 001840
NEWTOP.(MEMORY,MTLIST.(OUTPUT)) 001850
NEWTOP.($MEMORY$,OUTPUT) 001860
T'H DUMP, FOR I=1,1 I .G. 4 001870
DUMP NEWBOT.(MYTRAN(I),OUTPUT) 001880
LPRINT.(OUTPUT,SCRIPT) 001890
MTLIST.(OUTPUT) 001900
T'H WRITE, FOR I=0,1, I .G. 32 001910
POPMOR W'R LISTMT.(KEY(I)) .E. 0, T'O WRITE 001920
LPRINT.(POPTOP.(KEY(I)),SCRIPT) 001930
T'O POPMOR 001940
WRITE CONTINUE 001950
LPRINT.(MTLIST.(INPUT),SCRIPT) 001960
EXIT. 001970
R* * * * * * * * * * SCRIPT ERROR EXIT 001980
NOMATCH(1) PRINT COMMENT $PLEASE CONTINUE $ 002200
T'O START 002210
NOMATCH(2) PRINT COMMENT $HMMM $ 002220
T'O START 002230
NOMATCH(3) PRINT COMMENT $GO ON , PLEASE $ 002240
T'O START 002250
NOMATCH(4) PRINT COMMENT $I SEE $ 002260
T'O START 002270
VECTOR VALUES SNUMB= $I3 * $ 002280
E'M 002290