



Parser__parse (lines 1224-2097)
Back to List
Browsing parserm.h
1224 ! To simplify the picture a little, a rough map of the main routine:
1225 !
1226 ! (A) Get the input, do "oops" and "again"
1227 ! (B) Is it a direction, and so an implicit "go"? If so go to (K)
1228 ! (C) Is anyone being addressed?
1229 ! (D) Get the verb: try all the syntax lines for that verb
1230 ! (E) Break down a syntax line into analysed tokens
1231 ! (F) Look ahead for advance warning for multiexcept/multiinside
1232 ! (G) Parse each token in turn (calling ParseToken to do most of the work)
1233 ! (H) Cheaply parse otherwise unrecognised conversation and return
1234 ! (I) Print best possible error message
1235 ! (J) Retry the whole lot
1236 ! (K) Last thing: check for "then" and further instructions(s), return.
1237 !
1238 ! The strategic points (A) to (K) are marked in the commentary.
1239 !
1240 ! Note that there are three different places where a return can happen.
1241 ! ----------------------------------------------------------------------------
1242
1243 [ Parser__parse results syntax line num_lines line_address i j k
1244 token l m;
1245
1246 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1247 !
1248 ! A: Get the input, do "oops" and "again"
1249 !
1250 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1251
1252 ! Firstly, in "not held" mode, we still have a command left over from last
1253 ! time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
1254 ! last time, with "eat biscuit" tucked away until now). So we return that.
1255
1256 if (notheld_mode == 1) {
1257 for (i=0 : i<8 : i++) results-->i = kept_results-->i;
1258 notheld_mode = 0;
1259 rtrue;
1260 }
1261
1262 if (held_back_mode == 1) {
1263 held_back_mode = 0;
1264 Tokenise__(buffer, parse);
1265 jump ReParse;
1266 }
1267
1268 .ReType;
1269
1270 Keyboard(buffer,parse);
1271
1272 .ReParse;
1273
1274 parser_inflection = name;
1275
1276 ! Initially assume the command is aimed at the player, and the verb
1277 ! is the first word
1278
1279 #Ifdef TARGET_ZCODE;
1280 num_words = parse->1;
1281 #Ifnot; ! TARGET_GLULX
1282 num_words = parse-->0;
1283 #Endif; ! TARGET_
1284 wn = 1;
1285
1286 #Ifdef LanguageToInformese;
1287 LanguageToInformese();
1288 #IfV5;
1289 ! Re-tokenise:
1290 Tokenise__(buffer,parse);
1291 #Endif; ! V5
1292 #Endif; ! LanguageToInformese
1293
1294 BeforeParsing();
1295 #Ifdef TARGET_ZCODE;
1296 num_words = parse->1;
1297 #Ifnot; ! TARGET_GLULX
1298 num_words = parse-->0;
1299 #Endif; ! TARGET_
1300
1301 k=0;
1302 #Ifdef DEBUG;
1303 if (parser_trace >= 2) {
1304 print "[ ";
1305 for (i=0 : i<num_words : i++) {
1306
1307 #Ifdef TARGET_ZCODE;
1308 j = parse-->(i*2 + 1);
1309 #Ifnot; ! TARGET_GLULX
1310 j = parse-->(i*3 + 1);
1311 #Endif; ! TARGET_
1312 k = WordAddress(i+1);
1313 l = WordLength(i+1);
1314 print "~"; for (m=0 : m<l : m++) print (char) k->m; print "~ ";
1315
1316 if (j == 0) print "?";
1317 else {
1318 #Ifdef TARGET_ZCODE;
1319 if (UnsignedCompare(j, HDR_DICTIONARY-->0) >= 0 &&
1320 UnsignedCompare(j, HDR_HIGHMEMORY-->0) < 0)
1321 print (address) j;
1322 else print j;
1323 #Ifnot; ! TARGET_GLULX
1324 if (j->0 == $60) print (address) j;
1325 else print j;
1326 #Endif; ! TARGET_
1327 }
1328 if (i ~= num_words-1) print " / ";
1329 }
1330 print " ]^";
1331 }
1332 #Endif; ! DEBUG
1333 verb_wordnum = 1;
1334 actor = player;
1335 actors_location = ScopeCeiling(player);
1336 usual_grammar_after = 0;
1337
1338 .AlmostReParse;
1339
1340 scope_token = 0;
1341 action_to_be = NULL;
1342
1343 ! Begin from what we currently think is the verb word
1344
1345 .BeginCommand;
1346
1347 wn = verb_wordnum;
1348 verb_word = NextWordStopped();
1349
1350 ! If there's no input here, we must have something like "person,".
1351
1352 if (verb_word == -1) {
1353 best_etype = STUCK_PE;
1354 jump GiveError;
1355 }
1356
1357 ! Now try for "again" or "g", which are special cases: don't allow "again" if nothing
1358 ! has previously been typed; simply copy the previous text across
1359
1360 if (verb_word == AGAIN2__WD or AGAIN3__WD) verb_word = AGAIN1__WD;
1361 if (verb_word == AGAIN1__WD) {
1362 if (actor ~= player) {
1363 L__M(##Miscellany, 20);
1364 jump ReType;
1365 }
1366 #Ifdef TARGET_ZCODE;
1367 if (buffer3->1 == 0) {
1368 L__M(##Miscellany, 21);
1369 jump ReType;
1370 }
1371 #Ifnot; ! TARGET_GLULX
1372 if (buffer3-->0 == 0) {
1373 L__M(##Miscellany, 21);
1374 jump ReType;
1375 }
1376 #Endif; ! TARGET_
1377 for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer->i = buffer3->i;
1378 jump ReParse;
1379 }
1380
1381 ! Save the present input in case of an "again" next time
1382
1383 if (verb_word ~= AGAIN1__WD)
1384 for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer3->i = buffer->i;
1385
1386 if (usual_grammar_after == 0) {
1387 j = verb_wordnum;
1388 i = RunRoutines(actor, grammar);
1389 #Ifdef DEBUG;
1390 if (parser_trace >= 2 && actor.grammar ~= 0 or NULL)
1391 print " [Grammar property returned ", i, "]^";
1392 #Endif; ! DEBUG
1393
1394 #Ifdef TARGET_ZCODE;
1395 if ((i ~= 0 or 1) &&
1396 (UnsignedCompare(i, dict_start) < 0 ||
1397 UnsignedCompare(i, dict_end) >= 0 ||
1398 (i - dict_start) % dict_entry_size ~= 0)) {
1399 usual_grammar_after = j;
1400 i=-i;
1401 }
1402
1403 #Ifnot; ! TARGET_GLULX
1404 if (i < 0) { usual_grammar_after = verb_wordnum; i=-i; }
1405 #Endif;
1406
1407 if (i == 1) {
1408 results-->0 = action;
1409 results-->1 = noun;
1410 results-->2 = second;
1411 rtrue;
1412 }
1413 if (i ~= 0) { verb_word = i; wn--; verb_wordnum--; }
1414 else { wn = verb_wordnum; verb_word = NextWord(); }
1415 }
1416 else usual_grammar_after = 0;
1417
1418 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1419 !
1420 ! B: Is it a direction, and so an implicit "go"? If so go to (K)
1421 !
1422 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1423
1424 #Ifdef LanguageIsVerb;
1425 if (verb_word == 0) {
1426 i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum);
1427 wn = i;
1428 }
1429 #Endif; ! LanguageIsVerb
1430
1431 ! If the first word is not listed as a verb, it must be a direction
1432 ! or the name of someone to talk to
1433
1434 if (verb_word == 0 || ((verb_word->#dict_par1) & 1) == 0) {
1435
1436 ! So is the first word an object contained in the special object "compass"
1437 ! (i.e., a direction)? This needs use of NounDomain, a routine which
1438 ! does the object matching, returning the object number, or 0 if none found,
1439 ! or REPARSE_CODE if it has restructured the parse table so the whole parse
1440 ! must be begun again...
1441
1442 wn = verb_wordnum; indef_mode = false; token_filter = 0;
1443 l = NounDomain(compass, 0, 0);
1444 if (l == REPARSE_CODE) jump ReParse;
1445
1446 ! If it is a direction, send back the results:
1447 ! action=GoSub, no of arguments=1, argument 1=the direction.
1448
1449 if (l ~= 0) {
1450 results-->0 = ##Go;
1451 action_to_be = ##Go;
1452 results-->1 = 1;
1453 results-->2 = l;
1454 jump LookForMore;
1455 }
1456
1457 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1458 !
1459 ! C: Is anyone being addressed?
1460 !
1461 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1462
1463 ! Only check for a comma (a "someone, do something" command) if we are
1464 ! not already in the middle of one. (This simplification stops us from
1465 ! worrying about "robot, wizard, you are an idiot", telling the robot to
1466 ! tell the wizard that she is an idiot.)
1467
1468 if (actor == player) {
1469 for (j=2 : j<=num_words : j++) {
1470 i=NextWord();
1471 if (i == comma_word) jump Conversation;
1472 }
1473 verb_word = UnknownVerb(verb_word);
1474 if (verb_word ~= 0) jump VerbAccepted;
1475 }
1476 best_etype = VERB_PE;
1477 jump GiveError;
1478
1479 ! NextWord nudges the word number wn on by one each time, so we've now
1480 ! advanced past a comma. (A comma is a word all on its own in the table.)
1481
1482 .Conversation;
1483
1484 j = wn - 1;
1485 if (j == 1) {
1486 L__M(##Miscellany, 22);
1487 jump ReType;
1488 }
1489
1490 ! Use NounDomain (in the context of "animate creature") to see if the
1491 ! words make sense as the name of someone held or nearby
1492
1493 wn = 1; lookahead = HELD_TOKEN;
1494 scope_reason = TALKING_REASON;
1495 l = NounDomain(player,actors_location,6);
1496 scope_reason = PARSING_REASON;
1497 if (l == REPARSE_CODE) jump ReParse;
1498 if (l == 0) {
1499 L__M(##Miscellany, 23);
1500 jump ReType;
1501 }
1502
1503 .Conversation2;
1504
1505 ! The object addressed must at least be "talkable" if not actually "animate"
1506 ! (the distinction allows, for instance, a microphone to be spoken to,
1507 ! without the parser thinking that the microphone is human).
1508
1509 if (l hasnt animate && l hasnt talkable) {
1510 L__M(##Miscellany, 24, l);
1511 jump ReType;
1512 }
1513
1514 ! Check that there aren't any mystery words between the end of the person's
1515 ! name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
1516
1517 if (wn ~= j) {
1518 L__M(##Miscellany, 25);
1519 jump ReType;
1520 }
1521
1522 ! The player has now successfully named someone. Adjust "him", "her", "it":
1523
1524 PronounNotice(l);
1525
1526 ! Set the global variable "actor", adjust the number of the first word,
1527 ! and begin parsing again from there.
1528
1529 verb_wordnum = j + 1;
1530
1531 ! Stop things like "me, again":
1532
1533 if (l == player) {
1534 wn = verb_wordnum;
1535 if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
1536 L__M(##Miscellany, 20);
1537 jump ReType;
1538 }
1539 }
1540
1541 actor = l;
1542 actors_location = ScopeCeiling(l);
1543 #Ifdef DEBUG;
1544 if (parser_trace >= 1)
1545 print "[Actor is ", (the) actor, " in ", (name) actors_location, "]^";
1546 #Endif; ! DEBUG
1547 jump BeginCommand;
1548
1549 } ! end of first-word-not-a-verb
1550
1551 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1552 !
1553 ! D: Get the verb: try all the syntax lines for that verb
1554 !
1555 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1556
1557 .VerbAccepted;
1558
1559 ! We now definitely have a verb, not a direction, whether we got here by the
1560 ! "take ..." or "person, take ..." method. Get the meta flag for this verb:
1561
1562 meta = ((verb_word->#dict_par1) & 2)/2;
1563
1564 ! You can't order other people to "full score" for you, and so on...
1565
1566 if (meta == 1 && actor ~= player) {
1567 best_etype = VERB_PE;
1568 meta = 0;
1569 jump GiveError;
1570 }
1571
1572 ! Now let i be the corresponding verb number, stored in the dictionary entry
1573 ! (in a peculiar 255-n fashion for traditional Infocom reasons)...
1574
1575 i = $ff-(verb_word->#dict_par2);
1576
1577 ! ...then look up the i-th entry in the verb table, whose address is at word
1578 ! 7 in the Z-machine (in the header), so as to get the address of the syntax
1579 ! table for the given verb...
1580
1581 #Ifdef TARGET_ZCODE;
1582 syntax = (HDR_STATICMEMORY-->0)-->i;
1583 #Ifnot; ! TARGET_GLULX
1584 syntax = (#grammar_table)-->(i+1);
1585 #Endif; ! TARGET_
1586
1587 ! ...and then see how many lines (ie, different patterns corresponding to the
1588 ! same verb) are stored in the parse table...
1589
1590 num_lines = (syntax->0) - 1;
1591
1592 ! ...and now go through them all, one by one.
1593 ! To prevent pronoun_word 0 being misunderstood,
1594
1595 pronoun_word = NULL; pronoun_obj = NULL;
1596
1597 #Ifdef DEBUG;
1598 if (parser_trace >= 1) print "[Parsing for the verb '", (address) verb_word, "' (", num_lines+1, " lines)]^";
1599 #Endif; ! DEBUG
1600
1601 best_etype = STUCK_PE; nextbest_etype = STUCK_PE;
1602 multiflag = false;
1603
1604 ! "best_etype" is the current failure-to-match error - it is by default
1605 ! the least informative one, "don't understand that sentence".
1606 ! "nextbest_etype" remembers the best alternative to having to ask a
1607 ! scope token for an error message (i.e., the best not counting ASKSCOPE_PE).
1608 ! multiflag is used here to prevent inappropriate MULTI_PE errors
1609 ! in addition to its unrelated duties passing information to action routines
1610
1611 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1612 !
1613 ! E: Break down a syntax line into analysed tokens
1614 !
1615 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1616
1617 line_address = syntax + 1;
1618
1619 for (line=0 : line<=num_lines : line++) {
1620
1621 for (i=0 : i<32 : i++) {
1622 line_token-->i = ENDIT_TOKEN;
1623 line_ttype-->i = ELEMENTARY_TT;
1624 line_tdata-->i = ENDIT_TOKEN;
1625 }
1626
1627 ! Unpack the syntax line from Inform format into three arrays; ensure that
1628 ! the sequence of tokens ends in an ENDIT_TOKEN.
1629
1630 line_address = UnpackGrammarLine(line_address);
1631
1632 #Ifdef DEBUG;
1633 if (parser_trace >= 1) {
1634 if (parser_trace >= 2) new_line;
1635 print "[line ", line; DebugGrammarLine();
1636 print "]^";
1637 }
1638 #Endif; ! DEBUG
1639
1640 ! We aren't in "not holding" or inferring modes, and haven't entered
1641 ! any parameters on the line yet, or any special numbers; the multiple
1642 ! object is still empty.
1643
1644 not_holding = 0;
1645 inferfrom = 0;
1646 parameters = 0;
1647 nsns = 0; special_word = 0; special_number = 0;
1648 multiple_object-->0 = 0;
1649 multi_context = 0;
1650 etype = STUCK_PE;
1651
1652 ! Put the word marker back to just after the verb
1653
1654 wn = verb_wordnum+1;
1655
1656 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1657 !
1658 ! F: Look ahead for advance warning for multiexcept/multiinside
1659 !
1660 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1661
1662 ! There are two special cases where parsing a token now has to be
1663 ! affected by the result of parsing another token later, and these
1664 ! two cases (multiexcept and multiinside tokens) are helped by a quick
1665 ! look ahead, to work out the future token now. We can only carry this
1666 ! out in the simple (but by far the most common) case:
1667 !
1668 ! multiexcept