Inform 6 Reference Addendum

Language and compiler changes: releases 6.30 to 6.40 (in development)

Maintained by IFTF: <>

(Last update: June 27, 2022)

Copyright 2020-22 by the Interactive Fiction Technology Foundation. This document is licenced under a Creative Commons Attribution-ShareAlike 4.0 International License.

Graham Nelson's Inform 6 is a system for creating adventure games, and The Inform Designer's Manual is the book to read about it.

The fourth edition of that manual, published in 2001, began with that assertion. It's still true -- although these days we have to specify that it's about Inform 6. (Inform 7 is a more modern system, quite different, with its own manual called Writing With Inform.)

Inform 6 has remained broadly unchanged in the twenty years since the DM4 was published. While some authors continue to use I6 directly, its primary use is as an intermediate stage for the Inform 7 compiler. Stability and reliability have therefore been the watchwords of the I6 maintainers.

However, the language has not remained completely static. Most notably, the compiler now supports the Glulx VM as well as the original Z-machine. This required some additions to both the language and the compiler. Other corners of the system have been polished and rearranged, with the aim of making Inform a more flexible tool for both its target platforms.

As a result, the DM4 is now a bit out of date. This document is meant to fill in the gaps for the Inform 6 user of today.

Language versus library

This addendum covers updates to the Inform 6 language and compiler. A few errata are also included.

The changes described here are mostly to DM4 chapters 1-7, 36, and 38 and up. (Section references such as "§1" refer to DM4 chapters.) The intermediate chapters (the bulk of the DM4!) concern the Inform 6 library, which is not covered by this document.

The Inform library described by the DM4 is version 6/9, which was released along with Inform 6.21 in April of 1999. The library was subsequently updated to 6/10 (a bug-fix release) and then 6/11 (Glulx support and some additional features) before the author shifted his focus to Inform 7 development.

The Inform library is now a separate project managed by David Griffith. Updates can be found on the project page.

The Inform 6 compiler can be used with older versions of the Inform library, back through the Inform 5 libraries. There also exist a number of alternative Inform libraries, such as metro84 and PunyInform (supporting very old computers) and Platypus (a redesigned library). The Inform 6 compiler should be usable with all of them.

Compiler options and settings

The Inform compiler has many options which control its compilation behavior. These options come in three styles, and there are three ways to supply them.

Supplying options

All options can be supplied to the compiler in three ways (§39):

The third method is new since the DM4. It allows you to store your game's compilation options in the same file as its source code.

For example, this source file will be compiled in Glulx format, searching the i6lib directory for include files, raising the maximum number of verbs, and displaying compilation statistics:

!% -G
!% -s
!% +include_path=i6lib
!% $MAX_VERBS=255

Include "Parser";
Include "VerbLib";

[ Initialise;
  location = Kitchen;

Object Kitchen "Kitchen"
  with description "It's the kitchen.",
  has light;

Include "Grammar";

The options specified with !% must be at the start of the source file, with no blank lines or other comments before or between them.

Note that this section of the file is parsed before the source encoding is set. (Indeed, this section could specify the source encoding!) Therefore, options in this section must be plain ASCII.

!% options take precedence over command-line options. Command-line options are read in order; later options (including (file.icl) inclusions) take precedence over earlier ones.

[It would be better if command-line options took precedence over !% options. This would allow one-off changes while recompiling existing source code. This change may be considered in the future.]

Switch options (dash)

Classic options begin with a dash -- the traditional style of command-line tools. For example, the -h option (help) shows a page of output documenting how to run the compiler. -h2 will show a page listing all options that begin with a dash (§Table3).

New since the DM4:

-a2: As of 6.40, this displays assembly encoding of functions (like -a) plus byte encoding of the assembly opcodes. (Prior to 6.40, this was the -t option.)

-B: In Z-code versions 6 and 7 (only), use different offset values for the packed addresses of routines and strings. In this mode, strings are distinguished by having odd (packed) addresses, whereas routines are even. This allows the game file to contain more executable code. (The default is -~B, where the routine and string segments use the same offset value, so string and routine values have different ranges.)

-Cu: Treat the source files as being in UTF-8 (Unicode) encoding.

[Older options -C1, -C2, etc., are documented in the DM4 (§36); they treated the source files as being in one of the Latin-N encodings. -Cu is a new extension.]

[These options also affect the gametext.txt file created by the -r option. This transcript uses the same encoding as the source code.]

[When -Cu is used, the compiler does not add any characters to the Z-machine's "cheap" alphabet table. You can use the Zcharacter directive to add selected Unicode characters; see §36.]

-G: Compile to Glulx format rather than Z-machine. The default output file will be given an .ulx extension.

-G -vX.Y.Z: In Glulx, specify the VM version number to note in the generated game file. For example, -G -v3.1.0 would generate a game file that specifies VM spec 3.1.0.

[Normally the compiler works out the earliest version number compatible with the features used by your game. You can use this option to specify a later version.]

-H: In Glulx format, use Huffman encoding to compress strings. This is the default; to use uncompressed text, use -~H.

-k: Output debugging information to a file gameinfo.dbg. This switch is documented in the DM4 (§7), but the output format has changed. As of 6.33, it is an extremely verbose XML format; see below. Also, as of 6.35, the -k switch no longer automatically sets -D.

-g3: Trace calls to all functions. The DM4 documents -g2 as doing this, but as of 6.21, -g2 does not trace veneer functions. As of 6.35, -g3 traces everything.

[Note that -g2 and above will cause runtime errors in Glulx games, due to printing trace information when no output window is available.]

-W: Sets the number of words in the Z-code header extension table (Z-spec 1.0). The default is -W3. The $ZCODE_HEADER_EXT_WORDS setting does the same thing.

-V: Display the compiler version and exit with no further action.

Removed since the DM4:

The following switch options existed prior to 6.40, but are now gone.

The following switch options existed prior to 6.40, but have been removed in favor of trace options.

Path options (plus)

Options beginning with a plus set paths used by the compiler (§39). For example, +DIRNAME or +include_path=DIRNAME set the directory which is searched for include files.

You can also give a comma-separated list of paths: +include_path=DIR1,DIR2,DIR3

New since the DM4:

A double plus sign adds a new path or paths to an existing list. For example, ++include_path=DIRNAME will add DIRNAME to the paths which are searched for include files. The newly-added paths will be searched first.

On the command line (but not in ICL files or comments), path options can be also specified in Unix style. (This is new in 6.35.)

--path PATH=dir
--addpath PATH=dir

Removed since the DM4:

The +module_path and +temporary_path options no longer exist as of 6.40.

Compiler settings (dollar)

These were referred to as "memory settings" in the DM4 (§39). However, they now encompass a wide range of compiler behavior.

The $LIST setting will show a page of output listing all dollar-sign settings for Z-machine games. To see all settings for Glulx games, give the -G argument before $LIST.

[If you supply a dollar-sign setting as a command-line option on MacOS or Unix, remember that the shell treats the dollar sign as a special character. You must escape the dollar sign so that it reaches the compiler:
inform \$MAX_VERBS=255 source.inf If you supply such a setting in a source file, using the !% format, you should not use the backslash.]

New since the DM4:

A setting that begins with a hash sign ($#) will define an arbitrary numeric constant in the game code. If no value is supplied, the constant is defined as zero. This is equivalent to (and consistent with) the Constant directive. (New in 6.35.)


[Thus, passing the $#DEBUG argument is equivalent to the -D option. Remember that Inform symbols are not case-sensitive; $#debug or $#Debug=0 does the same thing.]

A setting that begins with an exclamation mark ($!) is a trace option. (New in 6.40.) These have a slightly different format; see Trace options, below.

On the command line (but not in ICL files or comments), compiler settings can also be specified in Unix style. (New in 6.35.) This avoids the need to escape dollar signs.

--opt SETTING=number
--define SYMBOL=number
--trace TRACEOPT=number


Other settings which are new or updated since the DM4:


The byte size of one character in the dictionary. This is only meaningful in Glulx. It can be 1 (dictionary characters are one byte) or 4 (dictionary characters are four-byte words).


The number of bytes in a dictionary word entry. (In Z-code this is 6, representing up to 9 Z-characters, and cannot be changed.)


The number of extra zero bytes to add to each object table entry. This is writable memory which the game can use freely. (This is only meaningful in Glulx, as the Z-code object format is fixed by the spec.)


The index number of the first individual property. This also determines the maximum number of common properties. (In Z-code this is 64 and cannot be changed.)


The number of abbreviations which may be used in economy (-e) mode. This setting is available in all versions of Inform, but in 6.35 the maximum for Z-code was raised from 64 to 96. However, this trades off against $MAX_DYNAMIC_STRINGS; see below.

The abbreviations (-u) switch now tries to create $MAX_ABBREVS abbreviations, rather than always creating 64. (As of 6.36.)

In Glulx, $MAX_ABBREVS is not needed and has no effect, except with the -u switch.


The number of string variables ("@00", etc) allowed in the game. In Z-code, this may be any value from 0 to 96. Setting this in Z-code automatically sets $MAX_ABBREVS to (96 - $MAX_DYNAMIC_STRINGS), as the two features draw from the same pool of 96 Z-machine abbreviations. Similarly, setting $MAX_ABBREVS sets $MAX_DYNAMIC_STRINGS to (96 - $MAX_ABBREVS).

In Glulx, the two settings are not connected. You may use any number of dynamic strings. If you use more than 100, you must set $MAX_DYNAMIC_STRINGS to a higher value.

(Added in 6.35. In earlier versions, 32 string variables and 64 abbreviations were available in Z-code; 64 string variables were available in Glulx. As of 6.36, $MAX_DYNAMIC_STRINGS defaulted to 100 in Glulx, which was the maximum. As of 6.40, $MAX_DYNAMIC_STRINGS is not limited in Glulx, but still defaults to 100.)


The amount of memory that the interpreter will reserve for the Glulx stack. (This is only meaningful in Glulx.)


The number of extra zero bytes to add to the end of the compiled game file. This is writable memory which the game can use freely. (This is only meaningful in Glulx.)


The number of bytes used for attribute flags in each object. The maximum number of attributes is 8 * NUM_ATTR_BYTES. (In Glulx, this must be a multiple of 4 plus 3. In Z-code this is always 6 and cannot be changed.)


If this is set to 1, the compiler will omit the compiled code of unused routines from the game file. (See $WARN_UNUSED_ROUTINES.)


Sets the game's serial number to the given six-digit number. (The Serial directive does the same thing; see §38.)


If set to 1 (the default), unreachable labels will be eliminated as part of branch optimization. Attempting to jump to an unreachable label is a compile-time error.

If set to 0, any label can be jumped to, at the cost of less optimized code.

(Added in 6.40. In earlier versions, any label could be jumped to.)


If set to 0 (the default), the gametext.txt transcript generated by the -r option uses the classic format, which is designed for human proofreaders. If set to 1, the transcript uses an alternate format which indicates the use of each string. This may be more useful for tools which want to parse the transcript.


If this is set to 2, the compiler will display a warning for each routine in the game file which is never called. (This includes routines called only from uncalled routines, etc.) If set to 1, it will warn only about functions in game code, not in library files.


This sets the number of words in the Z-code header extension table. The default is 3. The -W setting does the same thing. (See Z-spec 1.0. This is only meaningful in Z-code.)


This is the value to store in the Flags 3 word of the header extension table. (See Z-spec 1.1. This is only meaningful in Z-code.)


If this is set to 1, each dictionary entry will have two bytes of data instead of the usual three. (Added in 6.36. This is only meaningful in Z-code.)

With this switch set, you may not refer to #dict_par3. You also may not use grammar version 1, as that format needs to use the third byte for the preposition number.

Trace options

These are special compiler settings which display extra information about the compilation process. Most of them are only useful for debugging the compiler itself. The ones most useful to authors have simple switch options, documented above.

Trace options all have the form $!TRACEOPT or $!TRACEOPT=number. The option $! by itself will show a list of all supported trace options.

On the command line (but not in ICL files or comments), trace options can be also specified in Unix style.

--trace TRACEOPT
--trace TRACEOPT=number

All trace options are off (level 0) by default. The simple form $!TRACEOPT or --trace TRACEOPT sets the named option to level 1, which displays information during (or after) compilation. Some trace options support higher levels of verbosity to print more information.

The trace option system is new as of 6.40. In prior versions, much of this information was available through command-line switches or the Trace directive.


Show actions and fake-actions as they are defined.

(Prior to 6.40, this information was displayed by the -n switch.)

$!ASM (same as -a)

Show assembly-language encoding of functions as they are compiled. $!ASM=2 will also show the byte encoding of the assembly opcodes. $!ASM=3 will also show branch optimization info. $!ASM=4 will show more verbose branch info.

The $!ASM trace level can be changed at any point in the code with the Trace assembly directive.

(Prior to 6.40, the -t switch showed assembly plus opcode bytes, as $!ASM=2 or -a2 does now.)


Show backpatch markers as they are resolved. $!BPATCH=2 will also show backpatch markers being created.

(Prior to 6.40, this information was shown at higher $!ASM trace levels.)


Show the game dictionary when the game is complete. $!DICT=2 will also show the byte encoding of each dictionary entry.

The Trace dictionary directive will show this information at any point in the code.


Show expression trees as they are compiled. $!EXPR=2 and $!EXPR=3 will show more verbose information.

The $!EXPR trace level can be changed at any point in the code with the Trace expressions directive.


Show source and include files being opened and closed.

(Prior to 6.40, the -l switch and Trace lines directive showed this information.)


When computing abbreviations (-u), show selection decisions. $!FINDABBREVS=2 will also show three-letter-block scores.

(Prior to 6.40, the -u switch always showed this information.)

$!FREQ (same as -f)

When using abbreviations (-e), show how efficient each abbreviation was.

$!MAP (same as -z)

Display a memory map of the completed virtual machine. $!MAP=2 will also show the percentage of VM memory that each segment occupies.

(Prior to 6.40, the -p switch showed the segment percentages.)


Show memory allocations and deallocations within the compiler.

(Prior to 6.40, the -m switch showed this information.)


Show the object table when the game is complete.

The Trace objects directive will show this information at any point in the code.

(Prior to 6.40, the -j switch showed this information.)


Show properties and attributes as they are defined.

(Prior to 6.40, this information was displayed by the -n switch.)

$!RUNTIME (same as -g)

Compile function tracing into the game. Every game function called will print a trace message. $!RUNTIME=2 will also trace library calls; $!RUNTIME=3 will also trace veneer calls.

[This is the only trace option which affects the generated game file.]

In Glulx mode, $!RUNTIME=2 and above will cause Glk errors due to rulebooks printing trace messages when no output stream is set.

$!STATS (same as -s)

Print game statistics when compilation is complete.


Show the symbol table when the game is complete. $!SYMBOLS=2 will also show compiler-defined symbols.

The Trace symbols directive will show this information at any point in the code.


Show symbols as they are encountered and defined.


Show tokens as they are lexed. $!TOKENS=2 will also show token types; $!TOKENS=3 will also show their lexical contexts.

The $!TOKENS trace level can be changed at any point in the code with the Trace tokens directive.


Show the verb grammar table when the game is complete.

The Trace verbs directive will show this information at any point in the code.

Language changes

A few elements of the Inform language itself have been updated or extended.


A couple of notes on general directive syntax:

Directives in Inform 6 are not case-sensitive. These declarations are equivalent:

Global varname;
GLOBAL varname;
global varname;

The DM4 does not state this, but it has been true in every version of the Inform compiler, so we can accept it as a principle of the language.

[The DM4 mostly shows directives in title case: Global, Zcharacter, Ifdef. This is the manual's code style, not a language requirement.]

[Variable names and symbols are also case-insensitive. Language keywords, such as if and return, are not; they must be given in lower case. Case is also significant for the built-in print tokens (A), (a), (The), and (the); see §26.]

Directives can also be written with a # sign:

#Global varname;

For standalone directives, the # sign is optional. However, a few directives can be used within a routine or object definition. This allows conditional compilation of individual lines of code or object properties (§38). These directives are:

Ifdef, Ifndef, Ifnot, Ifv3, Ifv5, Iftrue, Iffalse, Endif

When one of these directives appears within a routine or object definition, the # sign is required.

[As of 6.35, the compiler will report errors for non-"if" directives appearing within a definition. In earlier versions, this error was not always checked.]


As of 6.40, the Abbreviate directive only creates an abbreviation when in economy (-e) mode. If the -e switch is not used, the directive is skipped and no space is consumed in the game's abbreviations table.


The Array directive (§2.4) supports four ways to specify values:

Array arrname --> N; 
Array arrname --> expr1 expr2 ... exprN; 
Array arrname --> [ expr1 expr2 ... exprN ]; 
Array arrname --> "string";

The third form, with square brackets, is not documented in the DM4, but it has been supported since 6.0. Expressions between the brackets may be delimited by optional semicolons.

[The bracket form allows us to define a one-entry array with an initializer: Array arrname --> [ expr ]; This would not otherwise be possible. Note that zero-entry arrays are not permitted.]

The Array directive supports a new array type in 6.30:

Array arrname buffer N; 
Array arrname buffer expr1 expr2 ... exprN; 
Array arrname buffer [ expr1 expr2 ... exprN ]; 
Array arrname buffer "string";

The buffer keyword defines arrname as a hybrid array, in which the first word array-->0 contains the length (N), and the following N bytes array->WORDSIZE, array->(WORDSIZE+1) ... array->(WORDSIZE+N-1) contain the specified expression values or string characters.

[This hybrid form is used by the print_to_array method (§3.12) and various library functions.]

All forms of the Array directive support the static keyword, which is new in 6.34:

Array arrname static -> 1 2 3 4;
Array arrname static string "plugh";

The static keyword must appear before the array type (->, -->, string, table, or buffer). It indicates that the array should be placed in non-writable memory.

[In Z-code, this places the array at the end of readable memory (after the dictionary and before the beginning of code storage). In Glulx, it places the array at the end of ROM (after string storage). A static array declaration should always provide initial values, since it cannot be updated at runtime. ]


The DM4 marked the Dictionary directive as obsolete (§Table5), but it has been reinstated as of 6.33. It allows you to add a word to the game dictionary and set its optional dictionary flags.

Dictionary 'word';
Dictionary 'word' val1;
Dictionary 'word' val1 val3;

The first form simply adds the word if it is not already in the dictionary. The second form also sets the dict_par1 flag to the given value, or bitwise-or's the value to that flag if the word already exists. The third form also sets the dict_par3 flag in the same way.

The values can be numeric literals or constants. They can be 0-255 for Z-code, or 0-65535 for Glulx.

[See also the $ZCODE_LESS_DICT_DATA setting above, which drops the dict_par3 field from dictionary entries.]

[dict_par2 cannot be set by this directive. It is always the verb number, or zero for words that are not verbs.]


This is mentioned in the DM4 as being for compiler maintenance only (§Table5). In fact it is used by the library, so it's worth documenting. This is a conditional compilation directive, like Iftrue, which compiles code when building Z-code V3 (only). It is equivalent to:

#Iftrue (#version_number == 3);
! ...code...


This misnamed directive is also used by the library (§Table5). It conditionally compiles code when building Z-code V4 and later or Glulx. Thus it is the converse of Ifv3. (The misleading name is left over from much earlier versions of Inform.)


The Import directive no longer exists as of 6.40. (This, like Link, was used for the obsolete module-linking feature.)


The Link directive no longer exists as of 6.40.


The Lowstring directive has been supported since (at least) Inform 5, but it is not needed in Inform 6. It is therefore deprecated and undocumented in the DM4. We will describe it here for information's sake.

Lowstring Text "text";

In Z-code, this places an encoded string within readable memory (the first 64k). Text is defined as a constant whose value is (address/2). The string can therefore be printed this way:

print (address) (2*Text);

The constant can also be used with the string statement:

string 14 Text;

This sets dynamic string 14 to the string "text". However, it is more usual (and documented in §1.11) to use a string literal rather than Lowstring:

string 14 "text";

The Lowstring directive is not supported in Glulx.


The Origsource directive allows you to mark part of a source file as having been generated from another file. (E.g, an Inform 7 source file.) This directive is new in 6.34.

Origsource "Filename";
Origsource "Filename" 10;
Origsource "Filename" 10 40;

This declares that all following lines are derived from the given filename. This will be reported in error messages and debug output. You may optionally provide a line number and a character number within the line (10 and 40, in these examples).

The declaration holds through the next Origsource directive (but does not apply to included files). The fourth form, with no arguments, clears the declaration.


As of 6.36, it is now possible to declare an individual property with the Property directive:

Property individual propname;

This gives us a total of four forms of the directive:

Property [additive] name;
Property [additive] name defaultexpr;
Property [additive] name alias oldname;
Property individual name;

Note that an individual property cannot be additive, have a default value, or alias another property.

The long keyword can be applied to any Property directive, but it has no effect except a deprecation warning. (All properties have been long, which is to say word-sized, since Inform 5.)

As of 6.36, it is possible to declare a property called long, additive, or individual, even though these are keywords for the Property directive.

! This declares an additive common property called "long" with default value 12.
Property additive long 12;

Concerning Z-code limits, the DM4 says:

Only 62 [common properties] are available, of which the compiler uses 3 and the library a further 47.

This is poorly phrased. The Z-machine (V4+) permits 63 common properties (numbered 1 to 63). The compiler defines three: the name property plus two hidden properties which are used internally. So the game and library may declare 60 more common properties between them.


The Replace directive (§25) allows you to redefine a function which has already been defined (such as in a library or in the veneer).

[The DM4 says that you can only replace functions in files marked with the System_file directive. This limitation has been removed.]

The Replace directive has two forms, of which the second is new in 6.33:

Replace Func;
Replace Func OriginalFunc;

Multiple definitions of Func() may follow the Replace directive. Func will refer to the last-defined version, except that definitions in normal files are preferred over definitions in System_file files or the veneer. With the second form, OriginalFunc will refer to the first-defined version of the function.


As of 6.40, the Switches directive is obsolete and generates a deprecation warning. Use !% header comments instead.

This has always been documented to be used "at the very beginning of your source code" (§39). However, this was only weakly enforced; the compiler only checked that it was used before the first constant was defined. As of 6.40, it may only be used before the first constant or routine is defined.


The Trace directive allows you to adjust various trace settings and display information during compilation. As of 6.40, it is deprecated in favor of trace options.

Prior to 6.36, it was limited to a few kinds of trace information and its syntax was rather ad-hoc. As of 6.40, it is still limited, but its syntax is more consistent.

The Trace directive has two flavors.

Trace dictionary
Trace objects
Trace symbols
Trace verbs

Each of these prints the named data table as of that point in the compilation. The equivalent trace option ($!DICT, $!OBJECTS, $!SYMBOLS, $!VERBS) print the named table when compilation is complete.

(For consistency with the trace option system, these four lines can take an optional number. This is only meaningful for Trace symbols 2, which, like $!SYMBOLS=2, prints more symbol info.)

Trace assembly [val]
Trace expressions [val]
Trace tokens [val]

Each of these adjusts the level of the equivalent named trace option ($!ASM, $!EXPR, $!TOKENS). The optional value may be off (0), on (1), or any number. If no value is given, the option is set on (1).

Trace [val]

Equivalent to Trace assembly [val].

Trace lines [val]
Trace linker [val]

Trace lines was intended to support a line-tracing feature which was never implemented. Trace linker supported the module-linking feature, which no longer exists. These options now do nothing.

[Trace options cannot print tables or change trace levels partway through compilation; that ability is only available through the Trace directive. The directive will therefore be retained. However, this ability is not very useful, so it seems reasonable to consider it deprecated.]


The Undef directive allows you to remove a previously-defined constant. This directive is new in 6.33.

Undef Const;

This leaves the Const symbol undefined. If Const was never defined, this does nothing.

[When tested with the Ifdef directive, a constant is considered undefined only after the Undef directive, just as it is considered defined only after its original declaration. This is somewhat inconsistent with Inform's handling of constants in expressions (routine code), where they can freely be used before or after being declared.]

[Undef'ing constants which are forward-declared leads to confusing behavior. It is best to consider Undef purely as an adjunct to Ifdef.]


The Version directive is mentioned in the DM4 as being obsolete (§Table5). As of 6.40, it generates a deprecation warning. Use the -v3 command-line switch or !% -v3 header comment instead.

As of 6.36, this directive may only be used before the first routine is defined. (This limitation reduces, but does not eliminate, various code generation bugs.)


The action statements <Action> and <<Action>> (§6) now support up to four arguments. The four-argument form is new as of 6.33.

<Action Noun>
<Action Noun Second>
<Action Noun Second, Actor>

All provided arguments are passed to the R_Process() veneer function.

[As in previous versions of Inform, the Action argument can either be a bare action name or a parenthesized expression which produces an action value. Thus, <Take lamp> and <(##Take) lamp> are equivalent.]

[This statement does not follow the traditional IF command syntax, which would put the actor first: "ACTOR, ACTION NOUN". Inform's lexer is not able to handle that ordering consistently, so the statement has to put the actor last.]

The capitalized (A) print token joins (a), (The), and (the) as of 6.30; see §26.

print (A) lamp;

This calls the CInDefArt() veneer function.

Dynamic strings

The DM4 (§1.11) describes "printing-variables", also called dynamic strings. By embedding a code like @01 in a string, you can interpolate another literal string value. You set the interpolated value with a statement like

string 1 "text";
print "This is the @01.^";

As of 6.40, dynamic string interpolations may look like @(1), with any nonnegative number inside the parens. You may also write @(N), where N is a defined numeric constant.

Constant TEXTVAL 1;

...and then...

string TEXTVAL "text";
print "This is the @(1), or equivalently @(TEXTVAL).^";

The old format @01 is still supported, but only supports numbers of exactly two digits.

[The string statement has always supported numbers and numeric constants, so it has not changed.]

The number of dynamic strings is limited, but the limit may be increased with the $MAX_DYNAMIC_STRINGS setting. See Compiler settings.


The compiler defines various constants describing the game file. The game (and library) may use these to examine compiled data.

[Some constants begin with #; others do not. This is a mostly internal distinction which is not important for authors. The difference is that regular constants must be defined at the start of compilation. # constants, such as #dictionary_table, may not be known until compilation is complete.]

[Only a few of the compiler's # constants are documented here. The majority are intended to support Infix, and are available only in Z-code.]

Constants defined with no given value are typically meant to be checked with #Ifdef. They have the default value 0.


The Z-code version number. (Z-code only.)


The address of the start of high memory (routine and string segments). This is also found in the header at address 4. (Z-code only.)


The address of the dictionary. In Z-code, this is also found in the header at address 8. (Glulx-only until 6.40.)


The address of the grammar table. In Z-code, Inform places this at the start of static memory, so it can be found in the header at address 14. (Glulx-only until 6.40.)


The address of the actions table. This table maps an action number (e.g. ##Jump) to its action routine (e.g. JumpSub).

#dict_par1, #dict_par2, #dict_par3

In Z-code, the byte offsets of the three flags within a dictionary word entry.

In Glulx, these are the byte offsets of the low byte (second byte) of the three flags.

If $ZCODE_LESS_DICT_DATA is set, #dict_par3 is not available.


Exactly one of these will be defined, depending on the game file format.


This is the byte size of a VM word or address: 2 in Z-code, 4 in Glulx.


These are defined by the -S, -D, -X switches respectively.

[Prior to 6.40, MODULE_MODE and USE_MODULES were defined by the -M and -U switches respectively. These no longer exist.]


This is the byte size (not Z-character count!) of a dictionary word string. In Z-code, it will be 4 (v3) or 6 (v4+). In Glulx, it is controlled by the $DICT_WORD_SIZE setting.


This is the byte size of a complete dictionary entry, including flags. In Z-code, it will be DICT_WORD_SIZE+3, or DICT_WORD_SIZE+2 if $ZCODE_LESS_DICT_DATA is set. In Glulx, it is DICT_WORD_SIZE+7, or 4*DICT_WORD_SIZE+12 if $DICT_CHAR_SIZE is 4.


The byte size of one character in a dictionary word. This is controlled by the $DICT_CHAR_SIZE setting; it will be either 1 or 4. (Glulx-only.)


This is defined as 1 if $DICT_CHAR_SIZE is 4. (Glulx-only.)


The number of bytes reserved in each object record for (boolean) attributes. (Each byte stores eight attributes.) In Z-code, this will be 6. In Glulx, it is controlled by the $NUM_ATTR_BYTES setting; it will always be a value of the form 4*i+3.


The property number of the first individual property. This is controlled by the $INDIV_PROP_START setting. (Glulx-only.) (In Z-code, this setting is always 64 so no constant is defined.)


The offset in words (not bytes!) of the six address fields in a Glulx object record. For example, the address of the object's property table can be found at obj-->GOBJFIELD_PROPTAB. (Glulx-only.)


The offset of bytes of the extended object data in an object record. The length of this data is controlled by the $GLULX_OBJECT_EXT_BYTES setting. (Glulx-only.)


The byte size of a complete object record. This will be 1+NUM_ATTR_BYTES+6*WORDSIZE, plus $GLULX_OBJECT_EXT_BYTES if that is set. (Glulx-only.)


Positive and negative infinity and NaN for Glulx floating-point operations. (Glulx-only.)


Positive and negative infinity and NaN for Glulx double-precision operations. (Glulx-only.)


Logical precedence

The table of condition operators (§Table1B) shows &&, ||, and ~~ as having equal precedence. This is ambiguous about expressions like (~~X && Y). In fact, the compiler has always (at least since 6.0) parsed this as applying ~~ to the conjunction (X && Y):

! These statements are equivalent:
val = ~~X && Y;
val = ~~(X && Y);

If you want the ~~ to apply only to X, you must add explicit parentheses:

! Not equivalent to the above!
val = (~~X) && Y;

Similarly, the bitwise expression (~X & Y) is parsed as (~(X & Y)).

Given this, it makes sense to consider the logical ~~ operator as having a precedence level of 1.5, and the bitwise ~ operator as having a precedence level of 5.5.

The or operator

The or operator is described in §1.8, but without a full explanation.

or may only be used to provide a list of values to the right of a comparison operator:

if (val == 2 or 4 or 8) ...
if (obj in trunk or knapsack) ...
if (obj has container or supporter) ...

The expression is true if the left-hand value matches any of the values on the right.

For negative-sense operators, the expression is true if the left-hand value matches none of the values on the right:

if (val ~= 2 or 4 or 8) ...
if (obj notin trunk or knapsack) ...
if (obj hasnt container or supporter) ...

[Strict Boolean logic would have us write these negative comparisons with "and" rather than "or". Inform doesn't go that far. All of the expressions above follow the natural English sense of "or".]

This becomes somewhat confusing when we consider ordering comparisons. The manual gives this example:

if (x > 100 or y) ...

[...] to test whether x is bigger than the minimum of 100 and y.

This is consistent with the earlier examples; the expression is true if (x > 100) or (x > y). However, it takes some squinting to realize this.

The >= and <= operators are even worse. The compiler treats these as negative-sense operators (because that's how the Z-machine architecture constructed them). Therefore:

if (x <= 100 or y) ...

This is true if x is less than or equal to both 100 and y. (That is, it is treated as the exact inverse of the previous example.) This is unintuitive even if you have absorbed the previous examples.

As of 6.36, if you use or with the >= and <= operators, the compiler will generate a warning.

Class behavior


The DM4 (§3.8) gives this example of multiple inheritance:

Object "goose that lays the golden eggs"
    class Bird Treasure;

[This goose] inherits from Object first and then Bird and then Treasure, attribute settings and property values from later-mentioned classes overriding earlier ones, so if these classes should give contradictory instructions then Treasure gets the last word.

In fact this is not true (and never has been). The language behavior is somewhat complex.

Property values are determined by the first class listed, whether that is given as a directive (Treasure goose) or a class keyword (Object goose class Treasure). Of course, if the object itself gives a property value, that overrides all inherited values.

If one class inherits from another, the derived class property overrides the superclass property, as one would expect.

Additive properties follow the same logic, but their values accumulate, rather than overriding each other. An additive property gains values from the object's own definition (first), then from each class in (forward) listing order.

[This supports the I6 library convention that additive properties (before, after, life) contain lists of inline routines. These are tested in forward order; each routine may return true to indicate that the event is handled, or false to pass control to the next routine. The precedence order is thus, again, the object followed by each class in listing order.]

Attributes follow the same rule, except that classes cannot negate attributes set by other classes.

Class Bird has ~heavy;
Class Treasure has heavy;
Object goose class Bird Treasure;

In this example, the goose object gains the heavy attribute because at least one of its classes sets it. This will be true no matter what order the classes are listed in. The has ~heavy declaration in class Bird has no effect.

Objects can of course use has ~attr to negate attributes inherited from classes. Derived classes can negate attributes inherited from superclasses. The limitation applies only to conflicts between an object's classes.

Class copy and recreate

The copy and recreate class methods likewise diverge from the manual. §3.11 says:

Plant.copy(Gilded_Branch, Poison_Ivy);

It's rather useful that recreate and copy can be sent for any instances, not just instances which have previously been created. For example, [the copy statement above] copies over all the features of a Plant from Poison_Ivy to Gilded_Branch, but leaves any other properties and attributes of the gilded branch alone. Likewise, [the recreate statement] only resets the properties to do with Treasure, leaving the Plant properties alone.

In fact, the question of what classes originated the properties and attributes does not arise. The copy method copies all attributes, positive and negative, from the source to the target. It also copies the value of every property which is declared by both the source and target and has the same length on each.

The recreate method does a copy from an unmodified instance of the class, followed by a create call. It will therefore reset all attributes, as well as any properties declared by both the object and the class.

[These behaviors are arguably compiler bugs. However, they have been established behavior since Inform 6.0. We prefer to document them rather than trying to adjust them at this late date.]

Glulx support

The ability to compile a Glulx game file (the -G option) was introduced in 6.30. This came with a few additions to the Inform language.

One of the constants TARGET_ZCODE and TARGET_GLULX is always defined. These can be used with the Ifdef directive to mark code that should only be compiled on one platform.

The constant WORDSIZE is always defined; it is 2 in Z-code and 4 in Glulx.

As noted above, code under the Ifv5 directive is also compiled for Glulx.

Language features supported only in Glulx

If the first argument to a function is named _vararg_count, arguments will be passed on the VM stack, with _vararg_count set to the number of arguments.

Literals beginning with $+ and $- are compiled as floating-point constants. For example: $+1.0, $-1e3, $+2.5e-3. Double-precision floating-point is also supported; these values require 64 bits, or two Glulx words each, and are thus written $>+1.0 (the high word) and $<+1.0 (the low word).


[Note that the standard Inform arithmetic operators (+, -, etc) do not work with floating-point values. You must use Glulx opcodes. (@fadd, @fsub, etc for single-precision; @dadd, @dsub, etc for double-precision.)]

The print_to_array() method (§3.12) requires two arguments, rather than one (as in Z-code).

Language features not supported in Glulx

Infix (the -X option, §7) is not available.

The Zcharacter directive (§36) is not available, since Glulx does not use the ZSCII character set.

The save, restore, and read statements (§1.15, §2.5) are not available. These features require more complex behavior in Glulx and cannot be encapsulated in a single statement. They must be implemented in library code using the @glk opcode.

Z-machine V3/4 limitations

Early releases of Inform (through Inform 5) were designed to support all versions of the Z-machine from 3 through 6. However, Inform 6 extended the language in ways which require more advanced VM support. Therefore, certain language features require Z-machine version 5 or later.

You can work around the lack of obj.prop() by writing:

@push self;
self = obj;
addr = obj.prop;
@pull self;

[The lines dealing with self may be omitted if the property routine does not rely on self.]

In general, Inform 6 is able to compile older source code to V3 if the source and the library avoids the obj.prop() syntax. This means you cannot use the Inform 6 library. You must use the Inform 5 library, or one of the alternative libraries designed for V3, such as metro84 or PunyInform.

[It is possible to re-implement a limited version of obj.prop() for V3 by replacing the CA__Pr and Cl__Ms veneer routines. Some alternative libraries do this.]

Debug file format

The -k switch generates a gameinfo.dbg file which describes the compiled game. The Inform Technical Manual (§12.5) documents a binary format ("Version 0") for this file. However, that format is no longer used. As of Inform 6.33, a more verbose XML format ("Version 1") is generated; this section describes it.

[By "verbose", we mean that the debug info for Advent.inf approaches 2 megabytes of XML. The debug info for a one-line Inform 7 game reaches 11 megabytes. The greatest bulk of the data is <source-code-location> and <sequence-point> tags.]


Debugging information files are written in XML and encoded in UTF-8. They therefore begin with the following declaration:

<?xml version="1.0" encoding="UTF-8"?>

Beyond the usual requirements for well-formed XML, the file adheres to the conventions that all numbers are written in decimal, all strings are case-sensitive, and all excerpts from binary files are Base64-encoded.

The top level

The root element is given by the tag <inform-story-file> with three attributes: the version of the debug file format being used, the name of the program that produced the file, and that program's version. For instance,

<inform-story-file version="1.0" content-creator="Inform"

Any of the elements described below (except <local-variable>, <sequence-point>, <source-code-location>) may appear in the ellipses.

Story file prefix

The story file prefix contains a Base64 encoding of the story file's first bytes so that a debugging tool can easily check whether the story and the debug information file are mismatched. For example, the prefix for a Glulx story might appear as


The story file prefix is mandatory, but its length is unspecified. The current version of the Inform compiler records 64 bytes, which seems sufficient.

Story file sections

Story file sections partition the story file according to how the data will be used. For the Inform 6 compiler, this partitioning is the same as the memory map printed by the -z switch.

A record for a story file section gives a name for that section, its beginning address (inclusive), and its end address (exclusive):

  <type>abbreviations table</type>

The names currently in use include those from the Inform Technical Manual (§12.5):

abbreviations table
header extension (Z-code only)
alphabets table (Z-code only)
Unicode table (Z-code only)
property defaults
object tree
common properties
class numbers
individual properties (Z-code only)
global variables
array space
grammar table
actions table
parsing routines (Z-code only)
adjectives table (Z-code only)
code area
strings area

plus one addition for Z-code:


two additions for Glulx:

memory layout id
string decoding table

and three additions for both targets:

identifier names
zero padding

Names may repeat; Glulx story files, for example, sometimes have two zero padding sections.

A compiler that does not wish to subdivide the story file should emit one section for the entirety and give it the name


Source files

Source files are encoded as in the example below. Each file has a unique index, which is used by other elements when referring to source code locations; these indices count from zero. The file's path is recorded in two forms, first as it was given to the compiler via a command-line argument or include directive but without any path abbreviations like > (the form suitable for presentation to a human) and second after resolution to an absolute path (the form suitable for loading the file contents). All paths are written according to the conventions of the host OS. The language is, at present, either "Inform 6" or "Inform 7". More languages may added in the future.

<source index="0">
  <language>Inform 6</language>

If the source file is known to appear in the story's Blorb, its chunk number will also be recorded:

<source index="0">
  <language>Inform 6</language>

Table entries; grammar lines

Table entries are data defined by particular parts of the source code, but without any corresponding identifiers. The <table-entry> element notes the entry's type, the address where it begins (inclusive), the address where it ends (exclusive), and the defining source code location(s), if any:

  <type>grammar line</type>

The current version of the Inform compiler only emits <table-entry> tags for grammar lines; these data are all located in the grammar table section.

Named values

Records for named values store their identifier, their value, and the source code location(s) of their definition, if any. For instance,


would represent a named constant.

Attributes, properties, actions, fake actions, objects, arrays, and routines are also names for numbers, and differ only in their use; they are represented in the same format under the tags <attribute>, <property>, <action>, <fake-action>, <object>, <array>, and <routine>. (Moreover, unlike in Version 0, fake actions are not recorded as both fake actions and actions.)

The records for constants include some extra entries for the system constants tabulated in the Inform Technical Manual (§12.2), even though these are not created by Constant directives. Entries for #undefed constants are also included, but necessarily without values.

Some records for objects will represent class objects. In that case, they will be given with the tag <class> rather than <object> and include an additional child to indicate their class number:


Records for arrays also have extra children, which record their size, their element size, and the intended semantics for their zeroth element:


And finally, <routine> records contain an <address> and a <byte-count> element, along with any number of the <local-variable> and <sequence-point> elements, which are described below. The address is provided because the identifier's value may be packed.

Sometimes what would otherwise be a named value is in fact anonymous; unnamed objects, embedded routines, some replaced routines, veneer properties, and the Infix attribute are all examples. In such a case, the <identifier> subelement will carry the XML attribute


to indicate that the compiler is providing a sensible name of its own, which could be presented to a human, but is not actually an identifier. For instance:

  <identifier artificial="true">lantern.time_left</identifier>

Artificial identifiers may contain characters, like the full stop in lantern.time_left, that would not be legal in the source language.

Global variables

Globals are similar to named values, except that they are not interpreted as a fixed value, but rather have an address where their value can be found. Their records therefore contain an <address> tag in place of the <value> tag, as in:


Local variables

The format for local variables mimics the format for global variables, except that a source code location is never included, and their memory locations are not given by address. For Z-code, locals are specified by index:


whereas for Glulx they are specified by frame offset:


If a local variable identifier is only in scope for part of a routine, its scope will be encoded as a beginning instruction address (inclusive) and an ending instruction address (exclusive):


Identifiers with noncontiguous scopes are recorded as one <local-variable> element per contiguous region. It is possible for the same identifier to map to different variables, so long as the corresponding scopes are disjoint.

Sequence points

Sequence points are stored as an instruction address and the corresponding single location in the source code:


The source code location will always be exactly one position with overlapping endpoints.

Sequence points are defined as in the Inform Technical Manual (§12.4), but with the further stipulation that labels do not influence their source code locations, as they did in Version 0. For instance, in code like

say__p = 1; ParaContent(); .L_Say59; .LSayX59;
t_0 = 0;

the sequence points are to be placed like this:

<*> say__p = 1; <*> ParaContent(); .L_Say59; .LSayX59;
<*> t_0 = 0;

rather than like this:

<*> say__p = 1; <*> ParaContent(); <*> .L_Say59; .LSayX59;
t_0 = 0;

Source code locations

Most source code locations take the following format, which describes their file, the line and character number where they begin (inclusive), the line and character number where they end (exclusive), and the file positions (in bytes) corresponding to those endpoints:


Line numbers and character numbers begin at one, but file positions count from zero.

In the special case where the endpoints coincide, as happens with sequence points, the end elements may be elided:


At the other extreme, sometimes definitions span several source files or appear in two different languages. The former case is dealt with by including multiple code location elements and indexing them to indicate order:

<!-- First Part of Inform 6 Definition -->
<source-code-location index="0">
  <!-- Assuming file 0 was given with the language "Inform 6" -->
<!-- Second Part of Inform 6 Definition -->
<source-code-location index="1">
  <!-- Assuming file 1 was given with the language "Inform 6" -->

The latter case is also handled with multiple elements. Note that indexing is only used to indicate order among locations in the same language.

<!-- Inform 7 Definition -->
  <!-- Assuming file 2 was given with the language "Inform 7" -->
<!-- Inform 6 Definition -->
  <!-- Assuming file 0 was given with the language "Inform 6" -->


A great number of bugs have been fixed since Inform 6.21. The list is not included here. See the Release Notes for details.

However, it is worth noting that Z-code V3 code generation had been neglected since 6.15 and a number of bugs had crept in. 6.34 and 6.35 have addressed these; V3 should once again be usable.

Particular thanks to Daniel Fremont for the many bug reports uncovered by his input-fuzzing project.