Idea for a unified framework for handling hyperlinks in Inform 7

I had been thinking that the tags would be global variables; extensions would include lines like these:

Keycodes hyperlink tag is initially 1.
Command replacement hyperlink tag is initially 2.
Choice nodes hyperlink tag is initially 10.

If the community doesn’t coordinate well, or two people publish extensions at the same time that use the same code, authors would be able to change those values manually.

So, your idea of registering an extension… I actually like that more. However I still like the idea of global variables, because an extension like Choice Nodes would need to know what its tag is in order to create its hyperlinks. It could look up the table for its associated rule, but that’s messy. Instead, what if each extension requested its tag like this:

After starting the virtual machine (this is the register choice nodes rule):
	now choice nodes hyperlink tag is the next available hyperlink tag;

A To decide what number is the next available hyperlink tag: phrase would keep a counter, returning and incrementing the counter. If someone has too many registered tags then it would display a runtime error.

So now that Choice Nodes has registered itself, it can use its tag in the hyperlink handling rules and phrases to create hyperlinks:

A glulx input handling rules for a hyperlink-event (this is the handle choice nodes events rule):
	if the current hyperlink matches choice nodes hyperlink tag:
		let new node be the hyperlink payload as an object;
		...
		
To say choice (C - a choice node):
	set hyperlink with tag choice nodes hyperlink tag and payload C;

The framework extension would provide a few phrases like current hyperlink matches (T - a number), hyperlink payload as an object, hyperlink payload as a signed number, set hyperlink with tag (T - a number) and payload (P - a value). These phrases would handle all the bitshifts, bitmasks, etc, and could even make it so that if anyone did need more than 24bits of payload we could make 25 or 26bits be a compile-time use option without needing any other extensions to be modified. The number of bits for tags could also be a use option.

(BTW, tables use much less memory than lists. Lists use the Flex system so there’s a lot of overhead.)

Yeah, the next available hyperlink tag thing is probably a good way to go, since the list-based approach seems to be out.

I’m a little squeamish about putting objects directly in a hyperlink id – that’d only be safe if you could guarantee that object addresses aren’t using the bits stomped by the extension tag/id.

Yes, I know. “Elegant” was referring to the author’s code, not necessarily the memory footprint :grin:

The theory was that registration would be as simple as:

Choice link processing rules are a number based rulebook producing text.
	
Choice link processing rule for number (called N):
	say "You clicked hyperlink [N].[paragraph break]".

After starting the virtual machine:
	add choice link processing rules to the hyperlink extension registry.

(This would be in the choice extension of course, with the actual author’s code even more elegant.)

And there was a use option to define how big the extension ids are as a number of bits. I assume it’s probably possible to size tables via use options, but I’m less sure you can do that based on bitshift math.

Or rather, I think you can size I6 tables/arrays that way. I don’t think you can for I7 tables, at least not without the deep magic.

Ooh, that is elegant. Your idea is that the hyperlink handling rule would extract the tag, access the registry list/table, and then invoke the rule? I like the simplicity of that, but don’t see how the choice extension could make a hyperlink without knowing its own tag.

Immediately after you added the rulebook to the registry, then the number of entries in the hyperlink extension registry would be equal to your tag/id, which you could save in a global and use to generate links later.

That part wasn’t quite as elegant yet though.

Probably you’d want to abuse a to decide phrase a bit and have the registration code actually be something closer to:

now choice link tag is a new hyperlink tag for choice link processing rules;

If I could only figure out how to make a list of rules :cry:

Or the register phrase could simply return a number.

While I do like the idea of having the framework invoke your rule so you don’t have to have check if the hyperlink number matches your tag, considering there will probably only be half a dozen hyperlink extensions max, is it worthwhile, to save one line of code (if the current hyperlink matches choice nodes hyperlink tag)?

Also, rather than just returning an object, it should be possible to make a hyperlink payload as a (K - a kind of object) phrase which checks that the encoded address is for an object of the appropriate kind.

To decide is how you return a number.

Yeah, that’s an advantage of the method you’re proposing, you can clobber the kinds a bit more freely. Of course, some people might consider that a disadvantage. :wink:

One of the reasons I was trying to keep the number based rulebook producing text thing is that this is how my Hyperlinks extension works, which already incorporates all the logic of re-injecting command text or keypresses, so the actual processing rules are very simple.

Just rule succeeds with result "go north" or rule succeeds with result "f" etc. Or you can execute arbitrary logic without providing a command at the end, and it all just works.

Sadly I don’t think there’s a way to declare a rulebook that is based on a pair of numbers rather than a single number. (There are the structured numbers but I wouldn’t trust passing an object address through them.)

This could still be made to work but each processing rule would have to decode and check the extension id/tag rather than doing it centrally as I was hoping.

Although… that gives me an idea…

So, good news and bad news.

The good news is that this works in Git. (I’m open to better names for some of the phrases.)

The bad news is that it doesn’t work in the IDE’s interpreter (Windows IDE as of 2018, at least), which appears to only support 16-bit hyperlinks. (I can’t tell whether they’re getting shaved at printing or at clicking, but only the low 16 bits of the id makes it through.)

The extension could be adapted fairly easily for a 16-bit link range but you definitely can’t pass object ids (or anything but numbers) in that sort of range (and not many numbers; shaving bits off for a tag at all is kinda problematic).

Huh. That’s weird. I had a look through your code, as well as Windows-Glk and the Inform 7 IDE code, and couldn’t see anything that looked like it would be cutting hyperlink numbers down to 16 bits.

@DavidK any ideas?

(And don’t take this the wrong way, but I also want to write an extension my own way, though I won’t get to it as quickly as you :wink:. Then we can compare and see how to make the ultimate one.)

I suspect this line is the problem:

sStyle is a 16-bit field.

1 Like

Indeed, you are correct: I’ve just spotted that too. Whoops! I will get that fixed, but I can’t say when. It is at least now recorded as an issue: https://github.com/DavidKinder/Windows-Inform7/issues/5

4 Likes

It seems to work if instead of trying to get Inform to parse your complicated syntax, to just set the list to an initial value which fits the bill:

Lab is room.

Do nothing link processing rules are a number based rulebook producing text.

Do nothing link processing rule:
	do nothing.
	
The hyperlink extension registry is initially { the do nothing link processing rules }.

Choice link processing rules are a number based rulebook producing text.

Choice link processing rule for number (called N):
	say "You clicked hyperlink [N].[paragraph break]".

When play begins:
	add the Choice link processing rules to the hyperlink extension registry.
	
Instead of waiting:
	repeat with R running through the hyperlink extension registry:
		follow R for 5.

test me with " z / rules / z".
2 Likes

That’s a neat trick. I’ve already solved this particular issue a different way (that means I don’t need to actually keep a list around), but I’ll remember that for next time, thanks!

1 Like

FYI I’ve updated Inline Hyperlinks to use the registry as well now, plus fleshed out the registry itself with a bit more documentation and sanity checking.

I think it’s pretty decent now (barring the unfortunate incompatibility with the Windows IDE), though it’s all still on a test branch so that we can mess around with it some more if you want before unleashing it.

(One possible downside of not keeping an actual list is that there isn’t any central way to ask which tags are assigned to which extensions – but other than for debugging purposes nobody should ever actually need to know that, and when debugging a single extension you can just print its specific tag variable with some suitable label as needed. So it’s not a big loss.)

I did wonder if I should add a “not for release” debugging command similar to rules that prints data about hyperlinks being clicked or lets you trace the hyperlink processing rules (currently even when rules itself is on, that is suppressed, for very good reasons). But on the other hand again it’s pretty easy to add your own specific to your extension if you really need it. The core link processing rules are simple enough that unless you’re hit with some weird platform issue (like the 16-bit truncation) it all Just Works™.

Interestingly, through a random spelunk through I7’s I6 layer I came across the “combination” kind, which is described as a small list of differing kinds – i.e. a tuple. This would be perfect for rules that want to use multiple values as parameters or results.

Sadly, I’m not aware of any way to actually access these from I7 itself. Perhaps not yet fully implemented?

1 Like

Usually you can use parentheses to disambiguate an invocation. I don’t know about a definition, but I think it would work?

The hyperlink extension registry is a list of (number based rulebooks producing text) that varies.

Weird. I swear I tried that and it didn’t work at the time, but now it seems to, at least in isolation. Perhaps I goofed elsewhere.

Oh well, I think I’m sufficiently happy with how the extension works now instead anyway.

FYI I’ve merged this (and the corresponding updates to Inline Hyperlinks) to the main branch now; it’s probably spent long enough languishing in an experimental branch.

Great, thanks.

I am still thinking that I’d like to make my own implementation of this idea, but I don’t know when I’ll get around to it.

You can of course do that (it was based on your idea to start with), but I still think it’d be better to collaborate on one definitive extension rather than dividing the waters. The idea of a registry does assume that everyone is using the same one, otherwise you just get conflicts again.

That is true. So I might not end up making my own!