Can a replace operation preserve unsubstituted text?

In Inform 7 (Documentation Ch 20.7-20.9)

For example, say I have a text “ab [player] ab”. Could I somehow run a regular expression on it to make either of these?

  1. “ab [player] ab” --> “cd [player] cd”
  2. “ab [player] ab” --> “cd [location] cd”

It seems as if the text always becomes substituted before regex acts on it, and then what is returned is always substituted. So the text becomes “ab yourself ab” before regex runs, and therefor the regex can only produce “cd yourself cd”. I’m trying to pass unsubstituted text to regex.

(EDIT: This question is for replacing with regular expressions or for simple replace operations – the limitations seems to work the same for both).

1 Like

I’m sorry, Jeremy, but I don’t see what you mean. Do you just want to substitute certain parts of the text? Or is it that you want to have something which holds a text substitution in it and then get changed, but still has part of it which is a substitution? Maybe an example transcript of how this would play out would help.

1 Like

Extrapolating a bit here but I think the issue is that there is no unsubstituted form that would exist as text during runtime. The [bracketed] substitutions exist as such only in I7 code, once the game is compiled they’re turned into functions that are called whenever the text is printed. For example, if you have:

let mytext be "ab [player] ab";
say mytext;

it compiles to something that’s roughly equivalent to: (simplified pseudocode!)

function mytext_func:
  print "ab ";
  print object_name(player); 
  print " ab";

let mytext be mytext_func;
call mytext();

So you can’t perform regex on the unsubstituted form because it’s a function, not text. In this particular case trying to modify tokens in substitutions as if they were text won’t work (e.g. replace the word "player" in "ab [player] ab" with "location") because text with substitutions has already been parsed during compile time so by the time you’re applying the regex during runtime the tokens are long gone.

The solution depends on what you’re actually trying to do. In the first case you could run the regex only when you need to print the text instead of beforehand, and in the second case you could move the logic from the regex into a say phrase:

To say player or location:
	if foo is bar:
		say player;
	otherwise:
		say location.

When play begins:
	say "ab [player or location] ab";
2 Likes

Just to make this explicit, here is a concrete test using [the location] and [time of day]. I simplified this to a test of replace, not regular expression, as the underlying issue seems to be the same.

"Right Twice a Day" by Jeremy Douglass

[ we can inspect whether a thing contains dynamic or static text ]
After examining a thing (called the item):
	if the description of the item is unsubstituted:
		say "(unsubstituted - dynamic)[line break]";
	otherwise:
		say "(substituted - static)[line break]";

Watchworld is a room. The description is "A watch shop in the mall. Which to wear?"
[ A watch shows the current time. ]
The watch is a wearable thing in Watchworld.
The description is "The watch says [time of day] right now."

[ A mirror reflects its location. ]
The mirror is here. The description is "In the mirror is [the location]."

[ Take the mirror to a different location, and it works.]
The Courtyard is north of Watchworld.
The description is "The mall courtyard."

[ Replace part of description but leave "[the location]". ]
Instead of attacking the mirror for the first time:
	replace the text "In the mirror" in the description of the mirror with "iN thE bRoKen mirRor";
	try examining the mirror;

[ Replace part of description but leave "[time of day]". ]
After wearing the watch for the first time:
	replace the text "The watch" in the description of the watch with "Your watch";
	try examining the watch;

[ Wearing the watch freezes time of day. ]
Test watch with "x watch / x watch / wear watch / x watch".

[ Hitting the mirror freezes location. ]
[ Now the mirror keeps reflecting the Courtyard, even when we leave. ]
Test mirror with "x mirror / take mirror / n / x mirror / hit mirror / s / x mirror".

Watchworld
A watch shop in the mall. Which to wear?

You can see a watch and a mirror here.

> test watch

(Testing.)

> [1] x watch

The watch says 9:00 am right now.

(unsubstituted - dynamic)

> [2] x watch

The watch says 9:01 am right now.

(unsubstituted - dynamic)

> [3] wear watch

(first taking the watch)

Your watch says 9:02 am right now.

(substituted - static)

> [4] x watch

Your watch says 9:02 am right now.

(substituted - static)

> test mirror

(Testing.)

> [1] x mirror

In the mirror is Watchworld.

(unsubstituted - dynamic)

> [2] take mirror

Taken.

> [3] n

Courtyard

The mall courtyard.

> [4] x mirror

In the mirror is the Courtyard.

(unsubstituted - dynamic)

> [5] hit mirror

iN thE bRoKen mirRor is the Courtyard.

(substituted - static)

> [6] s

Watchworld

A watch shop in the mall. Which to wear?

> [7] x mirror

iN thE bRoKen mirRor is the Courtyard.

(substituted - static)

1 Like

Thank you so much, I’ll take a look and think more about kind of refactoring.

A key point, if I understand right:

  1. Replace will always have the effect of flattening anything it runs on to unsubstituted text.
  2. Replace also cannot be used to inject dynamic text. For example, if I replace text with “[the location]”, that will write in the static text value of the current location – it won’t cause the newly written description to hold a dynamic text substitution.

This seems to be true for all replace operations, not just regular expression.

1 Like

That’s correct.

If you want text to stay dynamic, use substitutions with dynamic contents rather than replace.

1 Like

In your example usually you’d vary the descriptions by tracking item state. Have the text adapt to world state instead of having actions modify text, if that makes sense.

The watch is a wearable thing in Watchworld.
The description is "[if the watch is handled]Your[otherwise]The[end if] watch says [time of day] right now."

The mirror is here. The mirror can be broken.
The description is "[if the mirror is broken]iN thE bRoKen mirRor[otherwise]In the mirror[end if] is [the location]."

Instead of attacking the mirror for the first time:
	now the mirror is broken;
	try examining the mirror.

Thank you, @Juhana. This makes sense to me as an alternate strategy for the example.

I wrote the example to demonstrate the question, but my original use case was inspired by something different – actually, it was experimenting with mixing regex replacements of characters and text substitutions in long ascii art strings, inspired by this ascii thread. For a single 40 character wide ascii image the [if]@[end if] approach might require hundreds of conditional blocks, quickly overrunning the maximum string size – but I think I have identified a few other approaches that would work instead.

1 Like