Inform has a number of directives for controlling which pieces of source code are compiled: for instance, you can divide your source code into several files, compiled together or separately, or you can write source code in which different passages will be compiled on different occasions. Most of these directives are seldom seen, but almost every game uses:
Include "filename";
which instructs Inform to glue the whole of that source
code file into the program right here. 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: for instance,
it may automatically add an extension such as “.inf”
if your operating system normally uses filename extensions and it may
look in some special directory. Run Inform with the -h1 switch
for further 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 ‹name› defined as having some meaning? |
Ifndef ‹name›; |
Is ‹name› undefined? |
Iftrue ‹condition›; |
Is this ‹condition› true? |
Iffalse ‹condition›; |
Is this ‹condition› false? |
followed by a chunk of Inform and then, optionally,
Ifnot;
and another chunk of Inform; and finally
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.
▲ One kind of “if-defined” manoeuvre is so useful that it has an abbreviation:
Default
‹name›
‹value›;
defines ‹name› as a constant if it wasn't already the name of something: so it's equivalent to
Ifndef
‹name›; Constant
‹name› =
‹value›; Endif;
Similarly, though far less often used,
Stub <name> <number>;
defines a do-nothing
routine with this name and number (0 to 3) of local variables,
if it isn't already the name of something: it is equivalent to
Ifndef
‹name›; [
‹name›
x1 x2
…
x
‹number›; ]; Endif;
· · · · ·
Large standard chunks of Inform source code are often
bundled up into “libraries” which can be added to any
Inform story file using the Include
directive. Almost
all Inform adventure games include three library files called
“Parser”, “VerbLib” and “Grammar”,
and several dozen smaller libraries have also been written.
Sometimes, though, what you want to do is “include all of this
library file except for the definition of SomeRoutine
”.
You can do this by declaring:
Replace SomeRoutine;
before the relevant library file is included. You
still have to define your own SomeRoutine
, hence the
term “replace”.
▲▲
How does Inform know to ignore the SomeRoutine
definition
in the library file, but to accept yours as valid? The answer is that
a library file is marked out as having routines which can be replaced,
by containing the directive
System_file;
All eight of the standard Inform library files (the
three you normally Include
in games, plus the five others
which they Include
for you) begin with this directive.
It also has the effect of suppressing all compilation warnings
(but not errors) arising from the file.
· · · · ·
One way to follow what is being compiled is to use the
Message
directive. This makes the compiler print messages
as it compiles:
Message "An informational message"; Message error "An error message"; Message fatalerror "A fatal error message"; Message warning "A warning message";
Errors, fatal errors and warnings are treated as if
they had arisen from faults in the source code in the normal way. See
§40 for more about the kinds of error
Inform can produce, but for now, note that an error or fatal error will
prevent any story file from being produced, and that messages issued
by Message warning
will be suppressed if they occur in
a “system file” (one that you have marked with a
System_file
directive). Informational messages are
simply printed:
Message "Geometry library by Boris J. Parallelopiped";
prints this text, followed by a new-line.
▲ One reason to use this might be to ensure that a library file fails gracefully if it needs to use a feature which was only introduced on a later version of the Inform compiler than the one it finds itself running through. For example:
Ifndef VN_1610; Message fatalerror "The geometry extension needs Inform 6.1 or later"; Endif;
By special rule, the condition “VN_1610
is defined” is true if and only if the compiler's release number
is 6.10 or more; similarly for the previous releases 6.01, first to
include message-sending, 6.02, 6.03, 6.04, 6.05, 6.10, which expanded
numerous previous limits on grammar, 6.11, 6.12, which allowed Inform
to read from non-English character sets, 6.13, 6.15, which allowed
parametrised object creation, 6.20, which introduced strict error checking,
and finally (so far) 6.21, the first to feature Infix. A full history
can be found in the Technical Manual.
· · · · ·
Inform also has the ability to link together separately-compiled pieces of story file into the current compilation. This feature is provided primarily for users with slowish machines who would sooner not waste time compiling the standard Inform library routines over and over again. Linking isn't something you can do entirely freely, though, and if you have a fast machine you may prefer not to bother with it: the time taken to compile a story file is now often dominated by disc access times, so little or no time will be saved.
The pieces of pre-compiled story file are called “modules” and they cannot be interpreted or used for anything other than linking.
The process 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, very nearly, equivalent:
Include "Hyde";
in the source for
"Jekyll", and compiling "Jekyll".Link "Hyde";
into the same point in the source for "Jekyll", and
compiling "Jekyll".Option (2) is 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.h inform -M library/verblibm.h
where library/parserm.h should be replaced with whatever filename you keep the library file “parserm” in, and likewise for “verblibm”. This sounds good, but here are four caveats:
Attribute
or
Property
declarations before the Include
"Parser"
line in the source code, though after
that point is fine. Inform will warn you if you get this wrong.▲▲ If you intend to write your own pre-compilable library modules, or intend to subdivide a large game into many modular parts, you will need to know what the limitations are on linking. (In the last recourse you may want to look at the Technical Manual.) Here's a brief list:
Property
and
Attribute
directives as the main program and in the
same order. Including the library file "linklpa.h"
(“link library properties and attributes”) declares the
library's own stock, so it might be sensible to do this first, and
then include a similar file defining any extra common properties and
attributes you need.Verb
or Extend
directives) or create “fake actions”.Import
directive.
For example, writing 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.Class
; and
the quantities being compared in an Iftrue
or
Iffalse
.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.
· · · · ·
Every story file has a release number and a serial code. Games compiled with the Inform library print these numbers in one line of the “banner”. For instance, a game compiled in December 1998 might have the banner line:
Release 1 / Serial number 981218 / Inform v6.20 Library 6/8
The release number is 1 unless otherwise specified with the directive
Release <number>;
This can be any Inform number, but convention is for the first published copy of a game to be numbered 1, and releases 2, 3, 4,… to be amended re-releases.
The serial number is set automatically to the date
of compilation in the form “yymmdd”, so that 981218
means
“18th December 1998” and 000101
means
“1st January 2000”. You can fix the date differently by setting
Serial "dddddd";
where the text must be a string of 6 (decimal) digits. Inform's standard example games do this, so that the serial number will be the date of last modification of the source code, regardless of when the story file is eventually compiled.
▲
The Inform release number is written into the story file by Inform
itself, and you can't change it. But you can make the story file print
out this number with the special statement inversion;
.