1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "glk/agt/agility.h"
24 #include "glk/agt/interp.h"
25 
26 namespace Glk {
27 namespace AGT {
28 
29 /*
30 
31    This is probably the ugliest and least readable of all of the
32    source files.  The parser isn't really that complex in principle,
33    but it has to deal with a lot of of special cases and also be
34    downward-compatible with the original parsers (which sometimes did
35    strange things) which adds a lot of additional code.  The noun parsing
36    code is particularly convoluted.
37 
38    Also, there are a fair number of global variables in this module
39    that are really local variables at heart but ended up global
40    because they were needed in too many different places and I didn't
41    want to complicate things even more by having to explicitly pass
42    them all up and down through the parsing routines. (One of those
43    times it would be convenient to have the ability to nest procedures
44    and functions like Pascal.)
45 
46 */
47 
48 /* These store the previous turn's values for use in disambiguation and
49    implementing OOPS */
50 /* parse_ip saves the ip after parsing, ip_back saves it before. */
51 word input_back[MAXINPUT];
52 words in_text_back[MAXINPUT];
53 int ip_back, parse_ip;
54 
55 /* The following are global only for rfree() purposes and
56    in order to maintain state for disambiguation */
57 static int vnum;  /* Verb number from synonym scan */
58 
59 /* Pointers to negative-terminated arrays of possible nouns */
60 static parse_rec *lactor = NULL, *lobj = NULL, *lnoun = NULL;
61 
62 static int ambig_flag = 0;
63 /* Was last input ambiguous? (so player could be entering
64 disambiguation info?)  1=ambig actor, 2=ambig noun,
65 3=ambig obj */
66 
67 
68 /* Empty ALL error messages, for those verbs that have their own */
69 int all_err_msg[] = {73, 83, 113, 103, /* open, close, lock, unlock: 15 - 18 */
70 					 239, 239, 239, 239, /* 19 - 22 */
71 					 123, 125
72 					}; /* eat, drink: 23 - 24 */
73 
74 
75 
76 /*-------------------------------------------------------------------*/
77 /* DEBUGGING OUTPUT FUNCTIONS & MISC UTILITIES                        */
78 /*-------------------------------------------------------------------*/
79 
80 /* This routine frees the space used by the parsing data structures */
freeall(void)81 static void freeall(void) {
82 	rfree(lnoun);
83 	rfree(lobj);
84 	lnoun = lobj = NULL;
85 }
86 
87 
88 /* Print out parse error message and abort the parse */
parseerr(int msgid,const char * s,int n)89 static int parseerr(int msgid, const char *s, int n) {
90 	if (n >= 0)
91 		gen_sysmsg(msgid, s, MSG_PARSE, in_text[n]);
92 	else
93 		gen_sysmsg(msgid, s, MSG_PARSE, "");
94 	freeall();
95 	ep = n;
96 	ip = -1;
97 	return -1;
98 }
99 
100 
101 /* Print out noun list; used for debugging the parser */
print_nlist(parse_rec * n)102 static void print_nlist(parse_rec *n) {
103 	char *s;
104 	int c;
105 	char buff[100];
106 
107 	if (n->info == D_END)
108 		writestr("----");
109 	if (n->info == D_ALL) {
110 		writestr("ALL ");
111 		n++;
112 	}
113 	for (c = 0; n->info != D_END && c < 20; n++, c++)
114 		if (n->info == D_AND) writestr(" AND ");
115 		else if (n->info == D_NUM) { /* Number entered */
116 			sprintf(buff, "#%ld(%d); ", n->num, n->obj);
117 			writestr(buff);
118 		} else if (n->obj < 0) {
119 			writestr(dict[-(n->obj)]);
120 			sprintf(buff, "(%d); ", n->obj);
121 			writestr(buff);
122 		} else {
123 			s = objname(n->obj);
124 			writestr(s);
125 			rfree(s);
126 			sprintf(buff, "(%d) ['%s %s']; ", n->obj, dict[n->adj], dict[n->noun]);
127 			writestr(buff);
128 		}
129 	if (n->info != D_END) writestr("///");
130 	writeln("");
131 }
132 
133 /* Output the parser's analysis of the current input line; used
134    for debugging */
parse_out(parse_rec * lactor_,int vb_,parse_rec * lnoun_,int prep_,parse_rec * lobj_)135 static int parse_out(parse_rec *lactor_, int vb_, parse_rec *lnoun_, int prep_,
136 					 parse_rec *lobj_) {
137 	writeln("ANALYSIS:");
138 	writestr("Actor: ");
139 	print_nlist(lactor_);
140 	writestr("Verb:");
141 	writeln(dict[ syntbl[auxsyn[vb_]] ]);
142 	writestr("DObj: ");
143 	print_nlist(lnoun_);
144 	writestr("Prep: ");
145 	if (prep_ != 0) writeln(dict[prep_]);
146 	else writeln("---");
147 	writestr("IObj: ");
148 	print_nlist(lobj_);
149 	return 0;
150 }
151 
152 
save_input(void)153 static void save_input(void) {
154 	int i;
155 
156 	for (i = 0; input[i] != -1 && i < MAXINPUT; i++) {
157 		input_back[i] = input[i];
158 		strncpy(in_text_back[i], in_text[i], 24);
159 	}
160 	input_back[i] = -1;
161 	ip_back = ip;
162 }
163 
restore_input(void)164 static void restore_input(void) {
165 	int i;
166 
167 	for (i = 0; input_back[i] != -1 && i < MAXINPUT; i++) {
168 		input[i] = input_back[i];
169 		strncpy(in_text[i], in_text_back[i], 24);
170 	}
171 	input[i] = -1;
172 	ip = ip_back;
173 }
174 
175 
176 
177 /*-------------------------------------------------------------------*/
178 /* Misc. Parsing Routines (includes parsing of verbs and preps)      */
179 /*-------------------------------------------------------------------*/
180 
181 #define w_and(w) (w==ext_code[wand] || w==ext_code[wc])
182 #define w_but(w) (w==ext_code[wbut] || w==ext_code[wexcept])
183 #define w_isterm(w) (w==ext_code[wp] || w==ext_code[wthen] || \
184 					 w==ext_code[wsc] || w_and(w) || w==-1)
185 
186 
187 
check_comb(int combptr)188 static word check_comb(int combptr) {
189 	int k;
190 	word w;
191 
192 	if (combptr == 0) return 0;
193 	w = syntbl[combptr];
194 	for (combptr += 1, k = ip; syntbl[combptr] != 0; combptr++, k++)
195 		if (syntbl[combptr] != input[k]) break;
196 	if (syntbl[combptr] == 0) {
197 		ip = k - 1;
198 		return w;
199 	}
200 	return 0;
201 }
202 
203 
comb_verb(void)204 static int comb_verb(void)
205 /* This eliminates two-word verbs */
206 {
207 	int i;
208 	word w;
209 
210 	for (i = 0; i < num_comb; i++) {
211 		w = check_comb(comblist[i]);
212 		if (w != 0) return w;
213 	}
214 
215 	if (input[ip] == ext_code[wgo] && verb_authorsyn(ext_code[wgo]) == 0) {
216 		/* GO <dir> --> <dir> */
217 		w = input[ip + 1];
218 		if (w != 0) i = verb_builtin(w);
219 		else i = 0;
220 		if (i != 0) {
221 			ip++;
222 			return w;
223 		}
224 	}
225 
226 	for (i = 0; i < num_auxcomb; i++) {
227 		w = check_comb(auxcomb[i]);
228 		if (w != 0) return w;
229 	}
230 
231 	return input[ip];
232 }
233 
234 
235 
236 
237 
238 /* This return true if the word in question is in the list
239    of original AGT verbs, but is not the canonical verb.
240    This is needed because verbs on this list
241    are not overridden by dummy_verbs in pre-Magx games. */
orig_agt_verb(word w)242 static rbool orig_agt_verb(word w) {
243 	int i;
244 	if (aver <= AGT10 && w == ext_code[wg]) return 0; /* AGT 1.0 didn't have AGAIN */
245 	for (i = 0; old_agt_verb[i] != -1 && old_agt_verb[i] != w; i++);
246 	return (old_agt_verb[i] == w);
247 }
248 
249 /* A few comments on id_verb:
250    The sequence is as follows:
251 	 i) Convert built-in synonyms to the base form (TAKE-->GET, for example)
252 		_unless_ there is an author-defined synonym. (The original AGT
253 	didn't have this 'unless'.)
254 	 ii) Check room-synonyms
255 	 iii) ID the verb based first on game-specific synonyms and then
256 		falling back to the built-in ones.
257 	(AGT gave the built-in ones higher priority than the game-specific
258 	ones, but this causes problems and is generally a bad idea.)
259  */
260 
id_verb(void)261 static int id_verb(void)
262 /* Identify verb at ip=i ; return verb id if found, 0 otherwise */
263 {
264 	word w;
265 	int j, canon_word, tmp;
266 
267 	w = comb_verb(); /* Combine 2-word verbs */
268 	if (w == 0) return 0;
269 
270 	/* Pre-Canonization of w: see if w has any built-in synonyms */
271 	canon_word = verb_builtin(w);
272 	if (canon_word != 0) {
273 		if (aver < AGX00 && orig_agt_verb(w))
274 			/* In orig AGT, author-defined verbs don't override builtin syns */
275 			tmp = 0;
276 		else
277 			tmp = verb_authorsyn(w); /* Author-defined verbs override built-in ones */
278 		if (tmp == 0 || tmp == canon_word)
279 			w = syntbl[auxsyn[canon_word]];
280 	}
281 
282 	/* Now check room-specific synonyms (being the most localized,
283 	      they have the highest priority) */
284 	for (j = room[loc].replacing_word; syntbl[j] != 0; j++)
285 		if (w == syntbl[j])
286 			w = room[loc].replace_word;
287 
288 	/* Next check to see if we already have the canonical form of the verb. */
289 	/* and go through the built-in list of synonyms */
290 	canon_word = verb_code(w);
291 	if (!PURE_DUMMY && canon_word == 57) canon_word = 0; /* AFTER */
292 	return canon_word;
293 }
294 
295 
296 #define compr_prep(w1,w2,r) {if (w1==input[ip] && w2==input[ip+1]) \
297 		{ip+=2;return r;}}
298 #define cprep(c1,c2,r)  compr_prep(ext_code[c1],ext_code[c2],ext_code[r])
299 
300 /* Eventually should add support for the handful of two word preps */
301 /* (eg IN TO, OUT OF,...); otherwise, this is pretty trivial. */
parse_prep(void)302 static int parse_prep(void) {
303 	int i;
304 	int j, k;
305 
306 	for (j = 0; j < num_prep; j++) { /* Scan user-defined prepositions */
307 		/* This table is formatted like the multi-verb table:
308 		     end-prep prep-word1 prepword2 etc. */
309 		for (k = 0; syntbl[userprep[j] + k + 1] != 0; k++)
310 			if (syntbl[userprep[j] + k + 1] != input[ip + k]) break;
311 		if (syntbl[userprep[j] + k + 1] == 0) {
312 			ip += k;
313 			return syntbl[userprep[j]];
314 		}
315 	}
316 	cprep(win, wto, winto);
317 	cprep(wout, wof, wfrom);
318 	for (i = win; i <= wabout; ++i)
319 		if (ext_code[i] == input[ip]) return input[ip++];
320 	return 0;
321 }
322 
323 
324 
noun_syn(word w,int obj)325 static int noun_syn(word w, int obj)
326 /* Is the word w  a synonym for the object obj? */
327 /* obj is encoded as ususal. */
328 /* 0=no match, 1=adjective match, 2=synonym match, 3=noun match */
329 /* 2 will only occur if PURE_SYN is false */
330 {
331 	int i;
332 
333 	if (w <= 0) return 0;
334 
335 	if (obj >= first_noun && obj <= maxnoun) {
336 		obj = obj - first_noun;
337 		if (w == noun[obj].name) return 3;
338 		if (noun[obj].has_syns)
339 			for (i = noun[obj].syns; syntbl[i] != 0; i++)
340 				if (w == syntbl[i]) return (PURE_SYN ? 3 : 2);
341 		if (w == noun[obj].adj) return 1;
342 		return 0;
343 	}
344 	if (obj >= first_creat && obj <= maxcreat) {
345 		obj = obj - first_creat;
346 		if (w == creature[obj].name) return 3;
347 		if (creature[obj].has_syns)
348 			for (i = creature[obj].syns; syntbl[i] != 0; i++)
349 				if (w == syntbl[i]) return (PURE_SYN ? 3 : 2);
350 		if (w == creature[obj].adj) return 1;
351 		return 0;
352 	}
353 	return 0; /* If the object doesn't exist, can't have synonyms */
354 }
355 
356 
357 
358 /*-------------------------------------------------------------------*/
359 /*   Noun-list manipulation functions.                               */
360 /*-------------------------------------------------------------------*/
361 
new_list(void)362 static parse_rec *new_list(void) {
363 	parse_rec *list;
364 
365 	list = (parse_rec *)rmalloc(sizeof(parse_rec));
366 	list[0].obj = 0;
367 	list[0].num = 0;
368 	list[0].adj = list[0].noun = 0;
369 	list[0].info = D_END;
370 	return list;
371 }
372 
add_w_rec(parse_rec * pold,int obj0,long num0,int info0,word adj0,word noun0)373 static parse_rec *add_w_rec(parse_rec *pold, int obj0, long num0, int info0,
374 							word adj0, word noun0) {
375 	parse_rec *pnew;
376 	int n;
377 
378 	for (n = 0; pold[n].info != D_END; n++);
379 	pnew = (parse_rec *)rrealloc(pold, (n + 2) * sizeof(parse_rec));
380 	pnew[n].obj = obj0;
381 	pnew[n].num = num0;
382 	pnew[n].info = info0;
383 	pnew[n].adj = adj0;
384 	pnew[n].noun = noun0;
385 	pnew[n + 1].obj = 0;
386 	pnew[n + 1].info = D_END;
387 	return pnew;
388 }
389 
add_rec(parse_rec * old,int obj0,long num0,int info0)390 static parse_rec *add_rec(parse_rec *old, int obj0, long num0, int info0) {
391 	word w;
392 
393 	if (obj0 < 0) w = -obj0;  /* The NOUN field of literal words will just be
394 			   that word */
395 	else w = 0;
396 	return add_w_rec(old, obj0, num0, info0, 0, w);
397 }
398 
399 
kill_rec(parse_rec * old,int index)400 static parse_rec *kill_rec(parse_rec *old, int index)
401 /* Remove record old[index] */
402 {
403 	parse_rec *pnew;
404 	int i;
405 
406 	for (i = index; old[i].info != D_END; i++) {
407 		old[i].obj = old[i + 1].obj;
408 		old[i].num = old[i + 1].num;
409 		old[i].noun = old[i + 1].noun;
410 		old[i].adj = old[i + 1].adj;
411 		old[i].info = old[i + 1].info;
412 		old[i].score = old[i + 1].score;
413 	}
414 	pnew = (parse_rec *)rrealloc(old, i * sizeof(parse_rec)); /* Shrink it by one */
415 	return pnew;
416 }
417 
concat_list(parse_rec * dest,parse_rec * src)418 static parse_rec *concat_list(parse_rec *dest, parse_rec *src) {
419 	int i, j;
420 
421 	for (i = 0; dest[i].info != D_END; i++); /* Put i at end of first list */
422 	for (j = 0; src[j].info != D_END; j++); /* j marks end of the second list */
423 	dest = (parse_rec *)rrealloc(dest, sizeof(parse_rec) * (i + j + 1));
424 	memcpy(dest + i, src, (j + 1)*sizeof(parse_rec));
425 	return dest;
426 }
427 
purge_list(parse_rec * list)428 static parse_rec *purge_list(parse_rec *list)
429 /* It should be possible to make this more efficiant */
430 {
431 	int i;
432 
433 	for (i = 0; list[i].info != D_END;)
434 		if ((list[i].info & D_MARK) != 0)
435 			list = kill_rec(list, i);
436 		else i++;      /* (Note: we only increment i if we _don't_ kill) */
437 	return list;
438 }
439 
clean_list(parse_rec * list)440 static void clean_list(parse_rec *list) {
441 	for (; list->info != D_END; list++)
442 		list->info &= ~D_MARK; /* Clear mark */
443 }
444 
445 
copy_list(parse_rec * list)446 static parse_rec *copy_list(parse_rec *list) {
447 	parse_rec *cpy;
448 	int i;
449 
450 	cpy = new_list();
451 	for (i = 0; list[i].info != D_END; i++);
452 	cpy = (parse_rec *)rmalloc(sizeof(parse_rec) * (i + 1));
453 	memcpy(cpy, list, (i + 1)*sizeof(parse_rec));
454 	return cpy;
455 }
456 
457 
458 
459 /* Among other things, this is used to add disambiguation information */
scan_rec(int item,int num,parse_rec * list)460 static int scan_rec(int item, int num, parse_rec *list) {
461 	int i;
462 
463 	for (i = 0; list[i].info != D_END && list[i].info != D_AND; i++)
464 		if (list[i].obj == item && list[i].num == num)
465 			return i;
466 	return -1;
467 }
468 
scan_andrec(int item,parse_rec * list)469 static rbool scan_andrec(int item, parse_rec *list) {
470 	for (; list->info != D_END; list++)
471 		if (list->obj == item && list->info != D_AND
472 		        && list->info != D_ALL) return 1;
473 	return 0;
474 }
475 
multinoun(parse_rec * list)476 static rbool multinoun(parse_rec *list)
477 /* Determines if LIST refers to a multiple object */
478 {
479 	if (list->info == D_ALL) return 1;
480 	for (; list->info != D_END; list++)
481 		if (list->info == D_AND) return 1;
482 	return 0;
483 }
484 
485 /* This updates the adj and noun fields of a record and also
486    updates the disambiguation priority */
487 /* We are already either D_ADJ or D_SYN; this just handles
488    possible promotion */
add_words_to_rec(parse_rec * nrec,word w,int tmp)489 static void add_words_to_rec(parse_rec *nrec, word w, int tmp) {
490 	word w2;
491 
492 	if (tmp == 2 || tmp == 3) {
493 		w2 = nrec->noun;
494 		nrec->noun = w;
495 		w = w2;
496 	}
497 	if (nrec->adj == 0)
498 		nrec->adj = w;
499 	if (tmp == 2) nrec->info = D_SYN;
500 	else if (tmp == 3) nrec->info = D_NOUN;
501 }
502 
503 /* This updates nrec with the information in newobj; this routine
504    is called while adding disambiguation information; newobj is from
505    what the player just typed in to clarify the ambiguity */
update_rec_words(parse_rec * nrec,parse_rec * newobj)506 void update_rec_words(parse_rec *nrec, parse_rec *newobj) {
507 	int tmp;
508 
509 	if (nrec->adj == 0) nrec->adj = newobj->adj;
510 	if (newobj->info == D_ADJ) tmp = 1;
511 	else if (newobj->info == D_SYN) tmp = 2;
512 	else if (newobj->info == D_NOUN) tmp = 3;
513 	else return;
514 	add_words_to_rec(nrec, newobj->noun, tmp);
515 }
516 
517 
ident_objrec(parse_rec * p1,parse_rec * p2)518 static rbool ident_objrec(parse_rec *p1, parse_rec *p2) {
519 	word noun1, adj1, noun2, adj2;
520 
521 	if (p1->obj == p2->obj) return 1;
522 	if (p1->obj <= 0 || p2->obj <= 0) return 0;
523 	if (tnoun(p1->obj)) {
524 		noun1 = noun[p1->obj - first_noun].name;
525 		adj1 = noun[p1->obj - first_noun].adj;
526 	} else if (tcreat(p1->obj)) {
527 		noun1 = creature[p1->obj - first_creat].name;
528 		adj1 = creature[p1->obj - first_creat].adj;
529 	} else return 0;
530 	if (tnoun(p2->obj)) {
531 		noun2 = noun[p2->obj - first_noun].name;
532 		adj2 = noun[p2->obj - first_noun].adj;
533 	} else if (tcreat(p2->obj)) {
534 		noun2 = creature[p2->obj - first_creat].name;
535 		adj2 = creature[p2->obj - first_creat].adj;
536 	} else return 0;
537 	return (noun1 == noun2 && adj1 == adj2);
538 }
539 
540 /*-------------------------------------------------------------------*/
541 /*  Disambiguation and ALL expansion routines                        */
542 /*-------------------------------------------------------------------*/
543 
544 /* Eliminate non-creatures and non-present creatures from list. */
545 /* Very similar to disambig below, but simpler. */
fix_actor(parse_rec * alist)546 static parse_rec *fix_actor(parse_rec *alist) {
547 	int i, cnt;
548 
549 	assert(alist != NULL);
550 	if (alist[0].info == D_ALL) { /* ALL?! */
551 		rfree(alist);
552 		return new_list();
553 	}
554 
555 	/* Start by eliminating non-creatures */
556 	cnt = 0;
557 	for (i = 0; alist[i].info != D_END; i++)
558 		if ((alist[i].obj < first_creat || alist[i].obj > maxcreat)
559 		        && alist[i].obj != -ext_code[weverybody]) {
560 			if (alist[i].info != D_AND)
561 				alist[i].info |= D_MARK;
562 		} else cnt++;
563 	alist = purge_list(alist);
564 	if (cnt <= 1) return alist;
565 
566 	/* Now eliminate those not present */
567 	cnt = 0;
568 	for (i = 0; alist[i].info != D_END; i++)
569 		if (!genvisible(&alist[i])) {
570 			if (alist[i].info != D_AND)
571 				alist[i].info |= D_MARK;
572 		} else cnt++;
573 
574 	if (cnt == 0) alist[0].info &= ~D_MARK;
575 
576 	return purge_list(alist);
577 }
578 
579 
580 /* Convert disambig list to all list, moving things up. */
convert_to_all(parse_rec * list,int * ofsref)581 static parse_rec *convert_to_all(parse_rec *list, int *ofsref) {
582 	int i;
583 	int cnt;
584 
585 	for (i = *ofsref; list[i].info != D_AND && list[i].info != D_END; i++);
586 	cnt = i - *ofsref; /* Number of objects. We will add cnt-1 ANDs */
587 
588 	while (list[i].info != D_END) i++;
589 	list = (parse_rec *)rrealloc(list, (i + cnt) * sizeof(parse_rec));
590 	memmove(list + *ofsref + 2 * cnt - 1, list + *ofsref + cnt,
591 	        (i + 1 - cnt - *ofsref)*sizeof(parse_rec));
592 	for (i = cnt - 1; i >= 0; i--) {
593 		int k;
594 		list[*ofsref + 2 * i] = list[*ofsref + i];
595 		if (i == 0) break;
596 		k = *ofsref + 2 * i - 1;
597 		list[k].obj = 0;
598 		list[k].num = 0;
599 		list[k].adj = list[k].noun = 0;
600 		list[k].info = D_AND;
601 	}
602 	/* *ofsref+=2*cnt-1; */
603 	return list;
604 }
605 
606 
607 
add_disambig_info(parse_rec * ilist,int ofs,parse_rec * truenoun)608 static parse_rec *add_disambig_info(parse_rec *ilist, int ofs,
609 									parse_rec *truenoun)
610 /* Basically, try to find interesection of tmp and truenoun,
611 or failing that, between ilist and truenoun */
612 /*  truenoun is what the player just typed in to resolve
613 the ambiguity */
614 {
615 	int i, n;
616 
617 	for (i = ofs; ilist[i].info != D_AND && ilist[i].info != D_END; i++) {
618 		if (truenoun[0].info == D_EITHER) {
619 			/* Mark all but the first for deletion */
620 			if (i > ofs) ilist[i].info |= D_MARK;
621 		} else {
622 			n = scan_rec(ilist[i].obj, ilist[i].num, truenoun);
623 			if (n == -1)
624 				ilist[i].info |= D_MARK;
625 			else  /* Add any new information to the words */
626 				update_rec_words(&ilist[i], &truenoun[n]);
627 		}
628 	}
629 	ilist = purge_list(ilist);
630 	truenoun[0].obj = 0;
631 	truenoun[0].num = 0;
632 	truenoun[0].info = D_END; /* Truenoun no longer useful */
633 	return ilist;
634 }
635 
636 static int max_disambig_score;
637 
score_disambig(parse_rec * rec,int ambig_type)638 static int score_disambig(parse_rec *rec, int ambig_type)
639 /* This is just a wrapper for check_obj (defined in runverb.c) */
640 /* ambig_type=1 for actor, 2 for noun, 3 for object */
641 /* We can assume that the earlier bits have already been disambiguated */
642 /*  Parse of current command in lactor, vnum, lnoun, prep, and lobj */
643 {
644 	if (ambig_type == 1) /* ACTOR */
645 		return DISAMBIG_SUCC;
646 	else if (ambig_type == 2) /* NOUN */
647 		return check_obj(lactor, vnum, rec, prep, NULL);
648 	else if (ambig_type == 3) /* IOBJ */
649 		return check_obj(lactor, vnum, lnoun, prep, rec);
650 	else fatal("Invalid ambig_type!");
651 	return 0;
652 }
653 
654 
655 /* This routine does all expansion: it returns a list of ALL objects in the
656  current context (lactor, vnum, <ALL>, prep, lobj) */
657 /* lnoun is assumed to be a list of exceptions, which needs to be freed
658    at the end. (i.e. we want to expand to ALL EXCEPT <lnoun>) */
expand_all(parse_rec * lnoun_)659 static parse_rec *expand_all(parse_rec *lnoun_) {
660 	parse_rec *list;
661 	int i, j;
662 	rbool prev_obj; /* Is there a previous object on the list? */
663 	rbool kill_obj; /* Don't put current object on ALL list after all */
664 	parse_rec temp_obj; /* Used to pass object info to disambiguation routine */
665 
666 	if (debug_parse) {
667 		writestr("ALL BUT:");
668 		print_nlist(lnoun_);
669 	}
670 	tmpobj(&temp_obj);
671 	nounloop(i)
672 	noun[i].scratch = 0;
673 	creatloop(i)
674 	creature[i].scratch = 0;
675 	objloop(i)
676 	if (((verbflag[vnum]&VERB_GLOBAL) != 0 || visible(i))
677 	        && (lnoun_ == NULL || !scan_andrec(i, lnoun_))) {
678 		temp_obj.obj = i;
679 		if (score_disambig(&temp_obj, 2) >= 500) {
680 			if (tnoun(i)) noun[i - first_noun].scratch = 1;
681 			else if (tcreat(i)) creature[i - first_creat].scratch = 1;
682 			else writeln("INTERNAL ERROR: Invalid object type in expand_all().");
683 		}
684 	}
685 
686 	/* The following ensures that if an object and it's container
687 	   are both selected, only the container will actually make it
688 	   onto the list.*/
689 	list = new_list();
690 	prev_obj = 0;
691 	objloop(i)
692 	if (it_scratch(i)) {
693 		kill_obj = 0;
694 		for (j = it_loc(i); tnoun(j) || tcreat(j); j = it_loc(j))
695 			if (it_scratch(j)) {
696 				kill_obj = 1;
697 				break;
698 			}
699 		if (kill_obj) continue;
700 		/* Now actually add object to list. */
701 		if (prev_obj) list = add_rec(list, 0, 0, D_AND);
702 		list = add_rec(list, i, 0, D_SYN);
703 		prev_obj = 1;
704 	}
705 
706 	if (debug_parse) {
707 		writestr("ALL==>");
708 		print_nlist(list);
709 	}
710 	rfree(lnoun_);
711 	return list;
712 }
713 
714 
715 
716 /* disambig check checks for the various things that can eliminate a noun
717 	 from the list. The higher dlev is, the more things that are bad */
718 /* Returns 1 if we should keep it, 0 if we should kill it */
719 /* The elimination order is based on AGT:
720   0-Eliminate adjective matches if PURE_NOUN is set and
721 	  out-of-scope adjectives that are not at the head of the list.
722   1-Eliminate adj matches if the adjective is out-of-scope.
723   2-eliminate SCENE and DOOR (D_INTERN)
724   3-eliminate out-of-scope nouns that are not at the head of the list
725   4-eliminate out-of-scope nouns
726   5-eliminate numbers that don't have associated dictionary words
727   6-eliminate adj only matches (i.e. noun-free) [only if PURE_ADJ]
728   7-eliminate pronouns (D_PRO)
729    -eliminate ALL (D_ALL)
730    -eliminate numbers
731  (Never eliminated: FLAG,GLOBAL,PIX,SYN,NOUN)
732 */
733 
734 #define MAX_DSCHEME 3
735 #define MAX_DLEV 2
736 /* ambig_type=1 for actor, 2 for noun, 3 for object */
737 /* We can assume that the earlier bits have already been disambiguated */
738 /*  Parse of current command in lactor, vnum, lnoun, prep, and lobj */
739 /* pick_one is used to select the first noun in the case that all of
740    them get eliminated during visibility testing (if none of the nouns
741    are visible, we don't want to ask disambiguation questions) */
742 
disambig_check(parse_rec * rec,int dsch,int dlev,int ambig_type,rbool pick_one)743 static rbool disambig_check(parse_rec *rec, int dsch, int dlev,
744 							int ambig_type, rbool pick_one) {
745 	switch (dsch) {
746 	case 0:
747 		switch (dlev) { /* Syntactic checks: pre-scope */
748 		case 0:
749 			return (!PURE_ADJ || rec->info != D_ADJ);
750 		case 1:
751 			return (rec->info != D_INTERN); /* Elim SCENE and DOOR */
752 		case 2:
753 			return (rec->info != D_NUM || rec->obj != 0); /* Eliminate numbers w/o
754 						   corrosponding word matches */
755 		default:
756 			return 0;
757 		}
758 	case 1:
759 		switch (dlev) { /* Scope checks */
760 		case 0:
761 			/* Just compute the scores, but don't eliminate anything yet */
762 			/* if PURE_DISAMBIG, no intel dismbig */
763 			if (PURE_DISAMBIG || rec->info == D_NUM)
764 				rec->score = DISAMBIG_SUCC;
765 			else rec->score = score_disambig(rec, ambig_type);
766 			if (rec->score >= max_disambig_score)
767 				max_disambig_score = rec->score;
768 			return 1;
769 		case 1:
770 			return (rec->score == max_disambig_score);
771 		case 2:
772 			return (rec->info == D_NUM
773 			        || ((verbflag[vnum] & VERB_GLOBAL) != 0 && rec->score >= 500)
774 			        || (tnoun(rec->obj) && noun[rec->obj - first_noun].scope)
775 			        || (tcreat(rec->obj) && creature[rec->obj - first_creat].scope));
776 		default:
777 			return 1;
778 		}
779 	case 2:
780 		switch (dlev) { /* Syntax, take 2 */
781 		case 0: /* Reserved for alternative adjective checking */
782 			return 1;
783 		/* Kill internal matches */
784 		case 1:
785 			return (rec->info != D_PRO && rec->info != D_ALL &&
786 			        rec->info != D_INTERN && rec->info != D_NUM);
787 		default:
788 			return 0;
789 		}
790 	case 3:
791 		return pick_one;
792 	default:
793 		return 0;
794 	}
795 }
796 
797 
798 /* disambig_a_noun does disambiguation for a single AND-terminated block */
799 /* *list* contains the list of possible objects,                      */
800 /*    *ofs* is our starting offset within the list (since with ANDs we */
801 /*    may not be starting at list[0]) */
802 
disambig_a_noun(parse_rec * list,int ofs,int ambig_type)803 static parse_rec *disambig_a_noun(parse_rec *list, int ofs, int ambig_type)
804 /* ambig_type=1 for actor, 2 for noun, 3 for object */
805 /* We can assume that the earlier bits have already been disambiguated */
806 /*  Parse of current command in lactor, vnum, lnoun, prep, and lobj */
807 {
808 	int i, cnt; /* cnt keeps track of how many nouns we've let through */
809 	int dsch; /* Dismabiguation scheme; we run through these in turn,
810 		   pushing dlev as high as possible for each scheme */
811 	int dlev; /* Disambiguation level: how picky do we want to be?
812 		   We keep raising this until we get a unique choice
813 		   or until we reach MAX_DLEV and have to give up */
814 	rbool one_in_scope; /* True if at least one noun is visible; if no nouns
815 			are visible then we never ask for disambiguation
816 			from the player (to avoid giving things away) but
817 			just take the first one. */
818 
819 	cnt = 2; /* Arbitrary number > 1 */
820 	one_in_scope = 0;
821 	max_disambig_score = -1000;  /* Nothing built in returns anything lower than 0,
822 				but some game author might come up with a
823 				clever application of negative scores */
824 	for (dsch = 0; dsch <= MAX_DSCHEME; dsch++)
825 		for (dlev = 0; dlev <= MAX_DLEV; dlev++) {
826 			if (DEBUG_DISAMBIG)
827 				rprintf("\nDISAMBIG%c%d:%d: ", (dsch == 1 ? '*' : ' '), dsch, dlev);
828 			cnt = 0;
829 			for (i = ofs; list[i].info != D_END && list[i].info != D_AND; i++)
830 				if (disambig_check(&list[i], dsch, dlev, ambig_type,
831 				                   one_in_scope || (i == ofs))
832 				   ) {
833 					cnt++;
834 					if (DEBUG_DISAMBIG)
835 						rprintf("+%d ", list[i].obj);
836 				} else {
837 					if (DEBUG_DISAMBIG)
838 						rprintf("-%d ", list[i].obj);
839 					list[i].info |= D_MARK; /* Mark it for deletion */
840 				}
841 			if (cnt != 0) {
842 				list = purge_list(list); /* Delete marked items */
843 				if (cnt == 1) return list;
844 				if (dsch == 1 && dlev == MAX_DLEV)
845 					one_in_scope = 1;
846 			} else {
847 				clean_list(list); /* Remove marks; we're not purging */
848 				break;
849 			}
850 		}
851 	/* Check to make sure we don't have a list of multiple identical items */
852 	for (i = ofs; list[i].info != D_END && list[i].info != D_AND; i++) {
853 		if (!ident_objrec(&list[i], &list[ofs])) break;
854 		list[i].info |= D_MARK;
855 	}
856 	if (list[i].info == D_END || list[i].info == D_AND) {
857 		/* If all of the items are identical, just pick the first */
858 		if (one_in_scope) writeln("(Picking one at random)");
859 		list[0].info &= ~D_MARK;
860 		list = purge_list(list);
861 	} else clean_list(list);
862 	if (DEBUG_DISAMBIG) rprintf("\n");
863 	return list;
864 }
865 
866 
867 
868 /* Note that this routine must be _restartable_, when new input comes in. */
869 /* Truenoun is 0 or else the "correct" noun for where disambig first
870    got stuck. */
871 /* Returns the offset at which it got stuck, or -1 if everything
872  went ok. Return -2 if we eliminate everything */
873 /* *tn_ofs* contains the offset where truenoun is supposed to be used */
874 
875 #define list (*ilist)    /* in disambig_phrase only */
876 
877 
disambig_phrase(parse_rec ** ilist,parse_rec * truenoun,int tn_ofs,int ambig_type)878 static int disambig_phrase(parse_rec **ilist, parse_rec *truenoun, int tn_ofs,
879 						   int ambig_type)
880 /* Note that ilist is double dereferenced: this is so we can realloc it */
881 /* ambig_type=1 for actor, 2 for noun, 3 for object */
882 /* We can assume that the earlier bits have already been disambiguated */
883 {
884 	int ofs, i;
885 	char *s;
886 
887 	ofs = 0;
888 	if (list[0].info == D_END) return -1; /* No nouns, so no ambiguity */
889 	if (list[0].info == D_ALL) ofs = 1; /* might have ALL EXCEPT construction */
890 #ifdef OMEGA
891 	return -1; /* No ambiguity over ALL, either */
892 	/* (at least if it appears as the first element of the list) */
893 #endif
894 
895 	while (list[ofs].info != D_END) { /* Go through each AND block */
896 		if (ofs == tn_ofs) {
897 			if (truenoun[0].info == D_ALL) /* Convert to ALL list */
898 				list = convert_to_all(list, &ofs);
899 			else {
900 				list = add_disambig_info(list, ofs, truenoun);
901 				if (list[ofs].info == D_END) { /* We have eliminated all matches */
902 					gen_sysmsg(240, "In that case, I don't know what you mean.",
903 					           MSG_PARSE, "");
904 					return -2;
905 				}
906 			}
907 		}
908 		list = disambig_a_noun(list, ofs, ambig_type);
909 		assert(list[ofs].info != D_END && list[ofs].info != D_AND);
910 		if (list[ofs + 1].info != D_END &&  list[ofs + 1].info != D_AND)
911 			/* Disambiguation failed */
912 		{
913 			writestr("Do you mean");
914 			for (i = ofs; list[i].info != D_END && list[i].info != D_AND; i++) {
915 				if (list[i + 1].info == D_END || list[i + 1].info == D_AND)
916 					if (i > ofs + 1) writestr(", or");
917 					else writestr(" or");
918 				else if (i > ofs) writestr(",");
919 				writestr(" the ");
920 				if (list[i].info != D_NUM || list[i].obj != 0)
921 					s = (char *)objname(list[i].obj);
922 				else {
923 					s = (char *)rmalloc(30 * sizeof(char));
924 					sprintf(s, "%ld", list[i].num);
925 				}
926 				writestr(s);
927 				rfree(s);
928 			}
929 			writeln("?");
930 			return ofs;
931 		}
932 		/* Skip forward to next AND  */
933 		while ((*ilist)[ofs].info != D_END && (*ilist)[ofs].info != D_AND)
934 			ofs++;
935 		if ((*ilist)[ofs].info == D_AND) ofs++;
936 	}
937 	return -1;
938 }
939 
940 #undef list
941 
942 static int disambig_ofs; /* Offset where disambig failed */
943 
944 /* ambig_flag stores what we were disambiguating the last time this
945    routine was called: it tells us where we failed so that if the
946    player enters new disambiguation information, we can figure out where
947    it should go */
948 
disambig(int ambig_set,parse_rec * list,parse_rec * truenoun)949 static parse_rec *disambig(int ambig_set, parse_rec *list, parse_rec *truenoun)
950 /* ambig_set = 1 for actor, 2 for noun, 3 for object */
951 {
952 	if (ambig_flag == ambig_set || ambig_flag == 0) { /* restart where we left off...*/
953 		if (truenoun == NULL || truenoun[0].info == D_END) disambig_ofs = -1;
954 		disambig_ofs = disambig_phrase(&list, truenoun, disambig_ofs, ambig_set);
955 		if (disambig_ofs == -1) ambig_flag = 0; /* Success */
956 		else if (disambig_ofs == -2) ambig_flag = -1; /* Error: elim all choices */
957 		else ambig_flag = ambig_set;
958 	}
959 	return list;
960 }
961 
962 
963 
964 
965 /*-------------------------------------------------------------------*/
966 /*   Noun parsing routines                                           */
967 /*-------------------------------------------------------------------*/
968 
969 
970 /* PARSE_A_NOUN(), parses a single noun, leaves ip pointing after it. */
971 /*   Just be greedy: grab as many of the input words as possible */
972 /* Leave ip pointing _after_ last word we get. */
973 /*   Return list of all possible objects */
974 /*   Go to some difficullty to make sure "all the kings men" will
975 	 not be accidentally parsed as "all" + "the kings men" */
976 /*   (Yes, yes, I know -- you can't have an AGT object with a name as
977 	 complex as 'all the king's men'-- but you could try to simulate it using
978 	 synonyms) */
979 /*   May also want to use more intellegence along the adj--noun distinction */
980 /*   all_ok indicates whether ALL is acceptable */
981 
parse_a_noun(void)982 static parse_rec *parse_a_noun(void)
983 /* Returns a list of parse_rec's containing the various possible
984    nouns.  */
985 {
986 	parse_rec *nlist;
987 	char *errptr;
988 	int i, tmp, numval, num, oip;
989 
990 	nlist = new_list();
991 	oip = ip; /* Save starting input pointer value */
992 
993 	if (input[ip] == -1) /* End of input */
994 		return nlist;
995 	if (input[ip] == 0) { /* i.e. tokeniser threw up its hands */
996 		numval = strtol(in_text[ip], &errptr, 10); /* Is it a number? */
997 		if (errptr == in_text[ip]) /* Nope. */
998 			return nlist;
999 		if (*errptr != 0) return nlist; /* May want to be less picky */
1000 		nlist = add_rec(nlist, 0, numval, D_NUM);
1001 		ip++;
1002 		return nlist;
1003 	}
1004 
1005 	/* Basic strategy: try to match nouns. If all matches are of length<=1,
1006 	   then go looking for flag nouns, global nouns, ALL, DOOR, etc. */
1007 	num = 0;
1008 	objloop(i)
1009 	if ((tmp = noun_syn(input[ip], i)) != 0) {
1010 		numval = strtol(in_text[ip], &errptr, 10); /* Is it a number, too? */
1011 		if (*errptr != 0) numval = 0; /* Only accept perfectly formed numbers */
1012 		nlist = add_w_rec(nlist, i, numval,
1013 		                  (tmp == 1) ? D_ADJ : (tmp == 2 ? D_SYN : D_NOUN),
1014 		                  (tmp == 1) ? input[ip] : 0, /* Adjective */
1015 		                  (tmp == 1) ? 0 : input[ip]); /* Noun */
1016 		num++;
1017 	}
1018 
1019 	/* Now we need to try to match more words and reduce our list. */
1020 	/*   Basically, we keep advancing ip until we get no matches at all. */
1021 	/*   Note that nouns may only come at the end, so if we find one we know */
1022 	/*   we are done.  Synonyms and adjectives can come anywhere */
1023 	/* (If PURE_SYN is set, then we won't see any synonyms-- they */
1024 	/*   get converted into nouns by noun_syn() */
1025 	/* *num* is used to keep track of how many matches we have so we know */
1026 	/*    when to stop. */
1027 
1028 	/* compare against the next word in the queue */
1029 	while (num > 0 && input[ip] != -1 && ip < MAXINPUT) {
1030 		ip++;
1031 		if (input[ip] == -1) break;
1032 		num = 0;
1033 		for (i = 0; nlist[i].info != D_END; i++) /* Which nouns match the new word? */
1034 			if (nlist[i].info == D_NOUN)      /* Nothing can come after a noun */
1035 				nlist[i].info |= D_MARK;
1036 			else if ((tmp = noun_syn(input[ip], nlist[i].obj)) == 0)
1037 				nlist[i].info |= D_MARK; /* Mark this noun to be eliminated */
1038 			else {  /* Noun _does_ match */
1039 				num++;  /* Count them up */
1040 				add_words_to_rec(&nlist[i], input[ip], tmp);
1041 			}
1042 		/* If we had any matches, kill the nouns that didn't match */
1043 		if (num != 0) {
1044 			nlist = purge_list(nlist);
1045 			for (i = 0; nlist[i].info != D_END; i++)
1046 				nlist[i].num = 0; /* Multi-word nouns can't be numbers */
1047 		} else /* num==0; we need to clear all of the marks */
1048 			clean_list(nlist);
1049 	}
1050 
1051 	/* So at this point num is zero-- that is we have reached the limit
1052 	   of our matches. */
1053 	/* If ip==oip is 0 (meaning no matches so far) or ip==oip+1
1054 	   (meaning we have a one-word match) then we need to check for
1055 	   flag nouns, global nouns, ALL, DOOR, pronouns, etc.
1056 	   and add them to the list if neccessary */
1057 
1058 	if (ip == oip || ip == oip + 1) {
1059 
1060 		/* First match the built in things... */
1061 		if ((input[oip] == ext_code[wdoor] && aver <= AGX00)
1062 		        || input[oip] == ext_code[wscene])
1063 			nlist = add_rec(nlist, -input[oip], 0, D_INTERN);
1064 		else if (input[oip] == ext_code[wall] ||
1065 		         input[oip] == ext_code[weverything])
1066 			nlist = add_rec(nlist, ALL_MARK, 0, D_ALL);
1067 		else if (input[oip] == ext_code[whe] || input[oip] == ext_code[whim])
1068 			nlist = add_rec(nlist, last_he, 0, D_PRO);
1069 		else if (input[oip] == ext_code[wshe] || input[oip] == ext_code[wher])
1070 			nlist = add_rec(nlist, last_she, 0, D_PRO);
1071 		else if (input[oip] == ext_code[wit])
1072 			nlist = add_rec(nlist, last_it, 0, D_PRO);
1073 		else if (input[oip] == ext_code[wthey] || input[oip] == ext_code[wthem])
1074 			nlist = add_rec(nlist, last_they, 0, D_PRO);
1075 		else for (i = 0; i < 10; i++) /* Match direction names */
1076 				if (input[oip] == syntbl[auxsyn[i]])
1077 					nlist = add_rec(nlist, -syntbl[auxsyn[i]], 0, D_INTERN);
1078 				else if (input[oip] == ext_code[weveryone] ||
1079 				         input[oip] == ext_code[weverybody])
1080 					nlist = add_rec(nlist, -ext_code[weverybody], 0, D_INTERN);
1081 
1082 		/* Next look for number word matches */
1083 		numval = strtol(in_text[oip], &errptr, 10); /* Is it a number? */
1084 		if (*errptr == 0) /* Yes */
1085 			nlist = add_rec(nlist, -input[oip], numval, D_NUM);
1086 
1087 		/* Next handle the flag nouns and global nouns */
1088 		if (globalnoun != NULL)
1089 			for (i = 0; i < numglobal; i++)
1090 				if (input[oip] == globalnoun[i])
1091 					nlist = add_rec(nlist, -input[oip], 0, D_GLOBAL);
1092 		for (i = 0; i < MAX_FLAG_NOUN; i++)
1093 			if (flag_noun[i] != 0 && input[oip] == flag_noun[i]
1094 #if 0
1095 			        && (room[loc].flag_noun_bits & (1L << i)) != 0
1096 #endif
1097 			   )
1098 				nlist = add_rec(nlist, -input[oip], 0, D_FLAG);
1099 
1100 		/* Finally the PIX names */
1101 		for (i = 0; i < MAX_PIX; i++)
1102 			if (pix_name[i] != 0 && input[oip] == pix_name[i] &&
1103 			        (room[loc].PIX_bits & (1L << i)) != 0)
1104 				nlist = add_rec(nlist, -input[oip], 0, D_PIX);
1105 
1106 		if (nlist[0].info != D_END) ip = oip + 1;
1107 	}
1108 	return nlist;
1109 }
1110 
parse_noun(int and_ok,int is_actor)1111 static parse_rec *parse_noun(int and_ok, int is_actor)
1112 /* handles multiple nouns */
1113 /* and_ok indicates wheter multiple objects are acceptable */
1114 /* Either_ok indicates whether EITHER is okay (only true when
1115 resolving disambiguation) */
1116 {
1117 	parse_rec *next, *lnoun_;
1118 	int saveinfo;
1119 	rbool all_except;
1120 
1121 	all_except = 0;
1122 	next = lnoun_ = parse_a_noun();
1123 	saveinfo = next[0].info;
1124 
1125 #if 0  /* Let the main parser sort this out */
1126 	if (!and_ok) return lnoun_; /* If no ANDs allowed, stop here. */
1127 #endif
1128 	/* We need to explicitly handle the actor case here because
1129 	   the comma after an actor can be confused with the comma
1130 	   used to separate multiple objects */
1131 	if (is_actor) return lnoun_; /* If no ANDs allowed, stop here. */
1132 
1133 	if (lnoun_[0].info == D_ALL && w_but(input[ip])) /* ALL EXCEPT ... */
1134 		all_except = 1; /* This will cause us to skip over EXCEPT and
1135 			 start creating an AND list */
1136 
1137 	/* Now, itereate over <noun> AND <noun> AND ... */
1138 	while ((all_except || w_and(input[ip])) &&
1139 	        saveinfo != D_END) { /* && saveinfo!=D_ALL */
1140 		ip++; /* Skip over AND or EXCEPT */
1141 		next = parse_a_noun();
1142 		saveinfo = next[0].info;
1143 		if (next[0].info != D_END) {          /* We found a word */
1144 			if (!all_except) lnoun_ = add_rec(lnoun_, AND_MARK, 0, D_AND);
1145 			lnoun_ = concat_list(lnoun_, next);
1146 		} else ip--; /* We hit trouble: back up to the AND  */
1147 		all_except = 0; /* Only skip EXCEPT once */
1148 		rfree(next);
1149 	}
1150 	return lnoun_;
1151 }
1152 
1153 
parse_disambig_answer(void)1154 parse_rec *parse_disambig_answer(void) {
1155 	parse_rec *temp;
1156 
1157 	if (input[ip + 1] == -1) {
1158 		if (input[ip] == ext_code[wall] || input[ip] == ext_code[weverything]
1159 		        || input[ip] == ext_code[wboth]) {
1160 			temp = new_list();
1161 			ip++;
1162 			return add_rec(temp, ALL_MARK, 0, D_ALL);
1163 		}
1164 		if (input[ip] == ext_code[weither] || input[ip] == ext_code[w_any]) {
1165 			temp = new_list();
1166 			ip++;
1167 			return add_rec(temp, 0, 0, D_EITHER);
1168 		}
1169 	}
1170 	return parse_noun(0, 0);
1171 }
1172 
1173 
1174 
1175 /*-------------------------------------------------------------------*/
1176 /*      Main parsing routines                                        */
1177 /*-------------------------------------------------------------------*/
1178 
1179 
parse_cmd(void)1180 static int parse_cmd(void)
1181 /* Parse entered text and execute it, one statement at a time */
1182 /* Needs to leave ip pointing at beginning of next statement */
1183 {
1184 	rbool new_actor; /* This is used for some error checking; it is set
1185 			 if we just found an actor on this command
1186 			 (as opposed to inheriting one from a previous
1187 			 command in a multiple statement) */
1188 	parse_rec *tmp;
1189 	int tp;
1190 
1191 	/* First go looking for an actor. */
1192 	ap = ip;
1193 	new_actor = 0;
1194 	if (lactor == NULL) {
1195 		new_actor = 1;
1196 		lactor = parse_noun(0, 1);
1197 		/* Check that actor is a creature. */
1198 		if (lactor[0].info != D_END) {
1199 			lactor = fix_actor(lactor); /* eliminate non-creatures */
1200 			if (lactor[0].info == D_END) { /* Not a creature. */
1201 				/* print intelligent error message */
1202 				if (input[ip] == ext_code[wc]) /* ie there is a comma */
1203 					return parseerr(229, "Who is this '$word$' you are addressing?", ap);
1204 				else ip = ap; /* Otherwise, assume we shouldn't have parsed it as
1205 			   an actor-- it may be a verb. */
1206 			}
1207 		}
1208 		if (lactor[0].info != D_END && input[ip] == ext_code[wc])
1209 			ip++;  /* this skips over a comma after an actor. */
1210 	}
1211 	/* Now onwards... */
1212 	vp = ip;
1213 	vnum = id_verb(); /* May increment ip (ip will point at last word in verb) */
1214 	if (vnum == 0 && new_actor && lactor[0].info != D_END) {
1215 		/* maybe actor is messing us up. */
1216 		ip = ap; /* restart from beginning */
1217 		vnum = id_verb();
1218 		if (vnum == 0) /* if it's still bad, probably we really have an actor */
1219 			ip = vp;
1220 		else {  /* no actor; really a verb */
1221 			lactor[0].obj = 0;
1222 			lactor[0].info = D_END;
1223 			vp = ap;
1224 		}
1225 	}
1226 
1227 TELLHack:  /* This is used to restart the noun/prep/object scan
1228 		  if we find a TELL <actor> TO <verb> ... command */
1229 
1230 	if (vnum == 0)
1231 		return parseerr(230, "I don't understand '$word$' as a verb.", ip);
1232 
1233 	/* Now we need to find noun, obj, and prep (if they all exist) */
1234 	/* standard grammer:  verb noun prep obj */
1235 	prep = 0;
1236 	np = ++ip; /* ip now points just _after_ verb */
1237 	lnoun = parse_noun((verbflag[vnum] & VERB_MULTI) != 0, 0);
1238 	/* leaves ip pointing just after it.;
1239 	   lnoun[0].info==D_END means no noun. */
1240 	if (prep == 0) { /* prep==0 unless we've met the special TURN ON|OFF case */
1241 		pp = ip;
1242 		prep = parse_prep(); /* Should be trivial */
1243 		op = ip;
1244 		lobj = parse_noun(prep == 0, 0); /* and_ok if no prep */
1245 	}
1246 
1247 	/* Check for TELL <actor> TO <verb> ... construction */
1248 	/* If found, convert to <actor>, <verb> ... construction */
1249 	if (lactor[0].info == D_END && lnoun[0].info != D_END &&
1250 	        vnum == 31 && prep == ext_code[wto] && !multinoun(lnoun)) {
1251 		ip = op; /* Back up */
1252 		rfree(lactor);
1253 		rfree(lobj);
1254 		lactor = lnoun;
1255 		lnoun = NULL;
1256 		vp = ip; /* Replace TELL with new verb */
1257 		vnum = id_verb(); /* May increment ip (ip points att last word in verb) */
1258 		goto TELLHack;  /* Go back up and reparse the sentence from
1259 			 the new start point. */
1260 	}
1261 
1262 	/* Convert TURN <noun> ON to TURN ON <noun> */
1263 	if (vnum == 35 && (prep == ext_code[won] || prep == ext_code[woff])
1264 	        && lobj[0].info == D_END) {
1265 		tmp = lobj;
1266 		lobj = lnoun;
1267 		lnoun = tmp;
1268 		tp = op;
1269 		np = op;
1270 		op = tp;
1271 	}
1272 
1273 
1274 	/* For pre-Magx versions of AGT,
1275 	      convert <verb> <prep> <noun>  ==>  <verb> <noun> <prep> <noun> */
1276 	if (aver < AGX00 && lnoun[0].info == D_END && lobj[0].info != D_END) {
1277 		rfree(lnoun);
1278 		lnoun = copy_list(lobj);
1279 		np = op;
1280 	}
1281 
1282 	/* Next we check to convert SHOOT <noun> AT <object> to
1283 	   SHOOT <object> WITH <noun> */
1284 	if (vnum == 49 && prep == ext_code[wat] && !multinoun(lnoun)) {
1285 		tmp = lobj;
1286 		lobj = lnoun;
1287 		lnoun = tmp;
1288 		tp = np;
1289 		np = op;
1290 		op = tp;
1291 		prep = ext_code[wwith];
1292 	}
1293 
1294 	/* Now to convert SHOW <*nothing*> to SHOW EXITS */
1295 	if (vnum == 40 && prep == 0 && lnoun[0].info == D_END && lobj[0].info == D_END)
1296 		vnum = 42; /* LISTEXITS */
1297 
1298 	/* Convert LOOK <something> into EXAMINE <something> */
1299 	if (smart_look && vnum == 19 && lnoun[0].info != D_END) vnum = 20;
1300 
1301 	/* need better error msgs */
1302 	if ((verbflag[vnum]&VERB_MULTI) == 0 && multinoun(lnoun)) {
1303 		/* Multiple objects when they are not allowed */
1304 		int msgnum;
1305 		if (vnum == 31) msgnum = 155; /* TALK */
1306 		else if (vnum == 34) msgnum = 160; /* ASK */
1307 		else msgnum = 231;
1308 		return parseerr(msgnum,
1309 		                "The verb '$word$' doesn't take multiple objects.", vp);
1310 	} else if (multinoun(lobj))
1311 		return parseerr(232, "You can't use multiple indirect objects.", op);
1312 	else if (lnoun[0].info == D_END && !w_isterm(input[np]) && np != pp)
1313 		/* i.e. invalid noun */
1314 		return parseerr(233, "I don't understand the word '$word$' as a noun.", np);
1315 	else if (lnoun[0].obj == 0 && lnoun[0].info == D_PRO)
1316 		return parseerr(234, "I don't know to what '$word$' refers.", np);
1317 	else if (lobj[0].info == D_END && !w_isterm(input[op]))
1318 		/* i.e. invalid object */
1319 		return parseerr(235, "I don't understand the word '$word$' as a noun.", op);
1320 	else if (lobj[0].obj == 0 && lobj[0].info == D_PRO)
1321 		return parseerr(236, "I don't know to what '$word$' refers.", op);
1322 	else if (!w_isterm(input[ip]))
1323 		return parseerr(238, "Extra input found: \"$word$...\"", ip);
1324 
1325 	return 0;
1326 }
1327 
1328 
v_undo(void)1329 static void v_undo(void) {
1330 	if (undo_state == NULL) {
1331 		writeln("There is insufficiant memory to support UNDO");
1332 		ip = -1;
1333 		return;
1334 	}
1335 	if (can_undo == 0) {
1336 		if (newlife_flag)
1337 			writeln("You can't UNDO on the first turn.");
1338 		else writeln("You can only UNDO one turn.");
1339 		ip = -1;
1340 		return;
1341 	}
1342 	writeln("");
1343 	writeln("UNDOing a turn...");
1344 	can_undo = 0;
1345 	putstate(undo_state);
1346 	ip = 1;
1347 	set_statline();
1348 	return;
1349 }
1350 
parse(void)1351 rbool parse(void)
1352 /* Wrapper around parse_cmd to handle disambiguation, etc */
1353 /* It returns 1 if everything is okay; 0 if there is ambiguity */
1354 {
1355 	parse_rec *currnoun;
1356 	int fixword;
1357 	int start_ip;
1358 
1359 	currnoun = NULL;
1360 	start_ip = ip;
1361 	/* First, we need to see if someone has issued an OOPS command.
1362 	   OOPS commands are always assumed to stand alone. (i.e. no
1363 	   THEN nonsense). OOPS commands are always of the form
1364 	   OOPS <word> */
1365 	if (ip == 0 && input[0] == ext_code[woops] && input[1] > 0 &&
1366 	        input[2] == -1 && ep > -1) {
1367 		fixword = input[ip + 1];
1368 		restore_input();
1369 		input[ep] = fixword;
1370 		ambig_flag = 0;
1371 	}
1372 	ep = -1;
1373 
1374 
1375 	/* Next, we need to determine if someone is trying to do
1376 	   disambiguation. This is only the case if
1377 	   i)ambig_flag is set
1378 	   ii)ip=0 (no multiple command nonsense)
1379 	   iii)there is only one noun on the line. */
1380 	if (ip != 0) ambig_flag = 0;
1381 	if (ambig_flag) {
1382 		currnoun = parse_disambig_answer();
1383 		if (input[ip] == -1 && currnoun[0].info != D_END) {
1384 			restore_input();    /* Yep, we're disambiguaing. */
1385 			ip = parse_ip;
1386 		} else { /* nope; it's a new command */
1387 			ip = 0;
1388 			ambig_flag = 0;
1389 			rfree(currnoun);
1390 			freeall();
1391 			currnoun = NULL;
1392 		}
1393 	}
1394 
1395 	/* Next we go looking for UNDO; again, this must be the first
1396 	 thing on an empty line. */
1397 	if (ip == 0 && input[0] == ext_code[wundo] && input[1] == -1) {
1398 		v_undo();
1399 		return 1;
1400 	}
1401 
1402 	save_input();
1403 
1404 	/* If starting a new line, clear out old the old actor */
1405 	if (ip == 0) {
1406 		actor_in_scope = 0; /* Clear this */
1407 		rfree(lactor); /* This resets lactor to NULL  */
1408 	}
1409 
1410 	if (!ambig_flag)
1411 		if (parse_cmd() == -1)
1412 			return 1;  /* error condition */
1413 
1414 	parse_ip = ip;
1415 
1416 	if (debug_parse)
1417 		parse_out(lactor, vnum, lnoun, prep, lobj);
1418 
1419 	/*Disambiguation routines; do it here instead of earlier to get
1420 	 error messages in the right place (it's silly and annoying to ask the
1421 	 player for disambiguation and then die on a parse error after they've
1422 	 provided it) */
1423 	compute_scope(); /* The disambig routines use this information */
1424 	lactor = disambig(1, lactor, currnoun);
1425 	lnoun = disambig(2, lnoun, currnoun);
1426 	lobj = disambig(3, lobj, currnoun);
1427 	if (ambig_flag > 0) return 0; /* We need to get disambig info */
1428 	if (ambig_flag == -1) {
1429 		ambig_flag = 0;
1430 		return 1;
1431 	}
1432 	/* We got rid of too much */
1433 	rfree(currnoun);
1434 
1435 	/* Next, expand ALL if neccessary */
1436 	if (!PURE_ALL && lnoun[0].info == D_ALL) {
1437 		lnoun = expand_all(lnoun);
1438 		if (lnoun[0].info == D_END) { /* ALL expands to nothing */
1439 			int msgnum;
1440 			if (vnum >= 15 && vnum <= 24)
1441 				msgnum = all_err_msg[vnum - 15];
1442 			else
1443 				msgnum = 239;
1444 			parseerr(msgnum, "I don't know what you are referring to.", np);
1445 			return 1;
1446 		}
1447 	}
1448 
1449 	/* Now that we know that we have an executable command,
1450 	   we save the undo state before executing if this is the first command
1451 	   in a sequence. (That is, UNDO undoes whole lines of commands,
1452 	   not just individual commands) */
1453 	if (start_ip == 0 && undo_state != NULL) {
1454 		undo_state = getstate(undo_state);
1455 		can_undo = 1;
1456 	}
1457 
1458 	/* Now to actually execute the command that has been parsed. */
1459 	/* Remember: disambiguation has been done by this time. */
1460 
1461 	exec(&lactor[0], vnum, lnoun, prep, &lobj[0]);
1462 	rfree(lobj);
1463 
1464 	/* exec is responsible for freeing or whatever lnoun (this is for AGAIN) */
1465 
1466 	/* Now we clear lnoun and lobj; lactor is handled elsewhere since
1467 	   we might have FRED, GET ROCK THEN GO NORTH */
1468 	lnoun = lobj = NULL;
1469 
1470 	/* Finally check for THENs */
1471 
1472 	if (ip != -1 && w_and(input[ip]) && input[ip + 1] == ext_code[wthen])
1473 		ip++;  /* AND THEN construction */
1474 	if (ip != -1 && input[ip] != -1) ip++;
1475 	return 1;
1476 }
1477 
1478 
1479 
1480 
menu_cmd(void)1481 void menu_cmd(void) {
1482 	int i, j;
1483 	int choice;
1484 	char *curr_cmd, *tmp1, *tmp2;  /* String of current command */
1485 	int objcnt; /* Number of objects taken by the current verb */
1486 	int verbword; /* Verb word */
1487 	parse_rec actrec;
1488 
1489 	parse_rec mobj;
1490 	int vnum_;  /* Verb number */
1491 	word prep_;
1492 
1493 	menuentry *nounmenu;
1494 	int *nounval;  /* Object id's for the menu entries */
1495 	int nm_size, nm_width; /* Size and width of noun menu */
1496 
1497 
1498 	nounval = NULL;
1499 	nounmenu = NULL;
1500 	/* Get verb+prep */
1501 	choice = agt_menu("", vm_size, vm_width, verbmenu);
1502 	if (choice == -1 || doing_restore) return;
1503 
1504 	verbword = verbinfo[choice].verb;
1505 	prep_ = verbinfo[choice].prep;
1506 	objcnt = verbinfo[choice].objnum;
1507 
1508 	/* Now identify the verb */
1509 	ip = 0;
1510 	input[0] = verbword;
1511 	input[1] = input[2] = -1;
1512 	if (objcnt <= 1 && prep_ != 0) input[1] = prep_;
1513 	vnum_ = id_verb();
1514 
1515 	lnoun = (parse_rec *)rmalloc(sizeof(parse_rec) * 2);
1516 	lnoun[0].obj = 0;
1517 	lnoun[0].num = 0;
1518 	lnoun[0].info = D_END;
1519 
1520 	nm_size = nm_width = 0;
1521 
1522 	if (objcnt >= 1) {
1523 		/* Construct noun list */
1524 		nounval = get_nouns();
1525 		for (nm_size = 0; nounval[nm_size] != 0; nm_size++);
1526 		nounmenu = (menuentry *)rmalloc(nm_size * sizeof(menuentry));
1527 		nm_width = 0;
1528 		for (i = 0; i < nm_size; i++) {
1529 			tmp1 = objname(nounval[i]);
1530 			strncpy(nounmenu[i], tmp1, MENU_WIDTH);
1531 			j = strlen(tmp1);
1532 			if (j > nm_width) nm_width = j;
1533 		}
1534 		if (nm_width > MENU_WIDTH) nm_width = MENU_WIDTH;
1535 
1536 		if (objcnt >= 2 || prep_ == 0)
1537 			curr_cmd = rstrdup(dict[verbword]);
1538 		else
1539 			curr_cmd = concdup(dict[verbword], dict[prep_]);
1540 
1541 		choice = agt_menu(curr_cmd, nm_size, nm_width, nounmenu);
1542 		rfree(curr_cmd);
1543 		if (choice == -1 || doing_restore) {
1544 			rfree(nounmenu);
1545 			rfree(nounval);
1546 			rfree(lnoun);
1547 			return;
1548 		}
1549 
1550 		if (objcnt == 1 && prep_ != 0) { /* VERB PREP OBJ construction */
1551 			mobj.obj = nounval[choice];
1552 			mobj.num = 0;
1553 			mobj.info = D_NOUN;
1554 		} else {  /* Normal VERB OBJ construction */
1555 			lnoun[0].obj = nounval[choice];
1556 			lnoun[0].num = 0;
1557 			lnoun[0].info = D_NOUN;
1558 			lnoun[1].obj = 0;
1559 			lnoun[1].num = 0;
1560 			lnoun[1].info = D_END;
1561 		}
1562 	}
1563 
1564 	if (objcnt >= 2) {
1565 		tmp1 = objname(lnoun[0].obj);      /* Build up current command line */
1566 		tmp2 = concdup(dict[verbword], tmp1); /* VERB NOUN */
1567 		rfree(tmp1);
1568 		curr_cmd = concdup(tmp2, dict[prep_]); /* VERB NOUN PREP */
1569 		rfree(tmp2);
1570 
1571 		choice = agt_menu(curr_cmd, nm_size, nm_width, nounmenu);
1572 		rfree(curr_cmd);
1573 		if (choice == -1 || doing_restore) {
1574 			rfree(lnoun);
1575 			rfree(nounmenu);
1576 			rfree(nounval);
1577 			return;
1578 		}
1579 
1580 		mobj.obj = nounval[choice];
1581 		mobj.num = 0;
1582 		mobj.info = D_NOUN;
1583 	}
1584 
1585 	rfree(nounmenu);
1586 	rfree(nounval);
1587 
1588 	if (vnum_ == OLD_VERB + 3) { /* UNDO */
1589 		v_undo();
1590 		return;
1591 	}
1592 
1593 	if (undo_state != NULL) {
1594 		undo_state = getstate(undo_state);
1595 		can_undo = 1;
1596 	}
1597 
1598 	/* Now to actually execute the command that has been selected. */
1599 	tmpobj(&actrec);
1600 	actrec.obj = 0;
1601 	exec(&actrec, vnum_, lnoun, prep_, &mobj);
1602 	lnoun = NULL; /* exec() is responsible for freeing lnoun */
1603 }
1604 
1605 
1606 /* Grammer structures:
1607  sverb, dverb                     (n,s,e,w,...,q,l,....)
1608  overb noun              (close,examine,read,eat,drink,pull,light,ext)
1609  averb noun|ALL          (drop,get,wear,remove)
1610  TURN noun ON|OFF
1611  TURN ON|OFF noun
1612  a-verb noun ABOUT obj   (tell, ask)
1613  pverb noun [prep object]  (put, throw)
1614  w-verb noun [WITH object]  (attack, open, lock, unlock, push, shoot)
1615  SHOOT noun [AT object]
1616  dummy noun [prep obj]
1617 ( pverb obj noun  ==> pverb noun TO obj  e.g. give dog the bone)
1618 */
1619 
1620 } // End of namespace AGT
1621 } // End of namespace Glk
1622