grimoire 0.3.1

A fast, concurrent based scripting language for D

To use this package, run the following command in your project's root directory:

Manual usage
Put the following dependency into your project's dependences section:


What it looks like:

//Invert a string
main {
    assert("Hello World !":invert == "! dlroW olleH");

func invert(string str) string {
    let result = str as array(string);
    loop(i, result:size / 2)
        result[i], result[-(i + 1)] = result[-(i + 1)], result[i];
    return result as string;
main {
        func(int n) int {
            if(n < 2) return n;
            return self(n - 1) + self(n - 2);
        }(10) == 55);

Grimoire is an concurrent programming language that can easily be embedded into another D programs. You can very easily interface your program with Grimoire's scripts.

The language itself is focused on concurrency, and feature both static and dynamic typing.

Hope you have fun with this project !

Documentation here !


Use dub to include grimoire in your project (or just dump the files in your project if you're too tired). Open the "test" folder to see how to add Grimoire to your program or copy/paste it.

Grimoire is in 2 parts:

  • The compiler
  • The runtime


First you need to set up GrData The GrData object contains all binded D-functions (called primitives here) and types definitions shared with the scripts. If you want to bind D-functions or create types with it, you must do so before compiling your script. The GrData object will be used by the runtime as well, so it must remains the same between compilation and runtime.

GrData data = new GrData;
// Define you types and primitives in data here..
grLoadStdLibrary(data); //Like the provided default library for example.

Then, you need a compiler GrCompiler which will turn your scripts into bytecode with compileFile. If the compilation fails, you can fetch the error with getError().

GrBytecode bytecode;
GrCompiler compiler = new GrCompiler(data);
if(compiler.compileFile(bytecode, "")) {
    // Compilation successful
else {
    // Error while compiling
    import std.stdio: writeln;


Then, create the runtime's virtual machine GrEngine, load the data and bytecode then spawn the main task.

GrEngine engine = new GrEngine;
engine.load(data, bytecode);

You're not forced to spawn the main, you can spawn any other named event like this:

auto mangledName = grMangleNamedFunction("myEvent", [grString]);
if(engine.hasEvent(mangledName)) {
    GrContext context = engine.spawnEvent(mangledName);
    context.setString("Hello World!");

But be aware that every function/task/event are mangled with their signature, so use grMangleNamedFunction to generate the correct function's name.

If the event has parameters, you must push them into the context with the setXX functions.

To run the virtual machine, just call the process function (check if there's any task(Coroutine) running though):


The program will run until all tasks are finished, if you want them to run only for one step, replace the while with if.

You can then check if there are any unhandled errors that went through the VM (Caught exceptions won't trigger that).

    writeln("Unhandled Exception: " ~ to!string(engine.panicMessage));

Exemple Program

The classic Hello World ! nice to meet you !

main {
    printl("Hello World!");

You can find the language documentation > here ! <

  • Enalye
Sub packages:
grimoire:test, grimoire:unittests
0.8.1 2023-Mar-20
0.8.0 2023-Mar-06
0.7.0 2022-Nov-29
0.6.0 2022-Nov-01
0.5.2 2022-Jan-30
Show all 32 versions
Download Stats:
  • 1 downloads today

  • 1 downloads this week

  • 1 downloads this month

  • 230 downloads total

Short URL: