Inform6 "print"-like string concatentation

Hello again all!

I’m at a loss. I’ve read and re-read the DM and the forum here and I can’t figure this out. It seems to me that it should be simple but I’m stumped.

First, here’s what I WANT to do:
I want to make a call like print (RuleThatCallsQuit)"Some text", someValue, "^";. The problem is that the first rule gets processed and quits before anything else is processed. Which means I get an output of Some text.

So that’s my goal. I’ll tell you how I’ve tried to solve it but maybe I’m just approaching it wrong. I’ve tried to create a function that will concatenate a set of values. I create a buffer, open it as a memory stream, set it as the current stream, and print everything to it. This returns a buffer with the correct character count (–>0) but whatever I do, I can’t get the other elements (–>1 to value of -->0) to print. I either get something like [0x73203432] or [** Programming error: tried to print (char) 0, which is not a valid Glk character code for output **].

I know a simple solution to this specific scenario would be to not call “quit” in the print rule but that won’t address the need to be able to concatenate strings.

Could someone point me in the right direction?

I think that any printing rules that you create must accept only a single argument. However, that argument does not have to be meaningful to the routine’s function. Can you reorder things like this?

print "Some text", someValue, "^", (RuleThatCallsQuit) 0;

If that doesn’t address your problem, can you provide some example code to better illustrate the issue?

That’s an interesting suggestion and I’ll definitely keep it in mind for other applications!

However, it doesn’t address my question about string concatenation, which is what I’m interested in solving.

I’m not sure if I understood your problem but…

[ Rtn txt;
   print (string) txt;
];

[ Initialise   someValue;
   someValue = 10;
   print (rtn) "Some text ", someValue, "^";
];

I believe removing the rtn rule entirely would result in the same output.

[MyFunc str; ! must have one argument
  ! do something with the string
];

[ Initialise   someValue;
   someValue = 10;
   
   MyFunc("Some text ", someValue, "^"); ! How do I make this work?
];

You want to get a single string: “Some text 10”?

Correct!

You need an array that will be the address of your new string, and write your values inside with PrintToBuffer().

I didn’t find PrintToBuffer in the DM. Is it a new built-in function?

It’s in the ReleaseNotes.html

Darn those tricksy release notes.

From what I can gather:

Array buffer -> 200;

[ Initialise   someValue;
   someValue = 10;
   
   PrintToBuffer(buffer, 200, "Some text ", someValue, "^"); 
];

But I’m still experiencing the issue with printing the buffer’s contents.

Array MyString buffer 160;
Your Array is wrong. I’m looking for a simple solution with PrintToBuffer(), but I can’t find one, you have to do otherwise. And you have too many parameters in the function.

I got
PrintToBuffer ( array , arraylen , arg1 , arg2 , arg3 ) prints its arguments – a string, an object’s name, the value of an object’s property, or a routine with up to two arguments – to the buffer array.
from http://inform-fiction.org/inform63/whatsnew.html, am I looking at the wrong page?

I switched -> for buffer in my code but that didn’t resolve the printing issue.

Just to be clear, you do understand that the print statement itself will concatenate values that are separated by commas?

Absolutely! I’m trying to emulate just that but have the result end up in variable rather than print to the screen :slight_smile:

That’s why I tried to leverage it in the function I made, by switching the default output stream to a memory buffer and using print to fill it with information. I’m pretty sure it worked because the resulting length of the buffer matches the length of the string as it should be. I just can’t seem to then print the resulting buffer to the screen.

Maybe I’m missing something obvious. I’m just having trouble seeing how something as fundamental as string concatenation is so difficult in a platform devoted to text-based interfaces :smiley:

OK. It’s important to note that a “string array” (as it’s termed in DM4) is not the same as a “string”. The DM4 discussion on the .print_to_array() method for strings helps to illustrate the difference.

As auraes mentioned, you need to build a function to print the contents of a string array (or better yet, a buffer array, as the sample code below uses). The new function can be used in the context of a print statement, e.g.

Array my_buf buffer <size>;

[ MyBufPrint buf   i ;
    for (i=0:i<buf-->0:i++) ! expects first word of buffer to contain length of content
        print (char) buf->(2+i); ! skips first word of buffer
];

print (MyBufPrint) my_buf, "^";

Text is handled in unusual ways within Inform because of the restrictions imposed by the Z-machine format (which compresses string text to reduce its storage space requirement).

You may find it a lot easier to just let the print statement do the concatenation for you at the point of output, instead of trying to concatenated assorted contents to a buffer or string array first. Again, if that’s not helpful, it might be helpful to post some of your actual code (or an analog), so that it’s easier to see exactly what you’re trying to do.

FYI: You might also be interested in the istring.h library at https://www.ifarchive.org/if-archive/infocom/compilers/inform6/library/contributions/ , but I’ve never used it myself. It “provid[es] implementations of the standard ANSI C library string functions”.

1 Like

As requested, CODE!

Constant DOUBLE_HUGE_LENGTH = 2000;
Array doubleHugeBuffer buffer DOUBLE_HUGE_LENGTH;

[ Initialise val;
    val = BuildString("The answer is ", 42);

    print val-->0, "^"; ! output: 16

    print val->0, "^"; ! output: 0

    print (char)val->1, "^"; ! output: programming error

    print (char)val->(2 + 1), "^"; ! output: programming error

    print (char)val-->1, "^"; ! output: [0x54686520]

    print (char)val-->(2 + 1), "^"; ! output: [0x65722069]
];

[ StringOrArray str i;
	if (str ofclass String) {
		print (string) str;
	} else {
        for (i = 1: i <= str->0: i++) {
            print (char) str->i;
        }
	}
];

[ JSRuntimeError errorText ;
    print "*** ", (StringOrArray)AppName, " run-time error ***^";
    print (StringOrArray) errorText, "^";
    print "******^";

    ! Stop the story running completely
    quit;
];

[ BuildString val1 val2 val3 val4 val5 val6 val7;
   ! TODO: switch to _vararg_count
    if (EnableMemoryStream(doubleHugeBuffer, DOUBLE_HUGE_LENGTH)){
        if (val1 ~= 0){
            if (val1 ofclass String) print (StringOrArray) val1;
            else print val1; ! This assumes a number which metaclass/ofclass consider 'nothing'
        }

        if (val2 ~= 0){
            if (val2 ofclass String) print (StringOrArray) val2;
            else print val2; ! This assumes a number which metaclass/ofclass consider 'nothing'
        }

        if (val3 ~= 0){
            if (val3 ofclass String) print (StringOrArray) val3;
            else print val3; ! This assumes a number which metaclass/ofclass consider 'nothing'
        }

        if (val4 ~= 0){
            if (val4 ofclass String) print (StringOrArray) val4;
            else print val4; ! This assumes a number which metaclass/ofclass consider 'nothing'
        }

        if (val5 ~= 0){
            if (val5 ofclass String) print (StringOrArray) val5;
            else print val5; ! This assumes a number which metaclass/ofclass consider 'nothing'
        }

        if (val6 ~= 0){
            if (val6 ofclass String) print (StringOrArray) val6;
            else print val6; ! This assumes a number which metaclass/ofclass consider 'nothing'
        }

        if (val7 ~= 0){
            if (val7 ofclass String) print (StringOrArray) val7;
            else print val7; ! This assumes a number which metaclass/ofclass consider 'nothing'
        }

        DisableMemoryStream();
    }

    !print "bufferLength: ", doubleHugeBuffer-->0, "^";

    if (doubleHugeBuffer-->0 == DOUBLE_HUGE_LENGTH) {
        print (JSRuntimeError)"BuildString -> combined length too long; please increase DOUBLE_HUGE_LENGTH in JSCommunication.h";
    }

    return doubleHugeBuffer;
];

Pay close attention to your array access operators --> (word access) and -> (byte access). Any time that you’re printing a (char) value, it should be printing a value accessed via ->.

Likewise, in your StringOrArray() routine’s continuation condition, you want to access -->0 of your buffer to get the length value. The length requires a word’s worth of storage because, as your example demonstrates, length may exceed 255 (max for a byte).

The actual character bytes in the buffer begin at position ->2, because ->0 and ->1 are the same two bytes as -->0. Your StringOrArray() routine’s for loop initialization and print statement should reflect that.

For your print statements in Initialise(), the missing space between (char) and val-->... does not seem to be relevant, but the only one that I would expect to work correctly is:

print (char) val->(2 + 1), "^"; ! prints second character of string contents of buffer

and it did work when I just tried it.

Here’s my code after the changes you outlined (posting to see what I did wrong because it still doesn’t work for me).

[ Initialise val;
   ! reduced test statements for readability

    val = BuildString("The answer is ", 42);

    print val-->0, "^"; ! still outputs 16, all good

    print (char) val->(2 + 1), "^"; ! programming error, char 16 not valid Glk character code etc.
];

[ StringOrArray str i;
    if (str ofclass String) {
        print (string) str;
    } else {
        for (i = 2: i <= str-->0: i++) {
            print (char) str->i;
        }
    }
];

It looks like you’re compiling to Glulx, not Z-machine, so the offset of 2 for string contents is incorrect. This can be replaced with the library constant WORDSIZE, which is set automatically for the virtual machine in use. (The release notes have additional details. See the end of the section “Word size” under “Glulx differences” for notes on this exact scenario.)

@DavidG, I looked through Standard Library 6.12.4, and I didn’t see any routine that can be used as a printing rule for the contents of a buffer array. Is there one that I missed? If not, perhaps it makes sense to include such a printing routine (i.e. a print (buffer) ... built-in like print (string) ...) in the next release to ease the way for newcomers?

1 Like