The standard set of primitive invocations used within Inform.


§1. Status. The Inter specification allows for any number of primitive invocations to be declared and used; none are built-in or required.

The Inform compiler, however, has a set of around 90 different primitives. The back end of the compiler can compile those into valid Inform 6 code; the front end of the compiler is guaranteed to declare and use only (a subset of) those 90. That gives the following set of primitives a kind of halfway status: though they are not part of the inter specification, for the time being they're the only game in town.

§2. Arithmetic. The following are standard integer arithmetic operations:

§3. Logical operators. In general, the value 0 is false, and all other values are true.

§4. Bitwise operators. These differ in that they do not "short circuit", and do not squash values down to just 0 or 1.

§5. Numerical comparison. These are comparisons of signed integers. (If Inform needs to compare unsigned integers, it calls a routine in the I6 template.)

§6. Sequential evaluation. The reason for the existence of !ternarysequential is that it's a convenient shorthand, and also that it helps the code generator with I6 generation, because I6 has problems with the syntax of complicated sequential evals.

§7. Random numbers. There is just one primitive for this:

§8. Printing. These print data of various kinds:

While these correspond to standard I6 library functions, they should probably be removed from the set of primitives, but there are issues here to do with the Inform 6 "veneer":

There are also primitive ways to change the visual style of text:

Lastly, a primitive for a rum feature of Inform 6 allowing for the display of "box quotations" on screen:

§9. Stack access. The stack is not directly accessible anywhere in memory, so the only access is via the following.

§10. Accessing storage. Here the ref term is a refernce to a piece of storage: a property of an instance, or a global variable, or an entry in memory, for example.

Memory can be read with the following. The first value is the address of the array; the second is an offset, that is, with 0 being the first entry, 1 the second, and so on. "Word" in this context means either an int16 or an int32, depending on what virtual machine are compiling to.

Those however only read from array entries. To write to array entries, we need to use !store or similar (see above); but to do that, we need a reference for the memory cell. We do that with:

(There is at present no equivalent for byte arrays.)

§11. Indirect function calls. Invocations of functions can only be made with inv when the function is specified as a constant, and when its signature is therefore known. If we need to call "whatever function this variable refers to", we have to use one of the following. They differ only in their signatures. The first value is the function address, and subsequent ones are arguments.

§12. Control flow. The simplest control statement is an "if". Note that a different primitive is used if there is an "else" attached: it would be impossible to use the same primitive for both because they couldn't have the same signature.

!ifdebug is an oddity: it executes the code only if the program is being compiled in "debugging mode". (In Inform, that would mean that the story file is being made inside the application, or else released in a special testing configuration.) While the same effect could be achieved using conditional compliation splats, this is much more efficient.

There are then several loops.

A switch statement takes a value, and then executes at most one of an arbitrary number of possible code segments. This can't be implemented with a single primitive, because its signature would have to be of varying lengths with different uses (since some switches have many cases, some few). Instead: a switch takes a single code, but that code can in turn contain only invocations of !case, followed optionally by one of !default.

This looks a little baroque, but it works in practice:

    inv !switch
        val K_number X
        code
            inv !case
                val K_number 1
                code
                    inv !print
                        val K_text "One!"
            inv !case
                val K_number 2
                code
                    inv !print
                        val K_text "Two!"
            inv !default
                code
                    inv !print
                        val K_text "Something else!"

As in most C-like languages, there are primitives for:

Two ways to terminate what's happening:

And, lastly, the lowest-level way to travel:

§13. Interactive fiction-only primitives. The following would make no sense in a general-purpose program. Most mirror very low-level I6 features, and Inform uses them mainly when converting I6 code into inter: in almost all cases it's better to call routines in the template rather than to use these. First, the spatial containment object tree:

Object class membership:

Attributes can be handled as follows. The values used to refer to these attributes can be inter symbols for their properties, but only if those properties are indeed stored as Z-machine or Glulx attributes at run-time.

Direct access to virtual machine object properties: