Parser__parse (lines 732-1451)
Back to List
Browsing parserm.h
0732 ! To simplify the picture a little, a rough map of the main routine:
0733 !
0734 ! (A) Get the input, do "oops" and "again"
0735 ! (B) Is it a direction, and so an implicit "go"? If so go to (K)
0736 ! (C) Is anyone being addressed?
0737 ! (D) Get the verb: try all the syntax lines for that verb
0738 ! (E) Break down a syntax line into analysed tokens
0739 ! (F) Look ahead for advance warning for multiexcept/multiinside
0740 ! (G) Parse each token in turn (calling ParseToken to do most of the work)
0741 ! (H) Cheaply parse otherwise unrecognised conversation and return
0742 ! (I) Print best possible error message
0743 ! (J) Retry the whole lot
0744 ! (K) Last thing: check for "then" and further instructions(s), return.
0745 !
0746 ! The strategic points (A) to (K) are marked in the commentary.
0747 !
0748 ! Note that there are three different places where a return can happen.
0749 ! ----------------------------------------------------------------------------
0750
0751 [ Parser__parse results syntax line num_lines line_address i j k
0752 token l m;
0753
0754 ! **** (A) ****
0755
0756 ! Firstly, in "not held" mode, we still have a command left over from last
0757 ! time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
0758 ! last time, with "eat biscuit" tucked away until now). So we return that.
0759
0760 if (notheld_mode==1)
0761 { for (i=0:i<8:i++) results-->i=kept_results-->i;
0762 notheld_mode=0; rtrue;
0763 }
0764
0765 if (held_back_mode==1)
0766 { held_back_mode=0;
0767 Tokenise__(buffer,parse);
0768 jump ReParse;
0769 }
0770
0771 .ReType;
0772
0773 Keyboard(buffer,parse);
0774
0775 .ReParse;
0776
0777 parser_inflection = name;
0778
0779 ! Initially assume the command is aimed at the player, and the verb
0780 ! is the first word
0781
0782 num_words=parse->1;
0783 wn=1;
0784 #ifdef LanguageToInformese;
0785 LanguageToInformese();
0786 #ifv5;
0787 ! Re-tokenise:
0788 Tokenise__(buffer,parse);
0789 #endif;
0790 #endif;
0791
0792 BeforeParsing();
0793 num_words=parse->1;
0794
0795 k=0;
0796 #ifdef DEBUG;
0797 if (parser_trace>=2)
0798 { print "[ ";
0799 for (i=0:i<num_words:i++)
0800 { j=parse-->(i*2 + 1);
0801 k=WordAddress(i+1);
0802 l=WordLength(i+1);
0803 print "~"; for (m=0:m<l:m++) print (char) k->m; print "~ ";
0804
0805 if (j == 0) print "?";
0806 else
0807 { if (UnsignedCompare(j, 0-->4)>=0
0808 && UnsignedCompare(j, 0-->2)<0) print (address) j;
0809 else print j;
0810 }
0811 if (i ~= num_words-1) print " / ";
0812 }
0813 print " ]^";
0814 }
0815 #endif;
0816 verb_wordnum=1;
0817 actor=player;
0818 actors_location = ScopeCeiling(player);
0819 usual_grammar_after = 0;
0820
0821 .AlmostReParse;
0822
0823 scope_token = 0;
0824 action_to_be = NULL;
0825
0826 ! Begin from what we currently think is the verb word
0827
0828 .BeginCommand;
0829 wn=verb_wordnum;
0830 verb_word = NextWordStopped();
0831
0832 ! If there's no input here, we must have something like
0833 ! "person,".
0834
0835 if (verb_word==-1)
0836 { best_etype = STUCK_PE; jump GiveError; }
0837
0838 ! Now try for "again" or "g", which are special cases:
0839 ! don't allow "again" if nothing has previously been typed;
0840 ! simply copy the previous text across
0841
0842 if (verb_word==AGAIN2__WD or AGAIN3__WD) verb_word=AGAIN1__WD;
0843 if (verb_word==AGAIN1__WD)
0844 { if (actor~=player)
0845 { L__M(##Miscellany,20); jump ReType; }
0846 if (buffer3->1==0)
0847 { L__M(##Miscellany,21); jump ReType; }
0848 for (i=0:i<120:i++) buffer->i=buffer3->i;
0849 jump ReParse;
0850 }
0851
0852 ! Save the present input in case of an "again" next time
0853
0854 if (verb_word~=AGAIN1__WD)
0855 for (i=0:i<120:i++) buffer3->i=buffer->i;
0856
0857 if (usual_grammar_after==0)
0858 { i = RunRoutines(actor, grammar);
0859 #ifdef DEBUG;
0860 if (parser_trace>=2 && actor.grammar~=0 or NULL)
0861 print " [Grammar property returned ", i, "]^";
0862 #endif;
0863 if (i<0) { usual_grammar_after = verb_wordnum; i=-i; }
0864 if (i==1)
0865 { results-->0 = action;
0866 results-->1 = noun;
0867 results-->2 = second;
0868 rtrue;
0869 }
0870 if (i~=0) { verb_word = i; wn--; verb_wordnum--; }
0871 else
0872 { wn = verb_wordnum; verb_word=NextWord();
0873 }
0874 }
0875 else usual_grammar_after=0;
0876
0877 ! **** (B) ****
0878
0879 #ifdef LanguageIsVerb;
0880 if (verb_word==0)
0881 { i = wn; verb_word=LanguageIsVerb(buffer, parse, verb_wordnum);
0882 wn = i;
0883 }
0884 #endif;
0885
0886 ! If the first word is not listed as a verb, it must be a direction
0887 ! or the name of someone to talk to
0888
0889 if (verb_word==0 || ((verb_word->#dict_par1) & 1) == 0)
0890 {
0891
0892 ! So is the first word an object contained in the special object "compass"
0893 ! (i.e., a direction)? This needs use of NounDomain, a routine which
0894 ! does the object matching, returning the object number, or 0 if none found,
0895 ! or REPARSE_CODE if it has restructured the parse table so the whole parse
0896 ! must be begun again...
0897
0898 wn=verb_wordnum; indef_mode = false; token_filter = 0;
0899 l=NounDomain(compass,0,0); if (l==REPARSE_CODE) jump ReParse;
0900
0901 ! If it is a direction, send back the results:
0902 ! action=GoSub, no of arguments=1, argument 1=the direction.
0903
0904 if (l~=0)
0905 { results-->0 = ##Go;
0906 action_to_be = ##Go;
0907 results-->1 = 1;
0908 results-->2 = l;
0909 jump LookForMore;
0910 }
0911
0912 ! **** (C) ****
0913
0914 ! Only check for a comma (a "someone, do something" command) if we are
0915 ! not already in the middle of one. (This simplification stops us from
0916 ! worrying about "robot, wizard, you are an idiot", telling the robot to
0917 ! tell the wizard that she is an idiot.)
0918
0919 if (actor==player)
0920 { for (j=2:j<=num_words:j++)
0921 { i=NextWord(); if (i==comma_word) jump Conversation;
0922 }
0923
0924 verb_word=UnknownVerb(verb_word);
0925 if (verb_word~=0) jump VerbAccepted;
0926 }
0927
0928 best_etype=VERB_PE; jump GiveError;
0929
0930 ! NextWord nudges the word number wn on by one each time, so we've now
0931 ! advanced past a comma. (A comma is a word all on its own in the table.)
0932
0933 .Conversation;
0934 j=wn-1;
0935 if (j==1) { L__M(##Miscellany,22); jump ReType; }
0936
0937 ! Use NounDomain (in the context of "animate creature") to see if the
0938 ! words make sense as the name of someone held or nearby
0939
0940 wn=1; lookahead=HELD_TOKEN;
0941 scope_reason = TALKING_REASON;
0942 l=NounDomain(player,actors_location,6);
0943 scope_reason = PARSING_REASON;
0944 if (l==REPARSE_CODE) jump ReParse;
0945
0946 if (l==0) { L__M(##Miscellany,23); jump ReType; }
0947
0948 ! The object addressed must at least be "talkable" if not actually "animate"
0949 ! (the distinction allows, for instance, a microphone to be spoken to,
0950 ! without the parser thinking that the microphone is human).
0951
0952 if (l hasnt animate && l hasnt talkable)
0953 { L__M(##Miscellany, 24, l); jump ReType; }
0954
0955 ! Check that there aren't any mystery words between the end of the person's
0956 ! name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
0957
0958 if (wn~=j)
0959 { L__M(##Miscellany, 25); jump ReType; }
0960
0961 ! The player has now successfully named someone. Adjust "him", "her", "it":
0962
0963 PronounNotice(l);
0964
0965 ! Set the global variable "actor", adjust the number of the first word,
0966 ! and begin parsing again from there.
0967
0968 verb_wordnum=j+1;
0969
0970 ! Stop things like "me, again":
0971
0972 if (l == player)
0973 { wn = verb_wordnum;
0974 if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD)
0975 { L__M(##Miscellany,20); jump ReType;
0976 }
0977 }
0978
0979 actor=l;
0980 actors_location=ScopeCeiling(l);
0981 #ifdef DEBUG;
0982 if (parser_trace>=1)
0983 print "[Actor is ", (the) actor, " in ",
0984 (name) actors_location, "]^";
0985 #endif;
0986 jump BeginCommand;
0987 }
0988
0989 ! **** (D) ****
0990
0991 .VerbAccepted;
0992
0993 ! We now definitely have a verb, not a direction, whether we got here by the
0994 ! "take ..." or "person, take ..." method. Get the meta flag for this verb:
0995
0996 meta=((verb_word->#dict_par1) & 2)/2;
0997
0998 ! You can't order other people to "full score" for you, and so on...
0999
1000 if (meta==1 && actor~=player)
1001 { best_etype=VERB_PE; meta=0; jump GiveError; }
1002
1003 ! Now let i be the corresponding verb number, stored in the dictionary entry
1004 ! (in a peculiar 255-n fashion for traditional Infocom reasons)...
1005
1006 i=$ff-(verb_word->#dict_par2);
1007
1008 ! ...then look up the i-th entry in the verb table, whose address is at word
1009 ! 7 in the Z-machine (in the header), so as to get the address of the syntax
1010 ! table for the given verb...
1011
1012 syntax=(0-->7)-->i;
1013
1014 ! ...and then see how many lines (ie, different patterns corresponding to the
1015 ! same verb) are stored in the parse table...
1016
1017 num_lines=(syntax->0)-1;
1018
1019 ! ...and now go through them all, one by one.
1020 ! To prevent pronoun_word 0 being misunderstood,
1021
1022 pronoun_word=NULL; pronoun_obj=NULL;
1023
1024 #ifdef DEBUG;
1025 if (parser_trace>=1)
1026 { print "[Parsing for the verb '", (address) verb_word,
1027 "' (", num_lines+1, " lines)]^";
1028 }
1029 #endif;
1030
1031 best_etype=STUCK_PE; nextbest_etype=STUCK_PE;
1032
1033 ! "best_etype" is the current failure-to-match error - it is by default
1034 ! the least informative one, "don't understand that sentence".
1035 ! "nextbest_etype" remembers the best alternative to having to ask a
1036 ! scope token for an error message (i.e., the best not counting ASKSCOPE_PE).
1037
1038
1039 ! **** (E) ****
1040
1041 line_address = syntax + 1;
1042
1043 for (line=0:line<=num_lines:line++)
1044 {
1045 for (i = 0 : i < 32 : i++)
1046 { line_token-->i = ENDIT_TOKEN;
1047 line_ttype-->i = ELEMENTARY_TT;
1048 line_tdata-->i = ENDIT_TOKEN;
1049 }
1050
1051 ! Unpack the syntax line from Inform format into three arrays; ensure that
1052 ! the sequence of tokens ends in an ENDIT_TOKEN.
1053
1054 line_address = UnpackGrammarLine(line_address);
1055
1056 #ifdef DEBUG;
1057 if (parser_trace >= 1)
1058 { if (parser_trace >= 2) new_line;
1059 print "[line ", line; DebugGrammarLine();
1060 print "]^";
1061 }
1062 #endif;
1063
1064 ! We aren't in "not holding" or inferring modes, and haven't entered
1065 ! any parameters on the line yet, or any special numbers; the multiple
1066 ! object is still empty.
1067
1068 not_holding=0;
1069 inferfrom=0;
1070 parameters=0;
1071 nsns=0; special_word=0; special_number=0;
1072 multiple_object-->0 = 0;
1073 multi_context = 0;
1074 etype=STUCK_PE;
1075
1076 ! Put the word marker back to just after the verb
1077
1078 wn=verb_wordnum+1;
1079
1080 ! **** (F) ****
1081 ! There are two special cases where parsing a token now has to be
1082 ! affected by the result of parsing another token later, and these
1083 ! two cases (multiexcept and multiinside tokens) are helped by a quick
1084 ! look ahead, to work out the future token now. We can only carry this
1085 ! out in the simple (but by far the most common) case:
1086 !
1087 ! multiexcept
Last updated 27 February 2004.
This site is no longer supported; information may be out of date.
Maintained as a historical archive by the Interactive Fiction Technology Foundation.
Copyright 1993-2018 IFTF, CC-BY-SA unless otherwise noted.
This page was originally managed by Graham Nelson (graham@gnelson.demon.co.uk) assisted by C Knight.