Inform6: Example for working with strings

I understand how to pass strings into and out of Inform6, but am having trouble defining and working with them inside Inform6.
Unfortunately; the Inform6 documentation is a bit cryptic. Does anyone have a few examples or a reference that I could look at?

Are you referring to manipulating 6L02-style I7 strings in I6, or standard “print” strings?

(Life will be easiest from now on if we distinguish I6 strings from I7 texts. I assume the question is about I7 texts.)

Zarf;
You’re correct. I guess I wasn’t clear enough in my text, although I did title the post as “Inform6”. I6 and I7 are quite different and confusing in their handling of strings. I haven’t really seen a good “discussion” of the differences and how to use them properly. I have some situations in which I would like to define a string within the I6 code and only use it in that code (e.g… a cipher alphabet). That said; I also use string variables that I bring in from I7 and unpack before use and then repack and export back to I7. (eg. plaintext or cipher text). The latter case makes perfect sense to me (although not documented well anywhere --but perhaps just not yet??), but the former is not really well documented in the I6 docs. I looked at some I6 extensions (.h) and it’s almost as if “strings” were “discouraged” I6 (???).

Have I addressed the issue to which you were referring?

If you want to define I6 data and use it within I6, you should read the I6 manual: inform-fiction.org/manual/html/contents.html . The first three chapters are the most important for this.

An I6 static string is just text in double quotes. However, if you want to manipulate individual characters within it, you should use an I6 array instead.

Aha! That’s the impression I got from the PD4 manual I had found prior to my post.
The concept of I6 string functions really don’t exist. One can really only manipulate character arrays.
Will look again and see if I can find the answer to my questions relating “strings”.
Thanks, zarf. Half of the issue is understanding the programming paradigm for I6.

I believe I’ve found a routine that I need to use and what it is doing makes sense.
However; I’m still having a bit of difficulty using it correctly.
Would someone be able to help me understand the calling parameters?
I should note that I’ve Transmuted the strings that are being used and have found the correct length of each.

Here is the pre-defined routine I’m trying to use to find the position of one string within another.

[code]! ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
! RegExp.i6t: Matching Literal Text
! ==== ==== ==== ==== ==== ==== ==== ==== ==== ====

[ TEXT_TY_RE_MatchSubstring txt ipos mtxt mfrom mto insens
i ch;
if (mfrom < 0) return 0;
if (insens)
for (i=mfrom:i<mto:i++) {
ch = BlkValueRead(mtxt, i);
if (BlkValueRead(txt, ipos++) ~= ch or TEXT_TY_RevCase(ch))
return -1;
}
else
for (i=mfrom:i<mto:i++)
if (BlkValueRead(txt, ipos++) ~= BlkValueRead(mtxt, i))
return -1;
return mto-mfrom;
];
[/code]

That looks like an I7 text-manipulation routine. Why not just use an I7 text for that part?

The reason for not using I7 is to minimize transferring strings back and forth to I6 during encrypting and decrypting strings.
I’m hoping to keep things simple and keep the speed up.

Look at the Standard Rules to see how to call the function. And I think once a string has been transmuted it’s basically a noop so you don’t need to cut them out of the functions.

I looked for the documentation and realized it didn’t do what I wanted/needed.
Turns out the answer was very simple.
(For some reason, the code formatting gets messed up even when using the “code” tags when posting.)

Here is what I ended up writing:

[ Index_of_Char_in_Str str char c len i ; ! Index_of_Char_in_Str: Returns the index of the first occurance of a character in a string. ! Example: If str = 'This is a test' ! and char = 'i' ! then Index_of_Char_in_Str(str,char) ! returns 3. ! len = TEXT_TY_CharacterLength(str); ! Determine the length of the string. i=0; ! Set the initial character position while (i<=len) { ! Process each character in the string. c = BlkValueRead(str,i); ! Get the str character at the pointer i. if (c==char) return i; ! If char has been found in str, then return the pointer i. i++; ! Increment the pointer. } return 0; ! Return a 0 to indicate the char was not found in the string str. ];

Here is a question for someone with more experience in Inform6 and Inform7:

It appears that strings in Inform7 are stored in a packed form and often passed to Inform6 by reference.
It would appear that once one is “within the Inform6 world”, strings are usually processed in unpacked form.
So, it would seem to be reasonable to transmute on entry into Inform6 and un-transmute on exit from Inform6.
(Besides, unpacking and packing adds a processing delay, if it is used excessively).
I have a number of nested routines within Inform6, which may need to share the unpacked strings.
I would prefer not to use global variables to do this, since I don’t quite know how to clean them up on exiting Inform6.
(Of course, if someone can show me a way to do the clean-up, then this is an option.)

Question:
What is the best way to share variables between nested routines in the above situation?

Added Note

After a bit of thought, I was wondering if there is a way to create an instantiation of a class that contains the routines as private methods and the data as “global” variables to the instantiated class. That would allow the constructor to unpack the data and the destructor to remove all trace of it. The only issue is that I’m not familiar enough with Inform6 to utilize what I know from other OO languages. If this is possible, a generic example would really be appreciated. I should note that I’m not really planning on interacting directly with objects in the adventure. I’m using the above noted routines for encoding and decoding ciphers.

Small note: Inform is quite different from other languages in many ways. One way is the handling of global variables, which are treated more like registers (look at Parser.i6t for more information on that). Another is the handling of objects, which are (almost) always singletons and don’t really have public/private methods in the C++ or Java sense.

Thanks, Draconis.

Just been reading the manual on objects and have looked at the parser code. Not sure whether the concept of “objects” is worth pursuing.
That approach is definitely possible, but seems overly complicated. One can do the same thing by constructing the kinds and objects from within the Inform7 language.

Perhaps there is a better approach to the problem. I could take the cryptography routines and break out only the routines that are very difficult to implement in Inform7. If I call those from Inform7 code, Inform7 should handle the data aspects that are of concern without me worrying about memory management issues and inter-routine data accessibility.

Is that a more workable approach in your opinion?

As a person who uses I7 instead of I6 wherever possible, I would say yes. Others with more I6 experience may disagree.

I believe I’ve found why I was having so much trouble with I6 extensions.

There are two Inform7 calls to a certain routine, but using a different initial parameter:

[code]To decide which text is the Affine Encryption of (from_text - text) using (str_alphabet - text)and (par_a - a number) and (par_b - a number):
(- (Affine_Cipher(0, {-by-reference: from_text}, {-new:indexed text},{-by-reference:str_alphabet}, {par_a}, {par_b})) -).

To decide which text is the Affine Decryption of (from_text - text) using (str_alphabet - text)and (par_a - a number) and (par_b - a number):
(- (Affine_Cipher(1, {-by-reference: from_text}, {-new:indexed text},{-by-reference:Str_alphabet}, {par_a}, {par_b})) -).
[/code]

The associated Inform6 routine has the following formal parameters (only first line is relevant):

[ Affine_Cipher function str_from str_to str_alphabet a b cp_from pk_from cp_alphabet pk_alphabet;

Notice how the str_to parameter is defined by {-new:indexed text} in both calls.

Later in the code, I call an encrypt routine or decrypt routine, based on the initial parameter of the call.
In each routine, I use the following within the code in order to access the unpacked version of str_to that is passed to the routine:

TEXT_TY_Transmute(str_to);

If any combination of two Inform7 calls is used, I get:

[** Programming error: tried to read outside memory using -> **]

It looks like the second call to TEXT_TY_Transmute is seeing the unpacked str_to, rather than a new packed instance of str_to.

Can someone advise me on the best way to ensure that the second call sees a fresh copy of the variable, str_to?

Perhaps changing -by-reference to -by-value for that parameter? I don’t know how that works with the new text system though.

Some variables are passed by reference, since they are formed by Inform7 and must be updatable by Inform6. Admittedly; most of the variables here could be passed by value, since they are examined, but not changed with the intent of Inform7 seeing the changes.

The str_to variable in question is declared via “{-new:indexed text}” in the definition of the “To decide which text is the Affine …” clause. It is intended to allow one to assign the returned value to an indexed text variable. I’m not sure whether Inform7 treats it as a by reference or by value. I think it must be the former, since Inform7 can pick up changes to it in order to make the assignment implicit in “To decide …”. So, maybe the solution is to treat it as being by value and unpack it and then repack it before returning it. Hmmmmm … I wonder if the example I had contained a flaw???

If anyone knows the correct way to do this, please let me know. The foregoing is just a guess.

Looks like the crypto code is working, but Inform7 is having trouble deallocating some of the variables at the end of the run.
This is the message I get every time.

[code]*** Value handling failed: impossible deallocation ***

*** Run-time problem P49: Memory allocation proved impossible.

[ The game has finished ]
[/code]

It looks like the same bug as reported in http://inform7.com/mantis/view.php?id=1415
The only difference is that I’m using Inform (1.5/6.33/6L02) on a Mac OSX 10.9.4
Are there any known work-arounds???