-
Notifications
You must be signed in to change notification settings - Fork 0
Grammar
We have chosen to use a grammar that is close to c++ syntax as the envisioned users of the grammar are familiar with that language. Subsequent sections will detail different properties of a data format and how the grammar handles it
<object name>
{
<field name> : <field type> ;
(...)
<field name> : <field type> ;
}
Supported types
- signed integers: int
- unsigned integers: uint
- floating point: float
e.g.
SomeObject
{
field1 : int32;
field2 : uin16;
field3 : float64;
}
Header
{
field1 : int32;
field2 : int32;
field3 : uint32;
}
Object1
{
header : Header;
field1 : int64;
field2 : float64;
}
Object2
{
header : Header;
field1 : int8;
field2 : int8;
}
Statically size collections
SomeObject
{
field1 : int64;
field2 : uint32;
collection : uint8[32];
}
Collection sized by another field
SomeObject
{
field1 : int64;
collection_size : uint32;
collection : uint8[size];
}
Collection sized by mathematical expression The following example shows how to describe a data element that contains a size field with the size of the complete element. The last field is a collection of 32 bit integers. The size of the collection is equal to the complete element size, minus the size of the other fields in the message divided by the size of the elements in the collection
SomeObject
{
field1 : int64;
field2 : int8;
field3 : float64;
field4 : uint16;
total_message_size : uint32;
collection : uint32[ (total_message_size - 19) / 4];
}
Strings are similar to other collection types
SomeObject
{
field1 : int64;
string_length : uint8;
some_string : ascii_string[string_length];
}
Beside ASCII strings we could support other string types (like UTF-8 and UTF-16) as well.
In order for the parser to distinguish between different objects, the defined objects need to have some distinguishable characteristics. For this we use fixed value:
` Object1 { start_of_message : uint32 = 0x01020304; message_type : uint8 = 1; field1 : uint32; (...) footer : ascii_string = "footer"; }
Object2 { start_of_message : uint32 = 0x01020304; message_type : uint8 = 2; field1 : uint32; (...) footer : ascii_string = "footer"; } `
The fixed values are used for two different purposes here: The start and end of message fields allow the parser to detect the start and end of a message in a stream of data. The message_type field allows the parser to distinguish between different messages.
We can also use fixed fields in embedded objects
Header
{
header : uint32 = 0x01020304;
message_type : uint8'
}
Object1
{
header : Header(message_type = 1);
field1 : float32;
(...)
footer : uint32 : 0x04030201;
}
Object2
{
header : Header(message_type = 2);
field1 : float32;
(...)
footer : uint32 : 0x04030201;
}