Parsing multiples of a kind

Say I have the “compare” verb:

A fruit is a kind of thing.

Example Location is a room. An apple is a fruit. It is in example location. An orange is a fruit. It is in example location. 

comparing it to is an action applying to two things. Understand "compare [a fruit] to [a fruit]" as comparing it to.

But I’d really like to make “compare apple and orange” work. So following the example of “The Left Hand of Autumn” at 6.5. Examining, I start off like this:

comparing-singly is an action applying to one thing.

Understand "compare [fruits]" as comparing-singly.

...

But it turns out it doesn’t work, presumably because Inform won’t handle multiples of a kind, only multiple things. (And for this application, matching on the kind is crucial).

Can someone clue me in on an approach that might get this to work, or is matching in this way even possible without major surgery?

3 Likes

I think this is an I6-level restriction. You can’t impose a restriction on the type of noun for a grammar token that accepts multiple objects.

You might look at Understand "verb [thing] and [thing]" as one command.

Hunh. Haven’t really used the multiple object list before; this was trickier to get right than I expected.

lab is a room.

A fruit is a kind of thing.
apple, orange, banana are fruits.
all fruits are in lab.

before comparing-multiply:
let l be the multiple object list;
let len be the number of entries in l;
let all-fruit be true;
repeat with item running through l begin;
  if item is not a fruit begin;
    now all-fruit is false;
    break;
   end if;
end repeat;
if all-fruit is true begin;
  if len is 2, try comparing entry 1 in l to entry 2 in l;
  else say "[We] [if len < 2][must][else][can] only[end if] compare two fruits.";
else;
  say "[We] [can] only compare fruits.";
end if;
truncate l to 0 entries;
alter the multiple object list to l;  
stop the action.

comparing it to is an action applying to two things.
Understand "compare [a fruit] to/with [a fruit]" as comparing it to.
carry out comparing it to: say "[a noun] to [a second noun] comparison.".

comparing-multiply is an action applying to one thing [, ironically enough].
Understand "compare [things]" as comparing-multiply.

comparing-singly is an action applying to one thing.
Understand "compare [thing]" as comparing-singly.

check comparing-singly: instead say "[We] [if the noun is a fruit][must][else][can] only[end if] compare two fruits."

announce items from multiple object lists rule does nothing when comparing-multiply.
test me with "compare apple and orange / compare orange to apple / compare banana and apple and orange / compare banana / compare self / compare banana and self"

Heh. I should’ve followed your link. Yours handled the error cases in a more readable way.

This is where my use case gets tricky. As I said in the OP:

The problem is that in my WIP “fruit” is actually an abstraction that tends to overlap very closely with things. So using Understand "compare [things]" will lead to lots of unresolvable disambiguations. (Or will simply select the wrong thing.) Which prompted the original question.

An example:

Lab is a room. A juicy red apple is a thing. A plump orange is a thing.

An abstraction is a kind of thing.

Oranges-abstraction is an abstraction. It is privately-named. The printed name is "orange". Understand "orange" as oranges-abstraction.
Apples-abstraction is an abstraction. It is privately-named. The printed name is "apple". Understand "apple" as apples-abstraction.

arguing it vs is an action applying to two things. Understand "argue the relative merits of [any abstraction] vs [any abstraction]" as arguing it vs.

Report arguing something vs something:
	say "A worthy question."

arguing-multiply is an action applying to one thing.

Understand "argue the relative merits of [any things]" as arguing-multiply.

Report arguing-multiply:
	say "A worthy question."
	
test me with "argue the relative merits of orange and apple".

which results in

Lab

>test me
(Testing.)

>[1] argue the relative merits of orange and apple
Which do you mean, the plump orange or orange?

>
1 Like

OK, I get it now. Also, I was wrong – this is something that can be done in I6, but the I7 compiler won’t produce the right return value for certain parts of the routine unless it’s an [any things] grammar token. (At least that’s the case in 6M62. I’m pretty sure this has come up before.)

You can set up a hotwire to bypass the parser error for scope tokens, but it would apply to all [any...] grammar tokens.

EDIT: This is the block I’m talking about.

    if (~~token_allows_multiple) {
        if (multiflag) jump PassToken; ! give UPTO_PE error
        etype=MULTI_PE;
        if (~~scope_token) jump FailToken;	! MODIFIED
    }

This is totally unsafe, because it affects all verbs with [any... ] grammar tokens.

However, if the verb words you’ll be using are unique, you can change the conditional:

        if (~~(scope_token && verb_word == 'argue')) jump FailToken;

and that seems to work.

If you have a number of verbs and conflicting grammars, then it might be possible to use a trick @drpeterbatesuk came up with to make a better bypass.

1 Like

Incidentally, everything about the way that the 6M62 and 10.1 compilers build the scope routine for [any abstractions] seems OK except that the return value for scope_stage == 1 is wrong. (@Zed, is this a known bug? If not, it has the look of something relatively easy to fix.)

With the stage 1 response corrected and set up as a GPR, plus a workaround to force the parser to use the modified routine as a scope token, it works just fine. Here’s the workaround:

  1. Compile your game.
  2. Open up the generated I6 source.
  3. Find the verb declaration for the action using the [any abstractions] grammar token.
  4. Get the name of the routine being used for the scope token (i.e. scope=SomeToken)
  5. Find that routine and copy it.
  6. Put that routine in an Include within your I7 source, but rename it to something else (e.g. MyReplacementToken)
  7. Locate the rfalse statement in the renamed routine (switch case 1), and change it to rtrue.
  8. Add an Understand line to your I7 source to give the routine a grammar token (see WWI 27.23 Inform 6 Understand Tokens).
  9. Change the [any abstractions] grammar token in the Understand line to the new grammar token.

It will not work yet, because the I6 compiler will not understand that the new token is a scope token. Now you need to install a widget in the parser.

  1. Find the ParseToken__() routine. For 6M62 (which I think you’re using), copy the whole section labeled “Parse Token Letter A”.
  2. Make a replacement for that section in your I7 source via Include ... instead of ... in ... (see WWI 27.26 The template layer)
  3. Find the section labeled GPR_TT and add a line just under that: if (given_tdata == MyReplacementToken) jump Reroute;
  4. Find the section labeled SCOPE_TT and add a line just under that: .Reroute;

Now it should parse! You’ll still have to convert multiple matches to the other action.

>ARGUE THE RELATIVE MERITS OF APPLE AND ORANGE
apple*: That isn't available.
orange*: That isn't available.

>ARGUE THE RELATIVE MERITS OF APPLE VS ORANGE
A worthy question.

>ARGUE THE RELATIVE MERITS OF ME AND APPLE
That noun did not make sense in this context.

>ARGUE THE RELATIVE MERITS OF APPLE PIE
I only understood you as far as wanting to argue the relative merits of apple*.

>ARGUE THE RELATIVE MERITS OF ORANGE AND BANANA
orange*: That isn't available.
banana*: That isn't available.

>ARGUE THE RELATIVE MERITS OF ORANGE AND BANANA AND APPLE
orange*: That isn't available.
banana*: That isn't available.
apple*: That isn't available.

>ARGUE THE RELATIVE MERITS OF ORANGE, BANANA AND APPLE
orange*: That isn't available.
banana*: That isn't available.
apple*: That isn't available.

>ARGUE THE RELATIVE MERITS OF ORANGE, BANANA AND APPLE [Oxford comma bug, separate issue]
That noun did not make sense in this context.

(In my test code I put asterisks in the printed names of the abstractions.)

If you don’t want to change the parser code, you can write your own GPR (general parsing routine) to do the job instead of hotwiring the parser. That’s kind of a pain when you have to parse multiple items, but it can definitely be done. (I did manage to work out a GPR to do the job.)

1 Like

So far as I know, this is not a known bug. As of this very moment it is also, for me, not even an understood bug. :laughing: I’ll have to look at it more.

1 Like

I should have reread DM4 first. Do the above without modifying the parser, then add something like this to your Include:

[ MyReplacementTokenGPR ;
	return ParseToken(SCOPE_TT, MyReplacementToken);
];

Then use that short routine for the Understand line to create the new grammar token in I7.

This is the least invasive and most generic method of workaround that I’ve found. Hopefully future versions won’t require it.

Include (-

Global any_scope_kind = NULL;

[ Generic_Any_Scope_Token
	obj ! object loop variable
	o2 ! saved value of noun
	;
	if (any_scope_kind == NULL) return GPR_FAIL;
	switch (scope_stage) {
	    1: rtrue;
	    2: obj=noun;
	    objectloop ( noun ofclass any_scope_kind ) {
	        o2 = noun; noun = obj;
	        suppress_scope_loops = true; PlaceInScope(o2, true); suppress_scope_loops = false;
	        noun = o2;
	    }
	    noun=obj;
	    3: nextbest_etype = NOTINCONTEXT_PE; return -1;
	}
];

! build these as needed
[ AnyAbstractions;
	any_scope_kind = (+ abstraction +);
	return ParseToken(SCOPE_TT, Generic_Any_Scope_Token);
];

-).

The Understand token any-abstractions translates into I6 as "AnyAbstractions". [also as needed]
2 Likes

This is the template I use when dealing with this sort of thing.

{ACTION NAME} is an action applying to one thing. Understand "{VERB} [things]" as {ACTION NAME}.

Check {ACTION NAME} when the multiple object list is empty (this is the check single object {ACTION NAME} rule): say "[We] [need] to {VERB} [the noun] to something." (A) instead.

Carry out {ACTION NAME} when the noun is the first list object (this is the standard carry out {ACTION NAME} rule):
if the number of entries in the multiple object list is two begin;
try {OTHER ACTION NAME} the first list object to the last list object;
otherwise;
say "[We] [can't {VERB}] more than two things at a time." (A);
end if;
alter the multiple object list to {}.

This is the carefully announce items from multiple object lists rule: unless {ACTION NAME}, abide by the announce items from multiple object lists rule.

The carefully announce items from multiple object lists rule is listed instead of the announce items from multiple object lists rule in the action-processing rules.

Replacing ACTION NAME, OTHER ACTION NAME and VERB accordingly.

This comes from my combining extension that does this for the tying action.

That said, I am working an an extension that will allow auto distinguishing between “and” in these 2 forms and also when “and” is used as a “then” word. I plan to call it “Andy”! :laughing:

Hope this helps.

1 Like

This works wonderfully. Thanks!

Thanks for asking it. Now I know what the suppress_scope_loops global is for. Plus, the issue you ran into will hopefully be confirmed as a bug (or adopted as a feature request).