Addition Operator with Strings

The T3 System Manual page on strings describes the use of the addition operator (+) to concatenate string literals. However, I’m finding that this doesn’t work with string variables. I want to have an object whose appearance changes in a repeated, cycling fashion. To avoid spoilers, I’ll call the changes ‘round’, ‘square’, and ‘triangular’. This is stored in an object property called shape:

shape = 'round'
shapeList = ['round', 'square', 'triangular']
changeShape () { 
    nextShape++;
    if (nextShape > shapeRotation) nextShape = 1;
    tempShape = shapeList.nextShape;
    addVocab (name);
    return shapeList.nextShape;
    }
name () { 
    local str = shape + ' object';
    return str; }

This compiles, but I get a runtime error because the addition operator can’t process the string variable called shape.

For purposes of this puzzle, I absolutely do need to be able to concatenate string variables. How can I do that?

Concatenating with + should work for variables, as far as I see. I think the addition which is at issue here actually lies in “nextShape++;”, at least that’s the line which the Workbench marked as causing the problem, when I tested this in a small project. You can also do the counter-test by changing local str = shape + ' object'; to just local str = 'round object'; – the error will remain, so the variable is not the problem.

I may not have block-copied the code correctly while replacing my actual adjective with “shape.” In my code, the line that causes the problem is the string concatenation, not the change in the value of the variable.

But I’ve decided not to hassle with it. I’m going to do the whole thing iteratively, in a long block of “else if” statements. Boring, but reliable.

One way to run into the bug would be when shape is, despite appearances, not really initialized at the time, or doesn’t hold the right sort of value or is not in scope / not recognized. That was why the error happened on nextShape++; for me in my previous test.

When I add just this code to an object:

shape = 'round'
test () {
    local str = shape + ' object';
    return str;
}

and call test, it works. So as far as I can tell, it’s not a problem with string variable concatenation as such.

When I comment out shape = 'round':

// shape = 'round'
test () {
    local str = shape + ' object';
    return str;
}

… it still compiles, with the appropriate warning:

The symbol “shape” is undefined, but appears from context to be a property name. The compiler is assuming that this is a property.

… but then results in the runtime error. The error, predictably, also happens if we write shape = nil.

You could maybe try to do some debugging with the built-in debugger, or good old debug-printing, to see where and when shape (or rather, the variable in your code) gains or loses its value(s) during runtime.
I don’t know enough about TADS to make a better guess/suggestion, unfortunately.
But in the end, there’s of course absolutely nothing wrong with a “boring, but reliable” solution. :slightly_smiling_face: :+1:

Edited to add:
Sorry if I’m off base, but I’m not clear as to the purpose/syntax of shapeList.nextShape in your example. If it’s normal list access with a number, shouldn’t it be shapeList[nextShape] with brackets? I’m just asking because, in my test (which may of course be different from your code), shapeList.nextShape returns nil, so if we ever do an assignment like shape = shapeList.nextShape, then shape will be nil and we will run into the runtime error when trying to concatenate it with a string.

I don’t write using the workbench, but as near as I can tell you want something like this:

#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>

DefineIAction(Twiddle)
        execAction() {
                local shp;

                shp = widget.name();
                widget.changeShape();
                "Bingo-bango, the <<shp>> is now a <<widget.name>>. ";
        }
;
VerbRule(Twiddle)
        'twiddle'
        : TwiddleAction
        verbPhrase = 'twiddle/twiddling'
;

startRoom:      Room 'Void'
        "This is a featureless void. "
;
+ widget: Thing 'object' 'object'
        shapeList = static [ 'round', 'square', 'triangular' ]
        shapeIndex = 1

        // Set up our initial vocabulary.  We could hard code it in the
        // declaration above, but done this way we can just make changes
        // to the shapeList array and everything else just works.
        initializeThing() {
                inherited();
                setShapeVocab();
        }

        // Change the shape, doing the vocabulary-related bookkeeping as well
        changeShape() {
                // First we remove whatever vocabulary we had been using, so
                // our round object won't behave like it's still round after
                // it has become triangular.
                cmdDict.removeWord(self, shapeList[shapeIndex], &adjective);

                // Get the next shape
                shapeIndex = (shapeIndex % shapeList.length()) + 1;

                // Finally we set the vocabularly for our new shape.
                setShapeVocab();
        }
        // Make sure the vocabulary for our current shape is defined.
        setShapeVocab() {
                // Add our shape as an adjective
                cmdDict.addWord(self, shapeList[shapeIndex], &adjective);
                // ...and finally tweak our name
                name = shapeList[shapeIndex] + ' object';
        }
;
me:     Person
        location = startRoom
;

versionInfo:    GameID
        name = 'sample'
        byline = 'nobody'
        authorEmail = 'nobody <foo@bar.com>'
        desc = '[This space intentionally left blank]'
        version = '1.0'
        IFID = '12345'
;
gameMain:       GameMainDef
        initialPlayerChar = me
;

This “game” finds the protagonist alone in the void with a shape-shifting widget. You can change the shape at will using >TWIDDLE, and you can only refer to the widget using its currently-valid vocabulary:

Void
This is a featureless void.

You see a round object here.

>take square
The word "square" is not necessary in this story.

(If this was an accidental misspelling, you can correct it by typing OOPS
followed by the corrected word now.  Any time the story points out an unknown
word, you can correct a misspelling using OOPS as your next command.)

>take round
Taken.

>twiddle
Bingo-bango, the round object is now a square object.

>drop round
The word "round" is not necessary in this story.

>drop square 
Dropped.

>twiddle
Bingo-bango, the square object is now a triangular object.

>twiddle
Bingo-bango, the triangular object is now a round object.