Roger Firth's IF pages

Home

Inform 6: Frequently Asked Questions

Email
Back up

Into
the Intro

Setting
the scene

Preparing
to program

Learning
the lingo

Dabbling
in data

Operating
on objects

Verbal
versatility

Bothered
by bugs

History and
hereafter

Worldly
woes

Inside
information

Tips and
techniques

These topics are about mastering Inform's syntax and punctuation:

When are upper and lower case differentiated?
When do I use commas (,) and semicolons (;)?
When do I use apostrophes (') and quotes (")?
What does a string "..." as a statement by itself mean?
    The implicit return warning
    Assigning strings to variables
What are the circumflex (^) and tilde (~) characters used for?
How do I concatenate strings?
What's the difference between MyRoutine and MyRoutine()?
    Example: PartOfDay() routine
What's the difference between a Directive and a Statement?
What's the difference between Include and #Include?

When are upper and lower case differentiated?

Mostly, they're treated the same. When you assign a name to something like an object or a routine, you can use either upper and lower case letters (you can also use underscores (_), and you can have digits anywhere except as the first character).

The difference between upper and lower case is important for:

Even where case doesn't actually matter, there are a few conventions:

When do I use commas (,) and semicolons (;)?

You use a semicolon often; it's a terminator -- every statement and every directive ends with one. The comma is a separator, and you use it in only a few situations:

object definition

It's the first of those which seems to give novices most difficulty, so let's walk slowly through an example. This declaration starts with the Object directive, and ends several lines later with a semicolon:

object definition

You never need any punctuation in the object's header -- the first line which defines its internal and external names, and its parentage:

object definition

You always need these commas, to separate the name property from the initial property, initial from description, and description from after:

object definition

A comma at the end of the final property definition, before the has segment, is optional. It's not needed, but it doesn't do any harm (and it makes things a bit easier if you later add another property to the list):

object definition

These semicolons are within the [...] which surround the embedded property routine, and so have no effect on the structure of the mushroom's definition:

When do I use apostrophes (') and quotes (")?

If your game mentions words in apostrophes (for example, 'grumpy' 'old' 'man'), then the compiler stores those words in what's known as the game's dictionary. If your game mentions words or phrases in quotes (for example, "grumpy old man") then you're defining a string, which the compiler stores in another place entirely. There's a really important distinction here: words in the dictionary are effectively input tokens, to be matched against words that the player types during the game. Strings are output messages, to be printed by your program during the game. For example, in this fragment:

  >EXAMINE THE GRUMPY OLD MAN
  The grumpy old man stands silently in the corner.

the game is comparing the player's input against the words that it knows (the contents of the dictionary), recognising an EXAMINE operation on an object identified by the tokens GRUMPY, OLD and MAN, and printing the description associated with that object. The object itself might have been defined thus:

  Object  old_man "grumpy old man"
    with  name 'man' 'grumpy' 'cross' 'old' 'elderly' 'ancient' 'gnome',
          description "The grumpy old man stands silently in the corner.",
          ...
    has   animate;

and you can easily see the difference between the list of apostrophe-enclosed input tokens in the name property, and the quote-enclosed output string in the description property.

Unfortunately, Inform muddies this clear distinction by treating the name property (also the Extend and Verb directives) as a special case, and permitted you to enclose the input token in either apostrophes or in quotes. Please, don't take advantage of this unnecessary concession; stick to the simple and consistent policy: apostrophes around input (dictionary) words, quotes around output strings. Do that everywhere, and you won't get confused.

What does a string "..." as a statement by itself mean?

More information in the DM:
§1.12

One of Inform's more unusual program statements is nothing more than a string in quotes:

  "The grumpy old man takes no notice.";

which to the uninitiated can be pretty confusing. Just remember that this is merely a shorthand form of:

  print_ret "The grumpy old man takes no notice.";

and that in turn these are both exactly equivalent to the three statements:

  print "The grumpy old man takes no notice."; new_line; return true;

Once you grasp what's going on, you'll find this a useful and convenient shortcut, especially after an if statement; most people find the first of these two forms clearer to work with:

  if (...) "The grumpy old man takes no notice.";
  if (...) { print "The grumpy old man takes no notice."; new_line; return true; }

One aspect of the "..."; statement that regularly fools newcomers is its inbuilt return. The statement outputs some text, and then returns immediately from whatever routine it occurs in, which of course means that any statement(s) following it cannot possibly be executed. Misunderstanding this behaviour is far and away the most likely cause of a "This statement can never be reached" compiler warning.

By the way, if you assign a string to a variable:

  Global X = "The grumpy old man takes no notice.";

then you can achieve the same effect as a "..." statement with any of these:

  print_ret (string) X;
  print (string) X, "^"; return true;
  print (string) X; new_line; return true;

What are the circumflex (^) and tilde (~) characters used for?

Inform uses both of these characters, which occur only rarely in English texts, for special purposes.

More information in the DM:
§1.11

The circumflex can be included as part of a string within quotes "..." in order to output a newline character. For example:

  print "^^^A line on its own.^^^";

prints three newlines before and after the line of text. If you wish to output a literal circumflex, you need to use the rather clumsy construct @@94, thus:

  print "A line with a circumflex @@94 in the middle.^";

More information in the DM:
§1.4

The circumflex can also be included in a dictionary word within apostrophes '...' in order to store a literal apostrophe as part of that word. For example:

  name 'child^s' 'bicycle' 'bike' 'small',

might be part of the definition of an object which would respond to TAKE THE CHILD'S BICYCLE.

More information in the DM:
§1.11

The tilde can be included as part of a string within quotes "..." in order to output a literal quote character. For example:

  print "The bottle is labelled ~POISON~ in green letters.";

prints The bottle is labelled "POISON" in green letters. If you wish to output a literal tilde, you must use the construct @@126, (and, for completeness, a literal at-sign requires @@64).

More information in the DM:
§1.6 §1.8 §3.7 §39

In addition to embedding a quote within a string, the tilde is also employed to mean "not", in several different circumstances:

Usage

For example

Meaning

"not equal" operator

if (X ~= Y) { ... }

The condition is true if X is not equal to Y.

logical NOT operator

if (~~(X == Y)) { ... }

The condition is false if X is equal to Y (this example is logically identical to that on the line above).

X = ~~Y;

Invert Y's logical content. That is: if Y is false (0) then X becomes true (1); if Y is any other value then X becomes false (0).

bitwise NOT operator

X = ~Y;

Invert Y's literal content, processing each bit individually. That is, if Y contains 41 ($0029 in hexadecimal) then X becomes -42 ($FFD6).

attribute resetting

Object radio "radio"
       ...
  has  static ~on;

The radio object is declared with its on attribute not set. Strictly unnecessary (since all attributes are unset by default), it's a handy way of reminding yourself that the attribute is likely to be set and reset during the game.

give radio ~on;

Clear (or unset, or remove) the radio's on attribute.

compiler control

-~S

Turn off the S switch, disabling Strict and Debug modes.

By the way, if you're uncertain of the difference between bitwise and logical operators, see this article by Sonja Kesserich.

How do I concatenate strings?

The short answer: you can't. There's no simple expression similar to the string manipulation commands provided by other languages. If you were thinking of using string concatenation, comparison, subsetting, case-folding or anything else on these lines, you need to rethink your approach. In practice, however, you'll almost certainly find that this isn't a problem: most Inform programmers don't even notice the lack of string handling capabilities.

Here's a longer answer (though it comes to the same thing). If you've come to Inform with programming experience in Basic, you might (mistakenly) expect to be able to do stuff like this:

  Global X;
  ...
  X = "Combining";
  X = X + " some text";
  print X;

Well, although Inform happily runs that code, the outcome is undoubtedly not what you were hoping for. What gets printed is a decimal number, guaranteed to be completely and utterly meaningless; here's why: X is an Inform variable, capable of holding any 16-bit number. The first line stores the string "Combining" at some suitable location within the Z-machine's memory, and puts the 16-bit address of that location into the variable X. The second line stores the string "some text" at another suitable location, and adds the 16-bit address of that location to the current contents of X (the address of the first location). The third line prints the new contents of X... which is garbage.

That is, adding together two strings makes about as much sense as saying "I've got a two-bedroom house at 54 High Street, and my fiance has a one-bedroom apartment at number 43. Therefore, when we get married, we'll own a three-bedroom residence at number 97." Life, and Inform, doesn't work like that.

If you think about it, you'll see why string comparison is also a non-starter:

  X = "Combining";
  if (X == "Combining") { ... }

That test is never ever going to be true (because it's comparing the address where the first string is stored with the address where the second string is stored). However, you may have more success with this approach:

  Constant MYSTRING = "Combining";
  ...
  X = MYSTRING;
  if (X == MYSTRING) { ... }

Inform's string manipulation capabilities are virtually non-existent. A string -- one or more characters enclosed in quotes "..." -- is encoded by the compiler and packed into as few bytes as possible; these bytes are stored in a part of the Z-machine's memory which is dedicated to just this purpose, and referred to by the address of the first byte. Strings are like constants; they can be constructed only during compilation, and thereafter they don't vary. At run-time, the interpreter provides just two operations to handle strings: print unpacks the characters and displays them on the screen, and print_to_array unpacks the characters into the individual entries of a byte array. You'll notice that these are read-only operations; the Z-machine simply doesn't permit you to update a string after it's been created.

Despite this, clever folk have devised various extensions to extend Inform's handling of strings -- some of these Textual functions in the Archive may help. Take a look at istring.h.

What's the difference between MyRoutine and MyRoutine()?

There are several reasons why you might write a standalone -- named -- routine, most commonly because you find yourself repeating the same chunk of processing in different places. For example, suppose that your game was time-oriented, sensitive to the difference between morning, afternoon and night; you could create a routine to facilitate this calculation:

More information in the DM:
§1.7 §20

  [ PartOfDay i;
          switch (the_time) {       ! minutes since midnight
              360 to 719:  i = 1;   ! 06:00 - 11:59 (morning)
              720 to 1079: i = 2;   ! 12:00 - 17:59 (afternoon)
              default:     i = 0;   ! 18:00 - 05:59 (night)
              }
          return i; ];

Having written the routine once, you can now use it wherever you need to determine the difference:

  if (PartOfDay() == 0) print "The moon"; else print "The sun";
  print " shines thinly through the foliage overhead.";

When you call a routine, you supply any necessary arguments enclosed in parentheses after the routine's name. If the routine doesn't require an argument -- as is the case for PartOfDay -- it's important that you still supply a pair of empty parentheses. Consider these assignments:

  X = PartOfDay();
  Y = PartOfDay;
  Z = Y()

Afterwards, X contains the number (0, 1 or 2) returned by running the PartOfDay routine -- presumably the value that you were after -- whereas Y contains the routine's address; not nearly so useful. (Note, though, that Z is ok; like X, it contains 0, 1 or 2.)

What's the difference between a Directive and a Statement?

More information in the DM:
§1.2 §2.1

Put simply, a directive controls what happens while the game is being compiled, whereas a statement controls what happens while it's being played. This distinction is perhaps most apparent with Message (a directive which outputs a message during compilation) and print (a statement which outputs a message at run-time). However, you don't often need to use Message; the directives that you'll most commonly encounter are Include -- for merging files during compilation -- and the set used for creating data structures: Constant, Global and Array; Object and Class; Verb and Extend. Oh, and [...], the directive which wraps a succession of statements into a routine.

What's the difference between Include and #Include?

More information in the DM:
§38

Not very much. They both merge in the contents of a specified file, with the distinction that #Include can be placed both within and outside the definition of a routine, whereas the plain Include can be placed only outside a routine. There's no down side, so you can happily use #Include in all cases. The same goes for the conditional compilation directives like IfDef...IfNot...EndIf -- you might as well use #IfDef and so on.

Into
the Intro

Setting
the scene

Preparing
to program

Learning
the lingo

Dabbling
in data

Operating
on objects

Verbal
versatility

Bothered
by bugs

History and
hereafter

Worldly
woes

Inside
information

Tips and
techniques