hopp/design/pdl-compiler.md

3.5 KiB

PDL Compiler Specification

Given one or more PDL files representing a protocol, the compiler shall generate a Go package named "protocol", which shall contain types for message and type definitions, as well as encoding and decoding methods.

Static Section

The compiler shall write a static section alongside the generated code. It shall contain this text:

// Table is a KTV table with an undefined schema.
type Table map[uint16] any

// Message is any message that can be sent along this protocol.
type Message interface {
	codec.Encodable
	codec.Decodable

	// Method returns the method code of the message.
	Method() uint16
}

Preamble

At the start of each file but after the package name, the compiler shall emit this text:

/* # Do not edit this package by hand!
 * 
 * This file was automatically generated by the Holanet PDL compiler. The
 * source file is located at <path>
 * Please edit that file instead, and re-compile it to this location.
 *
 * HOPP, TAPE, METADAPT, PDL/0 (c) 2025 holanet.xyz
 */

Where <path> is the path of the protocol definition file relative to the generated file.

Message Definitions

For each defined message, the compiler shall generate a Go type named MessageName, where Name is the name of the message as written in its definition. The message shall be encodable, and shall have Encode and Decode methods as described below.

All messages shall satisfy a Message interface, which is defined in the static section.

Type Definitions

For each defined type, the compiler shall generate a Go type with the same name as written in its definition. The Go type shall be encodable, and shall have Encode and Decode methods as described below.

Encoding and Decoding Methods

Each encodable type shall be given an Encode method and a Decode method, which will take in a codec.Encoder and a codec.Decoder respectively. Both return an (int, error) pair describing the amount of bytes written and an error if the write stopped early. Encode will encode the data within the message to the given encoder, and Decode will decode data from the given decoder and place it in the type's value. The methods shall not retain or close any encoders or decoders they are given. Both methods shall have pointer receivers. In effect, these methods will satisfy codec.Encodable and codec.Decodable.

Connection

The compiler shall generate a Conn struct which embeds a hopp.Conn, which is the real "porcelain" of the generated code. It shall provide methods to create and accept transactions. Each transaction shall be a struct which embeds a hopp.Trans, and shall have methods for sending and receiving messages.

Sending

To send a message along a transaction, the program shall:

  1. Obtain the method code from the message
  2. Obtain a writer from the connection using the method code
  3. Wrap the writer in a codec.Encoder
  4. Use the encoder to encode the message
  5. Close the writer

Receiving

To receiving a message from a transaction, the program shall:

  1. Obtain a method code and reader from the connection
  2. Wrap the reader in a codec.Decoder
  3. Switch on the method code, and decode the correct message using the decoder
  4. Return the message to the caller as a value

The recieve function must return the message as a value instead of a pointer in order to avoid making an allocation. Because of this, the return value must be any instead of Message. The caller must then use a type switch to figure out what message was sent.