A DSL parser and code generator
  • Java 94.9%
  • ANTLR 3.2%
  • Shell 1.9%
Find a file
2025-12-16 09:21:43 +01:00
pannkaka-assembly [maven-release-plugin] prepare for next development iteration 2025-12-16 09:21:43 +01:00
pannkaka-cli [maven-release-plugin] prepare for next development iteration 2025-12-16 09:21:43 +01:00
pannkaka-generator [maven-release-plugin] prepare for next development iteration 2025-12-16 09:21:43 +01:00
pannkaka-maven-plugin [maven-release-plugin] prepare for next development iteration 2025-12-16 09:21:43 +01:00
pannkaka-parser [maven-release-plugin] prepare for next development iteration 2025-12-16 09:21:43 +01:00
.gitignore Ignore genreated pannkaka files 2025-11-13 16:51:23 +01:00
Jenkinsfile Package and deploy causes duplicate source jars to be attached which is not allowed 2025-11-21 06:03:30 +01:00
LICENSE Add license header 2025-11-15 21:04:20 +01:00
pom.xml [maven-release-plugin] prepare for next development iteration 2025-12-16 09:21:43 +01:00
publishing.txt Prepare for publishing to central 2025-11-16 12:55:42 +01:00
README.md Fix doc 2025-11-20 21:40:26 +01:00

pannkaka

Pannkaka ('pancake') is a tool for generating Java source code from a DSL.

Pannkaka uses the Apache Software Licenese 2.0

The code it generates consists of java Records and De-/Serializers using DataOutputStream and DataInputStream. The use case is to describe your data and messages in the DSL, then generate the code to Marshall those messages to an external format suitable for IO/Network transfer.

Pannkaka is not a generic framework for serialization like protobuf/Kryo or similar. It is meant to be a light-weight alternative for simple use cases. You generate the code, maybe even just one time and then treat it as source code in your own project.

How to install

Pannkaka requires Java 25 or later.

Download the archive from the release page and extract it. There is a bin directory with a launcher script that should work in bash.

How to write a DSL.

Here is an empty sample DSL:

config {}
records {}
messages {}

Config block

The config block is optional. It can be used to set package name and message interface name for the generated code.

    config {
        package: my.example.messages,
        messageInterface: MyMessage
    }

Records block

The records block defines the data structures (Java records) that will be generated.
Each entry becomes a Java record with the same name and fields in the same order.

In the sample DSL:

records {
    Foo(
        int intbar,
        boolean boolbar,
        char charbar,
        float floatbar,
        double doublebar,
        byte bytebar,
        short shortbar,
        long longbar,
        String stringbar
    );
    Bar(String baz, Foo foo);
}

This will generate two records, Foo and Bar. Foo contains primitive and String fields, while Bar shows that records can reference other records (Foo foo).

Primitive types

The types in the Foo record above are all the primitive types pannkaka supports.

Messages block

The messages block defines the messages that will be sent over the wire.
Each message is also generated as a Java record and typically composes previously defined records.

In the sample DSL:

messages {
    AddFoToBarMessage(Foo foo, Bar bar, boolean fakeFlag);
    RemoveBarFromFooMessage(char fakeChar, Bar bar, Foo foo);
}

Here, AddFoToBarMessage and RemoveBarFromFooMessage are messages that use Foo and Bar as payload types, along with primitive fields. These message definitions will be turned into serializable Java records together with matching de-/serializers.

The generated code

The generated code consists of three classes for each record and message:

  • A record class with the same name as the record in the DSL.
  • A serializer class with the same name as the record in the DSL, but suffixed with "Serializer".
  • A deserializer class with the same name as the record in the DSL, but suffixed with "Deserializer".

The generated code

SerDe

For each DSL, Pannkaka generates a SerDe (serializer/deserializer) class for your messages.
Its name is derived from the messageInterface value in the config block:

  • If you configure messageInterface: MyMessage
  • The generated SerDe class will be named MyMessageSerDe

This class exposes static methods to write and read messages using DataOutputStream and DataInputStream.

A typical usage from Java code looks like this:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;

// import your generated types and SerDe class:
// import my.example.messages.*; // e.g. MyMessage, Foo, Bar, MyMessageSerDe

public class Example {
    public static void main(String[] args) throws Exception {
        // Construct a message using generated records
        Foo foo = new Foo(1, true, 'x', 1.0f, 2.0, (byte)3, (short)4, 5L, "hello");
        Bar bar = new Bar("baz", foo);
        MyMessage message = new AddFoToBarMessage(foo, bar, false);
    
        // Serialize
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (DataOutputStream dos = new DataOutputStream(baos)) {
            MyMessageSerDe.write(message, dos);
        }
        byte[] bytes = baos.toByteArray();

        // Deserialize
        try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes))) {
            MyMessage roundTripped = MyMessageSerDe.read(dis);

            // Use the deserialized message (pattern match / instanceof / switch, etc.)
            if (roundTripped instanceof AddFoToBarMessage add) {
                System.out.println("Got AddFoToBarMessage with baz = " + add.bar().baz());
            }
        }
    }
}

In summary:

  • Construct your generated message record(s).
  • Use the generated *MessageSerDe.write(message, DataOutputStream) to serialize.
  • Use *MessageSerDe.read(DataInputStream) to get back an instance of your message interface.
  • Dispatch on the concrete message type (switch or instanceof) to handle different messages.

Maven

There is also a mven plugin to generate sources from DSL files. See pannkaka-maven-plugin