Updating extensions in the GitHub repo for Inform7 v10

There is a subtlety to the versioning: officially, the question for a major version update is whether you’re breaking backwards compatiblity of the Application Programming Interface. In other words, the documented interface. Changing internal implementation details isn’t supposed to matter.

The thing is, Inform 7 doesn’t have strong module boundaries, and the internal implementation details are all exposed to the story writer. So whether something is part of your “interface” is sort of a squishy question. In general, change the major version number if you think you might have broken backwards compatibility for someone using your Extension. But don’t sweat it if you technically broke backwards compatiblity on some internal element which story writers weren’t supposed to be poking at.

Which parts constitute the “interface” vs. implementation details should be in the documentation for the module. Any time something described in the documentation breaks, you definitely have to change the major version number.

In general, all Section (Part, etc.) names are designed to be overridden by story writers, and all rule names are designed to be overridden by story writers, and Tables are generally designed to be added to by story writers, so personally I usually change the major version every time any of those change. But you might make a different decision if you don’t think Story writers should really be overriding those.


Regarding minor vs. patchlevel, my rule is to change the patchlevel literally every time I release a file to the public, so that there are never two versions floating around with the same version number (outside my home computer). The minor version should be updated when it “feels like” an actual version change, IMO, while the patchlevel is updated for anything including a one-character whitespace fix – for which it just feels wrong to update the minor version.

2 Likes

Given, as you say, the generally open nature of I7, I’d suggest the practice of putting anything one wants to be subject to change without a major version increment under a header marked unindexed, a feature that’s little used outside of the Standard Rules. When things outside such a section change would be when a major version change is called for.

At least as an aspiration. I’m not imagining any attempt at policing anything (short of removing anything that didn’t compile or completely didn’t work that might have ended up in one of the version-specific branches) anyway.

Speaking of updating extensions of people no longer active in the community, I was already some 80% done with a refactoring of Object Kinds by Brady Garvin, parts of which no longer even worked in 6M62 (but those parts were minor enough that I didn’t consider withholding it from the 9.3 branch). So I’ll finish that and get it submitted within a couple of months… barring discovering that internal v10 changes fundamentally break it – we’ll see.

1 Like

Speaking of Brady Garvin’s work, I took a crack at Brady Garvin’s “Scopability” but I discovered a genuinely spectacular issue. So, it’s trying to replace DoScopeAction. But if you try to replace DoScopeAction, even with a textually identical copy lifted from the current Inform 7 code with no changes, it caused the Inform 7 compiler to fail. I do not see any way to update Scopability as long as that is true.

I’m not sure what’s going on there, but my theory is that it has to do with the Parser passing stuff outside of actual subroutines, using @push and @pop.

@push and @pop don’t let you pass info outside of the subroutine.

What do you mean by fail?

Something I noticed just yesterday, I think, is that v10 has a double standard: the parser rejects some valid I6 but will accept in kits things it rejects as I6 inclusions, and the kits have code for which that’s true. And something I noticed early on: error messages stemming from I6 inclusions are frequently spectacularly unhelpful. This source, with the routine cut and pasted from Parser.i6t:

Lab is a room.

Include (-
[ DoScopeAction item;

    #Ifdef DEBUG;
    if (parser_trace >= 6)
        print "[DSA on ", (the) item, " with reason = ", scope_reason,
            " p1 = ", parser_one, " p2 = ", parser_two, "]^";
    #Endif; ! DEBUG

    @push parser_one; @push scope_reason;

    switch(scope_reason) {
        TESTSCOPE_REASON: if (item == parser_one) parser_two = 1;
        LOOPOVERSCOPE_REASON: if (parser_one ofclass Routine) indirect(parser_one, item);
        PARSING_REASON, TALKING_REASON: MatchTextAgainstObject(item);
    }

    @pull scope_reason; @pull parser_one;
];
-) replacing "DoScopeAction".

gets this on compilation (with the current commit, 4b7ad580b7cb78602d77116a680684df59e3e269):

Inform 7 v10.1.0 has started.
I’ve now read your source text, which is 9 words long.
I’ve also read Basic Inform by Graham Nelson, which is 7691 words long.
I’ve also read English Language by Graham Nelson, which is 2328 words long.
I’ve also read Standard Rules by Graham Nelson, which is 32162 words long.

→ An internal error has occurred: INVALID node type in Inter tree. The
current sentence is ‘if the player’s command includes “lie”’ (the Standard
Rules, line 2010); the error was detected at line 117 of
“inter/final-module/Chapter 2/Vanilla.w”. This should never happen, and I
am now halting in abject failure.
Inform 6.36 for Linux (24th January 2022)
File “”; Line 0 # Error: No ‘Main’ routine has been defined

Compiled with 1 error

The difficulty seems to be with the indirect call. If you substitute

        LOOPOVERSCOPE_REASON: if (parser_one ofclass Routine) rfalse;

and make no other changes, it compiles. Haven’t the foggiest right now what its problem is with the indirect call.

1 Like

This is exactly the same as calling parser_one(item); so you can get around that.

(The indirect() function dates from Inform 5, or probably well before that, when function values were handled in a screwy way.)

3 Likes

yes, that works! Thank you.

1 Like

Thanks! I may go back to working on Scopability now. UPDATE: done and committed to master and 10.1 branch.


It might be just as well if someone (not me, please) submitted a patch to get rid of that confusing indirect() call in the base code. Eeek. indirect() is mentioned on page 516 of the Inform Designer’s Manual v4 in an example and nowhere else; you have to go back to Inform Designer’s Manual v3 to find a description of it. Wow.


Meanwhile, I finished updating Gavin Lambert/Enhanced Banner.i7x, but I rewrote it so heavily that I decided to move it over to my directory as Nathanael Nerode/Enhanced Banner.i7x.


OK. So, grepping through the current kits, “indirect” is used once in WorldModelKit/Sections/ZMachine.i6t in a way where I’m not sure it can be eliminated:

WorldModelKit/Sections/ZMachine.i6t:    indirect(#actions_table-->action);

And several times in the parser where it probably should be eliminated:

CommandParserKit/Sections/Parser.i6t:        if (indirect(scope_error) == -1) {
CommandParserKit/Sections/Parser.i6t:        l = indirect(given_tdata);
CommandParserKit/Sections/Parser.i6t:        l = indirect(scope_token);
CommandParserKit/Sections/Parser.i6t:           rv = indirect(token_filter);
CommandParserKit/Sections/Parser.i6t:        if (indirect(scope_token) ~= 0) rtrue;
CommandParserKit/Sections/Parser.i6t:           LOOPOVERSCOPE_REASON: if (parser_one ofclass Routine) indirect(parser_one, item);
CommandParserKit/Sections/Parser.i6t:        k = indirect(parser_inflection, obj, wd);

One of these is the one we just hit. But none of them seem necessary, do they? I suppose some ought to be wrapped in a typecheck. Does “indirect” give special return values if it calls a non-routine?

I know Graham Nelson doesn’t like to touch the parser code, but it’s not good to have code using calls which aren’t currently documented anywhere.

No, it really is exactly the same as a function call.

1 Like

Well, in that case I have a patch for all the obsolete uses of “indirect” in the parser.

parser-eliminate-indirect.diff.txt (2.7 KB)

As for the one in Z-Machine – I forget, in Inform 6 does this work?

(#actions_table-->action)()

or is a temp variable or some funny syntax necessary?

1 Like

Didn’t-work, mostly. A surprising number of extensions caused weird behavior with certain actions on directions that I’d defined (POINT CAMERA NORTH), and I never figured out why.

Now that there’s a completely new version out that people will be updating their extensions for, it’s probably worth trying to get all of those updated. Or at least figuring out what the underlying cause was to file a bug report.

3 Likes

I just committed i7x to my i7-helpers repo. It takes an extension as a command-line argument, extracts and compiles the examples, and if the examples have a “test me”, it runs it.

It’s a crude proof-of-concept, but it works. Adjust I7EXTERNAL, I7INTERNAL, I7TRANSIENT variables to taste. Depends on intest, inform7, inform6, cheap-glulxe being in your PATH. Output goes to A.i7, A.i6, A.ulx, etc., in the current working directory with no attempt to avoid clobbering things. Doesn’t attempt to clean up after itself. It’s also short enough that I’ll just dump it here…

#!/bin/bash
EXT="$@" 
I7EXTERNAL="$HOME/github/i7/External"
I7INTERNAL="$HOME/github/i7/inform/inform7/Internal"
I7TRANSIENT="$HOME/github/i7/Transient"

intest inform7 -using -extension "$EXT" -do -catalogue |
  perl -ne '/Example\s+(.+)\s+=/ && print "$1\n"' |
  while read letter; do
    INF="$letter.i7"
    I6="$letter.i6"
    ULX="$letter.ulx"
    intest inform7 -using -extension "$EXT" -do -source "$letter" -to "$INF"
    inform7 -no-census-update -transient "$I7TRANSIENT" -external "$I7EXTERNAL" -internal "$I7INTERNAL" -no-progress -o "$I6" -source "$INF"
    inform6 -E2SDwG "$I6" "$ULX"
    [[ "Z`intest inform7 -using -extension "$EXT" -do -script "$letter"`" != "Z" ]] && echo 'test me' | cheap-glulxe "$ULX"
  done

It’s fixed now. As Nathanael’s comment should have made clear, you need to rebuild inweb to get the new behavior. (I tried it after just rebuilding inform and was wondering why it still wasn’t working, sigh.)

1 Like

Mr. Stelzer: I would be happy to help working on updating / debugging extensions for Scroll Thief. Loved the game, and I’m a highly skilled debugger. You can private-message me if you like. There are two extensions by you referenced in the game source which don’t appear to be published (the two with !!! next to them), which I would need in order to start test compiles.

1 Like

Thanks for the i7x script! I had to alter it quite a bit to get it working. My version is at GitHub - neroden/i7-helpers – some of the changes were just personal directory swapouts, but others you might want to adopt, since they’re either for generalization or documentation.

Already used it to spot some more things I needed to fix in extension examples (now committed).

In particular, the tools don’t like extension examples to have authors listed the way story files can:

*: "Purgatory" by Erik Temple

doesn’t work; intest will fail to add examples with authors to the catalogue, thus causing automated testing to fail.

I found a workaround:

The story author is "Erik Temple".

But this would be a useful enhancement request for intest.

The elimination of indirect() from upstream has been submitted as a bug report:

and as a pull request:

So hopefully that source of confusion will be gone soon.

OK, finished my long-suspended work on rdc-improvements and merged the changes to Emily Short’s Room Description Control and the three related/subsidary extensions into both “master” and “10.1”.

Whew! Exciting!

This was all technical work, but it definitely changed the API in a couple of subtle places, so the major version numbers got bumped. Changes were designed to reduce conflict with games and eliminate the need for a workaround previously specified in the documentation, so some games may need no changes at all, but ones which were using the workaround or doing more invasive messing with the innards of RDC will need changes.

Next project is trying to push Print Stage Detection by Taryn Michelle upstream into Core Inform, I think. :sigh: Unless people think that the approach taken by Indefinite Article Substition Fix by Matt Weiner is better. Print Stage Detection offers more flexibility, as explained in its documentation.

I have received no comments on the best approach for this yet. I don’t really think this should require an extension, which is why I want to push it upstream.


So, with a desire to be systematic, having finished going through my own extensions, I was just going through the rest of the Emily Short extensions which haven’t been ported to the 10.1 branch.

Glulx Entry Points and Glulx Text Effects have new versions in the core Inform distribution; I’m not sure what is supposed to happen here. Do we leave them out and prefer the core Inform version? I think @Dannii probably knows best; I am extremely text-oriented and have never messed around with this stuff.

The other Emily Short extensions which haven’t arrived in the 10.1 branch:

'Approaches.i7x'
'Basic Hyperlinks.i7x'
'Location Images.i7x'
'Mood Variations.i7x'
'Simple Graphical Window.i7x'

Approaches – @Zed, did you already start working on this one? The version in the Smoketest isn’t the version in the repository?

Basic Hyperlinks depends on Glulx Entry Points so probably needs to be updated for that.
Simple Graphical Window currently depends on Flexible Windows by Jon Ingold (which was essentially rewritten by @Dannii) which depends on Alternative Startup rules by @Dannii which isn’t compiling yet.
Location Images depends on Simple Graphical Window.

Mood Variations is using utterly evil black-magic I6 inclusions of the most voodoo variety. Out of sheer perversity I might attempt to update it. – UPDATE – finished it. Turned out the key was to rewrite a hunk of the I6 inclusions in I7, and it came out really clean! Great example of how to define new multi-stage say-phrases now.

The only reason this one is broken is 'cause it Includes Version 6 of Locksmith and the modern version is 13. But before just upping the version number there, I wanted to bounce this off of folks:

If you’re not using locks and keys, Locksmith isn’t relevant to Approaches. If you remove the Locksmith inclusion, then of Approaches’ 6 examples, the output differs only for the first, “Easy Keys”.

So I’d like to suggest removing the extension’s inclusion of Locksmith, and then adding a documentation note to include Locksmith or Implicit Actions by Eric Eve if you want it to work with locks (and an explicit Locksmith inclusion to the Easy Keys example).

What do folks think? This would break existing code that included Approaches and made use of phrases/definitions etc. from Locksmith but didn’t explicitly include Locksmith. So maybe it wouldn’t be appropriate to include in 9.3, but just 10.1. (Of course, fixing any broken code would just mean including Locksmith.)

1 Like

I like! I prefer my extensions independent when possible.