27. Extensions

Writing with Inform

WI §27.1 The status of extensions

The range of simulation offered by Inform's model world is intentionally limited to a core of basic essentials. We could argue at the margins, and the choice of what's in and what's out is partly traditional, but most people find the model reasonable as far as it goes.

Between 1993 and 2006, quite a range of "library extensions" for Inform's predecessor language (Inform 6) was written. Most of these extensions aimed to fill out the model by simulating other aspects of life, too: money, clothing, pourable liquids. None of these extensions was official and all of them were: it was a free-for-all, and in several cases different authors wrote rival extensions to model the same basic ideas. The development of Inform 7 was strongly influenced by this history and by the recognition that the base of rules and grammar inside a typical modern story are seldom written by a single author. They combine the standard Inform material with extensions by several third parties, together with anything specific to the story in question.

Inform 7 has a more organised idea of extensions, as we shall see. But anyone is free to write an extension on any terms or for any reason. Writers may wish to use the techniques in this chapter to develop private extensions of their own, used in several projects, or to share them with associates but not more widely.

But most writers of extensions do so to contribute to the Inform community, and for the satisfaction of solving a problem. Inform does not recognise anyone's approach to a particular need as "the official solution" - for instance, although the standard Inform distribution includes a copy of Locksmith by Emily Short, that is not the "official" way to make automatically unlocking doors, and anyone is welcome to try a better one.

However, the Inform project does recognise some extensions as "public". Public extensions are the ones archived on the Inform website for the free use of all Inform writers. Those who wish to contribute an extension as a public one are obliged to follow a number of guidelines, which are mostly stylistic points intended to make the range of extensions easier to work with. Extension writers are asked to join in the spirit of these rules and help make the whole cooperative enterprise work harmoniously. Extensions which do play by these rules are also accepted into the Public Library, which makes them easy for all Inform users everywhere to find and obtain them.

Writers who wish to make their extensions public on the Inform website should also be clear that by doing so, they are donating their work to the community on the basis of the broadest form of Creative Commons license: that is, they retain copyright and the right to be identified as the author (and as we shall see they are automatically credited in any work of IF which uses their extension), but are giving unlimited permission to use, circulate and republish their extensions in any form, even as part of commercial works (should that arise). To publish a public extension is a public-spirited act, done for only the reward of a modest acknowledgement.

If the author of an extension has not made it public, or indicated in some other way that it is free to be used without the need for permission, then it would be both polite and prudent to check with the author before publishing something which incorporates his work.

WI §27.2 The Standard Rules

When any source text is run through Inform, a secret first line is inserted, which reads:

Include the Standard Rules by Graham Nelson.

The "Standard Rules" file contains the definitions of the basic kinds, phrases, actions and grammar described in this documentation: for instance, it includes lines like

A container is a kind of thing.

…without which Inform would be lost. Although including the Standard Rules is compulsory, it is treated internally as if it were any other "extension".

What happens when an "Include" sentence is reached is that the sentence is replaced with the whole text of the file in question, often many paragraphs long.

If the file has already been included, then the sentence is simply ignored. This is so that we can have two extensions, each of which needs the other: if A says to include B, and B says to include A, the result is that including one automatically includes the other, so we always get both which ever we ask for - not that there is a hideous infinite regress.

WI §27.3 Built-in, installed and project-specific extensions

To recap: Inform builds projects from both the source text typed by the author and from Extensions; one of these, the Standard Rules, is always included; others are added as authors please. About 20 are "built-in" to Inform, meaning that they are stored inside the application and always available. Others must be "installed", and each Inform user will have a folder somewhere on his computer which contains these. Users typically obtain these from the Public Library feature in the Inform application, but can also download them directly from the extension writer's website and then use an Install Extension menu option in the application. Either way, the application then squirrels the file away, and it becomes available to any projects that that user may be working on.

It is also possible to have extensions available to just one project. These must be stored in the Extensions subfolder of the project's ".materials" folder, but otherwise are arranged the same as installed extensions - there's an outer folder for each author's name, and extensions are named with a ".i7x" extension within. For example:

Mourning Hypercritical.inform
Mourning Hypercritical.materials
   Extensions
      John Siracusa
         Fixing The Finder.i7x

When Inform needs to find an extension, it looks here first, then in the installed area, then in its built-in area. That means that we can make our own revised or hacked version of an extension, put it in the ".materials" area, and then have it take precedence over the installed or built-in one. We could even have our own private version of the Standard Rules here.

(This has a number of possible uses - for example, to provide a convenient test-bed when working on an experimental version of an extension.)

WI §27.4 Authorship

Extensions are identified by author and by name, so that a given author can produce his or her own range of extensions, and need only ensure that these are named differently from each other. If John Smith and Mary Brown each want to write an extension called "Following People", there is no conflict.

The name of an extension, and of an author, should be written in Sentence Capitalisation: that is, upper case for the first letter in each word. (Inform uses this to minimise problems on machines where filenames are read with case sensitivity.) It is permitted for author names to include upper-case letters within words, as with the "G" in "Jesse McGrew". In general it is best to avoid accented or unusual letters in titles and author names, but the standard ISO Latin-1 characters should be allowed - for instance,

Étude Pour La Fênetre by Françoise Gauß begins here.

The author name must not start with "The", nor contain the words "by", "and" or "version", or contain punctuation, as in "John X. Doe"; the title similarly, except that "and" is permitted. Name and author's name must each be no more than 50 characters long, including any spaces between words.

Authors are asked to use real names rather than cryptic handles like "ifguy", and to use genteel, plausible pseudonyms like "Emily Short" rather than, say, "Drooling Zombie" or "Team Inform". Authors are also asked to use the same author's name for all their own extensions, and (it should go without saying) not to masquerade as anybody else.

Sometimes authorship is complicated. What if Mary Brown finds some Inform 6 code written by John Smith in the mid-90s, and puts an i7 gloss on it to make an i7 extension, but then Pierre Dupont translates it into French: who's the author of the result? The rule is that the person making the current, latest version is the author listed in the titling line, so we end up with

... by Pierre Dupont begins here.

But Mary and John deserve their credits too: see the next section for how to give them.

WI §27.5 A simple example extension

Extensions are plain text files, and can be created with any text editor. (It is sometimes said that "there is no such thing as plain text", there being so many ways to represent exotic characters: so to be precise, an extension is a text file with the Unicode UTF-8 encoding, either with or without a BOM marker, using any of the possible forms of line-ending (Unix, Windows, Macintosh, or Unicode line divider). This is a detail which will only matter if the extension contains accented letters or other exotica.)

Extensions look very much like passages of Inform source, because except for a special introductory and concluding sentence, and one convention, that is all they are:

The Ducking Action by Beatrix Potter begins here.
"An action for ducking one's head."
Ducking is an action applying to nothing. Report ducking: say "You duck!" Understand "duck" as ducking.
The Ducking Action ends here.

Not a useful or interesting extension, but those few words add a whole new action and everything needed to make it work. It is Inform's ability to mix up rooms, things, kinds, grammar, phrases and rules, in more or less any order, which makes it possible for extensions to work.

The introductory sentence must be placed as the only content of line 1 of the file, which must not contain comments, and has to be written in exactly the correct form. Inform checks this very carefully when performing its census of installed extensions, on each translation of the text. (In case the extension's title is a plural, we are allowed to write "begin" and "end" instead of "begins" and "ends". For instance, the last line of the standard rules is "The Standard Rules end here.")

The "one convention" mentioned above is that if a double-quoted text is placed immediately after the beginning sentence (and with no intervening comments), then it is taken to be a short description of the extension's content called the "rubric". Hence the line:

"An action for ducking one's head."

Providing a rubric is helpful, because it enables Inform to give a meaningful listing even for an as-yet unused and unindexed extension, and because it helps the Inform website to produce better directories. Note the word "short": such text is likely to be truncated if it exceeds 500 characters.

A second double-quoted text can also, optionally, be added in yet a third special starting paragraph. This is to provide additional credits to people who have contributed to this or earlier versions. For instance:

The Ducking Action by Beatrix Potter begins here.
"An action for ducking one's head."
"based on original Inform 6 code by Marc Canard"

Note the typical style here: it's a phrase rather than a sentence, and neither starts with an upper-case letter nor ends with a full stop. (The additional credit is then used in documentation and also in the VERSION text of any Inform story file using the extension.)

Example

Exemplifying the kind of source we might use in writing extensions for kitchen and bathroom appliances.

WI §27.6 Version numbering

As we have seen, extensions are referred to by name and author, but they can also (optionally) be referred to by version. For instance:

Include version 2 of the Ducking Action by Beatrix Potter.
Version 1.2.4 of the Ducking Action by Beatrix Potter begins here.

Version numbers should consist of one to three whole numbers divided by dots, with no negative numbers allowed. Thus "5", "3.3" and "2.1.71652" are all valid as version numbers, but "-4" and "3.1.2.5" are not. Any numbers not specified are taken to be 0: thus "3.3" means the same as "3.3.0", and "5" means the same as "5.0.0".

In versions of Inform before 2022, versions of extensions were also allowed to be written in the form "N/YYMMDD", as in this example:

Version 6/040426 of the Ducking Action by Beatrix Potter begins here.

The material after the slash '/' was expected to be a date, so that 040426 would mean 26 April 2004. In order to preserve compatibility with old extensions, Inform continues to allow this notation, but treats it as equivalent to writing "N.0.YYMMDD", though with any leading 0s trimmed. So the above sentence is equivalent to writing:

Version 6.0.40426 of the Ducking Action by Beatrix Potter begins here.

Extensions are usually intended to be shared and passed around between Inform users, and good use of version numbering can be a huge help to those users; and it's helpful if we can agree as a community on what good version-numbering is. Because of that, the Inform project tries to use a widely-recognised Internet standard called "semantic version numbering".

For full details see semver.org, but for Inform purposes the following fairly simple rules should be enough. "Semantic" just means that version number changes should communicate something meaningful. So, whenever an extension author puts out a new version of an extension, the extension number should change in a way that signals how drastic the change will be.

In this system, the three possible numbers X.Y.Z are called the "major", "minor" and "patch" numbers. Every time an extension is changed and re-released, even just informally among friends but certainly if posted somewhere on the Internet, X, Y or Z should change. The rules are:

(X) If the extension has changed so much that Inform projects using it will need to be changed in order to keep on working - for example, if a "To…" phrase has been taken out, or the name of a kind changed - then X should be increased. Y and Z then usually go back to 0. This is a "major version".

(Y) If the extension provides new features but doesn't do anything to change the way its existing features are used, then X can stay the same but Y should increase, and Z then usually rolls around to 0. This is a "minor version".

(Z) If the extension has changed only to fix bugs, or make its existing features work more efficiently, or provide better documentation or examples, then X and Y can stay the same but Z should increase. This is a "patch version".

So, for example, a user who currently has version 3.2.7 can update to 3.2.8 without really investigating. That same user can update to 3.3, 3.4, … without any trouble, choosing either to use or ignore whatever new features they are presenting. But the user knows that moving up to version 4 might well require some work - a project using version 3.Y.Z will likely need writing to adopt version 4.

Now let's turn to "Include" sentences. A request like:

Include the Ducking Action by Beatrix Potter.

will be happy with any version of the extension at all, whether numbered or not; but

Include version 2.4 of the Ducking Action by Beatrix Potter.

will only accept the extension if its version number is "compatible" with 2.4, which means, if it is 2.4 or later, but still belongs to the same major version, "2". So if we write this inclusion sentence, but the version we have installed is version 3.1, Inform will give a problem message. The fix may well be as simple as changing the inclusion sentence to match - but it may not, because a change in major version number is a signal that things have changed a lot inside the extension (see above).

During play of any story compiled by Inform 7, typing VERSION lists various serial numbers of the pieces of software used to make it. The list concludes with names, authors and version numbers of any extensions used. So every author whose work contributes to a story automatically gets a modest credit within it. The same list can be printed, at the discretion of the designer, using the textual substitution:

say "[the/-- list of extension credits]"

This text substitution expands to one or more lines of text crediting each of the extensions used by the current source text, along with their version numbers and authors. Extensions whose authors have chosen the "use authorial modesty" option are missed out.

If we want our extension to go uncredited - perhaps if it is a low-level enabling sort of thing, for instance - we can place the following sentence inside the definition of the extension:

Use authorial modesty.

The same sentence placed in the body of a source text causes all extensions by the same author as the main source text to go uncredited. In other words, if Isaac Miggins writes a source text and includes, say, Unlikely Events by Isaac Miggins, then this extension will go uncredited in the VERSION command.

A complete list, undiluted by modesty, can always be obtained using:

say "[the/-- complete list of extension credits]"

This text substitution expands to one or more lines of text crediting each of the extensions used by the current source text, along with their version numbers and authors. Every extension is included, even those whose authors have opted for "use authorial modesty".

WI §27.7 Extensions and story file formats

Inform compiles to several different story file formats, and in each case uses only a small part of their abilities - especially when it comes to fancy tricks with the keyboard or screen. So people may well want to write extensions which provide access to some of these tricks (like "Basic Screen Effects", included in the standard Inform distribution, but more so). Unfortunately, these tricks are very likely to fail to compile - or fail to work - on some of the possible story file formats, so the resulting extension would probably go wrong (and mysteriously wrong) for users who have chosen a different format.

Inform therefore provides a way for extensions to declare the formats they are compatible with. All that is required is to add a proviso in brackets after the title is declared:

Version 2 of Basic Screen Effects (for Z-Machine version 8 only) by Emily Short begins here.

Other examples might be "(for Glulx only)", or "(for Z-machine only)". If no such proviso is given, the extension is assumed to be compatible with every story file format.

Extensions are also able to include material which is only used on some story file formats and not others - in principle, this might allow the same facilities to be provided to the author whatever story file format is used, but to achieve these effects differently depending on the current Settings. The convention here is exactly like "not for release": if a heading or subheading in the source text contains a bracketed proviso, then the material under that heading (and under its dependent subheadings) will be ignored if the current story file format does not match. For example:

Section 2.3G (for Glulx only)
To reveal the explosion:
   [...the Glulx way...]
Section 2.3Z (for Z-machine only)
To reveal the explosion:
   [...the Z-machine way...]

would ensure that "reveal the explosion" works nicely whichever story file format is used.

Example

449.
Tilt 3 ★★
Displaying the card suits from our deck of cards with red and black colored unicode symbols.

WI §27.8 Extensions can include other extensions

Extensions can themselves contain "Include…" sentences asking for other extensions to be included. An extension might, for example, start like this:

Version 1 of Basic Help Menu by Emily Short begins here.
Include Menus by Emily Short.
...

A project which asks to include "Basic Help Menu" will then also include "Menus", even though the author might never even realise that. Indeed, the author could also have asked to include "Menus", not realising that "Basic Help Menu" was going to ask for the same thing.

So the same extension is often requested multiple times. This is fine if the version numbers in the requests are compatible, but they might not be. For instance, suppose the main source text asks to include version 2 of extension X, and also to include extension Y. Suppose further that Y contains a request to include version 4 of X. We now have two different requests for X, and they contradict each other - the major version of X cannot be both 2 and 4 at the same time. So Inform will produce a problem message in this case.

But in cases where it is possible for everyone to be satisfied, Inform will try to find a solution. If one extension asks for version 2.3 of X, and another asks just for X, and a third asks for version 2.7.2 of X, then Inform will work out that any version number in the range 2.7.2 up to (but not including) 3 will be fine. If it can in fact find such an extension, it will then use it. So if the user has version 2.8.17 installed, everything is fine.

If an extension does include other extensions, it is good style to place the "Include…" sentence(s) as early as possible after the introductory sentence, just so that human readers looking at the text of the extension can see these dependencies easily.

WI §27.9 Extensions can interact with other extensions

When one extension is being used, it's probably only one among several. A really general-purpose extension might want to behave differently depending on which other extensions are also present. This can be achieved using headings which are "for use with" (or "without") other extensions. For instance:

Chapter 2a (for use with Locksmith by Emily Short)

specifies that everything under this heading (and its subheadings, if any) will be ignored unless the extension Locksmith by Emily Short is included. Conversely,

Chapter 2b (for use without Locksmith by Emily Short)

will be ignored unless it isn't included. This allows an extension to give two variations on the same material - one if Locksmith is present, the other if not.

Headings can also replace portions of extensions which have been included. For instance:

Section 6 - Hacked locking (in place of Section 1 - Regular locking in Locksmith by Emily Short)

places the source text under the new heading in the place of the old (which is thrown away). If there should be two or more headings of the same name in the given extension, the first is the one replaced; if two or more headings attempt to replace the same heading in the given extension, the final attempt in source text order is the one which succeeds; and finally, heading dependencies like the above are scanned in a top-down way. Thus, if we have:

Chapter 2a (for use with Locksmith by Emily Short)
...
Section 1 - Hacked marbles (in place of Section 4 in Marbles by Peter Wong)
...

and we don't include Locksmith, then the replacement of Section 4 of Marbles is not made, because Section 1 - Hacked marbles is subordinate to the Chapter 2a heading which we've told Inform to ignore.

If the name of the heading to replace contains the word "in", it's a good idea to use quotation marks for clarity:

Section - Hacked questions (in place of "Section 4 - Phrase used to ask questions in closed mode" in Questions by Michael Callaghan)

WI §27.10 Extensions in the Index

As soon as a project has successfully been translated, its Index is brought up to date: pages of the index record all the kinds and what they are for, all the phrases which can be used, and so on. Any kind or phrase created in an extension is automatically included. The extension's presence in the project is itself recorded - the Contents index for any project contains a brief list of all extensions used in that project, along with their authors and version numbers.

The Kinds index aims to give the reader a brief note of what each kind is intended for. We can provide for this by writing a sentence like so:

The specification of player's holdall is "Represents a container which the player can carry around as a sort of rucksack, into which spare items are automatically stowed away."

There is no need to specify the properties which apply: that is all done automatically. "Specification" is a sort of pseudo-property used just for this: we can also give specifications to kinds of value and to actions, and these are similarly used in the Index pages.

Every extension has the right to its own set of headings and subheadings, independently of those used by the main source for the work or by any other extension which may be included. (So if the extension is divided into four sections and finishes on Section D, say, that doesn't mean that Section D will continue outside the extension as the main source of the story runs on.)

Extensions should, of course, be written so that they never produce Problem messages, so at first sight it appears that these headings will never be outwardly visible. In fact, though, Problems do occasionally turn up in extensions, usually when the user has made a mistake, or when two inconsistent extensions are used in the same project. But more importantly, the headings in an extension are used when indexing phrases (and also actions) to group similar phrases together. For instance, the Standard Rules contain the heading:

Section SR4/7 - Searching and sorting tables

The half-dozen phrases defined in this section of the Standard Rules are then indexed under the subheading "Searching and sorting tables": Inform looks for a hyphen in the heading and then uses any text which follows the hyphen. (If there is no hyphen, the entire heading text is used.)

If an extension contains no headings, its phrases (or actions) are indexed simply as "Miscellaneous".

Finally, any phrase or variable defined immediately under a heading whose name ends in the word "unindexed" will be omitted from the Phrasebook or Contents index respectively. (That won't apply to definitions under subheadings of the heading.) This is intended so that technical apparatus used only inside the extensions can be concealed from the outside user's immediate view. Inform as it is presently constituted does not allow extensions to make fully private definitions, but this feature at least allows them to make unadvertised ones.

WI §27.11 Extension documentation

A basic mechanism for documenting extensions is built into Inform. For many extensions, this will probably do instead of a manual; for more complex ones, it should still prove a useful supplement to one.

As described in Chapter 2 above, whenever an extension is installed, its documentation is made available to the user. Such text should be written concisely, while giving examples wherever appropriate. Stylistically, it should ideally follow the model of the main Inform documentation: just as an extension expands the standard rules, so its documentation expands this manual. "We need…" is preferred to "You need…", and so on: we're all in this together.

In order to be recognised as documentation, this text should appear at the foot of the extension file, after the compulsory end sentence. The first paragraph must have exactly the following form, with a skipped line before and after:

---- DOCUMENTATION ----

For instance, the "Ducking Action" example might end:

...
The Ducking Action ends here.
---- DOCUMENTATION ----
This is a modest extension, with much to be modest about. It allows us to use a new action for ducking, as in ducking the player's head (not as in ducking a witch). Ducking will do nothing unless rules are added:
   Instead of ducking in the Shooting Gallery, say "Too late!"
...

We obtain indented code examples by beginning a line with a tab. A double indentation can be got with two tabs in a row, and so forth. (Beware: some text editors, or emailers, flatten tabs into a row of four or perhaps eight spaces each. Inform will not recognise such a line of spaces as a tab.)

Note that text in square brackets should be avoided in the documentation, because that's taken as being comment matter on the extension, and omitted.

Tables should be similarly indented, and should begin with the word "Table …": the top line is taken to be the name of the table, and subsequent lines are tab-divided columns. Inform will automatically group this into a table, like so:

Table of Exemplariness
stellar objectexample
galaxy"Andromeda Galaxy M31"
star"Sirius"
planet"Neptune"
moon"Enceladus"
dwarf planet"Ceres"
plutino"38628 Huya"
cubewano"Easterbunny"

(Footnote: Since the first appearance of this book, Easterbunny has been renamed Makemake, the creator god in the mythology of the people of Easter Island.)

WI §27.12 Examples and headings in extension documentation

Extensions with very large amounts of documentation can, if the author chooses, divide the material up using headings and/or subheadings. These must be written as paragraphs exactly like so:

Chapter: Avoiding Events
Section: Ducking examinations and tests

Inform will then typeset them to stand out, will number them automatically, and will add a table of contents at the top of the page. (For most extensions, the documentation will be short and sweet, and this would just be clutter: headings and subheadings are best used only where the text would otherwise be difficult to read.)

Any extension's documentation can contain Examples, just as the main Inform documentation does: these are automatically labelled A, B, C, … rather than given numbers, to ensure that they do not clash with the numbering used in the built-in chapters. (The labels may be helpful in writing an extension's documentation: we can write, for instance, a note such as "see Example C below".)

Examples must be given last in the documentation, and there can be up to 26 of them, though most extensions will need one example at the most, and some will have none at all. Each example must begin with a paragraph exactly like so:

Example: ** We Must Perform a Quirkafleeg - Ducking to avoid arrows as one proceeds east across battlements.

Again, there must be a skipped line before and after. The row of asterisks must be *, **, *** or ****, just as in the main documentation, which we should follow on all points of style. The rest of the line contains the title, a hyphen, and then the description. The title should be given with Each Word except Prepositions and Similar Things Capitalized, while the description should look like a sentence, and end with a full stop.

The text of the example follows, of course, and continues until the end of the file, or the next "Example:" line, whichever comes first.

Each example should (normally) contain one single, complete, story, long enough to demonstrate the use of the extension and to have a little flavour to it, but not so long that the reader gets lost. It should have a title, which should match the name of the example (in the case above, "We Must Perform a Quirkafleeg"). It should conclude with a paragraph defining a test:

Test me with "east / duck / east / jump / east / duck / east / rescue esmerelda".

The idea is that typing one single command, TEST ME, into the resulting story should show off what the extension does.

When an extension contains more than one example, they should be given in order of asterisk rating, that is, starting with the * examples, then the ** examples, and so on up.

Extension documentation can provide "paste" buttons, much like the examples in this book. For example:

Here is a sample -
   *: "Coriander"
   Include Herbs by Charlotte Quirke.
   The Herb Marketing Centre is a room.
If we want to add some content -
   The coriander is a herb. Understand "cilantro" as the coriander.

Note that the paste button, denoted "*:", pastes in the text following it, but only as far as the next paragraph of unindented documentation - here, the one beginning "If we…". (But of course, an extension can have multiple paste buttons if desired.)

WI §27.13 Implications

Extensions often need to define new kinds or properties, which we want to make as helpful as possible for the user. In particular, we want them not to require additional work for the author just to obtain the effect which seems only natural.

For example, consider Inform's built-in "locked" property. If a door is locked, then it cannot be opened, which seems fair enough. But if the player tries to unlock the door, he might then find the following response:

That doesn't seem to be something you can unlock.

Which does not seem right. In real life, almost all locked items have outwardly exposed locks which it is perfectly sensible to try to unlock, given a key. The problem is that our door has the "locked" property, but not the "lockable" one.

The Standard Rules solve this problem by including the following line:

Something locked is usually lockable.

This ensures that any door said by the author only to be "locked" will be "lockable" as well, and adds a small but worthwhile touch of realism.

Such a sentence is called an "implication", as it is in the form "Condition A implies Condition B". Note that the two conditions must consist of either/or properties with or without kinds attached. Thus:

A room in the Open Desert is usually lighted.

will not work because "a room in the Open Desert" is a more complicated grammatical construction than, say, "lighted" or "a lighted room": it contains a relative clause. Inform can only deal with simple implications.

Inform never overrides certainties with mere implications, and is cautious about allowing them to build overly long chains of argument. This is to prevent the following kind of difficulty:

An open door is usually closed. A closed door is usually open.

Implications work just the same for values which aren't objects, so:

Colour is a kind of value. The colours are red, green and blue.
A colour can be zesty or flat. A colour can be bright or dull.
Red and blue are bright. Blue is flat.
A bright colour is usually zesty.

results in red being zesty, but blue and green being flat; blue because the source text explicitly says so (which trumps the "usually"), and green because this isn't a bright colour, so the implication doesn't arise.

Implications have not been mentioned up to now since they are only really needed by extensions, but also because they can be tricky, with unforeseen consequences. We should handle them with care.

WI §27.14 Using Inform 6 within Inform 7

The current Inform, "Inform 7", had a low-level precursor unsurprisingly called Inform, which ran through versions 1 to 6. What made Inform 6 low-level was that its style of coding was much more like traditional programming: it reads as a simple form of C, or an elaborate form of assembly-language, but with some interactive fiction tweaks.

That language is still used inside today's Inform project as a way to express very low-level operations. What happens to code like that is now very different (it is compiled into Inter, an intermediate-level representation used inside Inform, and no longer by the Inform 6 compiler). But the notation is the same, and the practical effect is that it is as if we are writing i6 code.

The final sections of this chapter show how such i6 code can be mixed directly in with natural-language source text. The remaining pages will therefore make little or no sense to those who do not already know i6 notation, and in any case, such programming is really a last resort - it is always best to write regular source text than to resort to so-called "inclusions" of i6. Ideally, all i6 content would be confined to extensions (and this may be mandated in future releases of Inform), and even writers of extensions are asked to pare down their usage of i6 to the minimum necessary.

The methods for incorporating i6 code into i7 have been designed with this in mind, that is, to encourage people to use i6 in as self-contained a way as possible: in particular to isolate the relatively few functions which need to be written in i6, and to give them natural language expression.

Finally, anyone hacking with i7 for a while is likely to become curious about the Basic Inform or Standard Rules extensions, and to look at the text which sets up the Inform language and world model. These extensions are, of course, no secret, but can be misleading to read. For one thing, they appear to have great freedom to set up the world model as it pleases, but in fact the i7 compiler may well crash unless certain things are done just so in the Standard Rules: they depend on each other.

Moreover, the Basic Inform and Standard Rules extensions use a number of syntaxes which are not documented in this chapter: these are constantly being altered, and it would not be safe to imitate them. Any i6-related syntax which is not documented in this chapter may be removed or changed in effect at any time without warning, for instance in an update of Inform to fix bugs.

WI §27.15 Defining phrases in Inform 6

The phrases described in this documentation, such as "end the story", are all defined in the Standard Rules, and are for the most part defined not in terms of other i7 phrases but instead reduced to equivalents in i6. For instance:

To end the story: (- deadflag=3; story_complete=false; -).

The notation "(-" and "-)" indicates that what comes in between is i6 code. The minus sign is supposed to be a mnemonic for the decrease from 7 to 6: later we shall use "(+" and "+)" to go back up the other way, from 6 to 7.

When a phrase is defined as containing only a single command, and that command is defined using i6 - as here - it is compiled in-line. This means that the phrase "end the story" will always be translated as "deadflag=3; story_complete=false;", rather than being translated into a call to a suitable function whose only statement is "deadflag=3; story_complete=false;".

This is an easy case since the wording never varies. More typical examples would be:

To say (something - number): (- print {something}; -).
To sort (T - table name) in (TC - table column) order:
   (- TableSort({T}, {TC}, 1); -).

When the braced name of one of the variables in the phrase preamble appears, this is compiled to the corresponding i6 expression at the relevant position in the i6 code. So, for instance,

say the capacity of the basket

might be compiled to

print O17_basket.capacity;

because "{something}" is expanded to "capacity of the basket" (i7 code) and then translated to "O17_basket.capacity" (i6 code), which is then spliced into the original i6 definition "print {something};".

Braces "{" are of course significant in i6. A real brace can be obtained by making the character following it a space, and then i7 will not attempt to read it as a request for substitution.

It's also possible for the pair of characters "-)" to occur in i6 code, for example here:

for (i=3 : i>0 : i--)

and i7 will read the "-)" as terminating the i6; we can get around this with an extra space:

for (i=3 : i>0 : i-- )

Warning: Inform 6 uses a restricted character set, allowing use of most of the accented characters in ISO Latin-1 (those found in a set called ZSCII) but little beyond that. It's therefore hazardous to use any exotic Unicode characters in an inclusion.

Example

450.
Pink or Blue ★★★
Asking the player to select a gender to begin play.

WI §27.16 Phrases to decide in Inform 6

There are basically three forms of phrase in i7: phrases which do something, but produce no value or opinion as a result; phrases to decide whether or not something is true; and phrases to decide on a value. We have already seen examples of writing the first form in i6:

To say (something - number): (- print {something}; -).

Here the i6 form is required to be i6 routine code in void context, that is, it will normally be one or more statements each of which ends in a semicolon (unless there are braced code blocks present). In this case, we have just one i6 statement, ending in a semicolon.

An example of a phrase to decide whether something is true would be:

To decide whether in darkness: (- (location==thedark) -).

Here the i6 code providing the definition must be a valid i6 condition, and be in round brackets, but there is no semicolon.

Lastly, an example of a phrase to decide on a value:

To decide which number is the hours part of (t - time): (- ({t}/60) -).

Again, this is a value in i6 as well: no semicolon. It is probably safest to place the value in round brackets.

WI §27.17 Handling phrase options

The Standard Rules use the Inform list-writer with the following definition, which shows how a much more complicated i6 routine can be given a natural-language expression.

To list the contents of (O - an object),
   with newlines,
   indented,
   giving inventory information,
   as a sentence,
   including contents,
   including all contents,
   tersely,
   giving brief inventory information,
   using the definite article,
   listing marked items only,
   prefacing with is/are,
   not listing concealed items,
   suppressing all articles
   and/or with extra indentation:
   (- I7WriteListFrom(child({O}), {phrase options}); -).

This can be used by, say:

list the contents of O, as a sentence, using the definite article

"{phrase options}" is a special substitution: it is a bitmap which assigns the given options one bit each, starting with the least significant bit for the first-mentioned option ("with newlines" above) and going up to the most significant bit for the last ("with extra indentation").

WI §27.18 Making and testing use options

Use options (see Chapter 2 above) manifest themselves in the i6 code generated by i7 as constants which are either defined, or not. For instance, the "use American dialect" option results in the constant DIALECT_US being defined, a constant which otherwise would not be. Some use options define the constant as a particular value, others simply define it (so that i6 gives this constant the value 0).

New use options can be created as in the following examples, which are found in the Standard Rules:

Use American dialect translates as (- Constant DIALECT_US; -).
Use full-length room descriptions translates as (- Constant I7_LOOKMODE = 2; -).

Most Inform users will not need to test whether a use option is currently set: after all, they will know whether or not their own story uses American dialect. But an extension does not know what use options apply in the story which is using it. An extension which needs to print a list, using its own formatting, might want to know whether "use serial comma" is set. Or it might want to speak differently in American dialect.

To test for American dialect, we should ideally not use i6 to look for the constant DIALECT_US using #ifdef: there is no guarantee that this constant will not be renamed at some point. Instead we can perform the test directly in i7:

if the American dialect option is active, ...

and similarly for all other named use options. The adjectives "active" and "inactive" have the obvious meanings for use options. This means it's possible to describe the current options like so:

say "We're currently using: [list of active use options].";

The result might be, say,

We're currently using: dynamic memory allocation option [8192], maximum text length option [1024], maximum things understood at once option [100], American dialect option and fast route-finding option.

This may be useful for testing purposes.

Use options can also allow the writer to raise certain maximum values. If we write an extension which needs some i6 array, say, and therefore has some limitation - for instance a footnotes presenter which can handle at most 100 footnotes before its array space runs out - it would obviously be cleaner to allow this maximum to be raised. We can set this up like so:

Use maximum presented footnotes of at least 100 translates as (- Constant MAX_PRESENTED_FOOTNOTES = {N}; -).

With such a definition, the number given is the default value, and the i6 source is included whether or not anybody uses the option: the default value being given if nobody does. The text "{N}" is replaced with the value. So the above definition normally results in this being defined:

Constant MAX_PRESENTED_FOOTNOTES = 100;

but if the user writes

Use maximum presented footnotes of at least 350.

then instead the i6 inclusion becomes:

Constant MAX_PRESENTED_FOOTNOTES = 350;

The i6 constant MAX_PRESENTED_FOOTNOTES can then be used as the size of an array, for instance.

Finally, note that it is legal to define the same use option more than once, but only if it has exactly the same meaning each time it is defined. (This is allowed so that multiple extensions all needing the same definition can safely make it, and still be used together.)

WI §27.19 Longer extracts of Inform 6 code

Whole routines, object and class definitions (or any other directives) can be pasted in wholesale using sentences like so:

Include (-
[ ExtraFunction a b; return a*b; ];
-).

Such inclusions are pasted into the final compiled code at the end of the file, after the i6 grammar has been declared.

In such extracts, we sometimes need to refer to objects, variables or values which can't be described using i6: or rather, which can be described, but we don't know how. To this end, any text in an inclusion written in "(+" and "+)" parentheses is treated as an i7 value, and compiled accordingly, with all type-checking waived for the occasion. For instance:

Include (-
Global my_global = (+ the tartan rucksack +);
-).

Here "the tartan rucksack" is translated into "O18_tartan_rucksack", or something similar: the i6 object created to represent the rucksack. Thus the actual line of code produced is

Global my_global = O18_tartan_rucksack;

The material between "(+" and "+)" is generally treated as a value, and thus compiles to the i6 form of that value. But it could also be a property name, which compiles to the i6 form in question, or a defined adjective, which compiles to the name of the routine to call which tests whether that adjective is true.

Three warnings. The material in "(-" and "-)" is not quite treated as literal. Certain characters cause Inform to react:

1. Beware of accidental "(+" usage - for instance,

Include (-
[ MyCleverLoop i; for (++i; i<10; i++) print i; ];
-).

looks reasonable, but contains "(+" and "+)". Spaces around the first "++" would have been enough to avoid this one; "+)" is only significant where it follows a "(+".

2. Beware of placing an "@" character in the first column, that is, immediately following a new line. (In template code this marks off paragraph divisions.) So for instance,

Include (-
[ Set_Stream ret;
@glk 67 ret;
];
-).

is tripped up by the Glulx assembly language opcode "@glk" because this occurs in column 1. Indenting it with a little space or a tab is enough to avoid the problem.

3. Be careful if you're creating an i6 variable holding initialised i7 text. For example,

Include (-
Global saved_optional_prompt = (+ "!!>" +);
-).

looks as if it will work, but doesn't, for reference-counting reasons we needn't go into; instead you need

Include (-
Array sop_storage --> PACKED_TEXT_STORAGE "!!>";
Global saved_optional_prompt = sop_storage;
-).

But it's far better to avoid initialising text variables from i6 entirely. The same problems arise with constant lists.

It should also be noted that the i6 syntax recognised inside "Include (- … -)" is slightly restricted compared to the full range recognised by the stand-alone Inform 6 compiler. In particular:

1. Only new-style "for" loops with colons in the header are allowed, so that "for (i=0: i<10: i++)" is okay but "for (i=0; i<10; i++)" is not. Moreover, "for" loops cannot contain empty clauses.

2. Local variable names are not allowed to be the same as an i6 statement keyword: for example, "style" and "spaces" are not allowed.

3. The (undocumented) Inform 6 function "indirect()" is not supported. But since "indirect(A)" is equivalent to "A()", which does work, this is no real loss. Similarly, the "glk()" function is not supported: function calls to BasicInformKit should be used instead.

4. Conditional compilation cannot be placed around cases in a "switch" statement.

5. Compile-time constant expression evaluation can be used with arithmetic operations, so "Constant FOO = BAR + 1;" is okay, but not with bitwise or logical operations, so "Constant FOO = (BAR | 1);" does not work.

6. Calculated values cannot occur as assembly-language operands.

7. Calculated values can be used for array extents, but need to be put in brackets. For example:

Include (-
Array unit_captured_text --> (UNIT_CAPTURE_BUFFER_LEN + 1);
-).

Example

A status line which has only the name of the location, centered.

WI §27.20 Primitive Inform 6 declarations of rules

By writing a sentence like this:

The underground rule translates into i6 as "UNDERGROUND_R".

we create a new rule, the "underground rule", and also notify Inform that it will have no definition as i7 source text: instead, it will be provided as an i6 routine called "UNDERGROUND_R". We can define this with an Include like so:

Include (-
[ UNDERGROUND_R;
   if (real_location hasnt light) { RulebookSucceeds(); rtrue; }
   rfalse;
];
-).

The rule should return false if it wants to make no decision, but call either RulebookSucceeds or RulebookFails and return true if it does. These routines can optionally take an argument: which will be the return value from the rulebook.

Note that UNDERGROUND_R itself has no arguments. In the case of an action based rulebook, the i6 variables noun, second and actor can be referred to, while for a value based rulebook the parameter is stored in the i6 global variable parameter_object (which is not necessarily an object, in spite of the name).

We can put this rule into a rulebook in the same way that any named rule can be:

The underground rule is listed in the spot danger rules.

WI §27.21 Inform 6 objects and classes

As might be expected, i7 compiles an i6 class for each kind, and an i6 object for each of its own objects. We can meddle with its compilation process here using a further refinement of Include. For instance, suppose we want the i6 class definition for things to come out containing a property like this:

Class K2_thing ...
   with marmalade_jar_size 6,
   ...

How to arrange this? One way is to create an ordinary i7 property, like so:

A thing has a number called marmalade jar size. The marmalade jar size of a thing is usually 6. The marmalade jar size property translates into i6 as "marmalade_jar_size".

(Without that last sentence, the property won't get any familiar name.) But sometimes we need more, and want to actually write new material to go into the definition. This can be done like so:

Include (- with before [; Go: return 1; ], -) when defining a vehicle.

This glues in a new property to the class compiled to represent the i7 kind "vehicle". (See the DM4 for why. However, since the entire actions machinery is different in the i7 world, note that "after", "react_before" and "react_after" no longer have any effect, and nor does "before" for rooms.)

And similarly:

Include (- has my_funny_attribute, -) when defining the hot air balloon.

If we need a particular i7 object or kind to end up with a particular i6 name, we can write:

The whatsit object translates into i6 as "whatsit".
The thingummy kind translates into i6 as "thingummy_class".

WARNING: The "Include (- … -) when defining …" usage still works for the moment (except in projects compiled to C at the command line, where it may fail), but it is deprecated and likely to be removed in later versions of Inform. Avoid it if at all possible.

WI §27.22 Inform 6 variables, properties, actions, and attributes

i7's variables are usually compiled as entries in an array rather than as i6 variables. However, we can instead tell Inform to use an existing i6 variable (either one that we declare ourselves, or one in the i6 template layer). For example:

Room description style is a kind of value. The room description styles are Brief, Verbose and Superbrief.
The current room description style is a room description style that varies.
The current room description style variable translates into i6 as "lookmode".

This is a feature provided to help i7 source text to use variables internal to the i6 template code. It can, if really necessary, also be used to give i7 names to entirely new i6-level variables, created like so:

Include (- Global my_variable = 0; -).

This style of hybrid coding is really not encouraged.

i7's properties are compiled sometimes as i6 properties, sometimes as i6 attributes, sometimes as bits in a bitmap somewhere. However, we can override i7 by telling it that one of its property names is equivalent to an already-existing i6 property or attribute: if so then i7 will use that name and will not compile any directive to create it. For example:

The switched on property translates into i6 as "on".
The initial appearance property translates into i6 as "initial".

We do not need to translate "switched off", the opposite to "switched on": i7 will now compile this to "~on".

Lastly, actions can also be translated (though it's usually better to translate their rules instead and invent new i7 actions covering them):

The unlocking it with action translates into i6 as "Unlock".

WI §27.23 Inform 6 Understand tokens

The parser which deciphers the player's typed commands is written in i6, and many of the basic tokens of Understand grammar are implemented as "general parsing routines" (GPRs), the specification of which is described fully in the Inform 6 Designer's Manual. i7 translates much of the source text's Understand grammar into GPRs, and once again we can bypass this process and supply an Understand token directly as an i6 GPR. For example:

The Understand token squiggle translates into i6 as "SQUIGGLE_TOKEN".

We then have to include a routine of that name into i7's output using the "Include" instruction, on which more later.

This creates a token "[squiggle]"; so for instance if the source text contains:

Understand "copy [squiggle]" as ...

then Inform would parse the command COPY FIGURE EIGHT by calling the SQUIGGLE_TOKEN routine as a GPR with the word marker at 2, that is, at the word FIGURE.

As always, this should be done only where there seems no better way, or where speed is very important. For any fairly simple range of possibilities, it's better to use the techniques in the Understand chapter, or to use unit specifications.

WI §27.24 Inform 6 adjectives

There are three ways to specify that an adjective is defined at the i6 level. For example:

Definition: a number is prime rather than composite if i6 routine
   "PRIMALITY_TEST" says so (it is greater than 1 and is divisible only by itself and 1).

Inform now actually tests if a number N is prime by calling PRIMALITY_TEST(N), and it assumes that we have also included such a routine in the output. The routine is expected to return true or false accordingly.

The text in brackets does nothing functional, but is the text used in the Lexicon dictionary part of the Phrasebook index for the user's benefit; it should be a brief definition. Extension authors are asked to provide these little definitions, so that their users won't be confused by blank lexicon entries.

The second way makes a more capable adjective, since it can not only be tested, but also made true or false using "now". For example:

Definition: a scene is crucial if i6 routine "SceneCrucial" makes it so
   (it is essential to winning).

The difference here is "makes it so", not "says so", and as this implies, the routine has more power. "SceneCrucial" is called with two arguments: SceneCrucial(S, -1) tests whether the scene is crucial or not and returns true or false; SceneCrucial(S, true) must make it true; and SceneCrucial(S, false) must make it false. Another useful difference is that if the kind of value is one which is stored in block form (e.g. for an adjective applying to text), the routine is given a pointer to the block, not a fresh copy.

A third way to define an adjective, which should be used only if speed is exceptionally important, is to provide a "schema" - a sort of i6 macro, like those provided by the C preprocessor. For example:

Definition: a rulebook is exciting if i6 condition
   "excitement_array-->(*1)==1" says so (it is really wild).

The escape "*1" is expanded to the value on which the adjective is being tested. (This is usually faster than calling a routine, but in case of side-effects, the "*1" should occur only once in the condition, just as with a C macro.) To repeat: if in doubt, use the i6 routine method above.

WI §27.26 Overriding definitions in kits

When Go is clicked, Inform translates the i7 source text into a large body of so-called "Inter" code: "Inter" is short for "intermediate". Large as this program is, it could not survive on its own: it needs a large body of pre-compiled code, also written in Inter, to sustain it. This additional material is organised in blocks called "kits". Most Inform users never need to know about kits, but for example, a typical Inform project includes kits called BasicInformKit, WorldModelKit and CommandParserKit.

These kits are compiled from what is (nearly) Inform 6-syntax source code, and for the details of that, see the documentation on the low-level tool "inter". While it's absolutely possible for Inform users to create and use their own kits, that's beyond the scope of this book. But what we will cover here is the ability to include just a little extra Inter code - perhaps only a few functions or constants.

In fact, we have seen the necessary syntax already:

Include (- ... -).

puts the given material "…" into the project. For example:

Include (-
   [ ExtraFunction a b; return a*b; ];
-).

adds just a single function called "ExtraFunction".

And this works fine, but if we tried the same trick to create a function called "SquareRoot", for example, then the result would be a problem message - because BasicInformKit also defines a function of the same name. This problem message is useful, because it warns us about accidental name clashes.

But what if the name clash was not an accident at all, and what we actually wanted to give our own definition of "SquareRoot", to be used instead of the one in BasicInformKit? This is also possible:

Include (-
[ SquareRoot num;
   "Nobody cares about square roots, son.";
];
-) replacing "SquareRoot".

And now whenever square roots are calculated, this snarky text will be printed, and the result will always be rather meaningless (since this i6 routine always returns 1). Unless one is very careful, the result of replacing kit definitions can be absolute chaos.

An important historical note: between about 2010 and 2021, kits did not exist, and instead there were "template files" of Inform 6 code which served roughly then same purpose. These had names like "Relations.i6t" or "Mathematics.i6t" and were internally divided into named subsections; and Inform supported syntax like the following:

Include (- ... -) before "Relations.i6t".
Include (- ... -) instead of "Relations.i6t".
Include (- ... -) after "Symmetric One To One Relations" in "Relations.i6t".

to allow new material to be placed at oddball positions in the final code. There is now no need to worry about the placement of code - Inform's final code generator manages things so that code-ordering issues do not arise; as a result, the "before" and "after" options are now unnecessary. For now, Inform ignores these usages, and just disregards the "before…" or "after…" parts. But in some later version of Inform they will begin to cause problem messages, so writers of extensions using these syntaxes should now please remove them.

The "instead of" option now cannot work at all, and throws a problem message. The new way to substitute a fresh definition of something built-in is to use the "replacing" notation described above.

With the demise of the "template layer", as it was called, another form of so-called "template hacking" has gone with it - the special notation:

Include (- {-segment:MyStuff.i6t} -).

to allow a whole extra file of Inform 6 code called "MyStuff.i6t" to be pasted in. The new way to do that is to create a new kit, say MyStuffKit, to hold the material in question. This is not hard to do, but beyond the scope of this book. See the documentation on the low-level Inform tool "inter".

WI §27.27 Translating the language of play

The "language of play" is the natural language used to communicate with the player at run-time: this is normally English.

That means that it is difficult to write, say, Spanish-language IF using Inform 7, though heroic work by the Spanish IF community has overcome this. Inform 6 provided for translation by isolating its linguistic code in a part of the i6 library called the "language definition file", which was normally "English.h". Translations were gradually made to most major European languages, resulting in alternative language definition files called "French.h", "Italian.h" and so on. Full details on how to write a language definition file were given in the Translations chapter of the DM4, that is, the fourth edition of the Inform 6 Designer's Manual.

In i7 the system is different. We use the template, not a library. Instead of providing a language definition file such as "French.h", a translator should create an extension called something like "French Language by Jacques Mensonge". (The language should be named in English, so "French Language by …", not "Langue français by …") This extension should then contain broadly the same material as an i6 language definition file, but written in a mostly higher-level way. See the extension "English Language by Graham Nelson" supplied with i7, which is included automatically by default.

WI §27.28 Segmented substitutions

A "segmented" substitution is a syntax where text is placed between two or more different text substitutions. Examples include:

"This hotel is [if the player is female]just awful[otherwise]basic[end if]."
"Annie [one of]dances[or]sulks[or]hangs out at Remo's[at random]."

To create such syntaxes, it is not enough just to define how each expands into i6 code: for one thing we may need to know about the later terms in order to expand the earlier ones, which is normally impossible, and for another thing, the individual text substitutions mean nothing in isolation. For instance, Inform produces a problem if the following is tried:

"The hotel [at random] is on fire."

because "[at random]" is only legal when closing a "[one of] …" construction. But if "[at random]" had been defined as just another text substitution, Inform would not have been able to detect such problems.

Inform therefore allows us to mark text substitutions as being any of three special kinds: beginning, in the middle of, or ending a segmented substitution. There can be any number of alternative forms for each of these three variants. The syntax policed is that

(a) Any usage must lie entirely within a single say or piece of text.
(b) It must begin with exactly one of the substitutions marked as "beginning".
(c) It can contain any number, including none, of the substitutions marked as "continuing" (if there are any).
(d) It must end with exactly one of the substitutions marked as "ending".

A simple example:

To say emphasis on -- beginning say_emphasis_on: (- style underline; -).
To say emphasis off -- ending say_emphasis_on: (- style roman; -).

This creates "[emphasis on]" and "[emphasis off]" such that they can only be used as a pair. The keyword "say_emphasis_on", which must be a valid i6 identifier (and hence a single word), is never seen by the user: it is simply an ID token so that Inform can identify the construction to which these belong. (We recommend that anybody creating such constructions should choose an ID token which consists of the construction's name but with underscores in place of spaces: this means that the namespace for ID tokens will only clash if the primary definitions would have clashed in any case.)

Example

Making paired italic and boldface tags like those used by HTML for web pages.

WI §27.29 Invocation labels, counters and storage

The process of expanding the i6 code which represents a phrase is called "invocation". As we have seen, when a phrase is defined using a single piece of i6 code, invocation consists of copying out that i6 code, except that tokens in braces "{thus}" are replaced:

To say (something - number): (- print {something}; -).

Ordinarily the only token names allowed are those matching up with names in the prototype, as here, but we have already seen one special syntax: "{phrase options}", which expands as a bitmap of the options chosen. And in fact the invocation language is larger still, as a skim through the Standard Rules will show. The notes below deliberately cover only some of its features: those which are likely to remain part of the permanent design of Inform, and which are adaptable to many uses. Please do not use any of the undocumented invocation syntaxes: they change frequently, without notice or even mention in the change log.

The first special syntaxes are textual tricks. {-delete} deletes the most recent character in the i6 expansion of the phrase so far. {-erase} erases the i6 expansion of the phrase so far. {-open-brace} and {-close-brace} produce literal "{" and "}" characters.

The following:

{-counter:NAME}
{-counter-up:NAME}
{-zero-counter:NAME}
{-counter-makes-array:NAME}

create (if one does not already exist) a counter called NAME. This is initially zero, and can be reset back to zero using "{-zero-counter:NAME}", which expands into no text. The token "{-counter:NAME}" expands into the current value of the counter, as a literal decimal number. The token "{-counter-up:NAME}" does the same, but then also increases it by one. Finally, the token "{-counter-makes-array:NAME}" expands to nothing, but tells Inform to create an "-->" array called "I7_ST_NAME" which includes entries from 0 up to the final value of the NAME counter.

This allows each instance in the source text of a given phrase to have both (i) a unique ID number for that invocation, and (ii) its own word of run-time storage, which can allow it to have a state preserved in between times when it is executed. For example:

To say once only -- beginning say_once_only:
   (- {-counter-makes-array:say_once_only}if (I7_ST_say_once_only-->{-counter:say_once_only} == false) {-open-brace} I7_ST_say_once_only-->{-counter-up:say_once_only} = true; -).
To say end once only -- ending say_once_only:
   (- {-close-brace} -).

To complete the tools available for defining a segmented substitution, we need a way for the definition of the head to know about the middle segments and the tail:

When invoking either the head or the tail, {-segment-count} expands to the literal decimal number of pieces of text in between the two, which is always one more than the number of middle segments, since the text comes in between the segments. When invoking any middle segment, {-segment-count} expands to the number of pieces of text so far -- thus it expands to 1 on the first middle segment invoked, 2 on the next, and so on.

Lastly {-final-segment-marker} expands to the i6 identifier which marks the end segment, or to I6_NULL if the end segment has no marker. The idea of markers is to enable the head's definition to know which of a number of choices has been used for the tail, supposing that this is a construction with a variety of legal endings. For example:

To say emphasise -- beginning say_emphasise:
   (- style {-final-segment-marker}; -).
To say with italics -- ending say_emphasise with marker underline:
   (- style roman; -).
To say with fixed space type -- ending say_emphasise with marker fixed:
   (- style roman; -).

The markers used for the tails here are "underline" and "fixed", and when the head is invoked, the marker for its tail is expanded into the argument of i6's "style" statement.

The examples above are all to do with segmented substitutions, which is where they are most useful, but most of the syntaxes above work equally well for ordinary "To…" phrase definitions.

WI §27.30 To say one of

Many of the invocation syntaxes described in the previous section are used in the definition by the Standard Rules of the "[one of] … [or] … [purely at random]" construction, so it makes a good example of how they can be used.

First, this is a segmented substitution with a single possible beginning ("[one of]"), a single possible middle ("[or]") but a choice of many possible endings. Almost everything is compiled by the invocation of the beginning:

To say one of -- beginning say_one_of (documented at phs_oneof): (-
   {-counter-makes-array:say_one_of}
   {-counter-makes-array:say_one_flag}
   if (I7_ST_say_one_flag-->{-counter:say_one_flag} == false) {
      I7_ST_say_one_of-->{-counter:say_one_of} = {-final-segment-marker}(I7_ST_say_one_of-->{-counter:say_one_of},
{-segment-count});
      I7_ST_say_one_flag-->{-counter:say_one_flag} = true;
   }
   if (say__comp == false) I7_ST_say_one_flag-->{-counter:say_one_flag}{-counter-up:say_one_flag} =
false;
   switch ((I7_ST_say_one_of-->{-counter:say_one_of}{-counter-up:say_one_of})%({-segment-count}+1)-1)
{-open-brace}
      0: -).
To say or -- continuing say_one_of (documented at phs_or):
   (- @nop; {-segment-count}: -).
To say purely at random -- ending say_one_of with marker I7_SOO_PAR (documented at phs_purelyrandom):
   (- {-close-brace} -).

The 3rd invocation of this (say) might compile the following:

I7_ST_say_one_of-->2 = I7_SOO_PAR(I7_ST_say_one_of-->2, 4);
switch((I7_ST_say_one_of-->2)%5 - 1) {
   0: ... first text ...
   1: ... second text ...
   2: ... third text ...
   3: ... fourth text ...
}

First, we notified Inform that it needs to allocate an array (I7_ST_say_one_of) providing storage associated with the counter "say_one_of". This we used to count off individual invocations of "[one of]", so that each would have its own word of storage - for the 3rd invocation, I7_ST_say_one_of-->2. We then call a state-changing routine, in this case I7_SOO_PAR, which is allowed to know the previous state and also the number of options available, and which returns the new state. The state is supposed to be the option chosen last time, but that means that there are not 4, but 5 possibilities: 0 for "there was no last time", then 1 to 4 for the possible outcomes. We reduce the state mod 5 to obtain the decision this time, and subtract 1 because it happens to be convenient to make the switch statement run from 0 to 3 rather than 1 to 4. (The reason we reduce the state mod 5 is to allow the state-changer to squirrel away secret information in the upper bits of the state, if it wants to. Note that subtracting one means that the switch value might be -1, which results in no text being printed: thus if the state-changer chooses 0, it can decide on none of the above.)

In this design, the marker attached to the choice of ending substitution is the name of the i6 state-changer: here is the I7_SOO_PAR routine.

[ I7_SOO_PAR oldval count; if (count <= 1) return count; return random(count); ];

As it happens, this ignores the old value: after all, it is meant to be purely at random, and nothing could be less pure than taking the last outcome into consideration when choosing the next.

Note that the counter say_one_of is advanced in invocation of the head. It might seem that the tidier design, somehow, would be to advance the counter in the invocation of the tails, but this is not a good idea. In general it is not safe to assume that the counter will have the same value when the tail is invoked that it had when the head was invoked, because segmented say constructions can legally be nested in Inform strings. Because of this, it is best to deal with a counter entirely in a single invocation, either of the beginning or the ending.

Because "[one of] … [or] …" is such a useful construction - switching between alternative forms of text, which writers of IF very often do - the above implementation is intentionally left open for new endings to be added, and the examples below show how easily this can be done.

Examples

453.
Blink
Making a "by atmosphere" token, allowing us to design our own text variations such as "[one of]normal[or]gloomy[or]scary[by atmosphere]".
454.
Making a "by viewpoint" token, allowing us to design our own text variations such as "[show to yourself]quaint[to Lolita]thrilling[to everyone else]squalid[end show]" depending on the identity of the player at the moment.