Dialog release announcements

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.


This is odd. I tried instantiating the Choice Mode example, and when compiling for z8, I get this:

Warning: A query is made to '(program entry point)', but there is no matching rule definition.
Warning: No release number declared.

In addition, it produces a story file that, when run, gives me a blank screen. The same happens when trying to compile the Cloak of Darkness example. I haven’t tried any others yet.

Sounds odd indeed.

Is there something wrong with your library file? That’s where the default release number is declared, and a rule for (program entry point) is defined.

…oh dear. Yes, that was my bad. I’ve been on hiatus and when I went back to Dialog, I didn’t check the manual for the inclusion of the library, I just looked at the command line instructions (–?), so in my grogginess I entirely overlooked the need for the library file.

Here is Dialog version 0k/03 (library 0.40).

Notable changes

  • When expanding a complex action (e.g. due to “TAKE ALL”), the library now prints the name of each object in turn, instead of spelling out the entire action.

  • “DOWN”, “UP”, and “OUT” (with some variations) are now understood as ambiguously referring to [leave $] and [go $] when the player is on a supporter, in or on a seat, or in a container, respectively. A rationale is in this post. Thanks @Karona!


  • Debugger: When updating a running program, the debugger needs to match the (select) statements in the old and new versions of the code using a diff-like algorithm, in order to preserve as much of the game state as possible. The code for this has been reimplemented from scratch in a cleaner way. The old version had a bug that would sometimes result in the debugger reporting an “internal inconsistency in select”. Hopefully that bug is no longer present.

  • Debugger: No longer crashes when encountering output boxes while collecting words.

  • Library: Updating the environment around the player (the cache variables) before drawing the status line. Reported by @Karona.

  • Library: Allowing (from $ go $ to room $) to be invoked with just the first parameter bound, to backtrack over all exits.

  • Library: (perform [leave $]) now uses (move player to $ $) instead of setting the location of the player object directly.

  • Library: (them $) can now deal with a list argument.

Also, minor improvements to the optimizer.


Having built up a somewhat complex library (threaded conversation) and most of a full game (Sand-dancer, re-implemented in Dialog) I have to say that the each Dialog upgrade to a new versions has added features, but been virtually painless. Great backwards compatibility - that’s an accomplishment I appreciate.


I’m happy to release Dialog version 0k/04 (library 0.41).


  • Open a closed container before stepping out of it. Thanks @JimB!

  • Don’t describe contents when opening a box from the inside. Thanks @JimB!

  • Fixed a bug where objects immediately inside a closed opaque visibility ceiling weren’t considered visible.

  • Added missing helper predicate: (Its $). Thanks @mount2010!


  • Allow (now) together with a double-negated flag, such as in (now) ~($Obj is on) when @($ is on) is an access predicate for ~($ is off). Thanks @mount2010!

  • Warn if the last filename on the commandline does not refer to a library. Thanks @mount2010, @jcompton, @Karona, @Eleas! A file is considered a library if it defines a rule for (library version).


  • Improved support for cursor keys in TTY driver.


  • Removed wrongly formatted CSS comments. Thanks @mount2010!

Dialog version 0k/05 (library 0.42) is out!

Group actions

When the player types something like “EAT OYSTER, HAM AND CHEESE”, the usual outcome is that three separate actions are tried in sequence, i.e. [eat #oyster], [eat #ham], and [eat #cheese].

This new feature lets you specify certain combinations of actions and objects that your code can deal with in a single go, e.g. as [eat [#oyster #ham #cheese]]. Read all about it in the new manual section on group actions.

Library improvements

  • The parser now deals properly with “and” and “,” inside object names, so that e.g. “TAKE BLACK AND WHITE PHOTOGRAPH” does not result in two separate attempts to take the same object.

  • (The $) and friends now list fungible objects with a definite article, i.e. “the N items” instead of “N items”.

  • Improved memory performance when working with fungible objects.

Compiler and debugger

  • An informative message is printed when attempting to display memory statistics from within the debugger. Thanks @Karona!

  • Fixed a bug in the Z-machine backend where (no space) would be ignored immediately after a unicode character. Thanks @jcompton!

  • Miscellaneous bugfixes.


At last.

The epic tale of Zork V: The Great Underground Sandwich Franchise can be told!


Here is Dialog version 0k/06 (library 0.43).

Default response

There is now a default perform-rule saying “You can’t” followed by the action description. So now, if you define some new grammar:

(grammar [transmogrify [object]] for [transmogrify $])

then you get this by default:

> transmogrify me
You can't transmogrify yourself.

This response is as bland as they get, so you’d normally want to override it. But this eliminates the dreaded blank response if you forget to create a fallback rule for a new action.


The dynamic flag ($ is opaque) has been renamed to (opaque $), and the negated alias ($ is transparent) has been removed.

Opaqueness is nearly always a static property, and the new name reflects that. If some particular object can change from opaque to transparent, it is better to model this with a global flag:

(opaque *)      (crystal ball is opaque)

or perhaps compute it in some other way:

(switchable *)
(opaque *)      (* is on)

This change breaks compatibility with existing code. For most projects, it should be easy to search for “opaque” and “transparent” and manually change them to the new syntax. But it’s also possible to get the old behaviour back by adding the following definitions to the story code:

(opaque $Obj)           ($Obj is opaque)
@($Obj is transparent)  ~($Obj is opaque)

Other library changes

  • Changed the output of “exits” to make compass directions clickable if library links are enabled, and to use “the” instead of “a” for doors.

  • When default actions are enabled, the library no longer adds “open” as a dict-word for any open object. This prevents “open door” from being ambiguously understood as [open #door] and [examine #door] when the door is open.

  • “Hug” is now a separate action from “kiss”. Hug redirects to kiss by default, for backwards compatibility.

  • The default rules for NPC actions, (let $NPC ...), now invoke (notice $NPC) to set the pronouns.

  • When describing each step of a complex action, the object names are now clickable (if library links are enabled).

  • When printing a group of fungible objects with (the $), the plural name is no longer clickable. This is consistent with (the $) for ordinary objects.

  • The default rules now prevent cycles in the object tree when the player attempts to pick up an object that is their ancestor, such as a seat. Thanks @ifste!

  • The default rules for climb and enter now use (move player to $ $).

  • Miscellaneous bugfixes and optimizations.


The debugger now imposes a limit of 50 undo steps, to conserve memory.

Undo states in the debugger are much larger than undo states in a normal interpreter. They contain information necessary for tracing, as well as references to old versions of predicates (to support modifications to the source code while the program is running). For large games with large transcripts, limiting the size of the undo history can save gigabytes of RAM and speed up testing substantially.


  • Added a section about creating custom grammar tokens.

  • Fixed an outdated example in the Items chapter.


Numbers in the range 90-99 are reserved for story authors … is there some reason why numbers greater than 99 are a problem? I just jumped up to 1000 or so for some grammar tokens inside the threaded conversation library; is that a problem?

My reading of that passage is that the standard library will always avoid using that range (and extension libraries probably too), so that the author of an individual story can always count on it to be free for their own use.

For example, if I wrote Scroll Thief in Dialog, I could use 90 for [spell], 91 for [zifmia-target], and so on—and I wouldn’t have to worry that a new standard library release, or a new version of Threaded Conversation, or a new extension that I install, would suddenly break that in inscrutable ways.

That’s one take; I don’t know the internals of the two machines, and was wondering if there’s an optimization based on size of that number (though I’d expect that to hit at either 127 or 255, for example).

Ahh, I see.

To my understanding, Dialog on the surface treats all numbers from 0 to 16383 in the same way.

On the Z-machine (don’t know about the Å-machine), there’s a certain low-level optimization for constant operands smaller than 256. But it only saves one byte per value, and only works with constants. So I wouldn’t worry about that.

(It’s a worthwhile optimization in general, of course, since there are a lot of small constants in a Z-machine program, and those single bytes add up. But the vast majority of those are e.g. branch offsets, loop counter increments, array indices, etc. Choosing a “small” vs “large” value for a magic number shouldn’t make any meaningful difference in the final code.)

Right, there is no particular reason to stay below 100 other than forward compatibility. Maybe larger numbers will represent something entirely different in the future, or maybe not.

There is no particular optimization of small numeric constants in this case.