Inform7/10 Reference Manual

I haven’t proofread it in detail this time, so there might be some typos, but here’s the last three sections I mentioned earlier.

Punctuation, articles, plurality
Punctuation The most important unit of punctuation in Inform source text is the period, which naturally enough ends a sentence. Normally, every sentence (and every phrasebook) is expected to end with a period. However, periods are implicitly inserted under two circumstances:
  • Before a blank line (paragraph break)
  • After quoted text ending in a period, exclamation mark, or question mark.

The comma (,) is also pretty significant, but it’s not really discussed in the documentation as far as I can tell. In many contexts it can precede (or even substitute for) the words or and and… but there are some places where that doesn’t appear to work. In assertions it can be placed between adjectives, which can help disambiguate multi-word adjectives in some cases, but that doesn’t seem to work in conditions.

Next on the list of general-purpose punctuation is parentheses (()). Though they have special meanings in many places, and using them in an assertion is not allowed outside of those special meanings, they can be used for grouping in both descriptions and conditions.

For example, suppose you have a room called “Road to Paradise” and want to disable exiting it on foot in a specific direction. The obvious way won’t work:

Instead of going west from Road to Paradise by nothing:

The parser unfortunately sees the “to” in the name and (if I understand correctly) thinks you’re trying to match the room gone to, so it would only match if west of the Road to Paradise was the Road to Paradise. You can work around this issue by enclosing the name of the room in parentheses:

Instead of going west from (Road to Paradise) by nothing:

Lastly, I’ll give an honorable mention to the colon and semicolon, which play a role in phrasebooks. However, their usage was already specified in the section in question, so I won’t say any more than that.

Filler Words

Some people might see my syntax definitions earlier in the thread and wonder, where are the articles? Well, I omitted them for a good reason. The words a, an, the, and some are in most cases treated by Inform as the same as whitespace – they have no semantic effect on the source text.

There are some cases where a specific article is required (such as an actor doing something), and articles can be significant in the name of a phrase because Inform prefers to use a phrase with more words, even if those words are articles. Besides that, there’s at least one more way in which articles are significant – when defining an object (but not a value), Inform looks at the article used and takes it into account when defining the object.

To be more precise, every object has an indefinite article property, which is blank by default. (I think being blank is exactly equivalent to setting it to "a" or "an", whichever is appropriate.) If you define the object via called the «name», Inform sets the indefinite article property to "the". It doesn’t seem to do this with some however, and I’m not completely clear on the rules for this.

However, if the object is defined with some, it does get the plural-named property. Or, if it’s defined with no article at all, it receives the proper-named property. So, the presence or absence of an article will affect how Inform thinks your object should be referred to. Of course, in all cases you can override its guess with a specific assertion.

Speaking of plurality, plurals are another thing that exists in the syntax but has no semantic significance. You can happily write ungrammatical sentences like 'The dogs is in the kitchen" or “Some water are scenery in the beach” and Inform won’t care at all. In other words, the plural and singular forms of a noun or verb are, in nearly all cases, completely interchangeable. The one case I know of where this isn’t true is rule and rulesrule refers to a single rule, but rules is a synonym for rulebook.

The ordinal suffix (and ordinal forms of small numbers) is another example of syntax that exists solely for familiarity but doesn’t have any special meaning. Though ordinal numbers seem to be an error (or misunderstood as part of a name) when used in assertions, in conditions and descriptions they’re synonymous with the equivalent cardinal number.

Imperative assertions
Understand

Understand «topic» as «action name» [( with nouns reversed)] [when/while «condition»].
Understand the command «command-list» as «command».
Understand the command «command» as something new.
Understand nothing as «action name».

These forms all work with verbs – defining a verb to mean an action, giving word-level synonyms, or clearing out previously-defined grammar.

For the first one, there’s a special restriction on the topic – there may not be any alternations on the first word.

Understand «topic» as «value or kind» [when/while «condition»].
Understand «topic» as the plural of «value or kind» [when/while «condition»].

These forms work on nouns. I don’t really have anything more to say here.

Understand «topic» as «token».
The understand token «name» translates into I6 as «quoted I6 identifier».

The token to be defined in the first form must be a quoted string containing nothing but a single square-bracketed token. The topic here is also restricted – it may contain at most one token in it, and if you use alternatives with or, every alternative must be compatible. Either they all contain no tokens, or they all contain a token of the same kind.

Understand «property» as describing «value or kind» [when/while «condition»].
Understand «property» as referring to «value or kind» [when/while «condition»].
Understand «topic» as «adjective».

The “describing” form allows the adjective to be used alone to refer to the object, whereas “referring to” means it can only refer to the object if it appears alongside the object’s name.

And lastly:

Understand «topic» as a mistake [( «text» )] [when/while «condition»].

Test

Text «name» with «script» [holding «thing» [ ( [ , ] and | , ) «thing» ] ] [in «room»].

The script is a quoted list of commands to be run, separated by forward slashes (/). Unlike everywhere else in the language, the forward slash here doesn’t separate words only – it functions more like a semicolon or period. I believe you can use [/] if you need a literal forward slash to be included in a command.

Include

Include [version «version» of] «extension» by «author».
Include (- «Inform 6 code» -) [when defining «kind» | replacing «quoted I6 identifier»].

Use

Use «option» [of at least/most «value»] (and «option» [of at least/most «value»])*.

I’m not going to cover the syntax of how to define use options, since it’s due to be overhauled in the next release.

Release

Release along with «release instruction» (and «release instruction»)*.

Possible release instructions include:

file «text» called «quoted file or directory name»
cover art [( «quoted alt text» )]
introductory booklet/postcard
«quoted template name» website
«quoted interpreter name» interpreter
existing story file [called «quoted filename»]
[public/private] solution
[public/private] source text
[public/private] library card

Index map

Index map with «mapping instruction» (and «mapping instruction»)*.

Possible mapping instructions are:

«room» mapped «cardinal direction» of «room»
«direction» mapped as «cardinal direction»
EPS file
rubric «text» [size «number»] [font «text»] [color «text»] at «offset» [from «room»]
«setting» [of «part»] set to «value»

When adjusting settings, the optional part can be one of the following:

«description of rooms»
«description of regions»
first room
level «number»

The level number can be negative.

Setting values can be any of the following (depending on the setting in question):

«quoted text»
«number»
on/off
«number»&«number»

That last type is called an offset.

The possible settings are:

font
minimum-map-width
title
title-size
title-font
title-colour
map-outline
border-size
vertical-spacing
monochrome
annotation-size
annotation-length
annotation-font
subtitle
subtitle-size
subtitle-font
subtitle-colour
grid-size
route-stiffness
route-thickness
route-colour
room-offset
room-size
room-colour
room-name
room-name-size
room-name-font
room-name-colour
room-name-length
room-name-offset
room-outline
room-outline-colour
room-outline-thickness
room-shape

Basic phrases

General Flow Control

rule succeeds – the containing rule succeeds with no outcome
rule fails – the containing rule fails with no outcome
make no decision – the containing rule makes no decision and produces no outcome
rule succeeds with value – the containing rule succeeds with no outcome and the specified value
«rulebook outcome» – the containing rule succeeds, fails, or makes no decision, and produces the specified outcome
continue the action/activity – equivalent to “make no decision”
stop [the action] – generic “return nothing” statement for phrases
[decide ] yes/no – return true or false
decide on – return specified value
next – go to next loop iteration
break – exit loop
do nothing – the placeholder for when you have a block that needs a phrase but no phrase to put there; this phrase just does nothing.

Blocks

A few core, built-in phrases introduce a block, which is a sequence of statements under the control of the introductory phrase. There are two ways to write a block, but you can’t mix the two ways in the same phrasebook.

The first way is to start with a colon (:) and then indent each subsidiary phrase one level deeper than the level of the controlling phrase. This is similar to how the Python programming language handles scoping.

The other form looks like this:

«first word of phrase» «rest of phrase» begin ; «sequence of subsidiary phrases» end «first word of phrase repeated»

I think it gets a bit more complicated than that in if statements, but at least that’s the basic idea.

Conditionals

if/unless «condition» , «single phrase»
[otherwise/else «single phrase»]

I’m pretty sure one-liners can only use the above form – there’s no “else if” equivalent.

if/unless «condition» : «sequence of phrases»
otherwise/else if/unless «condition» : «sequence of phrases»
otherwise/else : «sequence of phrases»

Note that otherwise and else are completely synonymous. On the other hand, if and unless are opposites – the latter negates the entire condition.

A switch statement has the following structure:

if «value» is : «block of phrases»
( -- «value» : «block of phrases»)*
[ -- otherwise : «block of phrases»]

Note that, unlike in regular conditional statements, we don’t have else as a synonym for otherwise here (which is weird), nor do we have the opion of using unless to negate the entire condition (which wouldn’t make sense in any case). Also note that a switch statement cannot be used in the begin/end style of blocks – if you want to use a switch statement you need to use indentation.

Loops

while «condition» :
repeat with «name» running from «arithmetic or enumerated value» to «value of same kind» :
repeat with «name» running through «description of values» :
repeat through «table name» [in [reverse] [«table column»] order] :

That last one is slightly imprecise – you need at least one of reverse and a table column. It’s not allowed to say in order.

Saying

The say phrase might be the most complicated phrase in the game. I think this is its most general form:

say «sayable thing» (, «sayable thing»)*

where a sayable thing is one of the following:

«text» [( «response letter» )]
«expression evaluating to sayable value»
«say phrase»

Saying a comma-separated list of things seems to be exactly equivalent to saying each of them in turn in separate say statements.

The first form of a sayable thing is by far the most common – any quoted text, with an optional response letter following it. However, it’s interesting to note that if the entire contents of your text is a bracket substitution, you can get the same effect by removing both the quotes and the brackets.

A response letter is only valid in named rules. In particular, it is not valid in named phrases (which seems a little strange to me, honestly). It must be a single uppercase letter, which means you’re limited to at most 26 responses in any given rule. (If you find yourself exceeding this, it’s probably a sign you should split the rule up anyway.) Using this form creates a value of kind response called “«name of the rule» response ( «response letter» )”. A response can be assigned to in assertions as though it’s a variable.

Other

I’m not going to cover every imaginable phrase, as the Phrasebook section in the index is pretty extensive, but I’ll list a few more of the most basic ones here.

let «name» be «value or kind»

When specifying the kind, the «name» must not already exist. However, when specifying a value, the name can already exist, and must be a local variable. (You can’t use let to assign to global variables.) If you only declare the kind, the initial value is the default value of that kind.

now «condition»

As crazy as it seems, this statement takes almost any condition and immediately arranges things so that it’s true. To be more precise, it will only accept the same kinds of conditions that you can use as static assertions. For example, no past tense or calculated adjectives or relations. I’m not sure if there are any types of conditions that work statically but not in now or vice versa.

whether or not «condition» … truth state

This value phrase effectively casts a condition to a truth state value. Since conditions and values are two different things in Inform, this allows converting between them. Note that true and false are not valid conditions on their own, unlike in many programming languages. If you have a truth state variable, you need to test it with is like anything else.

If I’ve missed some basic, core phrase, please let me know.


It is strange that Inform supports unless as the opposite of if but does not support until as the opposite of while – probably an oversight? It would seem to be easy to fix that, but for some reason I can’t get this to work:

To until (c - condition) begin -- end loop:
	(- while ~{c}  -).

When play begins:
	until true is true:
		do nothing.

Problem. You wrote ‘until true is true’: but the punctuation here ‘:’ makes me think this should be a definition of a phrase and it doesn’t begin as it should, with either ‘To’ (e.g. ‘To flood the riverplain:’), ‘Definition:’, a name for a rule (e.g. ‘This is the devilishly cunning rule:’), ‘At’ plus a time (e.g. ‘At 11:12 PM:’ or ‘At the time when the clock chimes’) or the name of a rulebook, possibly followed by some description of the action or value to apply to (e.g. ‘Instead of taking something:’ or ‘Every turn:’).

The error seems to indicate that it’s not recognizing that the phrase should begin a block. I can’t get it to work with the begin…end form of blocks either, though that gives different error messages.

Nope, common misconception. “Stop” and “stop the action” are defined exactly alike (return true at the I6 level). “Continue the action” and “make no decision” are also equivalent (return false). These phrases stop the current rule but do not record a rulebook outcome.

“Rule succeeds” and “rule fails” return true after recording a rulebook outcome.

1 Like

This works:

lab is a room.

To repeat until (c - condition) begin -- end loop:
        (- while (~~{c})  -).

When play begins:
let i be 1;
repeat until i is 4 begin;
  say i;
  increment i;
end repeat;
1 Like

I edited the post since that’s actually an error (if I’d stopped to think about it, it would’ve been obvious that continue was the same as make no decision), but I’m not so sure it’s incorrect to say that stop the action is equivalent to rule fails. After all, it stops the rulebook from processing other rules, which is pretty much what people expect from rule fails.

I can see from the definition that it indeed just returns true without calling RulebookFails(). It’s used everywhere throughout the Standard Rules in various check rules and the like. It’s not entirely clear to me how it differs from rule fails, but if what you’re saying is accurate, it sounds like a mostly undocumented fourth way that a rule can end (apart from fails, succeeds, and no decision).

  • rule fails – sets the rule failed to true and the rule succeeded to false, and the rulebook stops processing
  • rule succeeds – sets the rule succeeded to true and the rule failed to false, and the rulebook stops processing
  • make no decision or continue the action/activity – leaves the rule succeeded and the rule failed both false, and the rulebook just continues processing.
  • stop the action – leaves the rule succeeded and the rule failed both false, and the rulebook stops processing

And if that’s really how it works, it seems wrong to be using it so extensively in check rules. You’d think you’d want the check rule to fail if the action is stopped, wouldn’t you? But maybe I’m misunderstanding something else too?

Also, does this mean that decide no and make no decision are completely synonymous?

So it just doesn’t like the first word being until for some reason? I don’t really get it… but in any case, that also works in indented form, so…

I don’t remember all the cases here. I think it only matters if you check “if rule succeeded” after the rulebook runs. Most rulebook calls in the Standard Library don’t check that.

Also, does this mean that decide no and make no decision are completely synonymous?

They behave the same. But “decide no” is tagged “in to decide if only”. So the compiler objects if you use that in a “to” phrase.

(It lets you use “decide no” in a rule; I’m not sure why.)

Looks like it insists on the first word being “repeat”.

See here for how & why this can make a difference- most commonly to subsequent conditions such as ‘if we have <verbed>’- and why ending Check rules with instead or stop the action usually (but sometimes not, in unusual cases) has the desired effect.

[a salient extract:]

Inform also provides

stop the action;
... instead;        (or, equivalently, instead...;)

(NB ‘instead…’ refers to a phrase such as ‘Before drinking the yoghurt: instead try eating the yoghurt.’ rather than an Instead rule starting with, for example, ‘Instead of eating…’)

‘stop the action’ and ‘…instead’ are both at first sight a little odd. Reading the documentation, you might easily go away with the impression that these both mean the same as rule fails, but that’s not the case. Both of these mean ‘stop the rulebook immediately, but with an outcome in keeping with the last decision a previous rule or rulebook made’. The outcome of that last decision could be any of ‘failure’, ‘success’ or ‘no outcome’. To understand this, consider that both of these phrases cause the current rule to immediately return true but, crucially, and unlike ‘rule succeeds’ and ‘rule fails’, don’t modify latest_rule_result before doing so. Consequently, the calling rulebook will also immediately terminate, with whatever result was previously stored in latest_rule_result. Often this result won’t be important, but in situations where it’s of significance whether a rulebook terminates with success, failure or no outcome this behaviour can either be helpful or lead to results the author did not anticipate.

2 Likes

That all seems to suggest that stop the action or instead generally shouldn’t be used in rulebooks. I suppose it’s probably fine in places like Report or even Check though, since the Standard Rules use it in Check.

I’ve just sworn a mighty oath to never write code that depends on whether an action succeeds or fails. The standard library doesn’t either. (That I recall.) Therefore, the difference doesn’t matter in action rulebooks.

Special-use rulebooks, like DTPM or activities, are different. If a rulebook has custom outcome phrases (like “it is very likely”), I always use those phrases.

1 Like

So it just doesn’t like the first word being until for some reason? I don’t really get it… but in any case, that also works in indented form, so…

This works, too.

To while until (c - condition) begin -- end loop
        (documented at ph_while):
        (- while (~~{c})  -).

When play begins:
let i be 1;
while until i is 4 begin;
  say i;
  increment i;
end while

Seems to be the case that you can’t use original initial words for things ending begin -- end [...] – the existing values seem to be special-cased in the compiler. But I haven’t tried tracking it down in the source to verify.

1 Like

If I’m not mistaken, those are defined in the Preform. I think it the <control-structure-phrase> production? (At least in 6L38.)

Actually, a lot of the syntax for which @Celtic_Minstrel is writing a reference here is actually not set in stone and is defined by the Preform, which language extension can modify to change the syntax of Inform.

2 Likes

I believe the Unsuccessful Attempt rulebook only runs if you give a command to an NPC and the action fails.

2 Likes

So in other words, some of it probably doesn’t apply if you use eg the French language extension, right?

But I bet a lot of it does still apply, even if the exact wordings are different. And the Preform isn’t the most readable quick reference. I’ve been able to glean a few things from it (like the structure of a table entry reference… which as it happens haven’t been covered here, so maybe I still have a few missing things to add), but I wouldn’t recommend it for someone asking “How do I X?”.

I’m planning to eventually convert all of the big posts I wrote in here into an HTML document which I can then post on my website, though I’m not sure how long that’ll take (days, weeks, months, even a year or two if I get distracted and wander off to something else). Whenever it’s ready for viewer consumption, I’ll probably edit it into the first post in addition to adding a link in a new post.

That sounds right; when I wrote some puzzles involving giving orders to NPCs via persuasion, I had to do tons of rules about why actions failed. I copied the “Under Contract” example, which has code such as the following:

Unsuccessful attempt by Clark doing something:
	repeat through table of Clark Retorts:
		if the reason the action failed is the cause entry:
			say "[response entry][paragraph break]";
			rule succeeds;
	say "'I don't think that's in the script,' says Clark dubiously."

Table of Clark Retorts
cause	response
can't take yourself rule	"'I'm always self-possessed,' Clark remarks. You've heard that line before, but it sounds so much more convincing from him."
can't take other people rule	"'I don't think it would be appreciated if I tried to do that to [the noun],' he rumbles."
can't take component parts rule	"'I don't want to rip [the noun] out,' Clark remarks."
can't take people's possessions rule	"'I don't cotton to acting like a thief,' Clark replies. 'It ain't proper.'"

This doesn’t work, either.

lab is room.

Include (-

<control-structure-phrase> ::=
        if ... is begin |
        if ... is |
        if/unless ... |
        repeat ... |
        while ... |
        until ... |
        else/otherwise |
        else/otherwise if/unless ... |
        else/otherwise ... |
        -- otherwise |
        -- ...

<end-control-structure-phrase> ::=
        end if/unless |
        end while |
        end repeat |
        end until

-) in the Preform grammar.

To until (c - condition) begin -- end loop:
(- while (~~{c})  -).

When play begins:
let i be 1;
until i is 4 begin;
  say i;
  increment i;
end until

produces

You wrote ‘until i is 4 begin’ (source text, line 31): but this is an ‘else’ or ‘otherwise’ with no matching ‘if’ (or ‘unless’), which must be wrong.
Because of this problem, the source could not be translated into a working game. (Correct the source text to remove the difficulty and click on Go once again.)

(in the current dev version)

Some syntax, like string literals, are baked into the compiler, so that won’t change.

But all that is defined by the Preform can be totally different depending on the language. The wording, the order of the words, and so on. For example, in French, I made it possible to write adjectives after the noun in descriptions.

So yeah, the reference will only apply to those who write their source in English, but that’s the majority so it’s still useful. As far as I know, only the French translation went as far as translating the Preform, and it’s only compatible with 6L38, and doesn’t fully work, and authors aren’t even forced to use the French syntax. I find the Preform quite poorly documented.

I believe those lines in the Preform grammar are indexed by number; when Preform says “this matches the sixth pattern in <control-structure-phrase>”, the compiler says “ah, that pattern is for ‘else’, so this is an ‘else’ block”. That’s why you get that error message, because “until” is now the sixth pattern in <control-structure-phrase>.

Try putting “while/until” in the fifth line instead of adding a new one. (There’s a fancy way to mix up the ordering without confusing the compiler but I don’t understand it well enough to use it.)

2 Likes

Ha, this works. Good to know, thanks.

Include (-

<control-structure-phrase> ::=
        if ... is begin |
        if ... is |
        if/unless ... |
        repeat ... |
        while/until ... |
        else/otherwise |
        else/otherwise if/unless ... |
        else/otherwise ... |
        -- otherwise |
        -- ...

<end-control-structure-phrase> ::=
        end if/unless |
        end while/until |
        end repeat

-) in the Preform grammar.
2 Likes

Each line has an implicit label, starting with /a/. If you need to add a line (if the formulation is too complicated to be expressed as a simple slash alternative), you have to make all label explicit by adding /a/, /b/, and so on in front of each line. You can have duplicate labels, so you’d use the same letter for while and until. (Again, that’s for 6L38. It could have changed since.)

2 Likes

Ha, even this works!

[...]
<control-structure-phrase> ::= [...]
while/until/for ... | [...]

<end-control-structure-phrase> ::= [...]
       end while/until/for |
[...]

to for each/-- (i - nonexisting K variable) in (L - list of values of kind K)
  begin -- end loop:
  (- {-primitive-definition:repeat-through-list} -).

to for each/-- (loopvar - nonexisting K variable) in (OS - description of values of kind K)
  begin -- end loop:
  (- {-primitive-definition:repeat-through} -)
2 Likes

This is good stuff, and potentially very useful as a high-level quick reference similar to the “I7 Cheat Sheet.” Thank you for putting it together!

If it does get made into a standalone document, please be sure to post here with its location (and get it into the IF Archive). It would make a nice addition to the content on the I7 Resources pinned topic.