The standard set of primitive invocations used within Inform.


§1. What standard means. To recap from Code Packages in Textual Inter, primitives are like built-in atomic operations. The Inter specification allows for any desired set of primitives to be used, provided they are declared. However, in practice the building module of Inter defines a standard set of 95 or so primitives which are used across the Inform tool-chain, and:

That means the standard set is (for now at least) the only game in town, and the following catalogue runs through it. Textual Inter code does not need to declare primitives if they belong to this standard set, but the declarations they have behind the scenes are all listed below.

(See Inter Primitives (in building) for where in the inter source code these primitives are defined.)

§2. Arithmetic. The following are standard integer arithmetic operations, using signed twos-complement integers:

§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.)

This is a special operation allowing the comparisons to test for multiple possibilities at once. (Old-school Inform 6 users will recognise it as the or operator.)

For example,

    inv !eq
        val x
        inv !alternative
            val 2
            val 7

tests whether x equals either 2 or 7.

§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. This is essentially the built-in random function of Inform 6, given an Inter disguise. See the Inform 6 Designer's Manual for a specification.

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

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

The effect of these will depend on the platform the final Inter code is generated for. If the value supplied to !style is 0, 1, 2 or 3, then this should make an effort to achieve roman, bold, italic, or reverse-video type, respectively, and that should apply across all platforms. Use of any other value is likely to be less portable. On C, for example, all other uses of !style are (Inform) text values which supply names for styles.

Then there is a primitive for a rum feature of Inform 6 allowing for the display of "box quotations" on screen:

And another largely pointless primitive for issuing a run of a certain number of spaces, for users too lazy to write their own loops:

On some platforms, active steps need to be taken before text can actually appear: for example, those using the Glk input/output framework. As a convenience, this primitive will do anything which might be necessary. inform7 doesn't use this, instead compiling its own code to activate Glk, but it's useful to have this opcode for making small Inter test cases work:

§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 reference to a piece of storage: a property of an instance, or a global variable, or an entry in memory, for example.

Memory can be accessed 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.

Properties, like memory, can be converted to ref in order to write to them, and are accessible with propertyvalue. Their existence can be tested with propertyexists; the other two opcodes here are for the handful of "inline property values", where a property stores not a single value but a small array. In each of the four ternary property primitives, the operands are K, the weak kind ID of the owner; O, the owner; and P, the property. For properties of objects, K will always be OBJECT_TY.

propertyarray and propertylength both produce 0 (but not a run-time error) if called on a property value which does not exist, or is not an inline array. In particular, they always produce 0 if the owner O is not an object, since only objects can have inline property values.

§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. Message function calls. These are the special form of function call from Inform 6 with the syntax a.b(), a.b(c), a.b(c, d) or a.b(c, d, e). In effect, they look up a property value which is a function, and call it. But because they have very slightly different semantics from indirect function calls, they appear here as primitives of their own. Inform 7 never compiles these, but kit assimilation may do. To get an idea of how to handle these, see for example C Function Model (in final), which compiles them to C.

§13. External function calls. The following calls a function which is not part of the program itself, and which is assumed to be provided by code written in a different programming language. It cannot be used when Inter is being generated to Inform 6 code, because I6 has no ability to link with external code; but it can be used when generating C, for example.

The first value must be a literal double-quoted text, and is the name of the external function. The second value is an argument to pass to it; and the result is whatever value it returns.

§14. 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. Similarly for !ifstrict, which tests for "strict mode", in which run-time checking of program correctness is performed, but at some performance cost.

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
                inv !alternativecase
                    val K_number 2
                    val K_number 7
                code
                    inv !print
                        val K_text "Either two or seven!"
            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:

This is a sort of termination, too, loading in a fresh program state from a file; something which may not be very meaningful in all platforms. Note that there is no analogous !restart or !save primitive: those are handled by assembly language instead. This may eventually go, too.

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

§15. Interactive fiction-only primitives. The following would make no sense in a general-purpose program. Most mirror very low-level I6 features. First, the spatial containment object tree:

Object class membership: