Calling a rule from I6 code, with params

Hi!
I’m trying to execute an I7 rule from an I6 routine (an implementation of LanguageIsVerb() inside the Spanish kit).

rv = FollowRulebook( (+ SearchingFor rules +),word_in_buf, true);

The thing is that I need to send as a parameter a text taken from the variable “buffer” (chars from the keyboard).

SearchingFor is a text based rulebook.
SearchingFor a text (called W):
	let auxiliary response be "";
	repeat through the Table of Irregular Verbs:[search in a table for replace the player`s command]
		if W matches the text "[command entry]":
			now the auxiliary response is "[imperative entry]";
	say "[auxiliary response]".

But I have problem for casting the text to send. My code crash.

How can I cast correctly the param ‘word_in_buf’?
Using SNIPPET_TY_to_TEXT_TY() or something similar?

2 Likes

I’m not sure whether you’re dealing with a snippet (which is an integer that gets interpreted as a range of words within a command) or a character buffer itself. If it’s a snippet, something like this works…

frb-par is a text variable.

Include (-
[ frb param rv;
rv = FollowRulebook( (+ SearchingFor rules +), (+ frb-par +) , true);
];
-)

to frb: (- frb(); -)

after reading a command:
now frb-par is "[the player's command]";
frb.
2 Likes

Thanx Zed for answer.
Specifically the call is made for the parser in Parser.i6t:

line 716/letter B
    if (verb_word == 0) {
        i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum);
        wn = i;
    }


line 2412/.Incomplete
    first_word=(parse2-->1);
    if (first_word==0) {
        j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j;
    }

... etc

LanguageIsVerb() is a stub, Me (the translator) must provide the code for take care of solving.

In buffer var there is the unrecognized word (a verb, presumably) that the parser needs to know. I want to use an I7-look-up-in-table algorithm to resolve this.

Can I say that buffer var is a snippet?
It’s hard for me to understand the meaning of the term snippet.

We would really need to know the origin of word_in_buf.

buffer is a character array of what has been typed in the keyboard
parse is another array representing a complex index into the words in buffer.

Is word_in_buf a simple word number index into parse? (e.g. 3 representing the third word indexed in parse)
Or a snippet (a codified reference to a sequence of words indexed in parse- e.g the number 103 as a snippet refers to the first 3 words indexed in parse: 301 would represent just the third word indexed in parse)

Ah.

LanguageIsVerb() is called by the parser when it doesn’t find a word that it recognises as a verb word (or indeed as any dictionary word) where it expected to find one in the default English idiom.

So here verb_wordnum is the index into parse that is assumed to represent the verb word- so it’s simply 3 for the third word, 5 for the fifth word etc.etc.

In English, the verb word of a command is always the first word of a command, so verb_wordnum is usually assumed to be 1, and LanguageIsVerb() will usually be called with verb_wordnum as 1.

However, in a command such as Mr Darcy, open the cabinet the assumed verb_wordnum will be 4 because there are three words preceding it: ‘Mr’ ‘Darcy’ ‘,’ (a comma counts as a word) and LanguageIsVerb() will be called with verb_wordnum as 4.

I assume then that word_in_buf is equivalent to the word indexed in parse by verb_wordnum (in the last example above, verb_wordnum being 4 and the word in question being ‘open’)? and it’s this word you want to pass to the rulebook as a text “open”?

1 Like

Yes, Dr Bates! “open” is the text I need to pass to the i7 rule (in this example). I’m not sure how do that, and how to make happy the target rule. Perhaps the Zed code will be ok?

Nope, my previous code won’t help. This should, though.

to say foo: (- print (ParserWord) 4; -)

after reading a command: let t be "[foo]"; say t.

Include (-
[ ParserWord wn addr len;
        addr = WordAddress(wn);
        len = WordLength(wn);
        while (len) {
            print (char) addr->0;
            addr++; len--;
        }
];
-).

Yeah, not straightforward, not least because the LanguageIsVerb() routine can be called using the usual input buffer and parse buffer (buffer, parse) or using one of the secondary input buffers and parse buffers (buffer2 and parse2).

It’s easy to find the number encoding a snippet that represents a given word in the input buffer- it’s going to be verb_wordnum*100+1 (a snippet is encoded as starting-word-number * 100+number-of-words).

But the built-in routines in Inform that cast a sequence of words in the input buffer (a snippet) to an Inform 7 text type- SNIPPET_TY_to_TEXT_TY() assume that the snippet is always held in the usual input buffer (buffer- represented in Inform 7 by the player's command ) not a secondary buffer like buffer2. So if it’s called with buffer2, you’ll need to swap buffer contents between buffer and buffer2 before calling SNIPPET_TY_to_TEXT_TY() then swap them back again.

And when you’ve finished, I think you’ll need to print your I7 text word read from the table (auxiliary response) back to an I6 buffer and use VM_Tokenise() on that buffer to find the dictionary address of that word as a return value for LanguageIsVerb()…

1 Like

I7 has no representation of dictionary words; the usual way to match dict words is an I7 topic. Internally, a topic is an I6 function which is called on a snippet. (Really on the startword, numwords pair.)

This seems like what you want to leverage. You want to avoid the entire question of creating a temporary text value.

The problem is still the cases where it’s called on buffer2.

2 Likes

Even if using a snippet to perform the matching to a topic column in the table I think there’s still the awkward issue of how to return a dictionary word address from an I7 routine as a result to I6?

EDIT: Thinking about it, issue also here is that we aren’t matching against a dictionary word- it 's against random non-dictionary text: (LanguageIsVerb() is only called when the parser doesn’t recognise the word it expects to be a verb word as a dictionary word- the function is intended to take this unrecognised non-dictionary word and return a valid verb-word from the dictionary

Ah, right! Sorry. So we can’t avoid some kind of character-by-character comparison.

(Is LanguageIsVerb() documented anywhere? Not that this would slow us down…)

The brute-force solution would be

A dict-word is a kind of value.
To decide what dict-word is dict-word-take: (- ('take') -).
To decide what dict-word is dict-word-wait: (- ('wait') -).
1 Like

I think that instead of a text-based rulebook, you want a snippet-based rulebook.

I think that you also want your SearchingFor rules to produce a number. The number returned by the rulebook (which should be a dictionary word address) can then be returned from LanguageIsVerb().

As mentioned by drpeterbatesuk, you do need an I6 routine to try to tokenize the text in a text variable.

I put together a prototype based on what I could understand of what you’re trying to do. Since you didn’t provide anything from the Table of Irregular Verbs, this example just uses some synonyms that are not by default included in English projects. Unless I’ve misunderstood your intent, the principle should be the same. Note that this is a version for 9.3/6M62, so a few minor changes are needed to make it compatible with 10.1. Also note that this assumes Glulx, not Z-Machine.

Include (-

Array tmpbuf buffer INPUT_BUFFER_LEN;
Array tmpparse --> PARSE_BUFFER_LEN;

Array tknbuf buffer INPUT_BUFFER_LEN;
Array tknparse --> PARSE_BUFFER_LEN;

[ DictionaryWordFromText txt len     i ;
	if (len < 1) rfalse;
	i = 0;
	while (i < len) {
		tknbuf->(WORDSIZE+i) = BlkValueRead(txt, i);
		i++;
	}
	tknbuf-->0 = len;
	VM_Tokenise(tknbuf, tknparse);
	return tknparse-->1; ! only first word of interest
];

[ LanguageIsVerb buf prs word_in_buf bufsnp rv;
	if (buf == buffer2) {
		VM_CopyBuffer(tmpbuf, buffer);
		VM_CopyBuffer(buffer, buffer2);
	}
	if (prs == parse2) {
		Arrcpy(tmpparse, WORDSIZE, parse, WORDSIZE, PARSE_BUFFER_LEN);
		Arrcpy(parse, WORDSIZE, parse2, WORDSIZE, PARSE_BUFFER_LEN);
	}
	if (word_in_buf == 0) return 0;
	bufsnp = (word_in_buf*100)+1;
	FollowRulebook( (+ SearchingFor rules +), bufsnp, true);
	if (buf == buffer2) {
		VM_CopyBuffer(buffer2, buffer);
		VM_CopyBuffer(buffer, tmpbuf);
	}
	if (prs == parse2) {
		Arrcpy(parse2, WORDSIZE, parse, WORDSIZE, PARSE_BUFFER_LEN);
		Arrcpy(parse, WORDSIZE, tmpparse, WORDSIZE, PARSE_BUFFER_LEN);
	}
	return latest_rule_result-->2;
];

-) before "Parser.i6t".

To decide which number is wdnum of (T - text) with length (N - number):
	(- DictionaryWordFromText({T}, {N}) -).

To decide which snippet is the invalid snippet:
	(- 0 -).

SearchingFor is a snippet based rulebook producing a number.

SearchingFor a snippet (called W):
	if W is the invalid snippet, rule fails;
	let converted text be the substituted form of "[W]";
	let auxiliary response be "";
	repeat through the Table of Irregular Verbs:[search in a table for replace the player`s command]
		if converted text matches the text "[command entry]":
			now the auxiliary response is "[imperative entry]";
	if auxiliary response is empty, rule fails;
	say "<replacing '[converted text]' with '[auxiliary response]'>";
	let rv be wdnum of auxiliary response with length (number of characters in auxiliary response);
	rule succeeds with result rv.

Table of Irregular Verbs
command (text)	imperative (text)
"leap"	"jump"
"inspect"	"examine"

Test me with "jump / leap / x me / inspect me / inspect / me".

As drpeterbatesuk points out, the “translated” verb words (auxiliary response entry) must be in the I6 dictionary for this to work. If they are not part of the Spanish default project, you might need to write a special I6 routine with no real purpose other than to inject those words into the dictionary. Something like the following should do the trick (EDIT: but isn’t actually enough, see posts below):

Include (-

[InjectedWord wd;
	if (wd == 'word1' or 'word2' or 'word3') rtrue; ! <-- add as many dictionary words as you like to this line
	rfalse;
];

-).
2 Likes

Not knowing anything much about Spanish, I think what would now help here is an example of Table of Irregular Verbs in order to see why we need to invoke LanguageIsVerb(). It seems to me that it exists as a desperate last resort when simply aliasing additional verb words to existing verb commands or simple manipulations of the player’s command in After Reading A Command won’t work.

Include (-

[InjectedWord wd;
	if (wd == 'word1' or 'word2' or 'word3') rtrue; ! <-- add as many dictionary words as you like to this line
	rfalse;
];

-).

Alas, this simple scheme won’t work because these dictionary words must also be tagged as verbs in ‘word1’->#dict_par1 and have an associated verb number encoded in ‘word1’->#dict_par2

Furthermore, to make matters more awkward, I6 included in I7 source can’t use the Verb directive, so creating new verb words from I7 is non-trivial except by use of the standard Understand "foo..." as fooing or Understand the command "foo" as "bar" :slight_smile:

Not that I can find. It’s just mentioned in passing as a stub in the English Language Kit literate source and skimmed over entirely in the Parser Template literate source: I have derived its function as best I can by direct inspection of the parser code.

I’m not aware of a comprehensive guide to translating Inform 7 to languages other than English.

Right, there is none right now. For 6L38, you have to read the French extension, which contains most of Graham Nelson’s comments from his initial example, then the other translations if needed and some bits of the DM4. Then to update for 10.1 you have to read a thread here, and read some parts of the compiler’s documentation (regarding e.g. kits).

It’s not practical, I’m thinking about writing a guide, but I won’t have a lot of time in the coming weeks.

But in any case, I don’t think I ever heard of LanguageIsVerb, and the French Language doesn’t use it, so I can’t be of any help here… (It’s present in the I6 standard library, so there might be more informations there, but I didn’t really find anything.)

1 Like

A very important point!

It’s possible to build a Rube Goldberg machine to allow words to be marked as verbs and be mapped to another verb word via the Dictionary directive (see I6 patch for setting dict flags or I6 release notes), but the net result of the runaround is the same as using the Understand the command ... as ... construction.

@sarganar, can you explain a little more about what you’re trying to do? Perhaps you can provide an example transcript of the kind of interaction you’re trying to achieve, with notes explaining what should be happening while processing the player’s input?

Dictionary doesn’t directly allow the setting of dict_par2, as far as I know. How do you hack it to set that? Or are you just suggesting separately copying dict_par 2 across from a known verb word?

You are correct, #dict_par2 values for words cannot be set by the Dictionary directive. The abomination that I built used #dict_par3 to store the linking information (indirectly, because the Dictionary directive also won’t accept expressions or backpatched values as parameters) and an unused bit in #dict_par1 to signal to a modified DictionaryWordToVerbNum() that it should look at that instead of #dict_par2 for flagged words.

if ((flgs & DICT_ALT_VERB) == DICT_ALT_VERB) {				! flgs set from #dict_par1 beforehand
	dword = dword + #dict_par3 - 1;
	@aloads dword 0 vsindex;
	return DictionaryWordToVerbNum(VERB_SWAP-->vsindex);	! VERB_SWAP is array holding word address values
}

I did not share details because it is not a useful approach; in the end it is (as you put it) “simply aliasing additional verb words to existing verb commands,” which is exactly what is easily available via Understand the command ... as ....

Anything more complicated would fall more in line with (as you put it) “manipulations of the player’s command in After Reading A Command” – and if that’s what the OP is after, then it might be better to look at that activity (where regular expressions might be very useful) or even LanguageToInformese(), which is documented and has examples in DM4.

Sorry for my lack of good explanation. And thanx for your interest for help. I appreciate that.

I’ll try to explain more (including my own interpretation of some facts):

I try to improve the develop experience for the game author in inform7.

LanguageIsVerb () is a kind of hook that gives to the translator a chance to replace an uknown word with a know one from the dictionary. This can be useful when dealing with some language peculiarities, like in spanish.

In the spanish verbs, imperative is not equal to inifinitive. There are differences.
And, it is a tradition that a player use them interchangeably to write commands in the game/history.

ie:

language    action          infinitive       imperative
english     opening         open             open
spanish                     abrir            abre

english     jumping         jump             jump
spanish                     saltar           salta

The spanish player could write:

>abre la caja (open the box) [imperative form]

And some turns later, he could write:

>abrir la caja (open the box) [infinitive form]

To avoid the need for a game author to define inifinive and imperative forms of every verb for the game, we (the Spanish Extension) use LanguageIsVerb().

So, the author just define the imperative form of the verb and the parser can handle the infinitive(Ej: ‘salta’ for jump: Understand “salta” as jumping.).

We use LanguageIsVerb() spanish library since inform6 era (for more than 20 years) It is a stub function in factory library (english library) and could be used (or not) for translators. Of course, the code must be provided by the translator language extension.
You can inferred its role looking in the inform factory code. I’m afraid threre is no documentation.

In few words, with LanguageIsVerb you can say to the main library parser “hey, this word has a replacement and it could be a verb!”, before de main parse algorithm continues with compass and talk-to-character stages. (see Parser Letter B)

The output of LanguageIsVerb () could be:

   verb_word = LanguageIsVerb(buffer, parse, verb_wordnum)

  ** verb_wordnum ** is a number that points which word in **parser** is not recognized by the parse

  ** buffer ** is the special array that contains all the chars that the player has wrote

   verb_word = 0: LanguageIsVerb can't found a replacement to serve (english default output)
   verb_word  not 0: is a dictionary address of a word, could be a verb (it depends of flag #dict_par1 bit 1 later)

 the parser algorithm will decide how manage that output.

But…

There are a two groups of spanish verbs:

  • “regular” spanish verbs, just follow an easy patter: you make the imperative removing the last character (saltar → salta).

  • “irregular” spanish verbs, that not folllow an easy pattern when are used in imperative form.

For the first group there is no problem, it resolved internally by the spanish extension without the participation of the game author.

For the second is required a kind of strategy to provide a dictionary synonymous for a verb with participation of the game author. (a Table fits well I think).

Table of Irregular Verbs
command	       imperative	
"retorcer"	  "retuerce"	
"abrir"	      "abre"	
"adquirir"	  "adquiere"	
"balancear"	  "balanceate"

So I feed an I7 rule with my text-string and it gives me the synonymous. Then I seek that synonymous in the dictionary and that’s all.

LanguageIsVerb is the i6-hook that call the I7-rule and receives its result.

But I have problem trying to do that, with the params that I need to send, and with the format/type of them.

Why not just use Understand the command … as …?

Because the incomplete-commands sceneray:

You can see a box and a table here.

>open
What do you want to open?

The parser question need the infinitive form of the verb, but it is using [parser command so far] that takes the verb from the player buffer, and could be in spanish imperative. wrong!
We need to provide the infinitve synounymous in this situation. So, the table will be ok here to.

2 Likes