Contents
Back
Forward

4. The language of Inform


4.1. ICL

The Inform compiler is quite configurable: it has a number of settings which can be altered to suit the convenience of the user. Many of these settings are "switches'', which usually have just two possible states, off or on. However, some can be set to a single-digit number.

The other numerical settings are "memory settings'', which control how much of your computer's memory Inform uses while running (too low and it may not be able to compile games of the size you desire; too high and it may choke any other programs in the computer for space).

Finally, there are "path variables'', which contain text and are used to sort out filenames for the files Inform uses or creates. The usage of these variables varies widely from machine to machine, or rather, from one operating system to another.

If Inform seems to work adequately for you already, this section can safely be ignored until the day comes to compile a really big project. Times like that call for the ability to conveniently change many settings at once, and a tiny language called "ICL'' is provided for you to supply detailed specifications.

On many systems, though not usually the Apple Macintosh, the user sets Inform running by typing a command at the "command line'', that is, in response to a prompt printed by the computer. For example, under RISC OS one would press function key f12 from the desktop and be given the prompt *, to which one might reply

inform ruins
On computers with more doggedly windowed interfaces, there will be a higher-level interface of some kind provided with Inform, which should come with its own brief documentation.

The usual way to alter switches on the command line is to give a word of options after the inform command, introduced by a minus sign. The switches are all single letters, and by default are mostly off. For example, the -x switch causes Inform to print a row of hash signs as it compiles:

inform -x shell
RISC OS Inform 6.01 (April 25th 1996)
::###############################################################
One hash sign is printed for every 100 textual lines of source code compiled. (On my own machine, an Acorn Risc PC 700, about 10 hashes are printed every second: that is, the compilation speed is about 1000 lines per second.) Although -x is provided to indicate that a slow compilation is continuing normally, many designers use it to get a feeling for how large their games are, and it's a morale boost when the row of hashes spills over onto a second screen line.

Inform has documentation built-in on the subject of switches and other ICL features, which may vary from machine to machine. Running Inform with no filename will print this "help information''. In addition, -h1 will print details of filenaming conventions in use on your machine, and -h2 will print a list of switches and their settings.

The full command line syntax is

inform <ICL commands> <source file> <output file>
where only the <source file> is mandatory. By default, the full names to give the source and output files are derived in a way suitable for the machine Inform is running on: on a PC, for instance, advent may be understood as asking to compile advent.inf to advent.z5. This is called "filename translation''. No detailed information on filenaming rules is given here, because it varies so much from machine to machine: see the -h1 on-line documentation. Note however that a filename can contain spaces if it is written in double-quotes.

One possible ICL command is to give a filename in brackets: e.g.,

inform -x (skyfall_setup) ...
sets the -x switch, then runs through the text file skyfall_setup executing each line as an ICL command. As an example, this file might read as follows:
   ! Setup file for "Skyfall"

   -d                   ! Contract double spaces
   $max_objects=1000    ! 500 of them snowflakes
   (usual_setup)        ! include my favourite settings, too
   +module_path=mods    ! keep modules in the "mods" directory
Note that ICL can include comments after !, just as in Inform. Otherwise, an ICL file has one command per line (with no dividing semicolons), and the possibilities are as follows:

-<switches>

set these switches; or unset any switch preceded by a tilde ~. (For example, -a~bc sets a, unsets b and sets c.)

$list

list current memory settings

$?<name>

ask for information on what this memory setting is for

$small

set the whole collection of memory settings to suitable levels for a small game

$large

ditto, for a slightly larger game

$huge

ditto, for a reasonably big one

$<name>=<quantity>

alter the named memory setting to the given level

+<name>=<filename>

set the named pathname variable to the given filename, which should be one or more filenames of directories, separated by commas

compile <filename> <filename>

compile the first-named file, containing source code, writing the output program to the (optional) second-named file

(<filename>)

execute this ICL file (files may call each other in this way)


4.2. Controlling what is compiled

Several directives instruct Inform to "compile this part next'' or "only compile this...''. First,

    Include "filename";
instructs Inform to compile the whole of the source code in the given file, and only carry on compiling from here once that is complete. It is exactly equivalent to removing the Include directive and replacing it with the whole file "filename". (The rules for how Inform interprets "filename" vary from machine to machine: run Inform with the -h1 switch for information.) Note that you can write
    Include ">shortname";
to mean "the file called "shortname" which is in the same directory that the present file came from''. This is convenient if all the files making up the source code of your game are housed together.

/\ Next, there are a number of "conditional compilation'' directives. They take the general form of a condition:
    Ifdef <name>;          Is the name defined as having some meaning?
    Ifndef <name>;         Is the name undefined?
    Iftrue <condition>;    Is this condition true?
    Iffalse <condition>;   Is this condition false?
followed by a chunk of Inform and then either
    Ifnot;
and another chunk of Inform, or just
    Endif;

At this point it is perhaps worth mentioning that (most) directives can also be interspersed with statements in routine declarations, provided they are preceded by a # sign. For example:
    [ MyRoutine;
    #Iftrue MAX_SCORE > 1000;
      print "My, what a long game we're in for!^";
    #Ifnot;
      print "Let's have a quick game, then.^";
    #Endif;
      PlayTheGame();
    ];
which actually only compiles one of the two print statements, according to what the value of the constant MAX_SCORE is.

/\/\ Four more arcane directives control conditional compilation.
    Default <name> <value>;
defines <name> as a constant if it wasn't already the name of something: so it's equivalent to the manoeuvre
    Ifndef <name>;
    Constant <name> = <value>;
    Endif;
Similarly,
    Stub <name> <number>;
defines a routine with this name and number of local variables, if it isn't already the name of something: so it's equivalent to
    Ifndef <name>;
    [ <name> x1 x2 ... x<number>;
    ];
    Endif;

/\/\ Large blocks of code intended to be used in many different games, such as the files which make up the Inform library, should be marked somewhere with the directive
    System_file;
If this is done, it is possible for an outside program including the file to use Replace. The idea is that a sequence like:
    Replace DoSomething;
    ...
    Include "SomeLibrary";
    ...
    [ DoSomething; "Tarantaraa!"; ];
allows a routine DoSomething, which would normally be defined in the Include file "SomeLibrary", to be defined in this file instead. The definition in the Include file is simply ignored. In this way, one can override the library routines without actually having to modify the library source code. To recap, the rule here is that a routine's definition is ignored if both (a) it occurs in a declared "system file'', and (b) its name has been given in a Replace directive.

One way to follow what is being compiled is to use the Message directive. The compiler can be made to print messages at compile time using:

     Message "information"
     Message error "error message"
     Message fatalerror "fatal error message"
     Message warning "warning message"
For example,
    Ifndef VN_1610;
    Message fatalerror "This code can only be compiled by Inform 6.1";
    Endif;
(By a special rule, the condition VN_1610-is-defined is true if and only if the version number is 6.10 or more; similarly for other four-digit numbers beginning with a 1.) Informational messages are simply printed: e.g.,
    Message "Library extension by Boris J. Parallelopiped";
just prints out this line (with a carriage return).


4.3. Using the linker

The process of "linking'' is as follows. A game being compiled (called the "external'' program) may Link one or more pre-compiled sections of code called "modules''. Suppose the game Jekyll has a subsection called Hyde. Then these two methods of making Jekyll are, nearly, equivalent:

(i) -- Putting Include "Hyde"; in the source code for "Jekyll", and compiling "Jekyll".

(ii) -- Compiling "Hyde" with the -M ("module'') switch set, then putting Link "Hyde"; into the same point in the source code for "Jekyll", and compiling "Jekyll".

Option (ii) is much faster as long as "Hyde" does not change very often, since its ready-compiled module can be left lying around while "Jekyll" is being developed.

Because "linking the library'' is by far the most common use of the linker, this is made simple. All you have to do is compile your game with the -U switch set, or, equivalently, to begin your source code with

    Constant USE_MODULES;
(This assumes that you already have pre-compiled copies of the two library modules: if not, you'll need to make them with
    inform -M library.parserm
    inform -M library.verblibm
(where library.parserm should be replaced with the filename for your copy of the library file "parserm'', and likewise for "verblibm'').) Note that it is essential not to make any Attribute or Property declarations before the Include "Parser" line in the source code, though after that point is fine. (Library 6/2 and later will print an error message if you make this mistake, but under 6/1 it can be a source of mysterious problems.)

/\/\ You can also write your own library modules, or indeed subdivide a large game into many modular parts. But there are certain restrictions to the possibilities. (Real experts may want to look at the Technical Manual here.) Here's a brief list of these:

1. The module must make the same Property and Attribute directives as the main program. Including the library file "linklpa.h" ("link library properties and attributes'') declares the library's stock, so it would be sensible to begin a module with

    Include "linklpa";
and then include a similar file defining all the extra common properties and attributes which are needed by the program (if any).

2. The module cannot contain grammar (i.e., use Verb or Extend directives) or create fake actions.

3. The module can only use global variables defined outside the module if they are explicitly declared before use using the Import directive. For example,

     
    Import global frog;
allows the rest of the module's source code to refer to the variable frog (which must be defined in the outside program). Note that the Include file "linklv.h" ("link library variables'') imports all the library variables, so it would be sensible to include this.

4. An object in the module can't inherit from a class defined outside the module. (But an object outside can inherit from a class inside.)

5. Certain constant values in the module must be known at module-compile-time (and must not, for instance, be a symbol only defined outside the module). For instance: the size of an array must be known now, not later; the number of duplicate members of a Class; and the quantities being compared in an Iftrue or Iffalse.

6. The module can't: define the Main routine; use the Stub or Default directives; or define an object whose parent object is not also in the same module.

These restrictions are mild in practice. As an example, here is a short module to play with:

    Include "linklpa";        ! Make use of the properties, attributes
    Include "linklv";         ! and variables from the Library

    [ LitThings x;
      objectloop (x has light)
          print (The) x, " is currently giving off light.^";
    ];
It should be possible to compile this -M and then to Link it into another game, making the routine LitThings exist in that game.

Contents / Back / Forward
Chapter I / Chapter II / Chapter III / Chapter IV / Chapter V / Chapter VI / Appendix
Mechanically translated to HTML from third edition as revised 16 May 1997. Copyright © Graham Nelson 1993, 1994, 1995, 1996, 1997: all rights reserved.