Wexpr - Wolf Expressions

Author: thothonegan
Tags: gamedev endless wexpr wolf_engine

Configuration files. Everything needs them, and they're used often. In previous times I've used:

  • XML
  • YAML
  • JSON

All of these have benefits / disadvantages from each other. For my game engine, I have several requirements:

  • Must be human readable
  • Must be machine readable
  • Fast to parse
  • Allows comments
  • Ability to optimize later if desired (aka has/can have a binary format)
  • Easy to manipulate programmatically

Unfortunately, every one of the standard formats i've tried has missed one requirement or another. So I've created my own custom format called Wexpr (Wolf Expressions). Here's an example from the tileset system i'm implementing today:

@(
    ; Our tileset
    name "World Forge"
    
    randomArray #(1 2 3)

    templates @(
        0 @(
            texture "Tiles/SpaceTile"
            walkAbility "none"
            flyAbility "none"
        )
        
        1 @(
            texture "Tiles/Between/Between-Floor"
            walkAbility "normal"
            flyAbility "flyable"
        )
    )
)

Its very JSON-like using lisp syntax. ; is for comments, #() is an array, @() is a map, and everything else is strings. It has some general rules such as true/false for booleans, but they're enforced outside the language when parsing. Lastly, it has an alternate form for @() with extra parans for single line readability, so instead of:

@(repeat true speed 0)

you can do:

@( (repeat true) (speed 0) )

So what makes this better than the others?

  • Human readable. The rules are very straightforward and nestable. While you can make lisp a mess, it works pretty well for simple configurations.
  • Machine readable/ Fast to parse. Lisp has been around forever, and the parser for wexpr was written in an hour. Nothing complicated requiring look-aheads or anything weird.
  • Allows comments. Has ;, done.
  • Ability to optimize later. Theirs two major ways you can optimize this : either by optimizing whitespace (like json) or by converting to its binary format. The binary format for this is really straightforward too (very similar to the text format with markers/size offsets), which can easily be done as a WolfRez compile step.
  • Easy to manipulate programatically. Its the same tree structure as most other config layers, so the same workflow works perfectly with it.

Biggest issue with it of course is its custom, and the only reference implementation is my internal one, which is heavily tied to the engine. Sorry, but no code to give out on this one unless people clamor for it. As I mentioned though, it literally took an hour to write a parser for it, so it'd be a straightforward project for someone.

Most of my engine is still using YAML due to legacy, but as new stuff gets added, its slowly using the newer format.