Topic variables and text in I7

Hi All,

I’m trying to implement a vintage computer with a few BASIC commands available, requiring the player to enter commands such as ‘Type LIST’.

As a convenience, I’m diverting truncated commands such as ‘LIST’ into the appropriate typing command, not least because by instinct I keep doing it myself in testing:

Typing is an action applying to one topic.
Understand "Type [text]" as typing.

Understand "Type on/into [the computer]" as a mistake ("Just [']Type foo[']").
Understand "Type [text] on/into [the computer]" as a mistake ("[justtype]").
Understand "Reset [the computer]" as a mistake ("[typeresetinstead]").
Understand "Hit the/-- reset button/key/-- on/-- [the computer]" as a mistake ("[typeresetinstead]").
To say typeresetinstead: try typing "reset".
To say justtype: try typing the topic understood.

Understand "LIST" as a mistake ("[typelist]").
Understand "RUN" as a mistake ("[typerun]").
Understand "CLOAD" as a mistake ("[typecload]").
Understand "NEW" as a mistake ("[typenew]").

To say typelist: substitute "LIST".
To say typerun: substitute "RUN".
To say typecload: substitute "CLOAD".
To say typenew: substitute "NEW".

To substitute (t - a text):
	if the computer is touchable:
		say "(typing)[^]";
		if t is "LIST":
			try typing "LIST";
		if t is "RUN":
			try typing "RUN";
		if t is "CLOAD":
			try typing "CLOAD";
		if t is "NEW":
			try typing "NEW";
	else:
		say "No computer is in the vicinity,";

This is a bit klunky , although it works. I would like to make the code a bit more elegant by passing a topic to the substitution routine rather than a text, like so:

Understand "LIST" as a mistake ("[substitute 'list']").

To substitute (t - a topic):
	if the computer is touchable:
		say "(typing)[line break]";
		try typing t;

but can’t work out how to get Inform to recognise ‘list’ as a topic rather than text- so this falls foul of Inform’s type safety which will not automatically cast a text to a topic.

In similar vein, if attempting the cast in the substitution routine as so:

> To substitute (t - a text):
> 	if the computer is touchable:
> 		say "(typing)[line break]";
> 		try typing t;

Inform complains when compiling ‘try typing t’ that t is a text when it was expecting a topic.

Any ideas?

This seems very awkward! You could make the BASIC commands a kind of value, allow a bare BASIC command to be understood as typing, and make a text action to catch things that aren’t BASIC commands. Like (untested):

A BASIC command is a kind of value. The BASIC commands are list, run, cload, and new.

Typing is an action applying to one BASIC command. 
Understand "type [BASIC command]" as typing.
Understand "[BASIC command]" as typing.

Free typing is an action applying to one topic.
Understand "type [text]" as free typing.

Instead of Free typing: say "SYNTAX ERROR[paragraph break]".
1 Like

Genius!

That’s a much neater basic idea, thanks.

I’ll give it a spin…

OK, it’s all coded up and working. Thanks. Although it was an alternate pain to have to code up two different actions, now that’s done making additions to the BASIC language is a bit less painful.

It’s still confusing to me at times when Inform regards quoted text as a text and when as a snippet.

The basic rule is:

  • If it’s in a table column named topic, it’s a topic
  • If it’s in an Understand … as … line, it’s a topic
  • If it’s being used as a noun in a description of an action, it’s a topic
  • Otherwise, it’s text

Converting a topic to text is impossible, which is understandable, because a topic is really an arbitrary parsing routine (I think I6 calls these “GPRs”) which can’t necessarily be printed in any meaningful way.

But converting a text variable to a topic is also supposed to be impossible, which doesn’t make sense, since it has a very reasonable interpretation (“match this literal text and nothing else”).

As with many of Inform’s weird quirks, there’s an easy but obscure workaround.

To swap in (t - text):
    try typing "[t]".

Since the double-quotes occur in the description of an action, they’re taken as a topic, and a topic can include references to a text in it, meaning “compare the snippet against this text variable”.

Thanks Daniel, that’s a very helpful and insightful summary.

Should save me a lot of trial-and-error in future :slight_smile:

Hi Daniel, for some reason I still can’t get this workaround to work- e.g.


> "Typing" by PB
> 
> The Office is a room.
> 
> Typing is an action applying to one topic.
> Understand "Type [text]" as typing.
> 
> To swap in (t - a text):
> 	try typing "[t]".

Problem. In the line ‘"[t]"’ I was expecting that ‘t’ would be something to ‘say’, but it didn’t look like any form of ‘say’ that I know. So I tried to read ‘t’ as a value of some kind (because it’s legal to say values), but couldn’t make sense of it that way either.

When I tried this I got an error message whose second half referred to something about how you couldn’t use substitutions that refer to temporary values in a context like this, because the temporary values might be gone by the time you need to refer to them. It seems to me that using “the substituted form” should take care of that, because that ensures that you don’t actually need to refer to the temporary value, but I guess the compiler can’t figure that out at compile time. (See §20.7 on “substituted form.”)

Anyway, this means that if we really want to work around it in this way, we can use a global variable:

The global workaround is a text that varies.

To substitute (t - a text):
	if the computer is touchable:
		say "(typing)[line break]";
		now the global workaround is "[t]";
		try typing "[global workaround]".

(I think you don’t have to set the global workaround to the substituted form of “[t]” because when you set a text global to something that refers to a temporary variable, it automatically substitutes–again see §20.7.)

This is obviously less than ideal. I would say that if you really want to do something like this, the thing to do would probably be to put the work of the typing action in a phrase like “To type (t - a text)…” that you could call from elsewhere, and have the carry out typing rule call that phrase with “[the topic understood]”.

But again, when you’re trying to do something fancy with text, and there are only a few valid text strings that really make sense, it’s often much easier to make them into a kind of value–those can be passed between routines easily.

1 Like

Interesting, I get exactly the error I gave above, which didn’t make any sense to me as clearly a text, temporary or otherwise, is absolutely something sayable.

At least your error, if not truly justifiable, makes some sort of sense- it seems to be saying that Inform doesn’t want to commit to passing a temporary variable to an action rulebook.Which version of Inform are you testing this on? I have 6M62, Feb 23rd 2018.

Forcing a straight substitution with 'try typing the substituted form of “[t]” gets a bit further, but still fails because Inform now rejects the substituted text as being a text rather than a topic (assuming one is trying to pass a topic to the action rather than a text)

I don’t think your solution of breaking out a To type (t - a text)… phrase and calling it by Carry out typing: type "[the topic understood] works, because the initial conundrum was how to pass a topic to represent ‘the topic understood’ to the action in the first place…?

Still like your idea of kinds the best.

As a retrospective insight, I hit upon the following possibility for diverting any topic not coincidentally introduced by a recognised verb (i.e. not-understood-text normally eliciting the response ‘I didn’t understand that sentence’) to the free typing action:

Free typing is an action applying to one topic.
Understand “type [text]” as free typing

Understand “[text]” as free typing when the computer is touchable.

This allows the player to type undefined BASIC commands without the unintended side-effect that any nonsense or typos when not sat at the computer are interpreted as an attempt to type.

Curious…

Here’s my test code:

Instead of telling the player about a topic:
	let t be "<< [topic understood] >>";
	try asking the player about "[t]".

Instead of asking the player about a topic:
	say "Topic: [the topic understood]".

Result:

>tell me about fish
Topic: << fish >>

I’m using 6L38 (because bugs in 6M stop Scroll Thief from compiling); this may be one of those bugs?

That’s the first part of the error I got, but the helpful part was in the third paragraph (after something unhelpful):

Problem. In the line ‘“[t]”’ , I was expecting that ‘t’ would be something to ‘say’, but it didn’t look like any form of ‘say’ that I know. So I tried to read ‘t’ as a value of some kind (because it’s legal to say values), but couldn’t make sense of it that way either.

Sometimes this happens because punctuation has gone wrong - for instance, if you’ve omitted a semicolon or full stop at the end of the ‘say’ phrase.

It may be worth adding that this problem arose in text which both contains substitutions and is also being used as a value - being put into a variable, or used as one of the ingredients in a phrase other than ‘say’. Because that means it needs to be used in places outside its immediate context, it is not allowed to refer to any ‘let’ values or phrase options - those are temporary things, long gone by the time it would need to be printed.

See the last sentence.

(The first paragraph is basically saying that it’s looking for a definition “To say t:” and isn’t finding one.)

Here’s what I mean:

 The Office is a room.
 
 Typing is an action applying to one topic.
 Understand "Type [text]" as typing.
 
To type (t - a text):
	say "Typing '[t]'."

Carry out typing: type "[the topic understood]".

Instead of jumping: type "jump".

There’s this phrase “to type” that applies to text. We can represent the topic understood to this phrase by using it as a text substitution in quoted text–this converts it to a text, which there’s no problem passing to a phrase. (We’d have trouble passing text to an action, because actions apply to topics–or maybe snippets–but that doesn’t apply here.)

And whenever we special-case something, instead of having to try a typing action, we just call the “to type” phrase. This won’t let us do any of the Before, Instead, etc. rules though.

Anyhow you’re right that it’s going to work better with kinds!

Ah, I see.

Sorry about not reading to the bottom of the 3rd paragraph :flushed:

So, the idea of the To type … phrase is to avoid the complications of using an action to deal with special cases.

All makes sense now, thank you (apart from the point that Daniel’s Inform seems to compile passing a substituted local text variable as a topic but mine doesn’t).

I’ve cut & pasted Daniel’s example code and it fails with the same error as above.

Very weird. I went through the changelog and couldn’t find anything about this.

I’d honestly call it a bug introduced in the new version, since nothing about it seems like it shouldn’t work, and the error message doesn’t really make sense. Topics can’t be compared against topics, anyway; they can only be compared against snippets (that’s what “the topic understood” really is) (*), and there’s no direct way to create snippets within I7. So saying that “try asking the player about…” requires a topic argument is somewhat nonsensical.

(*) If you care about technical details: a snippet is a pair of numbers (**), which are indexes into the parse buffer (the list of parsed words formed out of each command): one for the start, and one for the end. “The player’s command” and “the topic understood” are really the only snippets anyone ever cares about.

(**) Weirdly, the two numbers are stored in a single word as (X * 100) + Y rather than the more obvious(***) (X << 16) | Y, which is why a command can never be longer than a hundred words without “the player’s command” failing in exciting and hard-to-debug ways.

(***) Well, more obvious in C, at least. Inform 6’s syntax doesn’t actually have any inline operators for bit-shifting, even though the underlying virtual machine has instructions for it, so writing the latter in Inform would be a bit ugly. On the other hand, it would cope properly with commands up to 65535 words long, rather than 99, with no real cost except slightly uglier code.

Counterpoint: don’t type 100-word commands, dang

1 Like

Don’t tell me what to do!

(keeps typing Z. Z. Z. Z. Z. Z. Z. Z. Z. Z. Z. Z. Z…)

1 Like

I think you mean “Z, Z, Z, Z, Z, Z, …”

1 Like

I think you mean “Z, Z, Z, Z, Z, Z, …”

lol :laughing:

The only rather hacky way I’ve discovered is through ‘change the text of the player’s command to’.
This enables you to temporarily save the current player’s command, create a new snippet from any text as the player’s command, do those things you can only easily do with snippets then (if required) restore the player’s command to its former glory, e.g.

"Utter Madness" by PB

The Office is a room.
The Closet is east of the office.

The computer is in the office.

Every turn:
	say "[line break]";
	let txt be the substituted form of "The voice in your head says '[one of][we] [are] mad as a mongoose![or][we] [are] an utter lunatic![or][we] [are] nutty as squirrel ****[or] [we] [are] delightfully crackers[cycling]'";
	If topic in row 1 of the Table of Insanity occurs in txt:
		say "[txt][line break]I agree.  You're batty as a badger!";
	else:
		say "[txt][line break]But I say you're cold stone sane.";

To decide whether (t - a topic) occurs in (txt - some text):
	let snp be the substituted form of "[the player's command]";
	change the text of the player's command to txt;
	if the player's command includes t:
		change the text of the player's command to snp;	
		yes;
	else:
		change the text of the player's command to snp;	
		no;
	
Table of Insanity
topic
"mad/nuts/nutty/bonkers as/-- a/-- mongoose/squirrrel/--"

test me with "e / z / w / x computer".

I assume it’s the ‘fix’ of this bug documented in the 6M62 changes log which means your code no longer works:

Bug fixed (0001495, 0001425, 0001448) whereby Inform would allow a text value to be assigned to a topic variable, but the result would be an I6 error about a nonexistent routine called TEXT_TY_to_UNDERSTANDING_TY, or in some cases a Glulx virtual machine error. Such assignments aren’t legal, and there’s now a problem message for it.

http://inform7.com/changes/CI_2_17.html

I’d thought that might be it, but it doesn’t give any sort of I6 error (or malfunction in any way).

Besides, the “try” phrase should be looking for a snippet, not a topic, in the first place…

That’s true, thinking about it, as in theory ‘try asking the player about “[t]”’. should just generate an action with “[t]” taking the place of a snippet representing ‘the topic understood’…

Furthermore, as Matt noted Inform 6M62 will still cast a text variable to a topic/snippet in this context as long as the text is held in a global variable, so it doesn’t seem to be a type safety issue but an undocumented change to scoping rules when passing local variables.