preserving formatting while saying [list of something]

Two questions in one day!

I have a (perhaps needlessly) complex text that prints based on number and proximity (a property) paired with a for printing the name of [a kind of thing] that applies formatting to the name.

However, the formatting seems to be applying in one case but not the other. I’ve been staring at this too long. The problem may be something obvious, but I can’t see it. Here’s a MVP… Some of these properties are probably irrelevant but just in case…

lab is a room.

enemy is a kind of person.
proximity is a kind of value.
the proximities are nearby, distant, and intermediate.
an enemy has a proximity.
an enemy is usually nearby.

monster is an enemy in lab.
monster is intermediate.

creature is an enemy in lab.
creature is nearby.

beast is an enemy in lab.
beast is intermediate.

definition: an enemy is within range rather than out of range if it is nearby.

rule for printing the name of an enemy (called E):
	let P be the printed name of E;
	say "[bold type][p][roman type]";
	
last after looking:
	let R be the location;
	if there is an enemy in the location:
		let inrange be the number of within range enemies in the location;
		let outrange be the number of out of range enemies in the location;
		say "Resident dangers:[line break]";
		if inrange is greater than zero, say "[A list of within range enemies in the location][roman type] [regarding inrange][are] within melee range range";
		if outrange is greater than zero and inrange is greater than zero, say ", and [a list of out of range enemies in the location][roman type] [regarding outrange][are] closing in fast";
		if outrange is greater than zero and inrange is zero, say "[line break][A list of out of range enemies in the location][roman type] [regarding outrange][are] closing in fast";
		say "!";

I’ll provide a borogove snippet as well to demonstrate the formatting.

1 Like

Here’s a cut-down example to demonstrate the problem:

Lab is a room.

First before printing the name of a thing: say bold type.
Last after printing the name of a thing: say roman type.

apple is in the Lab.
banana is in the Lab.

Definition: a thing is other if it is not the player.

After looking: say "[A list of other things in the location] are here. That's right, [a list of other things in the location]."

Lab
You can see apple and banana here.

Apple and banana are here. That’s right, apple and banana.

A couple things to note:

  • I changed the “rule for printing the name of” to a before and an after, so that it doesn’t override all the other “printing the name of” rules
  • The apple and banana are currently proper-named but lowercase, which is usually not desirable unless you want Scott-Adams-style prose, but I wanted to match your example in this regard

So, what’s going on here? Well, let’s look at the definitions of “a list of” and “A list of”.

To say a list of (OS - description of objects)
    (documented at phs_alistof): (-
        objectloop({-my:1} ofclass Object)
            if ({-matches-description:1:OS})
                give {-my:1} workflag2;
            else
                give {-my:1} ~workflag2;
        WriteListOfMarkedObjects(ENGLISH_BIT);
    -).
To say A list of (OS - description of objects)
    (documented at phs_Alistof):
    (-
        objectloop({-my:1} ofclass Object)
            if ({-matches-description:1:OS})
                give {-my:1} workflag2;
            else
                give {-my:1} ~workflag2;
        TEXT_TY_Say_Capitalised(CaptureListOfMarkedObjectsText);
    -).

And here’s the problem! To say “A list of” something, Inform does the equivalent of this:[1]

To say A list of (OS - description of objects):
    let T be "[a list of OS]";
    say T with the first letter capitalized.

And converting a text to an indexed text like this discards all formatting. A “text” is underlyingly a block of code that prints what you want, along with instructions like “print the current value of this variable” and “switch to bold type here”. An “indexed text” is underlyingly just an array of characters; all those special instructions get lost. But manipulating the individual characters can only be done on an indexed text.

Can this problem be fixed? In the most common case, yes! It’ll require a bit of fiddling with the list-writer so it can be passed a CAPITAL_BIT that tells it to call CDefArt or CIndefArt for the first entry of the list. In this specific case, though, where you’re going for the Scott Adams article-less nouns, there’s not an easy solution. Articles are convenient because they generally don’t contain special formatting instructions, so they can be manipulated and capitalized and such without losing anything important.


  1. There is no “say T with the first letter capitalized” phrase in the Standard Rules, but its name should make it clear what it does. ↩︎

4 Likes

Hey, wait a minute…

Constant CFIRSTART_BIT      = $$0100000000000000; ! Capitalise first article in list

There’s already a bit for capitalizing the first article! I wonder why it’s not used?

2 Likes

This is a potential workaround. It won’t work if you actually want articles.

capmode is initially false.

rule for printing the name of an enemy (called E):
	let P be text;
	if capmode is true:
		now P is printed name of E in sentence case;
		now capmode is false;
	otherwise:
		now P is printed name of E;
	say "[bold type][p][roman type]";

To say A special list of (D - description of objects):
	now capmode is true;
	say "[a list of D]".
	
last after looking:
	let R be the location;
	if there is an enemy in the location:
		let inrange be the number of within range enemies in the location;
		let outrange be the number of out of range enemies in the location;
		say "Resident dangers:[line break]";
		if inrange is greater than zero, say "[A special list of within range enemies in the location][roman type] [regarding inrange][are] within melee range";
		if outrange is greater than zero and inrange is greater than zero, say ", and [a list of out of range enemies in the location][roman type] [regarding outrange][are] closing in fast";
		if outrange is greater than zero and inrange is zero, say "[line break][A special list of out of range enemies in the location][roman type] [regarding outrange][are] closing in fast";
		say "!";

[EDIT: See alternate solution below.]

1 Like

Considering that “text” in Inform is actually a mixed array of hard text, objects, and lambda functions, wouldn’t it be possible to rewrite the X with first letter capitalized routine to skip over the bold type call and instead capitalize the first letter of the following text?

The difficulty would be how it knows that bold text doesn’t output anything… because if it does then you’d want the first letter of its output capitalized instead, recursively applying the same methodology.

That said, this doesn’t really help someone who just wants it to work right now, as it implies a change to the core libraries.

1 Like

It would be nice, but manipulating the individual components of a “text” (routine) is almost impossible with the current implementation. Dannii has found a way in certain cases by abusing the Glulx stack frame format, but that’s not at all easy to do in the general case.

This is also part of why spacing in Inform is such a difficult topic!

1 Like

I’m not ignoring the responses, which I appreciate! I’ll be back on later

If I recall correctly, that’s because they’re compiled to code, rather than an array of the components, right?

More detail if you want it.

So "[bold type]There is a [cat] on the [table]![roman type]" would be compiled to a routine similar to:

To say there_is_a_cat_on_the_table:
	say bold type;
	say "There is a ";
	carry out the printing the name activity with cat;
	say " on the ";
	carry out the printing the name activity with table;
	say "!";
	say roman type;

For my suggestion to be viable, it would instead need to compile down to something like this (pseudo-code):

Array there_is_a_cat_on_the_table = [
	{ type = TEXT.CALL, value = &(say bold type) },
	{ type = TEXT.LITERAL, value = "There is a " },
	{ type = TEXT.OBJECT_NAME, value = &cat },
	{ type = TEXT.LITERAL, value = " on the " },
	{ type = TEXT.OBJECT_NAME, value = &table },
	{ type = TEXT.LITERAL, value = "!" },
	{ type = TEXT.CALL, value = &(say roman type) },
];

Pretty much, yeah! A “text” is just a sequence of instructions. That was the inspiration for Dialog doing away with it entirely and having no data type equivalent to indexed text. If you want to pass a piece of text around, you use closures, or you just…don’t.

1 Like

I had a similar idea to @otistdog: rather than print a list of something, it might be better to print the entries in a list. I had two takes on this, neither of which have been tested rigorously. There’s a lot to think about while printing with/without articles and with different placements in-sentence. So option one might be adding a tweaked style to the Complex Listing extension. The second is to iterate through a list.

I don’t care for the way an assortment of (articularity - a specific) for (case - a placement) with (collection - a description of objects) reads, hopefully I’ll come up with something more elegant! Both methods are crunky in their own way, but both should just “work” if there’s a need to do it again. If I got it right, that is.

Edit. Coding before bedtime again! My idea about iterating through listed items individually did not work (nor does iterating through a table). I’ve replaced my original example with one that only uses Emily Short’s complex listing.

See below for an option that does not require an extension.

the Lab is a room.

use the serial comma.
include complex listing by emily short.

First before printing the name of a thing: say bold type.
Last after printing the name of a thing: say roman type.

the apple is in the Lab.
the banana is in the Lab.
the cat is an animal in lab.
the shoe is an animal in lab.

Definition: a thing is other if it is not the player.

After looking:
	say "Complex listing:[line break]";
	prepare a list of other things in the location;
	say "[the prepared list delimited in painstaking style] are here. ";
	prepare a list of other things in the location;
	say "That's right, [the prepared list].";
	say line break;
	

table of list style assignments (continued)
list style	first delimiter	second delimiter	alternate second delimiter	indefinite name phrase	definite name phrase
painstaking	", "	"[if the serial comma option is active],[end if] and "	" and "	"[if current listing index is current listing total][A current listed object][otherwise][a current listed object]"	"[if current listing index is current listing total][The current listed object][otherwise][the current listed object]"

I’m not sure I fully understand your problem, since it looks like Method 2 still suffers from the same basic issue, i.e. loss of bold type when converting case. (At least, I’m seeing an unbolded “monster” at the start of Method 2 output.)

I7 text manipulation that alters the characters of a piece of text will trigger the process that loses the formatting instructions, and I don’t think there’s any way around that. The reason that your Method 1 output is working is that the I7 compiler creates a special upper case version of an object’s printed name (presumably for use with [The ...] substitutions on proper-named objects), and that’s what’s being shown – there’s no text manipulation required to get the initial capital letter in that case.

I followed up on Draconis’s sleuthing, and it turns out that the underlying machinery is prepared to do what you want – the proximal cause of your problem is the definition of the To say A list... phrase in the Standard Rules. If you override the To say A list of... phrase with:

To say A list of (OS - description of objects)
	(documented at phs_Alistof):
	(-
		objectloop({-my:1} ofclass Object)
			if ({-matches-description:1:OS})
				give {-my:1} workflag2;
			else
				give {-my:1} ~workflag2;
		WriteListOfMarkedObjects(ENGLISH_BIT+CFIRSTART_BIT);
	-).

and also replace your For printing the name of... rule with the Before printing the name of... and After printing the name of... rules you use in your second example, then I think your first example works as you want.

Globally replacing that phrase may introduce other problems. If so, then you can change the name of the phrase (e.g. To say a special list...) and use it only when needed.

3 Likes

I think I was coding too late at night (and then became distracted with correct comma placement). You’re right, it doesn’t work. I’m surprised complex listing handles this situation satisfactorily. There’s no i6 in there so it must have to do with how its lists are generated. I haven’t dug too deeply into that.

This is a good solution, in any case. I’ll mark the problem solved (and update my previous post). Thanks!