Three weeks ago, LLVM added an obscure feature to its WebAssembly linker. That feature turns out to be a game-changer for the design of Bedquilt.
The feature in question is support for .init_array
sections, which collect a list of pointers to functions that should be executed before main
is invoked. This made it possible for Rust’s inventory
crate to support WebAssembly. Some limited support was merged a few weeks ago, and I now have a pull request out [EDIT: it’s been merged and released now] which extends that support to all kinds of WebAssembly modules, including those intended for processing by Wasm2Glulx.
The inventory
crate makes it possible to declare a collection of objects, called a registry, which can be added onto in a decentralized fashion. Any crate can declare new objects that should be included in the registry, with no need to maintain any central list of everything that should be there. Then, at runtime, a program can enumerate everything that was registered by any crate anywhere in its dependency tree.
The application of this capability to an interactive fiction system is likely obvious: when adding an object to your game world, you’d like to just declare the object and its properties and be done with it. It would be irritating, verbose, and bug-prone if at some central point in your program, you also had create a giant listing of everything that had been declared elsewhere in order to have it included in your game.
Notes on Bedquilt's world model described my ideas for a domain-specific language, implemented by a preprocessor which runs from build.rs
and compiles it down to Rust, for declaring the contents of your game world. The preprocessor would allow developing your game in a declarative style and would take care of generating that giant-list-of-objects and keeping it in sync.
Now, thanks to inventory
, a dedicated preprocessor isn’t going to be necessary any more: all code-generation can now be handled by Rust’s macro system. Although Rust macros are extremely powerful, they have one critical limitation: macros aren’t able to communicate with each other. All they can do is transform a list of input tokens into a list of output tokens, and this transformation is expected to be deterministic and not dependent on anything but the input. (There are ways to cheat, but they’re ugly, brittle, and generally to be discouraged). This means that “gather up a scattered bunch of declarations and regurgitate them in one central place” is not something that macros can readily handle. But now they don’t have to: inventory
can do that instead, and the macros can do all the rest.
Getting rid of the preprocessor is going to be a big win for the ergonomics of developing in Bedquilt. It means you’ll be able to freely mix your entity declarations with other Rust code, rather than having to maintain them in a separate file. It means the macro invocations can now be the output of other, user-defined macros, so you can create your own syntactic sugar at will. And it means the whole thing will integrate a lot more smoothly with your IDE, and with tools such as rust-analyzer.