Contents
Back
Forward

1. The language of routines


1.1. First principles

This chapter aims to introduce beginners to Inform as though it were a general-purpose programming language (rather than a tool for designing adventure games). The examples given will be short programs performing simple calculations (rather than games). To begin with, the Inform language is:

1. -- Compiled.

-- That is, the Inform compiler translates text written by the author (called the "source code'') into a program (called the "object code'' since it is the object of the exercise). This translation is only done once, but the resulting program can be run many times.

2. -- Procedural.

-- That is, a program is divided into a number of "routines'' (also called "functions'' or "procedures''), each being a list of orders to be obeyed (though these orders are traditionally called "statements''). When the program is run, only one thing happens at a time: at any given moment, a single routine is being obeyed.

3. -- Object-oriented.

-- That is, the fabric of a typical Inform program will be woven around "objects'' being dealt with, which are regarded as being self-contained. For example, a program to simulate a warehouse might have objects representing lorries and containers; each object would have a position and contents at any given time. The program would have general rules describing "lorry'' and "container'' as well as actual examples of each. A lorry would have the ability to receive a message telling it to do something, such as "load up with a container and leave the warehouse''.

4. -- Portable.

-- That is, once Inform has compiled the source code (having found no mistakes), the resulting program can be run on almost any model of computer. It will exhibit exactly the same behaviour on each of them. It cannot depend on the "environment'': it cannot suddenly run out of memory and crash, for instance.

The computer runs an Inform program (which need not be a game) with the aid of an "interpreter''. There are at least 40 different interpreters available for this format (called the "Z-machine'' or "Infocom format'') and there may be a choice available for your model of computer: it is a good idea to get the most modern and accurate possible. Look to see if they support the Z-Machine Standard, and if so, up to what revision number.


1.2. Example 1: Hello World

Traditionally, all programming language tutorials begin by giving a program which does nothing but print "Hello world'' and stop. Here is such a program in Inform:

    !  "Hello world" example program

    [ Main;
      print "Hello world^";
    ];
The text after the exclamation mark is a "comment'', that is, it is text written in the margin by the author to remind himself of what is going on here. Such text means nothing to Inform, which ignores anything on the same line and to the right of an exclamation mark.

Once commentary has been stripped out, Inform regards the source code as a list of things to look at, divided by semicolons ;. It treats line breaks, tab characters and spaces all as so-called "white space'': that is, a gap between two things whose size is unimportant. Thus, exactly the same program would be produced by the source code

       [        
          Main   ;
    print
             "Hello world^"          ;
        ] 
      ;
or, at the other extreme, by
    [ Main;print"Hello world^";];
Laying out programs legibly is a matter of forming good habits.

/\ The exception to the rule about ignoring white space is inside quoted text, where
Hello    world^
and
Hello world^
are genuinely different pieces of text and are treated as such. Inform treats text inside quotation marks with much more care than its ordinary program material: for instance, an exclamation mark inside quotation marks will not cause the rest of its line to be thrown away as a comment.

Every program must contain a routine called Main, and in this example it is the only routine. When a program is set running, the first instruction obeyed is the first one in Main, and it carries on line by line from there. This process is called "execution''. When the Main routine is finished, the program stops.

The routine has only one statement:

    print "Hello world^"
Printing is the process of writing text onto the computer screen. This statement prints the two words "Hello world'' and then skips the rest of the line (or "prints a new-line''): the ^ character, in quoted text, means "new-line''. For example, the statement
    print "Blue^Red^Green^"

prints up:

Blue
Red
Green
print is one of 28 statements in the Inform language. The full list is as follows:
box        break      continue   do       font      for         give
if         inversion  jump       move     new_line  objectloop  print
print_ret  quit       read       remove   restore   return      rfalse
rtrue      save       spaces     string   style     switch      while
(Only about 20 of these are commonly used.) Section 1 covers all those not concerned with objects, which are left until Section 3.


1.3. Example 2: Elsinore

The following source code has three routines, Main, Rosencrantz and Hamlet:

    [ Main;
      print "Hello from Elsinore.^";
      Rosencrantz();
    ];
    [ Rosencrantz;
      print "Greetings from Rosencrantz.^";
    ];
    [ Hamlet;
      print "The rest is silence.^";
    ];

The resulting program prints up

Hello from Elsinore.
Greetings from Rosencrantz.
but the text "The rest is silence.'' is never printed. Execution begins at Main, and "Hello from Elsinore'' is printed; next, the statement Rosencrantz() causes the Rosencrantz routine to be executed. That continues until it ends with the close-routine marker ], whereupon execution goes back to Main just after the point where it left off: since there is nothing more to do in Main, the program finishes. Thus, Rosencrantz is executed but Hamlet is not.

In fact, when the above program is compiled, Inform notices that Hamlet is never needed and prints out a warning to that effect. The exact text produced by Inform varies from machine to machine, but will be something like this:

    RISC OS Inform 6.03 (May 11th 1996)
    line 8: Warning: Routine "Hamlet" declared but not used
    Compiled with 0 errors and 1 warning
Errors are mistakes in the program which cause Inform to refuse to compile it, but this is only a warning. It alerts the programmer that a mistake may have been made (because presumably the programmer has simply forgotten to put in a statement calling Hamlet) but it doesn't prevent the compilation from taking place. Note that the opening line of the routine Hamlet occurs on the 8th line of the program above.

Usually there are mistakes in a newly-written program and one goes through a cycle of running a first draft through Inform, receiving a batch of error messages, correcting the draft according to these messages, and trying again. A typical error message would occur if, on line 3, we had mistyped Rosncrantz() for Rosencrantz(). Inform would then have produced:

    RISC OS Inform 6.03 (May 11th 1996)
    line 5: Warning: Routine "Rosencrantz" declared but not used
    line 8: Warning: Routine "Hamlet" declared but not used
    line 3: Error: No such constant as "Rosncrantz"
    Compiled with 1 error and 2 warnings (no output)
The error message means that on line 3 Inform ran into a name which did not correspond to any known quantity (it isn't the name of any routine, in particular). Note that Inform never produces the final story file if errors occur during compilation: this prevents it from producing damaged story files. Note also that Inform now thinks the routine Rosencrantz is never used, since it didn't recognise the mistype in the way that a human reader would have done. Warnings are sometimes produced by accident this way, so it is generally a good idea to worry about fixing errors first and warnings afterward.


1.4. Numbers and variables

Internally -- that is, whatever the outward appearance -- all programs essentially manipulate numbers. Inform understands "number'' to be a whole number in the range -32768 to +32767. (Special programming would be required to represent larger numbers or fractions.) There are three notations for writing numbers in Inform: here is an example of each.

    -4205
    $3f08
    $$1000111010110
The difference is the radix, or base, in which they are expressed. The first is in decimal (base 10), the second hexadecimal (base 16, where the digits after 9 are written a to f or A to F) and the third binary (base 2). Once Inform has read in a number, it forgets which notation was used: for instance, if the source code is altered so that $$10110 is replaced by 22, this makes no difference to the program produced.

A print statement can print numbers as well as text, though it always prints them back in ordinary decimal notation. For example, the program

    [ Main;
      print "Today's number is ", $3f08, ".^";
    ];

prints up

Today's number is 16136.
since 16136 in base 10 is the same number as 3f08 in hexadecimal.

Inform recognises many other notations as "constants'', that is, values which are literally described in the source code. A full list will appear later, but one other is that a single character between single quotation marks, for instance

    'x'
is a constant. A "character'' is a single letter or typewriter-symbol, and all that the programmer needs to know is that each possible character has its own numerical value.

/\ For most characters, this numerical value is the standard ASCII value for the character: for instance, 'x' has numerical value 120. (This is true even if Inform is being run on a model of computer which doesn't normally use the ASCII character set.) Exotic characters such as '@ss' (the Inform notation for German sz) have non-standard codes: see the Z-Machine Standards Document if you really need to know.

Finally, in this initial batch of constant notations, Inform provides two special constants:

true
false
which are used to describe the truth or otherwise of possible conditions.

/\ true has the numerical value 1; false has the numerical value 0.

Inform has a concept of "variable'' like that used in algebra, where it is easy but limiting to express facts using only numbers:

34 - 34 = 0

11 - 11 = 0

694 - 694 = 0

Although suggestive this fails to express the general case: that any number subtracted from itself leaves zero. We express this fact symbolically in algebra by writing
x - x = 0
where x is a variable; the implication being "whatever value x actually is, the statement is still true".

Likewise, in Inform what seems to be a word of text may be a variable which represents a number: when the source code is compiled, Inform cannot know what numerical value this text represents. When the program is run, it will always have a numerical value at any given time. If oil_left is a variable, the statement

    print "There are ", oil_left, " gallons remaining.^";
is executed as if oil_left were replaced by whatever that value currently is. Later on, the same statement may be executed again, producing different text because by that time oil_left has a different value.

Inform can only know that text (such as oil_left) represents a variable if the source code has "declared'' that it does. Each routine can declare its own selection of variables on its opening line. For example, in the program

    [ Main alpha b;
      alpha = 2200;
      b = 201;
      print "Alpha is ", alpha, " while b is ", b, "^";
    ];
the Main routine has two variables, alpha and b. Like most names given in source code (called "identifiers''), variable names can be at most 32 characters long and may contain letters of the alphabet, decimal digits or the underscore _ character (often used to imitate a space). To prevent them looking too much like numbers, though, they may not start with a decimal digit. (So a44 is legal but 44a is not.) For example:
    turns_still_to_play
    chart45
    X
are all possible variable names. Inform ignores any difference between upper and lower case letters in such names, for example considering CHArt45 as the same name as chArT45.

The = sign occurring twice in the above routine is an example of an "operator'': a notation usually made up of the symbols on the non-alphabetic keys on a typewriter and which means something is to be done with the items it is written next to. In this context, = means "set equal to''. When the statement alpha = 2200 is executed at run time, the current value of the variable alpha becomes 2200 (and it keeps that value until another such statement changes it).

The variables alpha and b are called "local variables'' because they are local to Main: in effect, they are its private property. The program

    [ Main alpha;
      alpha = 2200;
      Rival();
    ];
    [ Rival;
      print alpha;
    ];
causes an error on the print statement in Rival, since alpha does not exist there. Indeed, Rival could even have defined a variable of its own also called alpha and this would have been a separate variable with a probably different value.


1.5. Arithmetic expressions

The Inform language is rich with operators, making it concise but not always very readable. Feeling comfortable with the operators is the main step towards being able to follow Inform source code. Fortunately, these operators are based on the usual rules for writing arithmetic formulae, which gives them a headstart in familiarity.

Indeed, the most commonly used operators are "arithmetic'': they combine one or more numbers to give one resulting number. Whenever a number is expected in a statement, a general "expression'' can be given instead: that is, a calculation giving a number as a result. For example, the statement

    seconds = 60*minutes + 3600*hours
sets the variable seconds equal to 60 times the variable minutes plus 3600 times the variable hours. White space is not needed between operators and "operands'' (the numbers to be operated on): the spaces on either side of the + sign are only provided for legibility.

Ordinary arithmetic is carried out with the operators + (plus), - (minus), * (times) and / (divided by).

Usually dividing one integer by another leaves a remainder: for example, 3 goes into 7 twice, with remainder 1. In Inform notation,

7/3 evaluates to 2
7%3 evaluates to 1
the % operator meaning "remainder after division'', usually called just "remainder''. Dividing by zero is impossible and a program which tries to do this will go wrong.

/\ As a brief aside, this gives an example of how Inform can and can't help the programmer to spot mistakes. The program
    [ Main;
      print 73/0;
    ];
produces an error when compiled:
    line 2: Error: Division of constant by zero
    >   print 73/0;
since Inform can see that it definitely involves doing something illegal. However, Inform fails to notice anything amiss with the equivalent program
    [ Main x;
      x = 0;
      print 73/x;
    ];
and this program compiles correctly. The resulting story file will "crash'' when it is run, that is, catastrophically halt. The moral is that just because Inform compiles a program without errors, it does not follow that the program does what the programmer intends.

In a complicated expression the order in which the operators work may affect the result. As most human readers would, Inform works out both of

    23 + 2 * 700
    2 * 700 + 23
to 1423, because the operator * has "precedence'' over + and so is acted on first. Brackets may be used to overcome this:
    (23 + 2) * 700
    2 * (700 + 23)
evaluate to 17500 and 1446 respectively. Each operator has such a "precedence level''. When two operators have the same precedence level (for example, + and - are of equal precedence) calculation is (almost always) "left associative'', that is, carried out left to right: the notation
    a - b - c
is equivalent to
    (a - b) - c
The standard rules for writing mathematics give + and - equal precedence, lower than that of * and / (which are also equal). Inform agrees and also pegs % equal to * and /.

The final purely arithmetic operator is "unary minus''. This is written as a minus sign - but is not the same as ordinary subtraction. The expression:

    -credit
means the same thing as:
    0 - credit
The operator - is different from all those mentioned so far because it operates only on one number. It has higher precedence than any of the five "binary'' operations above. For example,
    -credit - 5
means (-credit) - 5 and not -(credit - 5).

One way to imagine precedence is to think of it as glue attached to the operator. A higher level means stronger glue. Thus, in

    23 + 2 * 700
the glue around the * is stronger than that around the +, so that 2 and 700 belong bound to the *.

Some operators do not simply act on values but actually change the current values of variables: expressions containing these are called "assignments'' (because they assign values as well as working them out). One such operator is 'set equals':

    alpha = 72
sets the variable alpha equal to 72. Just like + and the others, it also comes up with an answer: as it happens, this value is also 72.

The other two assignment operators are ++ and --, which will be familiar to any C programmer. They are unary operators, and can be used in any of the following ways:

    variable++
    ++variable
    variable--
    --variable
The first of these means "read off the value of variable, and afterwards increase that value by one''. In ++variable the "increment'' (or increase by 1) happens first, and then the value is read off. -- acts in a similar way but "decrements'' (decreases by 1). These operators are provided as convenient shorthand forms, since their effect could usually be achieved in other ways (just using + and -).

For example, suppose the variable has value 12. Then the result would be 12, 13, 12 or 11 respectively; the value left in variable afterwards would be 13, 13, 11 or 11.

Note that expressions like

    500++        (4*alpha)--     34 = beta
are meaningless: the values of 500 and 34 cannot be altered, and Inform knows no way to adjust alpha so as to make 4*alpha decrease by 1. All three will cause errors.

/\ "Bitwise operators'' are provided for manipulating binary numbers on a digit-by-digit basis, something which is often done in programs which are working with low-level data or data which has to be stored very compactly. Inform provides &, bitwise AND, |, bitwise OR and ~, bitwise NOT. For each digit, such an operator works out the value in the answer from the values in the operands. Bitwise NOT acts on a single operand and results in the number whose i-th binary digit is the opposite of that in the operand (a 1 for a 0, a 0 for a 1). Bitwise AND (and OR) acts on two numbers and sets the i-th digit to 1 if both operands have (either operand has) i-th digit set. So, for example,
    $$10111100 & $$01010001  ==  $$00010000

/\ The remaining operators will be described as needed: the full table is laid out in Appendix A1.


1.6. Arguments and Return Values

As has already been said, in Inform jargon the word "function'' is synonymous with "routine''. A function might be defined as a correspondence

(x1, ..., xn) ---> f(x1, ..., xn)
where a set of input numbers are fed in, and a single value comes out. These input numbers are called "arguments''. The value coming out is the "return value'', or is said to be "returned''.

All Inform routines are like this. A number of arguments are fed in when the routine is "called'' (that is, set running) and there is always a single numerical result. This result is called the "return value'' because it is returned to the rest of the program. Some very simple routines conceal this. For instance, consider Sonnet:

    [ Main;
      Sonnet();
    ];
    [ Sonnet;
      print "When to the sessions of sweet silent thought^";
      print "I summon up remembrance of things past^";
    ];
Sonnet is a routine which takes as input no arguments at all (it is an example of the $n=0$ case), so it is called with nothing in between the round brackets. Although it does return a value (as it happens, this value is true) the statement Sonnet() simply calls the routine and throws the return value away. If Main were instead given by
    [ Main;
      print Sonnet();
    ];

then the output would be

When to the sessions of sweet silent thought
I summon up remembrance of things past
1
because the print statement in Main has been told to print the number resulting from a call to Sonnet.

Thus in Inform there is no such thing as a "void function'' or "procedure'': every routine returns a number even though this may immediately be thrown away as unwanted.

When a routine is called,

    Routine(arg1, ...)
the arguments given are substituted into the first variables declared for Routine, and execution begins running through Routine. Usually, there can be any number of arguments from none up to 7, though a limit of 3 applies if Inform has been told to compile an early-model story file (see Section 31 for details).

If execution runs into the ] end-of-routine marker, so that the routine is finished without having specified any definite return value, then this value is true. (This is why the printed return value of Sonnet is 1: true has the value 1.)


1.7. Example 3: Cubes

A more typical, though less aesthetic, example than Sonnet:

    [ Main;
      print Cube(1), " ";
      print Cube(2), " ";
      print Cube(3), " ";
      print Cube(4), " ";
      print Cube(5), "^";
    ];
    [ Cube x;
      return x*x*x;
    ];

which, when executed, prints

1 8 27 64 125
The expression Cube(3) is calculated by substituting the number 3 into the variable x when Cube is set running: the result of the expression is the number returned by Cube.

Any "missing arguments'' in a routine call are set equal to zero, so the call Cube() is legal and does the same as Cube(0).


1.8. Conditions: if, true and false

Such routines are too simple, so far, even to express many mathematical functions, and more flexibility will be needed.

A "control construct'' is a kind of statement which controls whether or not, and if so how many times or in what order, other statements are executed. The simplest of these is if:

if (<condition>) <statement>
which executes the <statement> only if the <condition>, when it is tested, turns out to be true. For example, when the statement
    if (alpha == 3) print "Hello";
is executed, the word "Hello'' is printed only if the variable alpha currently has value 3. It is important not to confuse the == operator (test whether or not equal to) with the = operator (set equal to).

Conditions are always given in (round) brackets. The basic conditions are as follows:
(a == b) Number a equals number b
(a ~= b) Number a doesn't equal number b
(a >= b) a is greater than or equal to b
(a <= b) a is less than or equal to b
(a > b) a is greater than b
(a < b) a is less than b
(o1 in o2) Object o1 possessed by o2
(o1 notin o2) Object o1 not possessed by o2
(o1 has a) Object o1 has attribute a
(o1 hasnt a) Object o1 hasn't attribute a
(o1 provides m) Object o1 provides property m
(o1 ofclass c) Object o1 inherits from class c
(The conditions relating to objects will be discussed later.) A useful extension to this set is provided by the special operator or, which gives alternative possibilities. For example,

    if (alpha == 3 or 4) print "Scott";
    if (alpha ~= 5 or 7 or 9) print "Amundsen";
where two or more values are given with the word or between. Scott is printed if alpha has value either 3 or 4, and Amundsen if the value of alpha is not 5, is not 7 and is not 9. or can be used with any of the conditions, and any number of alternatives can be given. For example
    if (player in Forest or Village or Building) ...
often makes code much clearer than writing three separate conditions out; or
    if (x > 100 or y) ...
can be convenient to check whether x is bigger than the minimum of 100 or y.

Conditions can also be built up from simpler ones (just as long expressions are built up from single operators) using the three logical operators &&, || and ~~ (pronounced "and'', "or'' and "not''). For example,

    if (alpha == 1 && (beta > 10 || beta < -10)) print "Lewis";
    if (~~(alpha > 6)) print "Clark";
"Lewis'' is printed if alpha equals 1 and beta is outside the range -10 to 10; "Clark'' is printed if alpha is less than or equal to 6.

The discussion above makes it look as if conditions are special kinds of expression which can only use certain operators (==, &&, or and so on). But this is not true: conditions are expressions like any other. It's legal to write

    print (beta == 4);

for instance, and this results in 1 being printed if beta equals 4, and 0 otherwise. Thus:

the result of a true condition is 1;
the result of a false condition is 0.
This is why true and false are defined to be 1 and 0 respectively. Thus one might write code along the lines of
    betaisfour = (beta == 4);
    ...
    if (betaisfour == true) ...
though it would be easier to write
    betaisfour = (beta == 4);
    ...
    if (betaisfour) ...
because, just as conditions can be used as numbers, so numbers can be used as conditions. Zero is considered to be "false'', and all other values are considered to be "true''. Thus
    if (1) print "Magellan";
    if (0) print "da Gama";
always results in "Magellan'', never "da Gama'', being printed.

One common use of variables is as "flags''. A flag can only hold the value 0 or 1, false or true according to some state of the program. The fact that a number can be used as a condition allows natural-looking statements like

    if (lower_caves_explored) print "You've already been that way.";
where lower_caves_explored is a variable being used in the program as a flag.

/\ Note that && and || only work out what they absolutely need to in order to decide the truth. That is,
    if (A && B) ...
will work out A first. If this is false, there's no need to work out B, and it never is worked out. Only if A is true is B actually tested. This only matters when working out conditions like
    if (x==7 && Routine(5)) ...
where it can be important to know that the Routine is never called if x has a value other than 7.


1.9. Example 4: Factorials

The factorial of a positive integer n is defined as the product $$ 1 \times 2 \times 3 \times ... \times n $$ so that, for example, the factorial of 4 is 24. Here is an Inform routine to calculate factorials:

    [ Main;
      print Factorial(7), "^";
    ];
    [ Factorial n;
      if (n==1) return 1;
      return n*Factorial(n-1);
    ];
This calculates 7 factorial and comes up with 5040. (Factorials grow rapidly and 8 factorial is already too large to hold in a standard Inform number, so calling Factorial(8) would give a wrong answer.)

The routine Factorial actually calls itself: this is called "recursion''. Execution reaches "seven routines deep'' before starting to return back up. Each of these copies of Factorial runs with its own private copy of the variable n.

Recursion is hazardous. If one calls the routine

    [ Disaster;
      return Disaster();
    ];
then despite the reassuring presence of the word return, execution is tied up forever, unable to finish evaluating the return value. The first call to Disaster needs to make a second before it can finish; the second needs to make a third; and so on. This is an example of a programming error which will prove disastrous when the program is run, yet will cause no errors when the source code is compiled. (It can be proved that it is impossible to construct a compiler capable of detecting this general class of mistake. Inform does not even try.)


1.10. Code blocks, else and switch

A feature of all control constructs is that instead of just giving a <statement>, one can give a list of statements grouped together into a unit called a "code block''. Such a group begins with an open brace { and ends with a close brace }. For example,

    if (alpha > 5)
    {   print "The square of alpha is ";
        print alpha*alpha;
        print ".^";
    }

If alpha is 3, nothing is printed; if alpha is 9,

The square of alpha is 81.
is printed. (As usual the layout is a matter of convention: it is usual to write code blocks on margins indented inwards by some standard number of characters.) In some ways, code blocks are like routines, and at first it may seem inconsistent to write routines between [ and ] brackets and code blocks between braces { and }. However, code blocks cannot have private variables of their own and do not return values: and it is possible for execution to break out of code blocks again, or to jump from block to block, which is impossible with routines.

An if statement can optionally have the form

if (<condition>) <statement1> else <statement2>
in which case <statement1> is executed if the condition is true, and <statement2> if it is false. For example,
    if (alpha == 5) print "Five."; else print "Not five.";
Note that the condition is only checked once. The statement
    if (alpha == 5)
    {   print "Five.";
        alpha = 10;
    }
    else print "Not five.";
cannot ever print both "Five'' and then "Not five''.

The else clause has a snag attached: the problem of "hanging elses''.

    if (alpha == 1)
        if (beta == 2)
            print "Clearly if alpha=1 and beta=2.^";
        else
            print "Ambiguous.^";
is ambiguous as to which if statement the else attaches to. The answer (in Inform 6, though this has changed since earlier versions of the language) is that an else always pairs to its nearest if, unless there is bracing to indicate the contrary. Thus the else above pairs with the beta condition, not the alpha condition.

In any case it is much safer to use braces to express what is meant, as in:

    if (alpha == 1)
    {   if (beta == 2)
            print "Clearly if alpha=1 and beta=2.^";
        else
            print "Clearly if alpha=1 but beta not 2.^";
    }
The if...else... construct is ideal for switching execution between two possible "tracks'', like railway signals, but it is a nuisance trying to divide between many different outcomes this way. To follow the analogy, the construct switch is like a railway turntable.
    print "The train on platform 1 is going to ";
    switch(DestinationOnPlatform(1))
    {   1: print "Dover Priory.";
        2: print "Bristol Parkway.";
        3: print "Edinburgh Waverley.";
    }
Each possible value must be a constant, so
    switch(alpha)
    {   beta: print "The variables alpha and beta are equal!";
    }
is illegal.

Any number of outcomes can be specified, and values can be grouped together to a common outcome. For example,

    print "The mission STS-", num, " was flown on the Space Shuttle";
    switch(num)
    {   1 to 5, 9: print " Columbia.";
        6 to 8:    print " Challenger.";
        10 to 25:  if (num == 12) print " Discovery";
                   print ", but it was given a flight number like 51-B.";
        default:   print ".";
    }

will result in a true statement being printed (as long as num is between 1 and, at time of writing, 78), if an incomplete one. The default clause is executed if the original expression matches none of the other values, and it must always come last if given at all. In this case, it means that if num is 62, then

The mission STS-62 was flown on the Space Shuttle.
is printed.

Note that each clause is automatically a code block and needs no braces { to } to delimit it from the rest of the routine: this shorthand makes switch statements much more legible.


1.11. while, do...until, for, break, continue

The other four Inform control constructs are all "loops'', that is, ways to repeat the execution of a given statement (or code block). Discussion of one of the four, called objectloop, is deferred until Section 3.4.

The two basic forms of loop are while and do...until:

while (<condition>) <statement>
do <statement> until (<condition>)
The first repeatedly tests the condition and, provided it is still true, executes the statement. (If the condition is not even true the first time, the statement is never executed.) For example:
    [ SquareRoot n;
      x = n;
      while (x*x > n) x=x-1;
      return x;
    ];
a (fairly chronic) method for finding square roots. (If SquareRoot(200) is called, then x runs down through the values 200, 199, ..., 14, at which point x*x <= n since $14\times 14 = 196$.)

The do...until loop repeats the given statement until the condition is found to be true. (Even if the condition is already satisfied, like (true), the statement is always executed the first time through.)

One particular kind of while loop is needed so often that there is an abbreviation for it, called for. For example,

    counter = 1;
    while (counter <= 10)
    {   print counter, " ";
        counter++;
    }

which produces the output

1 2 3 4 5 6 7 8 9 10
(Recall that counter++ adds 1 to the variable counter.) Languages like BASIC make extensive use of this kind of loop. For example, in BBC BASIC, the above loop would be written
    FOR counter = 1 TO 10
        PRINT counter;" ";
    NEXT
NEXT is a word which (slightly clumsily) means "the code block ends here'', and is therefore the equivalent of Inform's }. The whole is used to mean "for values of the counter running through 1 to 10, do...'', hence the choice of the word FOR.

Inform (like the language C) uses a more flexible construct than this, but which is still called for. It can produce any loop in the form

<start>
while (<condition>)
{ ...
<update>
}
where <start> and <update> are assignments. The notation to achieve this is
for (<start> : <condition> : <update>) ...
For example, the loop described above is achieved by
    for (counter=1 : counter<=10 : counter++)
        print counter, " ";
Note that if the condition is false even the first time, the loop is never executed. For instance,
    for (counter=1 : counter<0 : counter++)
        print "Banana";
prints nothing.

/\ At this point it is worth mentioning that several assignments can be combined into a single statement in Inform. For example,
    i++, score=50, j++
(three assignments separated by commas) is a single statement. This is never useful in ordinary code, where the assignments can be divided up by semicolons in the usual way. In for loops it is useful, though:
    for (i=1, j=5: i<=5: i++, j--) print i, " ", j, ", ";
produces the output "1 5, 2 4, 3 3, 4 2, 5 1,''.

Any of the three parts of a for statement can be omitted. If the condition is missed out, it is assumed to be always true, i.e. there is no check made to see if the loop should be ended and so the loop continues forever.

On the face of it, the following loops all repeat forever:

while (true) <statement>
do <statement> until (false)
for (::) <statement>
But there is always an escape. One way is to return from the current routine. Another is to jump to a label outside the loop (jump will be covered in Section 1.13 below). It's neater to use the statement break, which causes execution to "break out of'' the current innermost loop or switch statement: it can be read as "finish early''. All these ways out are entirely "safe'', and there is no harm in leaving a loop only half-done.

The other simple statement used inside loops is continue. This causes the current iteration to end immediately, but does not end the whole loop. For example,

    for (i=1: i<=5: i++)
    {   if (i==3) continue;
        print i, " ";
    }
will output "1 2 4 5''.


1.12. Example 5: A number puzzle

The routine RunPuzzle is an interesting example of a loop which, though apparently simple enough, contains a trap for the unwary.

    [ RunPuzzle n count;

      do
      {   print n, " ";
          n = NextNumber(n);
          count++;
      }
      until (n==1);
      print "1^(taking ", count, " steps to reach 1)^";
    ];
    [ NextNumber n;
      if (n%2 == 0) return n/2;     ! If n is even, halve it
      return 3*n + 1;               ! If n is odd, triple and add 1
    ];

The call RunPuzzle(10), for example, results in the output

10 5 16 8 4 2 1
(taking 6 steps to reach 1)
The source code assumes that, no matter what the initial value of n, enough iteration will end up back at 1. If this did not happen, the program would lock up into an infinite loop, printing numbers forever.

The routine is apparently very simple, so it would seem reasonable that by thinking carefully enough about it, we ought to be able to decide whether or not it is "safe'' to use (i.e., whether it can be guaranteed to finish or not).

And yet nobody knows whether this routine is "safe''. The conjecture that all n eventually step down to 1 is at least fifty years old but has never been proved, having resisted all mathematical attack. (Alarmingly, RunPuzzle(27) takes 111 iterations to fall back down to 1.)


1.13. quit, jump and the program state

There are four statements left which control the flow of execution. quit ends the program immediately (as if a return had taken place from the Main routine). This drastic measure is best reserved for points in the program which have detected some error condition so awful that there is no point carrying on. Better yet, do not use it at all.

The jump statement transfers execution to some other named place in the same routine. (Some programming languages call this goto. Since it can be and has been put to ugly uses, the construct itself was at one time frowned on as a vulgar construct leading programmers into sin. Good use of control constructs will almost always avoid the need for jump and result in more legible programs. But sin is universal.)

To use jump a notation is needed to mark particular places in the source code. Such markers are called "labels''. For example:

    [ Main i;
      i=1;
      .Marker;
      print "I have now printed this ", i++, " times.^";
      jump Marker;
    ];
This program has one label, Marker. A statement consisting only of a full stop and then an identifier means "put a label here and call it this''.

/\/\ An Inform program has the ability to save a snapshot of its entire state and to restore back to that previous state. This snapshot includes values of variables, the point where code is currently being executed, and so on. Just as we cannot know if the universe is only six thousand years old, as creationists claim, having been endowed by God with a carefully faked fossil record; so an Inform program cannot know if it has been executing all along or if it was only recently restarted. The statements required are save and restore:
save <label>
restore <label>
This is a rare example of an Inform feature which may depend on the host machine's state of health: for example, if all disc storage is full, then save will fail. It should always be assumed that these statements may well fail. A jump to the label provided occurs if the operation has been a success. (This is irrelevant in the case of a restore since, if all has gone well, execution is now resuming from the successful branch of the save statement: because that is where execution was when the state was saved.)


1.14. Printing output

When text is printed, normally each character is printed exactly as specified in the source code. Four characters, however, have special meanings. As explained above means "print a new-line''. The character ~, meaning "print a quotation mark'', is needed since quotation marks otherwise finish strings. Thus,

    "~Look,~ says Peter. ~Socks can jump.~^Jane agrees."

is printed as

"Look,'' says Peter. "Socks can jump.''
Jane agrees.
The third remaining special character is @, which is used for accented characters and other unusual effects, as described below. Finally, \ is reserved for "folding lines'', and used to be needed in Inform 5 when text spilled over more than one line. (It's no longer needed but kept so that old programs still work.) If you really want to print a ~, a ^, an @ or a \, see below.

Text still spills over more than one line, even in the present golden age of Inform 6. When a statement like

    print "Here in her hairs
           the painter plays the spider, and hath woven
           a golden mesh t'untrap the hearts of men
           faster than gnats in cobwebs";
is read in by Inform, the line breaks are replaced with a single space each. Thus the text printed is: "Here in her hairs the painter plays the spider, and hath woven a golden mesh...'' and so on. (There is one exception: if a line finishes with a ^ (new-line) character, then no space is added before the next line begins.)

So far, only the print statement has been used for printing, to print both numbers and strings (that is, double-quoted pieces of text). Since Inform is primarily a language for writing Adventure games, its business is text, and it provides many other facilities for printing.

new_line
is a statement which simply prints a new-line (otherwise known as a carriage return, as if the lever on the carriage of an old manual typewriter had been pulled to move it right back to the left margin and turn it forward one line). This is equivalent to
print "^"
but is a convenient abbreviation. Similarly,
spaces <number>
prints a sequence of that many spaces.
inversion
prints the version number of Inform which was used to compile the program (it might, for instance, print "6.01'').
box <string1> ... <stringn>
displays a reverse-video box in the centre of the screen, containing
string1
string2
...
stringn
and is usually used for popping up quotations: for example,
    box "Passio domini nostri" "Jesu Christi Secundum" "Joannem"

displays

Passio domini nostri
Jesu Christi Secundum
Joannem
(the opening line of the libretto to Arvo P\"art's 'St John Passion').

Text is normally displayed in ordinary (or "Roman'') type. Its actual appearance will vary from machine to machine running the program. On many machines, it will be displayed using a "font'' which is variably-pitched, so that for example a "w'' will be wider on-screen than an "i''. Such text is much easier to read, but makes it very difficult to print out diagrams. The statement

    print "+------------+
          ^+   Hello    +
          ^+------------+^";
will print something quite irregular if the characters "-'', "+'' and " '' (space) do not all have the same width. Because one sometimes does want to print such a diagram (to represent a sketch-map, say, or to print out a table), the statement font is provided:
    font on
    font off
font off switches into a fixed-pitch display style (in which all characters definitely have the same width); font on goes back to the original.

In addition to this, a few textual effects can be achieved.

    style roman
switches to ordinary Roman text (the default), and there are also
    style bold
    style underline
    style reverse
(reverse meaning "reverse colour'': e.g. yellow on blue if the normal text appearance is blue on yellow). An attempt will be made to approximate these effects on any machine, but it may be that underline comes out as italicised text, for example, or that bold is rendered by printing ordinary Roman text but in a different colour.

Inform programs are starting to be written which communicate in languages other than English: Italian, Dutch, German, French and Spanish games have all been attempted. A comprehensive range of accented characters is available: these are reached with the aid of the escape character, @.

Most accented characters are written as @, followed by an accent marker, then the letter on which the accent appears:
@^ put a circumflex on the next letter: a,e,i,o,u,A,E,I,O or U
@' put an acute on the next letter: a,e,i,o,u,y,A,E,I,O,U or Y
@' put a grave on the next letter: a,e,i,o,u,A,E,I,O or U
@: put a diaeresis on the next letter: a,e,i,o,u,A,E,I,O or U
@c put a cedilla on the next letter: c or C
@~ put a tilde on the next letter: a,n,o,A,N or O
@\ put a slash on the next letter: o or O
@o put a ring on the next letter: a or A
In addition, there are a few others:
@ss German sz
@<< continental European quotation marks
@>>
@ae ligatures
@AE
@oe
@OE
@th Icelandic accents
@et
@Th
@Et
@LL pound sign
@!! Spanish (upside-down) exclamation mark
@?? Spanish (upside-down) question mark
For instance,

    print "Les @oeuvres d'@Aesop en fran@ccais, mon @'el@`eve!";
    print "Na@:ive readers of the New Yorker will re@:elect Mr Clinton.";
    print "Carl Gau@ss first proved the Fundamental Theorem of Algebra.";
Accented characters can also be referred to as constants, like other characters. Just as 'x' represents the character lower-case-X, so '@^A' represents capital-A-circumflex.

/\ The @ escape character has two other uses. One gets around the problem that, so far, it is impossible to print an "@". A double @ sign, followed by a number, prints the character with this numerical code. The most useful cases are:
@@92 comes out as "\''
@@64 comes out as "@''
@@94 comes out as "^''
@@126 comes out as "~''
enabling us to print the four characters which can't be typed directly because they have other meanings.

/\/\ The second use is more obscure. Inform keeps a stock of 32 pseudo-variables to hold text, numbered from 0 to 31.
@00 prints out as the current contents of string 0
... ...
@31 prints out as the current contents of string 31
and these variables are set with the string statement:
    string 0 "toadstool";
sets string 0 to the text of the word "toadstool''. (There is a technical reason why these strings cannot be set equal to any text: only to literal text, as in the above example, or to strings previously declared using the Low_string directive.)

Finally, it is time to discuss print. There are two forms, print and print_ret. The only difference is that the second prints out an extra new-line character and returns from the current routine with the value true. Thus, print_ret should be read as "print and then return'', and

    print_ret "That's enough of that.";
is equivalent to
    print "That's enough of that.^"; rtrue;
In fact, as an abbreviation, it can even be shortened to:
    "That's enough of that.";
Although Inform newcomers are often confused by the fact that this apparently innocent statement actually causes a return from the current routine, it's an abbreviation which very much pays off in adventure-writing situations. Note that if the program:
    [ Main;
      "Hello, and now for a number...";
      print 45*764;
    ];
is compiled, Inform will produce the warning message:
    line 3: Warning: This statement can never be reached.
    >   print 45*764;
because the bare string on line 2 is printed using print_ret: so the text is printed, then a new-line is printed, and then a return takes place immediately. As the warning message indicates, there is no way the statement on line 3 can ever be executed.

So what can be printed? The answer is a list of terms, separated by commas. For example,

    print "The value is ", value, ".";
contains three terms. A term can take the following forms:
<a numerical quantity> printed as a (signed, decimal) number
<text in double-quotes> printed as text
(<rule>) <quantity> printed according to some special rule
Inform provides a stock of special printing rules built-in, and also allows the programmer to create new ones. The most important rules are:
(char) print out the character which this is the numerical code for
(string) print this string out
(address) print out the text at this array address
(this is seldom used, and then mainly to print the
text of a word entry in a game's dictionary)

/\ print (string) ... requires a little explanation.
    x = "Hello!";
    print (string) x;
prints out "Hello!'', whereas
    x = "Hello!";
    print x;
prints a mysterious number. This is because strings are internally represented by numbers (just as everything else is).

The remaining stock of rules is provided for use in conjunction with the Library and is documented in Chapter V: briefly,
(the) print definite article then name of this object
(The) ditto, but capitalised
(name) ditto, but with no article
(a) ditto, but with the indefinite article
(number) print this number out in English
(property) (for debugging) print the name of this property
(object) (ditto) print the hardware-name of this object
Note that (the) in lower case does something different from (The) with an upper case T. This is very unusual! (Directive names, which will turn up in Section 2, variable names and so on are allowed to use upper case and the case is simply ignored, so that fRoG means the same as frog. But statement keywords, like print or (name), have to be in lower case -- except for (The).)

To create a new rule, provide a routine with this name, and use the rule-name in brackets.


1.15. Example 6: Printing in hexadecimal

The following pair of routines provides for printing out a number as a four-digit, unsigned hexadecimal number. For example, so that

    print (hex) 16339;
prints "3fd3''.
    [ hex x y;
      y = (x & $ff00) / $100;
      x = x & $ff;
      print (hdigit) y/$10, (hdigit) y, (hdigit) x/$10, (hdigit) x;
    ];
    [ hdigit x;
      x = x % $10;
      if (x<10) print x; else print (char) 'a'+x-10;
    ];
Once these routines have been defined, hex and hdigit are available anywhere in the same program for use as new printing rules.


1.16. Built-in functions 1: random and indirect

Inform provides a small stock of functions ready-defined, but which are used much as other functions are. All but two of these concern objects and will be left until chapter 3.

random has two forms:

random(N)
returns a uniformly random number in the range $1, 2, ..., N$. N should always be a positive number (between 1 and 32767) for this to work properly.
random(two or more constant quantities, separated by commas)
returns a uniformly random choice from this selection. Thus,
    print (string) random("red", "blue", "green", "purple", "orange");
randomly prints the name of one of these five colours (each being equally likely to appear). Likewise,
    print random(13, 17);
has a 50% chance of printing 13, and a 50% chance of printing 17.

/\/\ The other built-in function discussed here is indirect.
    indirect(function, arg1, arg2, ...)
calls the given function with given arguments. Thus, this is equivalent to
    function(arg1, arg2, ...)
but has the additional virtue that the function can be given, not just as a literal function name, but as some calculated value:
    indirect(random(OneRoutine, OtherRoutine), 45);
has a 50% chance of calling OneRoutine(45), and a 50% chance of calling OtherRoutine(45). indirect should be used with caution: if supplied with a numerical first argument which doesn't correspond to any function in the program, the program may resoundingly crash. In any event, it is often best to achieve such effects using messages to objects.


1.17. Accepting input

/\/\ Inform programmers seldom need to take input from the keyboard, in practice, since in all game situations the Library's parser routines take care of all that. However, for completeness this section covers the read statement which is the main route by which keyboard input is taken. It will not make much sense to readers who have not yet read the rest of this book.

The syntax is
read <text array> <parse buffer> <routine>
where the <routine> is optional: if provided, it is called just before the input takes place so that the screen's top line or lines of data (the "status line'' present in many games) can be renewed.

What the statement does is to read in a single line of text (waiting until the user has finished typing a line and then pressed RETURN), copy this text into the text array and then try to comprehend it, writing the results of this comprehension exercise ("parsing'') into the parse buffer.

Before the statement is reached, the program should have entered the maximum number of characters which can be accepted (say, 60) into the 0th entry of the text array; the statement will then write the actual number typed into the 1st entry, and the characters themselves into entries 2 and onward. Thus,

    text_array -> 0 = 60;
    read text_array 0;
    for (n = 0: n< text_array->1: n++) print (char) text_array->(n+2);
    new_line;
will read in a line of up to 60 characters, and then print it back again. (The array text_array must have been created first, and so must the local variable n, of course.)

Note that in this case, no "parse buffer'' has been given (0 was given in its place). If, instead of 0, an array is given here, then the read statement makes an attempt to divide up the input text into individual words, and to match these words against the game's dictionary. See Section 2.5 for details.


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.