Of course, this doesn’t work, because two nouns separated by ‘and’ will be interpreted as a multi token.
I found a very hacky way to do something which works, but I won’t detail it here for now unless asked (it involves making AND1__WD a Global instead of a Constant and temporarily setting it to a different value before calling ParseNoun in a routine). I’m hoping that there is a cleaner way.
Ideally, the grammar could, instead of fighting against the handling of multiple objects, embrace it:
[ TieTwoSub;
! Check exactly two nouns were provided
! and call `Tie` with both.
! Else politely refuse.
];
Verb "combine"
* noun -> Tie
* noun 'with' noun -> Tie
* multi -> TieTwo;
I attempted to do so, but TieTwoSub would get called once for every object rather than once with both objects.
That’s where I am and I figured it’s the right time to ask for advice here
I believe the issue here is that ‘verb noun and noun’ is treated as a multi action and is auto converted to ‘verb noun’ then ‘verb noun’. The way I’ve dealt with this in Inform 7 is to create an action matching the grammar ‘verb nouns’ (plural) then add a rule to extract the first and last items from the multiple object list and then redirect that to the original action. The Inform 6 equivalent of this is most likely the cleanest way of doing it.
So, multiple_object-->0 will give you the size of the multiple object list (needs to be exactly 2), multiple_object-->1 will give you the first element and multiple_object-->(multiple_object-->0) will give you the last element.
You don’t. However, on the first run, after pulling out the first and last elements, you set the multiple object list to zero elements (empty it). That way the action only runs once.
I’d rather avoid modifying parserm.h, because that’s not something I could embed in the library translations or in an optional extension distributed with it.
I gave it a go anyway. I hope that I followed your instructions precisely. It produces the following diff:
diff --git a/parserm.h b/parserm.h
index 0b41728..31a79cc 100644
--- a/parserm.h
+++ b/parserm.h
@@ -2617,6 +2617,12 @@ Constant UNLIT_BIT = 32;
if (parser_trace >= 3) print " [Read connective '", (address) o, "']^";
#Endif; ! DEBUG
+ ! BEGIN ADDITION
+ if (PrepositionChain(o, pcount) > 0) {
+ if (~~token_allows_multiple) jump ConnectivePass;
+ }
+ ! END ADDITION
+
if (~~token_allows_multiple) {
if (multiflag) jump PassToken; ! give UPTO_PE error
etype=MULTI_PE;
@@ -2637,6 +2643,8 @@ Constant UNLIT_BIT = 32;
jump ObjectList; ! And back around
}
+ .ConnectivePass; ! ADDED
+
wn--; ! Word marker back to first not-understood word
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
However, I don’t see a difference in my tests. Did I place the .ConnectivePass label at the right place?
Ah. I thought that you were already doing some parser hacking because you said you had modified the definition of AND1__WD, but I understand that in your case you are changing that anyway because you’re supplying a new language file. (I am curious about the details of your current workaround. Is ParseNoun() used routinely in the French library?)
The method that climbingstars recommended from the world of I7 is unfortunately not easily translatable to StdLib 6/11. There are no hooks for interacting with the loop that processes multiple actions as there are in I7. As you have already discovered, once the parser logic has committed to the branch for processing a multiple object list, it is not responsive to attempts to manipulate the length of the list during loop processing (because it uses cached data about the size of the multiple object list).
If you don’t want to ship a modified parserm.h file, you can try to use the Replace directive to change only the affected routine. I’m not sure that this is something that can be handled with changes only to the language file, though.
I haven’t toyed around with direct I6 enough to know the exact methods here. I kinda assumed that I6 loop for multiple actions is equivalent to a for loop running through the multiple object list.
Generally, you can “manually” end a for loop early using a break command or adjusting the loop variable to fail the loop conditional.
Yes, it would certainly be possible to modify the loop in question, but it’s part of core parser code, so it would presumably be out-of-bounds for stormi’s purposes. This is what the relevant code looks like in StdLib 6/11:
As you can see, the equivalent of I7’s announce items from multiple object lists rule is just a hardcoded print statement here, and it is placed before the attempt to execute the action for the current object. Without the rulebook hooks of I7, it seemed like it would be more painful to try to convert from multiple action to single action than to just get it to parse a single action in the first place.
Very true! Although I don’t know if that is possible without replacing chunks of parser code since the mechanism is pretty much hard coded in there (I could be wrong though).
My current workaround I only implemented in one game, then opened a discussion within the French community to see if changing constant AND1__WD would be acceptable in the lib translation, and whether they had better ideas.
Here it is, simplified (it used to also check that at least one of the two objects is held), translated, and untested after these changes.
[Noun1AndNoun2 noun1 noun2 saved_wn;
AND1__WD = '.N/A';
noun1 = ParseToken(NOUN_TOKEN);
if (noun1 == GPR_FAIL) jump fail;
if (~~(NextWord() == 'and')) jump fail;
saved_wn = wn;
noun2 = ParseToken(NOUN_TOKEN);
if (noun2 == GPR_FAIL) jump fail;
! We can only return one noun, so rewind before the second one
wn = saved_wn;
AND1__WD = 'and';
return noun1;
.fail;
AND1__WD = 'and';
return GPR_FAIL;
];
Verb 'combine'
* noun -> Tie
! Noun1AndNoun2 actually only returns one noun,
! despite it parses both, so we need to repeat noun in the grammar line
* Noun1AndNoun2 noun -> Tie;
I don’t think there’s any way to do something like that. However, you can use ParseToken() within a GPR, as you are doing already.
You seem to be familiar with GPRs as a concept, since the Noun1AndNoun2() routine is being used as one. What I’m suggesting is that you modify your GPR to match a noun and then a word from the AND*__WD group. Leave wn pointing to the word after the AND*_WD, and the next token will be a noun token that the parser will automatically interpret as seeking second. The command should be parsed correctly as a single command with two nouns.
So basically what I’m doing already, but without parsing the last noun unnecessarily? It looks like a useful simplification indeed.
I’m trying to find a way to parse just the first noun + AND1_WD, but am currently failing because ParseToken insists on parsing a multiple object (unless I keep this ugly AND1__WD = '.N/A' in the GPR).
(actually there’s a reason why I was parsing both nouns in the GPR, because in the version of the routine I actually used, I’m also checking that at least one of the two objects is held)