Matching quoted text in verb grammar

I want to create a new line of grammar to match commands like ANSWER “HELLO” TO DRAGON, including the quotation marks. The expected line of Understand "answer '[text]' to [someone]" as answering it that (with nouns reversed). compiles but doesn’t actually match the given player command at runtime. (Also, seriously, “answering it that”?) There’s a mildly awkward workaround of reformatting a text token within an appropriate rule, but that demotes the relevant snippet to text, which means I can’t then match it against any topics. (And why can’t I do that? This isn’t static text, so it’s just sitting around in a buffer in memory somewhere; can’t I just point the topic-parsing routine at that buffer instead of whichever one the player’s input is stored in? Is there some I6 workaround for it?) Is there a magic syntax for matching quotes in grammar lines?

(I am aware that I can step through the buffer manually and check for quotation marks; that’s pretty much what Short’s “Punctuation Removal” package does. I’m looking for a more idiomatic way of solving this problem without just giving up again on I7 and resorting to I6.)

I wound up just doing it (mostly) in I6. Here’s the code, in case any future reader has the same problem:

To sanitize conversation: (- RemoveSurroundingQuotes();  -)

Include (- 
[ RemoveSurroundingQuotes   start_word end_word;
	start_word = consult_from;
	end_word = consult_from + consult_words - 1;
	if (start_word == end_word) return;

	if (WordLength(start_word) == 1 && WordAddress(start_word)->0 == '"' &&
	    WordLength(end_word) == 1 && WordAddress(end_word)->0 == '"') {

		WordAddress(start_word)->0 = ' ';
		WordAddress(end_word)->0 = ' ';
		
		! This is how to update the topic understood variable in I7. The (consult_from, consult_words) pair
		! is represented by parser_number = 100 * consult_from + consult_words.
		! Note that consult_words counts both original quotation marks as separate words.
		parsed_number = (consult_from + 1) * 100 + consult_words - 2;
	}
];
-)
	
Carry out answering it that:
	sanitize conversation;
	[...]

Why not just use double quote stripping on the original player’s command?

I don’t want to delete every double quote from the input because of an unrelated part of the code. I already have some dirty tricks involving the input; I don’t want the two routines to conflict.

With respect to your original questions:

A text “buffer” is a bit different from the command buffer. The former is a virtual array inside a block value while the latter is an actual I6 array.

The compiler doesn’t like the idea of double quotes in a grammar line at all. Your original grammar line gets translated to carets (I6 literal for single quotes), which is why it doesn’t match the player’s entered command (containing double quotes).

The most radical approach is to change the way that I6 topic tokens are processed to allow it to ignore wrapping double quotes. Then you don’t have to worry about any special processing. Here’s a 6M62-compatible change:

Mad Science parser mod (6M62 version)
Include (-

Constant	dq_wd = '"//';	! " (this comment unconfuses IDE highlighter)
Global	open_quote_found;	! for modified TOPIC_TOKEN block

-) after "Definitions.i6t".

Include (-

! ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
! Parser.i6t: Parse Token Letter A
! ==== ==== ==== ==== ==== ==== ==== ==== ==== ====

	token_filter = 0;
	parser_inflection = name;

	switch (given_ttype) {
	  ELEMENTARY_TT:
	    switch (given_tdata) {
	      SPECIAL_TOKEN:
	        l = TryNumber(wn);
	        special_word = NextWord();
	        #Ifdef DEBUG;
	        if (l ~= -1000)
	            if (parser_trace >= 3) print "  [Read special as the number ", l, "]^";
	        #Endif; ! DEBUG
	        if (l == -1000) {
	            #Ifdef DEBUG;
	            if (parser_trace >= 3) print "  [Read special word at word number ", wn, "]^";
	            #Endif; ! DEBUG
	            l = special_word;
	        }
	        parsed_number = l;
	        return GPR_NUMBER;

	      NUMBER_TOKEN:
	        l=TryNumber(wn++);
	        if (l == -1000) {
	            etype = NUMBER_PE;
	            return GPR_FAIL;
	        }
	        #Ifdef DEBUG;
	        if (parser_trace>=3) print "  [Read number as ", l, "]^";
	        #Endif; ! DEBUG
	        parsed_number = l;
	        return GPR_NUMBER;

	      CREATURE_TOKEN:
	        if (action_to_be == ##Answer or ##Ask or ##AskFor or ##Tell)
	            scope_reason = TALKING_REASON;

	     TOPIC_TOKEN:
	        open_quote_found = false;	! ADDED
	        consult_from = wn;
	        if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT) &&
	           (line_token-->(token_n+1) ~= ENDIT_TOKEN)) {
	           	RunTimeProblem(RTP_TEXTTOKENTOOHARD);
	           	return GPR_PREPOSITION;
	    	}
	        !do o = NextWordStopped();
	        !until (o == -1 || PrepositionChain(o, token_n+1) ~= -1);
	        ! BEGIN MODIFICATION
	        l = wn;	! need to recycle local, looks like next use of l reassigns unconditionally
	        while (wn <= num_words) {
		o = NextWordStopped();
	            if (o == dq_wd) {
			if (open_quote_found || wn ~= l + 1) break;
			else open_quote_found = true;
		}
	            if (o == -1 || PrepositionChain(o, token_n+1) ~= -1) {
			if (open_quote_found) return GPR_FAIL;
			else break; 
	            }
	        }
	        if (~~open_quote_found) wn--;
	        if (open_quote_found) consult_from++;
	        consult_words = wn-consult_from;
	        if (open_quote_found) consult_words--;
	        if (consult_words < 1) return GPR_FAIL;
	        ! END MODIFICATION
	        if (action_to_be == ##Ask or ##Answer or ##Tell) {
	            o = wn; wn = consult_from; parsed_number = NextWord();
	            wn = o; return 1;
	        }
	        if (o==-1 && (line_ttype-->(token_n+1) == PREPOSITION_TT))
	            return GPR_FAIL;    ! don't infer if required preposition is absent
	        return GPR_PREPOSITION;
	    }

	  PREPOSITION_TT:
	    ! Is it an unnecessary alternative preposition, when a previous choice
	    ! has already been matched?
	    if ((token->0) & $10) return GPR_PREPOSITION;

	    ! If we've run out of the player's input, but still have parameters to
	    ! specify, we go into "infer" mode, remembering where we are and the
	    ! preposition we are inferring...

	    if (wn > num_words) {
	        if (inferfrom==0 && parameters<params_wanted) {
	            inferfrom = pcount; inferword = token;
	            pattern-->pcount = REPARSE_CODE + VM_DictionaryAddressToNumber(given_tdata);
	        }

	        ! If we are not inferring, then the line is wrong...

	        if (inferfrom == 0) return -1;

	        ! If not, then the line is right but we mark in the preposition...

	        pattern-->pcount = REPARSE_CODE + VM_DictionaryAddressToNumber(given_tdata);
	        return GPR_PREPOSITION;
	    }

	    o = NextWord();

	    pattern-->pcount = REPARSE_CODE + VM_DictionaryAddressToNumber(o);

	    ! Whereas, if the player has typed something here, see if it is the
	    ! required preposition... if it's wrong, the line must be wrong,
	    ! but if it's right, the token is passed (jump to finish this token).

	    if (o == given_tdata) return GPR_PREPOSITION;
	    if (PrepositionChain(o, token_n) ~= -1) return GPR_PREPOSITION;
	    return -1;

	  GPR_TT:
	    l = indirect(given_tdata);
	    #Ifdef DEBUG;
	    if (parser_trace >= 3) print "  [Outside parsing routine returned ", l, "]^";
	    #Endif; ! DEBUG
	    return l;

	  SCOPE_TT:
	    scope_token = given_tdata;
	    scope_stage = 1;
	    #Ifdef DEBUG;
	    if (parser_trace >= 3) print "  [Scope routine called at stage 1]^";
	    #Endif; ! DEBUG
	    l = indirect(scope_token);
	    #Ifdef DEBUG;
	    if (parser_trace >= 3) print "  [Scope routine returned multiple-flag of ", l, "]^";
	    #Endif; ! DEBUG
	    if (l == 1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN;

	  ATTR_FILTER_TT:
	    token_filter = 1 + given_tdata;
	    given_tdata = NOUN_TOKEN;

	  ROUTINE_FILTER_TT:
	    token_filter = given_tdata;
	    given_tdata = NOUN_TOKEN;

	} ! end of switch(given_ttype)

	token = given_tdata;

-) instead of "Parse Token Letter A" in "Parser.i6t".

It shouldn’t be too tough to adapt that to 10.1.

[EDIT: Oops – corrected new loop condition.]

2 Likes