hopp/design/pdl.md

5.8 KiB

PDL Language Definition

PDL allows defining a protocol using HOPP and TAPE.

Data Types

Syntax TN CN Description
I5 SI
I8 LI 0
I16 LI 1
I32 LI 3
I64 LI 7
I1281 LI 15
I2561 LI 31
U5 SI
U8 LI 0
U16 LI 1
U32 LI 3
U64 LI 7
U1281 LI 15
U2561 LI 31
F16 FP 1
F32 FP 3
F64 FP 7
F1281 FP 15
F2561 FP 31
String SBA/LBA * UTF-8 string
Buffer SBA/LBA * Byte array
[]<TYPE> OTA * Array of any type2
Table KTV * Table with undefined schema
{...} KTV * Table with defined schema

Tokens

PDL files are divided into tokens, which assemble together into larger language structures. They are separated by whitespace.

Name Syntax Description
Magic PDL/0 Must appear at the very start of the file.
Method M[0-9A-Fa-f]{4} A 16-bit hexadecimal method code.
Key [0-9A-Fa-f]{4} A 16-bit hexadecimal table key.
Ident [A-Z][A-Za-z0-9] An identifier.
Comma , A comma separator.
LBrace { A left curly brace.
RBrace } A right curly brace.
LBracket [ A left square bracket.
RBracket ] A right square bracket.

Syntax

All files must begin with a Magic token.

Types are expressed with an Ident. A table can be used by either writing the name of the type (Table), or by defining a schema with curly braces. Arrays must be expressed using two matching square brackets before their element type.

A table schema contains comma-separated fields in-between its braces. Each field has three parts: the key number (Key), the field name (Ident), and the field type. Tables, Arrays, etc. can be nested.

Files directly contain messages and types, which start with a Method token and an Ident token respectively. A message consists of the method code (Method), the message name (Ident), and the message's root type. This is usually a table, but can be anything.

Here is an example of all that:

PDL/0

M0000 Connect {
	0000 Name String,
	0001 Password String,
}

M0001 UserList {
	0000 Users []User,
}

User {
	0000 Name      String,
	0001 Bio       String,
	0002 Followers U32,
}

EBNF Description

Below is an EBNF description of the language.

<file>    -> <magic> (<message> | <typedef)*
<magic>   -> "PDL/0"
<method>  -> /M[0-9A-Fa-f]{4}/
<key>     -> /[0-9A-Fa-f]{4}/
<ident>   -> /[A-Z][A-Za-z0-9]/
<field>   -> <key> <ident> <type>
<type> -> <ident>
        | "[" "]" <type>
        | "{" (<field> ",")* <field>? "}"
<message> -> <method> <ident> <type>
<typedef> -> <ident> <type>

Go Code Generation

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. Any


  1. Some systems may lack support for this. ↩︎

  2. Excluding SI and SBA. I5 and U5 cannot be used in an array, but String and Buffer are simply forced to use their "long" variant. ↩︎