Dialog release announcements

Dialog version 0h/04 (library 0.31) contains several bugfixes and performance improvements.

  • Library: Reduced heap usage during nested disambiguation questions. A few players managed to crash Pas De Deux by triggering several disambiguation questions in a row. When the game asks “Did you mean …”, and the player doesn’t respond with one of the options, the library treats the input as a new command and tries to parse it as a nested operation. If that command, too, is ambiguous, a new question is asked, and so on. In the old version of the library, if the player kept typing ambiguous commands, the heap would overflow, and the library would print an error message and attempt to UNDO. In the new version, if the player types something other than a response to the question, that command is stored temporarily on the long-term heap, while regular heap memory is recovered through backtracking.

  • Library: The shortest-path routine has been rewritten to reduce static memory usage. The old version, a rather hastily implemented variant of the Bellman-Ford algorithm, required six bytes of storage for every object in the game. Since all the room connectors (the “obvious exits”) are unweighted, I have now switched to a breadth-first search. This reduced the storage requirements to a single per-object variable (two bytes). Furthermore, because rooms are never located in or on other objects in Dialog, the per-object variable ($ has relation $) can be used for temporary storage during path-finding, which eliminates the remaining two bytes. In other words, the new version reduces static RAM usage by 1 kB for every 171 objects. This leaves more RAM for paging, which improves performance on vintage hardware.

  • Compiler: The triple-verbose (-vvv) output from the compiler describes how every predicate is queried by the rest of the code. For each parameter, you get to know whether it can be unbound, and if so, an example of where in the code such a query is made. This lets you trace backwards from a compiler warning (about using a potentially unbound value in a place where it’s not allowed) to the root cause of the problem. But sometimes, for recursive queries, only the recursive call was reported, which would put an end to your tracing. This has now been fixed.

  • Several odd fixes and corner cases:

    • Compiler: Added support for (now) ~($ has parent $), for completeness.

    • Compiler: Reduced temporary register usage. Added a check to report an error if we’re running out of registers.

    • Compiler: Fixed additional corner-case bugs discovered through fuzzing.

    • Å-backend: Collecting into a value (e.g. a list expression) now works properly.

    • Å-backend: Fixed a compiler error due to e.g. (#a = $X).

    • Debugger: Now handles auxiliary heap overflow gracefully.

    • Debugger: Correct handling of undo inside div, e.g. from the runtime error handler.

    • Debugger: Fixed a bug related to single-digit input.

    • Z-backend and debugger: Allows dynamic predicates to be unset for non-objects (nothing happens).

  • Improved the frontend optimizer, with a special focus on if-statements.

  • Several improvements to make the Å-machine backend generate smaller and faster code.


I’m happy to announce Dialog version 0h/05 (library 0.32). This is primarily a library upgrade.

  • The big thing is that the library now supports NPCs that move around and do things, and there’s a whole new NPC chapter in the manual. The new chapter also describes a feature called topic objects—objects that are always in scope when the grammar calls for a topic.

  • An after-stage has been added to the action-handling process: *(after $Action)

  • Taking other people’s clothing or possessions is now disallowed by default. Thanks @MultidimensionalStep!

  • There are new narration predicates for holding or wearing nothing, invoked by the default implementation of the inventory action. The default message when nothing is worn has been left blank. Thanks @hlship!

  • There are new predicates for understanding some words as any object, e.g. (understand $Words as any object $Obj). Such objects don’t have to be in scope, but they must be located in a visited room, and not marked as hidden.

  • There are new utility predicates for printing adverbs corresponding to directions (e.g. above/below) (present-adverb $), (from-adverb $); obtaining the opposite of a direction (opposite of $ is $); and selecting a random element from a list (randomly select $ from $).

  • It is now possible to use (from $ go $ to room $) backwards, i.e. to specify the current and neighbouring rooms, and obtain the direction. This is useful when writing custom messages about NPC movement.

  • The banner can be extended by defining a rule for (additional banner text).

  • Disambiguation now takes ‘(dict $)’ synonyms into account. Thanks @MultidimensionalStep!

  • The path-finding algorithm has been slightly refined, and a new predicate (first step from $ to $ is $) is provided for when the complete path isn’t needed.

Outside of the library, this release fixes a couple of bugs in the Z-machine backend and the interactive debugger. The manual briefly mentions the 8-bit Å-machine interpreter, and the diagram in the Software chapter has been updated.


In addition to the usual assortment of incremental improvements and bugfixes, Dialog version 0i/01 (library 0.33) contains a couple of—gasp!—compatibility-breaking changes. Details below!

Changes to the language

Initial object tree

The initial object tree is computed in a new way that makes the order of children predictable. Earlier, the compiler would loop through each object in the game (in some random but consistent order), and make a query to ($ has parent $) to determine its initial location. Now, the compiler instead makes a single multi-query to ($ has parent $), and exhausts every branch of the choice structure. The first parent associated with a given object is stored in the object tree, and the order in which children are encountered is retained.

For most games, there is no need to change anything in the source code because of this. The order of children may change, but from now on you can easily modify that order to your liking, by moving the rule definitions around. If the story code says:

(#lamp is #heldby #player)
(#hat/#balloon is #heldby #player)

then the lamp, the hat, and the balloon will appear in the initial inventory listing in that order. This was not the case before.

There is one important subtlety, however: To assign a common parent to every object identified by a trait, it is now necessary to include an asterisk before the trait name.

Example. If the code says:

(edible #apple)
(edible #lettuce)

(#apple is #in #bowl)

(*(edible $) is #in #fridge)    %% Note the '*'!

then the compile-time multi-query will succeed three times, first with (#apple has parent #bowl), then with (#apple has parent #fridge), and finally with (#lettuce has parent #fridge). The apple will be located in the bowl (its first parent encountered), and the lettuce in the fridge.

The initial values of other dynamic predicates—besides ($ has parent $)—are computed as before.

Short-form hyperlinks

The original syntax for hyperlinks is: (link [list of words]) { visible link label }

In practice, the list of words is often identical to the visible link label, leading to redundant code such as:

        The (link [axolotl]) axolotl observes you from its
        hiding place in the (link [fish tank]) {fish tank}.

Starting with this version of Dialog, there is a short form, “(link) statement”, where the link target (the list of words) is derived from the visible text. The following is equivalent to the previous example:

        The (link) axolotl observes you from its hiding
        place in the (link) {fish tank}.

For the time being, this only works if the visible link label is a single word, or a block containing only words. In the future, I intend to add support for arbitrary statements, but that requires a change to the Å-machine.

Debug logging

The construct “(log) statement” will execute the given statement if (and only if) the program is currently running in the debugger. The output will appear between line breaks, and in a distinct style. This is handy for inserting temporary printouts during debugging.

        (current visibility ceiling $Ceiling)
        (log) { The current visibility ceiling is $Ceiling. }
        %% or perhaps just:
        (log) $Ceiling

The statement executes in its own stoppable environment.

This feature was requested by @hlship.

Changes to the standard library

  • The library will now automatically notice (set the pronouns from) an object when its appearance predicate succeeds. Thus, to disable the default appearance message, you must now prefix the rule definition by a tilde character, e.g.: ~(appearance $ #on *)

  • The library defines no rewrite-rules anymore. All synonyms are handled by ordinary understand-rules, and (rewrite $ into $) is reserved for use by the story author. Custom understand-rules may need to be adjusted, if they were relying on rewrites that the library was doing. For instance, if you’re defining a new action [look surprised], it is now up to you to deal with L for LOOK:

(understand [look/l surprised] as [look surprised])

Alternatively, you could reinstate the old rewrite-rule in story code:

(rewrite [l | $Words] into [look | $Words])
  • The actions [ask $ about $] and [tell $ about $] now redirect to an action called [talk to $ about $], which in turn redirects to [talk to $] by default. This allows you to focus on implementing a single action, [talk to $ about $], in a game that cares about conversation topics but doesn’t make the ask/tell distinction. Thanks @MultidimensionalStep!

  • A short form of the appearance predicate is provided: (appearance $Obj). This has the same semantics as (appearance $Obj $ $), but with lower priority.

  • A new predicate, (list objects $Rel $Obj), prints a sentence about the set of objects that have the given relation to the given object. The output for e.g. (list objects #in #fruitbowl) might be "In the fruit bowl is an apple and a kumquat”, or nothing at all if the bowl was empty.

  • Fixed several cases of hardcoded “is” where a plural-sensitive copula was required. Two of these were reported by @hlship.

  • Actions involving rooms are still “very unlikely”, but actions involving the unrecognized topic are now considered “very very unlikely”. The distinction is important when the player asks an NPC about a room, or about something that’s ambiguously a room or an object. This change also makes it easier to disable “very unlikely” for actions that really do expect a room.


Thanks for log! I look forward to using it once the brew recipe is updated and I can use it.

I will definitely be using (list objects $Rel $Obj) and (appearance $Obj).

I’m a little concerned about notice from appearance as it feels like I might mention an object explicitly, but if my room contains any items, those will then get noticed, overriding my mention.

1 Like

Dialog version 0i/02 (library 0.34) is a small bugfix release:

  • Library: Fixed a bug in how the visibility ceiling was computed. Thanks @hlship!

  • Debugger: Removed spurious extra “Query succeeded” after each interactive query. Thanks @hlship!

  • Debugger: Proper reporting of interactive queries to access predicates. Try e.g.: *($ is $ $)

  • Manual: Added missing multi-query asterisks to the fungibility examples. Thanks @Mikawa!


Another bugfix release: Dialog version 0i/03 (library 0.34).

  • Compiler: Fixed a bug where, under very specific circumstances, a register could get overwritten by an else-clause.

  • Å-backend: Fixed a bug where text containing non-ASCII characters would occasionally be converted to lowercase.

  • Debugger: Fixed a bug in how UTF-8 input is divided into words.

  • Manual: Added a clarification about the pristineness of nested objects, including the initial possessions of the player. Thanks @hlship!


Glad to see the change to the manual; just to be picky, there’s a problem below the new example:

All about appearances

You may have noticed a problem with the last example:

The wallet/receipt is now the “last example”, not the “bowl of gems”.

1 Like

Dialog version 0j/01 (library 0.35) is ready! Also make sure to get the companion release, Å-machine tools 0.4.1.


There is now a (span $) construct for applying style attributes to inline content, as a complement to (div $). The library provides style classes @bold and @italic by default, so you can do e.g.:

        (span @italic) { Lorem Ipsum } , he (span @bold) emphasized .

The old built-in predicates (bold), (italic), (reverse), (fixed pitch), (roman), and (unstyle) still work, but spans are preferable because they have full CSS support (when using the web interpreter) and provide a clean separation between content and presentation.

Better hyperlinks

The short form of (link) can now be followed by any kind of statement, not just a plain list of words. This makes it much easier to create clickable text on the fly:

(descr (fruit $Obj))
        You're really yearning to (link) { eat (the $Obj) }.

There’s also a new builtin, (clear links), that turns old hyperlinks into plain text. This is useful when the scope changes drastically, e.g. when moving to a different room.

Word manipulation

There are new builtins for splitting and joining words. This opens up all kinds of possibilities, like printing words backwards, counting the number of letters, or accepting transliterated input (such as “oe” for “ö”).

A new builtin, (unknown word $), checks if a word is listed in the game dictionary. This can be used to implement responses such as “You don’t need to use the word ‘borborygm’ in this story”. It is also required for the venerable “OOPS” command, which has now been added to the standard library.

Parentheses are now treated as individual words when parsing player input. The full set of such characters is: . , ; * ( ) "

From now on, such characters are also treated as separate words during (collect words) operations, even when they appear as part of larger words in the source code. This makes it a lot easier to work with object names like “purple envelope (open)”.

Also, when these single-character words are printed back as values, whitespace is inhibited before . , ; ) and after (. Earlier, if you wanted a comma in the printed name of an object, you had to do something like this:

(name #snowman)
        the tall , tall snowman

but now the following works exactly the same:

(name #snowman)
        the tall, tall snowman

Development and debugging

It is now possible to declare interfaces, a simple form of machine-readable documentation that describes how a predicate is supposed to work. Such declarations have been added to the standard library.

The compiler is able to trace unbound values, and print warnings about potential interface violations. Thanks to the interface declarations, the compiler is better equipped to report problems at the file and line where they actually originate, and not just at the place where they might cause problems at runtime.

The compiler will now also warn about variable names that only appear once in a rule definition, as these tend to be typos. To get rid of such a warning, change the variable into a wildcard ($).


(append $A $B $AB) is now built into the language, rather than defined by the library, for performance reasons.

The Å-machine backend produces smaller and faster storyfiles thanks to several new Å-machine 0.4 features.


  • Library: Fixed a bug in how reachability is determined. Thanks @hlship!

  • Library: (understand $ as any object $ preferably $) didn’t take the specified policy into account; this is now fixed.

  • Manual: Moved the section about the pristineness of nested objects to the end of the Items chapter.



Dialog version 0j/02 (library 0.36) is a small bugfix upgrade:

  • Debugger: Removed stray warnings about singleton variables when merging changes to the running program.

  • Library: Visibility is now recomputed after updating the current room variable and moving any floating objects.

1 Like

Thanks for the quick turnaround; I can verify that in my project, the problems I saw previously are gone.


Here’s Dialog version 0j/03 (library 0.37)!

  • The new big thing is choice mode, i.e. integrated support for switching between parser-based and choice-based interaction. There’s a new section in the manual about it—with diagrams!—and another section describing how to use it for choice-based conversations with NPCs.

  • Two new hooks, (early on every tick) and (late on every tick), have been added. Thanks @hlship for suggesting these!

  • Miscellaneous bugfixes and improvements.


You are adding so many compelling features to the Dialog development system, it is becoming a strong option for parser based IF. I have two i6 games currently in development. I may begin writing one of them in parallel with Dialog.

Thank you,

1 Like

It is alsready a strong option, in my opinion, plus the big advantage of being actually developed, unlike TADS and Inform.


Agreed. I should have said, “Becoming a stronger option”. Dialog is brilliant.



I would like to have an option for aambundle which lets you only export the new story.js file from an .aastory file. I am using a customized web enviroment for my game and it’s tedious always have to export the whole directory structure. It would be more convenient to only exchange the story itself, especially when doing only minor enhancements.


Good idea! Noted.

1 Like

New in Dialog version 0j/04 (library 0.38):

  • Undo is now treated as an action, [undo], and reported via (narrate undoing). Requested by @hlship.

  • Fixed a bug that disallowed @-prefixed words in slash-expressions. Thanks @hlship!

  • When the player types an object name in response to a direct question (e.g. “To whom?”), this is no longer ambiguously understood as a request to perform the default action (e.g. examine).

  • Several improvements to the optimizer.


This is now possible in Å-machine version 0.4.4. Be aware that the web interpreter also expects to find the original .aastory file in the resources directory, with a mangled filename. This is the file that gets served when the player selects “Download story file” from the menu.

1 Like

Time for some changes! Get Dialog version 0k/01 (library 0.39) here.

Simplified grammar

Language change: Access predicates can now have multiple definitions, and pattern matching is applied to the parameters. Thus:

@(magic rule [$Head | $Tail])    (ordinary rule $Head) (magic rule $Tail)
@(magic rule $Other)             (ordinary rule $Other)

With the above access predicate definitions, the query (magic rule [hello little world]) would be transformed, at compile-time, into:

        (ordinary rule @hello)
        (ordinary rule @little)
        (ordinary rule @world)
        (ordinary rule [])

Such compile-time transformations are a sharp tool, to be used sparingly and with caution. But with this small language change, the library can now offer a simplified syntax for extending the parser grammar and adding new actions.

Previously, adding an action such as “TRANSMOGRIFY object WITH object” required a lot of boilerplate code:

(understand [transmogrify | $Words] as [transmogrify $Obj with $Tool])
        *(split $Words by [with] into $Left and $Right)
        *(understand $Left as non-all object $Obj)
        *(understand $Right as single object $Tool preferably held)

But now, all you need is:

(grammar [transmogrify [object] with [single held]] for [transmogrify $ with $])

In light of this new syntax, most of the Understanding player input chapter of the manual has been reworked, and should be much easier to follow now.

You can still define (understand $ as $) rules, for those odd situations where the new grammar-syntax is too simplistic. In fact, the parser still queries (understand $ as $) as before, but the library provides a new rule for this predicate, which looks into a grammar table constructed at compile-time from (grammar $ for $) definitions.

Not only is the new grammar syntax easier to read and work with, it also results in a more compact runtime representation, thus lowering the memory requirements of the parser and improving performance on 8-bit systems.

Accumulating sums

A new variant of the collect-into mechanism has been added to the language. This will exhaust every branch of the inner statement, look at the values taken on by a given expression, and add those values together.

You can use it e.g. to compute a score:

        (accumulate $Score)
                (puzzle a is solved) ($Score = 5)
                (puzzle b is solved) ($Score = 1)
                (puzzle c is solved) ($Score = 10)
        (into $Sum)
        You have a total score of $Sum.

Or merely count the branches:

        (accumulate 1)
                (puzzle a is solved)
                (puzzle b is solved)
                (puzzle c is solved)
        (into $Sum)
        You have solved $Sum puzzles.

You could also e.g. compute the total recursive weight of an object and its descendants. Assuming you’ve defined ($ has weight $) for all relevant objects:

        (accumulate $W)
                        ($Obj = #player)
                        *($Obj has ancestor #player)
                ($Obj has weight $W)
        (into $Sum)
        The total weight is $Sum.

Other language changes

  • Added builtin (fully bound $) to check if a value is bound and, in case of a list, contains only fully bound elements.

  • (split $ by $ into $ and $) now accepts a single keyword as its second parameter (or a list of keywords, as before).

  • Added special keypress words @\u, @\d, @\l, @\r, @\n, @\s, and @\b. Removed (word representing up $) and friends.

  • The character + may now be used in the source-code names of objects and local variables.

Other library changes

  • Relation objects (#on etc.) no longer have dictionary words. They appear directly inside action grammar rules instead, e.g. [put [held] on/onto [single]].

  • Added intermediate action [switch $] that delegates to [switch on $] or [switch off $].

  • Bugfix: Duplicates are now removed from the list of choices in choice-mode.

And finally, there are some bugfixes and improvements to the optimizing compiler.


Dialog version 0k/02 (library 0.39) fixes a bug, found by @Karona, that prevented per-object variables from being initialized to complex values.