Text Capture by Eric Eve

This topic is for discussions related to Text Capture by Eric Eve

This extension looks interesting, but I can’t find it on the inform’s website :frowning:

inform7.com/extensions/all-exten … y-title/#T

If I recall correctly, it is in the Public Library integrated within the Inform IDE (extension tab then small tab in the top right corner).

In fact since 6L38, it’s better to look in the Public Library and the Github repo (github.com/i7) first.

I don’t use 6L38 (and the extension public search doesn’t seem to work on linux), it was for an old game.
It was not longer listed no the website but I’ve found it, it’s there: inform7.com/extensions/Eric%20Ev … index.html

(I couldn’t find it on github)

ps : you like owls too? :wink:

Yeah, it occured to me after I posted that you wanted a old version. I remember searching Text Capture on the Inform website without finding it.

(I like owls, but I sometimes regret it: they are more and more popular…)

So I’d like to be able to adapt Text Capture to somehow recursively capture text. So you can have a phrase to “push the captured text stack” when you might be already capturing text, instead of throwing out the old captured text, it pushes it onto a list of texts and lets you continue capturing text into the text buffer. Which could easily be done by copying the captured text to a new variable.

And–this is the new part–you have a command to “pop the captured text stack” which loads the captured text buffer into “[captured text]” (just as “stop capturing text” does). Then, if there’s anything on the stack of captured texts, refills the capture text buffer with the last thing the stack and continues capturing text into that buffer. If there’s nothing on the stack, it stops capturing text.

So basically, I need a way of knowing how to copy something back into the captured text buffer so we can keep adding onto the end. How could this be done? Would it be a simple or simpleish I6 routine, if I knew I6?

An example might work like this:

[code]To say start reversing:
push the captured text stack.

To say finish reversing:
let new string be captured text;
pop the captured text stack;
say “[new string in reverse]”.

[and there’d be a routine for reversing a captured text]

When play begins: say “[start reversing]Foo[start reversing]bar[finish reversing]baz[finish reversing].”[/code]

Desired behavior:
You start reversing, pushing the captured text stack. If there were anything in the captured text buffer, it would get put on the stack. You also start capturing text into the buffer.
You say “Foo.” This gets captured into the buffer, so the captured text buffer reads “Foo”.
You start reversing again, pushing the captured text stack. So “Foo” moves from the captured text buffer to the top of the captured text stack.
You say “bar”. This goes into the captured text buffer. So the captured text buffer is “bar” and the first element of the stack is “Foo”.
You stop reversing. This loads the captured text buffer, “bar”, into new string. Then it pops the captured text stack, which loads “Foo” back into the captured text buffer and continues capturing text. Then we say “[new string in reverse]”, which is “rab”, and since we’re still capturing text it gets added to the end of the captured text buffer. So the captured text buffer is “Foorab” and the stack is empty.
Then we say “baz” which gets added to the back of the captured text buffer, so we have “Foorabbaz” in the captured text buffer.
Then we stop reversing again. This loads “Foorabbaz” into new string from the captured text buffer. Then it pops the captured text buffer, which since the stack is empty stops capturing text. So when we say “[new string in reverse]” it actually gets printed, and in the end we print “zabbarooF”.

Anyway. Any ideas as to what I need to do to push things back from the stack into the captured text buffer?

The first place I looked is the “change the text of the player’s command to” phrase, which effectively copies an I7 text into an I6 buffer (and then re-tokenizes it and such). The I6 definition (in 6L02) is:

[ SetPlayersCommand from_txt i len at p cp;
	cp = from_txt-->0; p = TEXT_TY_Temporarily_Transmute(from_txt);
	len = TEXT_TY_CharacterLength(from_txt);
	if (len > 118) len = 118;
	#ifdef TARGET_ZCODE;
	buffer->1 = len; at = 2;
	#ifnot;
	buffer-->0 = len; at = 4;
	#endif;
	for (i=0:i<len:i++) buffer->(i+at) = CharToCase(BlkValueRead(from_txt, i), 0);
	for (:at+i<120:i++) buffer->(at+i) = ' ';
	VM_Tokenise(buffer, parse);
	players_command = 100 + WordCount(); ! The snippet variable "player's command"
	TEXT_TY_Untransmute(from_txt, p, cp);
];

Trimming out a bit of the stuff we don’t need gives

[ WriteBuffer from_txt i len at p cp;
	cp = from_txt-->0; p = TEXT_TY_Temporarily_Transmute(from_txt);
	len = TEXT_TY_CharacterLength(from_txt);
	if (len > 118) len = 118; ! Or whatever your maximum length is
	#ifdef TARGET_ZCODE;
	YOUR_BUFFER_HERE->1 = len; at = 2;
	#ifnot;
	YOUR_BUFFER_HERE-->0 = len; at = 4;
	#endif;
	for (i=0:i<len:i++) YOUR_BUFFER_HERE->(i+at) = BlkValueRead(from_txt, i);
	TEXT_TY_Untransmute(from_txt, p, cp);
];

This will put the specified text into YOUR_BUFFER_HERE. You’ll probably also need to store how many characters you inserted (which is here the local variable “len”) to offset Text Capture’s starting point by that amount. I’ll do more experimentation when I have a chance.

OK, while I was falling asleep a few nights ago an idea came to me about just doing this with I7 texts anyway, and then when I went to implement it I realized that I’d forgotten what my original desired behavior was. But I figured maybe I can do this with I7 texts anyhow–have one list of texts to be the capture stack, have one text to be the stuff that would ordinarily go in “[the captured text]” (that’s popped text), and then have another text to be the stuff that I wanted to push back to the capture text buffer (that’s cumulative buffer), and when I need to push or pop the captured text again I just add the cumulative buffer on to the front of it.

Here’s what I have right now. I’ve only tested it in the one case but I have to go to bed, where perhaps I’ll get another idea. As I say below I’m pretty unsure now whether the second half of the definition of “pop the text capture stack” makes any sense at all.

[code]Lab is a room.

Include Text Capture by Eric Eve.

The text capture stack is a list of texts that varies. The popped text is a text that varies. The cumulative buffer is a text that varies.

To clear the text capture stack: truncate the text capture stack to 0 entries.

To push the text capture stack:
if text capturing is active:
stop capturing text;
let T be the substituted form of “[cumulative buffer][captured text]”; [not sure if substituted form is necessary]
add T at entry 1 in the text capture stack;
start capturing text.

To pop the text capture stack:
if text capturing is active:
stop capturing text;
now the popped text is the substituted form of “[cumulative buffer][captured text]”;
if the number of entries in the text capture stack is greater than 0:
now the cumulative buffer is entry 1 of the text capture stack;
remove entry 1 from the text capture stack;
start capturing text;
otherwise if the number of entries in the text capture stack is greater than 0: [I’m not sure any of this is needed, or works]
now the popped text is entry 1 of the text capture stack;
remove entry 1 from the text capture stack;
otherwise:
now the popped text is “”.

To say start reversing:
push the text capture stack.

To say finish reversing:
pop the text capture stack;
say “[popped text in reverse]”.

To decide what text is (T - a text) in reverse:
let output string be some text;
let N be the number of characters in T;
repeat with index running from 1 to N:
now output string is “[character number index in T][output string]”;
decide on output string.

When play begins: say “[start reversing]Foo[start reversing]bar[finish reversing]baz[finish reversing].”[/code]

So the current text capture implementation uses memory streams, because it is simple, and because it allows it to work for the Z-Machine.

If you were happy to limit it to Glulx it may be possible to use the filter IO system instead of memory streams.

Start capturing would add a new text to the stack, and if it’s the first entry on the stack, switch to the filter IO system.

The filter function would add the character to the end of the top text of the stack.

End capturing would return the topmost text on the stack, and turn off the filter IO system if the stack is empty.

This might be simpler to implement, but if anything used the @glk opcode to print directly, then it wouldn’t be captured. Depending on what you need it for, that might be fine.

Not for me!

Sorry for being flip about that! The thing is, I know so little about the sub-I7 level that what you said sort of reads to me like “Instead of cooking this finicky recipe, it might be simpler to genetically alter yourself so you can photosynthesize nutrients from sunlight.” If anyone wants to try the filter IO method I’d be happy to look at it, but given my other adventures in I6 I’m trying to do as much at the I7 level as I can.

Anyway, I figured out that I wasn’t clearing the cumulative buffer when I needed to–when you push something onto the stack you should clear it (because the old buffer has been pushed onto the stack), and when you stop capturing text after popping the last thing off the stack (because you’ve cleared the stack completely and aren’t capturing text at all).

Also, thinking about the second half of the popping code, you really shouldn’t be able to reach the case where the stack is nonempty and you’re not capturing text; if your only interaction with the text is pushing and popping it, those will always start capturing text again unless you have popped the last text off the stack.

So here’s what I have, with a couple new test cases and an option to pop the stack “loudly” (pop the stack and say the captured text):

[code]Lab is a room.

Include Text Capture by Eric Eve.

The text capture stack is a list of texts that varies. The popped text is a text that varies. The cumulative buffer is a text that varies.

To clear the text capture stack: truncate the text capture stack to 0 entries.

To push the text capture stack:
if text capturing is active:
stop capturing text;
let T be the substituted form of “[cumulative buffer][captured text]”; [not sure if substituted form is necessary]
add T at entry 1 in the text capture stack;
now the cumulative buffer is “”; [we pushed the buffer onto the stack, so we clear it]
start capturing text.

To pop the text capture stack, loudly:
if text capturing is active:
stop capturing text;
now the popped text is the substituted form of “[cumulative buffer][captured text]”;
if the number of entries in the text capture stack is greater than 0:
now the cumulative buffer is entry 1 of the text capture stack; [reload the top of the stack back into the buffer]
remove entry 1 from the text capture stack;
start capturing text;
otherwise: [we’ve emptied the stack and stopped capturing text, so we clear the buffer]
now the cumulative buffer is “”;
otherwise if the number of entries in the text capture stack is greater than 0: [I’m not sure any of this is needed, or works]
now the popped text is entry 1 of the text capture stack;
remove entry 1 from the text capture stack;
otherwise:
now the popped text is “”;
if loudly and the popped text is not “”:
say the popped text.

To say start reversing:
push the text capture stack.

To say finish reversing:
pop the text capture stack;
say “[popped text in reverse]”.

To decide what text is (T - a text) in reverse:
let output string be some text;
let N be the number of characters in T;
repeat with index running from 1 to N:
now output string is “[character number index in T][output string]”;
decide on output string.

When play begins:
say “[start reversing]Foo[start reversing]bar[finish reversing]baz[finish reversing].”;
say “[start reversing]Foo[start reversing]bar[finish reversing]bop[start reversing]bam[finish reversing]baz[finish reversing].”;
clear the text capture stack;
now the cumulative buffer is “”;
push the text capture stack;
say "Foo ";
push the text capture stack;
say "bar ";
pop the text capture stack, loudly;
say "bam ";
push the text capture stack;
say "bop ";
pop the text capture stack, loudly;
say “baz”;
pop the text capture stack, loudly;
say “.”[/code]

Output:

I’m not quite sure exactly what the desired behavior in the last two cases should be, but this output isn’t obviously wrong!

No worries! It wouldn’t be a breeze for me either, I’d have to go dig into the Glulx spec to do it myself. I just thought from a technical perspective it might be simpler because you’d only have one stack rather than both memory streams and a stack. But code that works is always better than code that doesn’t exist!

Ah, it would be simpler under the hood, that seems right. Beyond me, though! By a similar light, it’d probably be simpler if I could load the text that pops off the stack back into the buffer instead of using the cumulative buffer as a workaround, but I keep failing to reset critical counters when messing around with I6 buffers, so I’ll keep it I7.

So right now I have the above wrapped up in an extension, which might just need some documentation before being ready to upload:

[spoiler][code]Recursive Text Capture by Matt Weiner begins here.

Section 1 - Dependency

Include Text Capture by Eric Eve.

Section 2 - Variables

The text stack is a list of texts that varies.

The popped text is a text that varies. [The thing that gets popped off the stack at the end. Basically the equivalent of “[captured text]” in Text Capture.]

The cumulative buffer is a text that varies. [The text that has already been written to the buffer in the current layer of text capturing. Probably best restricted to internal use only.]

Section 3 - Pushing, Popping, and Clearing the Stack

To push the text stack:
if text capturing is active:
stop capturing text;
let T be the substituted form of “[cumulative buffer][captured text]”; [not sure if substituted form is necessary]
add T at entry 1 in the text stack;
now the cumulative buffer is “”; [we pushed the buffer onto the stack, so we clear it]
start capturing text.

To pop the text stack, loudly:
if text capturing is active:
stop capturing text;
now the popped text is the substituted form of “[cumulative buffer][captured text]”;
if the number of entries in the text stack is greater than 0:
now the cumulative buffer is entry 1 of the text stack; [reload the top of the stack back into the buffer]
remove entry 1 from the text stack;
start capturing text;
otherwise: [we’ve emptied the stack and stopped capturing text, so we clear the buffer]
now the cumulative buffer is “”;
otherwise if the number of entries in the text stack is greater than 0: [I’m not sure any of this is needed, or works]
now the popped text is entry 1 of the text stack;
remove entry 1 from the text stack;
otherwise:
now the popped text is “”;
if loudly and the popped text is not “”:
say the popped text.

To clear the text stack: truncate the text stack to 0 entries.

Recursive Text Capture ends here.[/code][/spoiler]

And it actually seems to be working on the case I wanted it for:

[spoiler][code]Include Recursive Text Capture by Matt Weiner.

Lab is a room.

To say a-an of – beginning a-an_of:
push the text stack.

To say end a-an – ending a-an_of:
pop the text stack;
if “[popped text]” starts with a vowel sound:
say “an [popped text]”;
otherwise:
say “a [popped text]”.

To say uppercase A-An of – beginning uppercase_A-An_of:
push the text stack.

To say end uppercase A-An – ending uppercase_A-An_of:
pop the text stack;
if the popped text starts with a vowel sound:
say “An [popped text]”;
otherwise:
say “A [popped text]”.

To decide whether (string - a text) starts with a vowel sound:
let the first word be word number 1 in string;
if the first word is a word listed in the Table of Words That Start With Vowel Sounds, yes;
if the first word is a word listed in the Table of Words That Don’t Start With Vowel Sounds, no;
if character number 1 in the first word is a vowel, yes;
no.

To decide whether (letter - a text) is a vowel:
if letter exactly matches the regular expression “a|e|i|o|u|A|E|I|O|U”, yes;
no.

Table of Words That Start With Vowel Sounds
word
“hour”
“hourglass”
“honest”
“yttrium”

Table of Words That Don’t Start With Vowel Sounds
word
“uniform”
“unicorn”
“united”
“United”

Color is a kind of value. The colors are red, orange, yellow, green, blue, indigo, and violet.

The light is in the lab. For writing a paragraph about the light: say “[Uppercase A-an of][light][end uppercase A-an] is visible.”

The light has a color called primary color. The light has a color called secondary color.

Before printing the name of the light: say "[primary color of the light] “.
After printing the name of the light: say " with [a-an of][secondary color of the light][end a-an] tint”.

Every turn:
now the primary color of the light is a random color;
now the secondary color of the light is a random color.[/code][/spoiler]

There was a fair amount of testing that went into that because I forgot to put “light” in brackets and thought that the text capture stuff was swallowing up the before/after printing the name of the light rules, when they were never getting called in the first place. (Hard to use debug text to see what’s getting called her, since the debug text winds up in the buffers.)