§1. Undocumented features of Inform are no longer very secret (here you are reading about them, after all), but they can be hard to find by raking through the source code at random. What we mean by "undocumented features" here are capabilities intentionally included in Inform, but not mentioned in any of the user-facing documentation. That documentation used to be just the two books built in to the apps (Writing with Inform and The Recipe Book), but those are now supplemented by the manuals for the three command-line tools inform7, inbuild and inter, which cover many features invisible from within the app and at present available only for command-line users.

This page covers a handful of other features still, but which are intended mainly for maintainers of Inform and may change at any time without notice.

§2. In working out what the compiler is doing (or has just done), the debugging log is always the first thing to investigate. This a text file like a running journal of what the compiler is doing; if it halts partway, either with problem messages or an internal error, then the log is still written up to that point, and can be revealing. The log can be viewed in the Inform apps, but usually only if an "advanced" preference setting has been ticked. Once that is done, the log appears as one of the tabs in the Results pane after each compilation (successful or not). A similar preference setting enables the Inform 6 output to be viewed, too, though of course only if the compiler gets that far.

The compiler is more instrumented than a first look at the log might suggest. Most of its "debugging aspects" are switched off by default: if they were all switched on, the log would be simply enormous. Each aspect has a textual name — for example, "predicate calculus" shows the logical propositions formed and simplified inside the compiler. (On each run, the bottom of the debugging log lists the range of available aspects.) There are four ways to change what aspects are switched on:

The French Kitchen is a room. An oak table is in the Kitchen.
*. A ball is on the oak table. *.
To consider (N - a number):
    ***;
    showme N times N;
    ***.
Every turn:
    *** "predicate calculus";
    now the rock is in the bucket;
    ***.

§3. Inform compiles first to an intermediate representation called Inter, and only then compiles that in turn to its actual output. The Inter produced is then discarded and normally never leaves memory, so it's invisible to the user. When debugging, though, we sometimes want to see what it was.

Two special debugging aspects are provided for this.

§4. The "Test... with... " feature of Inform is familiar to most users, since it's used by almost all of the examples, usually with source text like:

Test me with "examine box / take box".

Less well-known is that it can also run unit tests built in to the compiler. For example:

Test description (internal) with a number which is even.

The test here is called description. The marker (internal) is what causes this sentence to be read as an internal test case. Most tests, description among them, then expect some details to follow the word with. In this case, it's a description, "a number which is even", and what the test does is to convert this to a logical proposition and print that proposition out.

What happens is not that inform7 prints the test output to the console, or even to the debugging log. It compiles the test output into the story file it is generating, so that this is printed out when the story file runs. That may sound needlessly indirect, but it's convenient both in the app and also for intest to check. Add the above test sentence to some project, click Go in the app and you see the result.

1. a number which is even
<< number(x) & even(x) >>
x (free) - number.
Test pattern (internal) with asking Hans to try taking the counter.
Test pattern (internal) with list looking or taking inventory in the presence of Hans in the Laboratory.
1. everyone likes Peter
<< ForAll x IN< person(x) IN> : amity(x, 'peter') >>
x - object.

§5. inform7 has a test suite of roughly 2000 test cases, some of which are large and elaborate. Running through this test suite must be done at the command line (indeed, the tests are not shipped inside the apps, only as part of the inform Git repository). The intest tool is provided for this, and reading the intest manual is a good place to begin. A test suite to be operated by intest is configured by a *.intest file, and the full details of how the I7 suite works are laid out in inform/inform7/Tests/inform7.intest. About 25% of the tests are the Examples from the manuals; another 25% more or less systematically poke at language features, and edge cases which proved tricky in the past; and the remaining 50% test that the compiler produces correct problem messages.

Tests in the suite can be divided up into the following categories:

$ ../intest/Tangled/intest inform7 -find Bruneseau
Test cases matching 'Bruneseau':
Candle = Bruneseau's Journey

§6. Each individual test has a name. The following naming conventions apply:

§7. The complete test suite takes a long time to run — typically 10 to 11 minutes on the author's computer in 2022, even running 16 simultaneous tests on different cores. It's sometimes more practical to run subsets of the suite. For example, if debugging the part of Inform which generates C code from Inter, there's no point verifying the problem messages or the 900 or so tests which compile to Z or Glulx.

For example, these three commands:

$ ../intest/Tangled/intest inform7 all
$ ../intest/Tangled/intest inform7 problems
$ ../intest/Tangled/intest inform7 :calculus

run the full test suite, just the Test Problems cases, and just the :calculus group respectively. (Note the colon.) Particularly useful groups include :c, which performs every test involving C output, and :basic, which performs the whole BIP sub-suite.

§8. Internal errors in inform7 generate the infamous "abject failure" problem message. These are essentially failed assertions in the compiler, but they cause a clean termination of the process with exit code 1, just as any more orthodox problem message would. A Results page in HTML is generated normally. As a result, they're no harder for the GUI apps to deal with than any other sort of problem would be.

Harder to handle is an actual crash of the inform7 executable, in which an abrupt exit occurs where no problem message is generated: say if it dereferences a null pointer, or divides by zero. This of course should never happen, but we want the GUI apps to be able to cope if it does. And in order to test that, we give inform7 the ability to simulate such crashes.

This is done with three words too unspeakable to write, the "secret hieroglyphs of dread power". They're actually not much to look at. The first is:

ni--crash--1 is a room.

This crashes out with exit code 1. The second and third are much the same, but are written ni--crash--10 and ni--crash--11, giving exit codes 10 and 11. (On MacOS, at least, these are what result from derefencing a bad pointer or overflowing the stack through infinite recursion.)

§9. If Inform does crash, or throw an internal error, or even simply produce an unwanted problem message, but in circumstances which are unclear, then it's useful to see a stack backtrace at the point where the error came to light. This is where Intest's -debug feature is useful. For example:

$ ../intest/Tangled/intest inform7 -debug Acidity

This runs the test Acidity, but with the inform7 executable running in the lldb debugger (on MacOS, anyway), and moreover with it set to deliberately cause a division by zero after any problem message is generated. This forces inform7 to exit into the debugger, at which point a stack backtrace can be seen (in lldb, anyway) by typing bt.

Of course, Inform is unlikely to crash on any of the tests in the regular test suite: more likely you have some wacky source text causing a crash or unwanted problem message, but which isn't in the suite. In that case, though, you just need to create a temporary test case for it — the author likes to keep this as the file inform/inform7/Tests/Test Cases/temp.txt, and then:

$ ../intest/Tangled/intest inform7 -debug temp

does the trick. But be sure not to add this temporary file to the repository.