Schema language

Let’s call it prophylang. Prophy messages are meant to be held in .prophy files. These files contain structs and unions composed with numeric types, constants, enums, arrays and nested structs and unions. All these primitives are explained below.

Encoding section explains wire representation of these language constructs.

There are constraints on constructs composability, pointed out in notes.

Numeric types

Prophy has following numeric types:

i8 signed 8-bit integer
i16 signed 16-bit integer
i32 signed 32-bit integer
i64 signed 64-bit integer
u8 unsigned 8-bit integer
u16 unsigned 16-bit integer
u32 unsigned 32-bit integer
u64 unsigned 64-bit integer
float 32-bit floating single-precision number
double 64-bit floating double-precision number

Constant

Constants for use as enumerator values, array lengths or union discriminators may be defined this way:

const MY_MIN = -1;
const MY_MAX = 0xFFF;
const MY_AVG = (MY_MIN + MY_MAX) / 2;

Signed, unsigned decimal, octal, hexal numbers and expressions are allowed.

Enum

Enumerations may be defined in following manner:

enum MyEnum
{
    MyEnum_1 = 1,
    MyEnum_2 = 2,
    MyEnum_3 = (MyEnum_1 + MyEnum_2) << 2
};

Enumerator definitions follow the same rules as constants.

Typedef

Typedef is an alias for previously defined type. Numeric type, enum, struct, union or other typedef may be aliased:

typedef u32 my_aliased_int;
typedef X my_aliased_x;

Struct

Struct contains fields. Fields can be numeric types, enums, structs, unions, typedefs or arrays of any former. There are 4 kinds of arrays:

kind schema fixed-length on wire variable number of elements
fixed Type x[Size] yes no
dynamic Type x<> no yes
limited Type x<Limit> yes yes (up to limit)
greedy Type x<...> no yes
ext. sized Type x<@sizer> no yes

Note

Fixed and limited array cannot hold dynamic nor unlimited struct.

Note

Greedy array may only be used in the last field of struct.

Note

Externally sized array can be sized explicity by an exisiting field of a structure. A few arrays of different types can be sized by the same field.

This is how they can be used in a struct:

struct X
{
    u32 a;
    MyUserDefinedType b;
    u32 c[3]; // fixed array of length 3
    u32 d<>; // dynamic array
    u32 e<3>; // limited array of limit 3
    u32 f<...>; // greedy array
    u8 g<@a>; // External sized array 1, size in a
    u32 g<@a>; // External sized array 2, size in a
};

Field may be declared as bytes field, if it’s meant to hold opaque binary data. Codec may use this information to manipulate its data in more effective manner. Same rules as with arrays apply:

struct X
{
    bytes a[3];
    bytes b<>;
    bytes c<3>;
    bytes d<...>;
};

Field may be optional:

struct X
{
    u32* x;
};

Note

Optional field cannot hold dynamic nor unlimited struct. There’s no optional array.

Array field size may be given as expression:

const A = 10;
const B = 2;

struct X
{
    u32 a[A * B];
};

Union

Discriminated unions are defined like structs, but with unsigned discriminators at the beginning of each field:

union MyUnion
{
    1: i8 a;
    2: u64 b;
    3: SomeType c;
};

Discriminators may be literals or references to constants or enumerators.

Note

Union arm cannot hold dynamic nor unlimited struct, nor array.

Include

Prophy files may include definitions and constants from different files using the usual include syntax borrowed from C language:

#include "A_is_inside.prophy"

struct X
{
    A x;
}

File “A_is_inside.prophy” may exist in current directory of including file or in any directory provided as include dir in compiler invocation.

Limitations

Currently there are no scoped definitions in the language.