1# Packet Description Language 2 3[TOC] 4 5## Notation 6 7| Notation | Example | Meaning | 8|:-------------:|:----------------------------:|:----------------------------------------------------:| 9| __ANY__ | __ANY__ | Any character | 10| CAPITAL | IDENTIFIER, INT | A token production | 11| snake_case | declaration, constraint | A syntactical production | 12| `string` | `enum`, `=` | The exact character(s) | 13| \x | \n, \r, \t, \0 | The character represented by this escape | 14| x? | `,`? | An optional item | 15| x* | ALPHANUM* | 0 or more of x | 16| x+ | HEXDIGIT+ | 1 or more of x | 17| x \| y | ALPHA \| DIGIT, `0x` \| `0X` | Either x or y | 18| [x-y] | [`a`-`z`] | Any of the characters in the range from x to y | 19| !x | !\n | Negative Predicate (lookahead), do not consume input | 20| () | (`,` enum_tag) | Groups items | 21 22 23[WHITESPACE](#Whitespace) and [COMMENT](#Comment) are implicitly inserted between every item 24and repetitions in syntactical rules (snake_case). 25 26``` 27file: endianess declaration* 28``` 29behaves like: 30``` 31file: (WHITESPACE | COMMENT)* endianess (WHITESPACE | COMMENT)* (declaration | WHITESPACE | COMMENT)* 32``` 33 34## File 35 36> file:\ 37> endianess [declaration](#declarations)* 38> 39> endianess:\ 40> `little_endian_packets` | `big_endian_packets` 41 42The structure of a `.pdl`file is: 431. A declaration of the protocol endianess: `little_endian_packets` or `big_endian_packets`. Followed by 442. Declarations describing the structure of the protocol. 45 46``` 47// The protocol is little endian 48little_endian_packets 49 50// Brew a coffee 51packet Brew { 52 pot: 8, // Output Pot: 8bit, 0-255 53 additions: CoffeeAddition[2] // Coffee Additions: array of 2 CoffeeAddition 54} 55``` 56 57The endianess affects how fields of fractional byte sizes (hence named 58bit-fields) are parsed or serialized. Such fields are grouped together to the 59next byte boundary, least significant bit first, and then byte-swapped to the 60required endianess before being written to memory, or after being read from 61memory. 62 63``` 64packet Coffee { 65 a: 1, 66 b: 15, 67 c: 3, 68 d: 5, 69} 70 71// The first two field are laid out as a single 72// integer of 16-bits 73// MSB LSB 74// 16 8 0 75// +---------------------------------------+ 76// | b14 .. .. b0 |a| 77// +---------------------------------------+ 78// 79// The file endianness is applied to this integer 80// to obtain the byte layout of the packet fields. 81// 82// Little endian layout 83// MSB LSB 84// 7 6 5 4 3 2 1 0 85// +---------------------------------------+ 86// 0 | b[6:0] | a | 87// +---------------------------------------+ 88// 1 | b[14:7] | 89// +---------------------------------------+ 90// 2 | d | c | 91// +---------------------------------------+ 92// 93// Big endian layout 94// MSB LSB 95// 7 6 5 4 3 2 1 0 96// +---------------------------------------+ 97// 0 | b[14:7] | 98// +---------------------------------------+ 99// 1 | b[6:0] | a | 100// +---------------------------------------+ 101// 2 | d | c | 102// +---------------------------------------+ 103``` 104 105Fields which qualify as bit-fields are: 106- [Scalar](#fields-scalar) fields 107- [Size](#fields-size) fields 108- [Count](#fields-count) fields 109- [Fixed](#fields-fixed) fields 110- [Reserved](#fields-reserved) fields 111- [Typedef](#fields-typedef) fields, when the field type is an 112 [Enum](#enum) 113 114Fields that do not qualify as bit-fields _must_ start and end on a byte boundary. 115 116## Identifiers 117 118- Identifiers can denote a field; an enumeration tag; or a declared type. 119 120- Field identifiers declared in a [packet](#packet) (resp. [struct](#struct)) belong to the _scope_ that extends 121 to the packet (resp. struct), and all derived packets (resp. structs). 122 123- Field identifiers declared in a [group](#group) belong to the _scope_ that 124 extends to the packets declaring a [group field](#group_field) for this group. 125 126- Two fields may not be declared with the same identifier in any packet scope. 127 128- Two types may not be declared width the same identifier. 129 130## Declarations 131 132> declaration: {#declaration}\ 133> [enum_declaration](#enum) |\ 134> [packet_declaration](#packet) |\ 135> [struct_declaration](#struct) |\ 136> [group_declaration](#group) |\ 137> [checksum_declaration](#checksum) |\ 138> [custom_field_declaration](#custom-field) |\ 139> [test_declaration](#test) 140 141A *declaration* defines a type inside a `.pdl` file. A declaration can reference 142another declaration appearing later in the file. 143 144A declaration is either: 145- an [Enum](#enum) declaration 146- a [Packet](#packet) declaration 147- a [Struct](#struct) declaration 148- a [Group](#group) declaration 149- a [Checksum](#checksum) declaration 150- a [Custom Field](#custom-field) declaration 151- a [Test](#test) declaration 152 153### Enum 154 155> enum_declaration:\ 156> `enum` [IDENTIFIER](#identifier) `:` [INTEGER](#integer) `{`\ 157> enum_tag_list\ 158> `}` 159> 160> enum_tag_list:\ 161> enum_tag (`,` enum_tag)* `,`? 162> 163> enum_tag:\ 164> enum_range | enum_value 165> 166> enum_range:\ 167> [IDENTIFIER](#identifier) `=` [INTEGER](#integer) `..` [INTEGER](#integer)) (`{`\ 168> enum_value_list\ 169> `}`)? 170> 171> enum_value_list:\ 172> enum_value (`,` enum_value)* `,`? 173> 174> enum_value:\ 175> [IDENTIFIER](#identifier) `=` [INTEGER](#integer) 176 177An *enumeration* or for short *enum*, is a declaration of a set of named [integer](#integer) constants 178or named [integer](#integer) ranges. [integer](#integer) ranges are inclusive in both ends. 179[integer](#integer) value within a range *must* be unique. [integer](#integer) ranges 180*must not* overlap. 181 182The [integer](#integer) following the name specifies the bit size of the values. 183 184``` 185enum CoffeeAddition: 5 { 186 Empty = 0, 187 188 NonAlcoholic = 1..9 { 189 Cream = 1, 190 Vanilla = 2, 191 Chocolate = 3, 192 }, 193 194 Alcoholic = 10..19 { 195 Whisky = 10, 196 Rum = 11, 197 Kahlua = 12, 198 Aquavit = 13, 199 }, 200 201 Custom = 20..29, 202} 203``` 204 205### Packet 206 207> packet_declaration:\ 208> `packet` [IDENTIFIER](#identifier)\ 209> (`:` [IDENTIFIER](#identifier)\ 210> (`(` [constraint_list](#constraints) `)`)?\ 211> )?\ 212> `{`\ 213> [field_list](#fields)?\ 214> `}` 215 216A *packet* is a declaration of a sequence of [fields](#fields). While packets 217can contain bit-fields, the size of the whole packet must be a multiple of 8 218bits. 219 220A *packet* can optionally inherit from another *packet* declaration. In this case the packet 221inherits the parent's fields and the child's fields replace the 222[*\_payload\_*](#fields-payload) or [*\_body\_*](#fields-body) field of the parent. 223 224When inheriting, you can use constraints to set values on parent fields. 225See [constraints](#constraints) for more details. 226 227``` 228packet Error { 229 code: 32, 230 _payload_ 231} 232 233packet ImATeapot: Error(code = 418) { 234 brand_id: 8 235} 236``` 237 238### Struct 239 240> struct_declaration:\ 241> `struct` [IDENTIFIER](#identifier)\ 242> (`:` [IDENTIFIER](#identifier)\ 243> (`(` [constraint_list](#constraints) `)`)?\ 244> )?\ 245> `{`\ 246> [field_list](#fields)?\ 247> `}` 248 249A *struct* follows the same rules as a [*packet*](#packet) with the following differences: 250- It inherits from a *struct* declaration instead of *packet* declaration. 251- A [typedef](#fields-typedef) field can reference a *struct*. 252 253### Group 254 255> group_declaration:\ 256> `group` [IDENTIFIER](#identifier) `{`\ 257> [field_list](#fields)\ 258> `}` 259 260A *group* is a sequence of [fields](#fields) that expand in a 261[packet](#packet) or [struct](#struct) when used. 262 263See also the [Group field](#fields-group). 264 265``` 266group Paged { 267 offset: 8, 268 limit: 8 269} 270 271packet AskBrewHistory { 272 pot: 8, // Coffee Pot 273 Paged 274} 275``` 276behaves like: 277``` 278packet AskBrewHistory { 279 pot: 8, // Coffee Pot 280 offset: 8, 281 limit: 8 282} 283``` 284 285### Checksum 286 287> checksum_declaration:\ 288> `checksum` [IDENTIFIER](#identifier) `:` [INTEGER](#integer) [STRING](#string) 289 290A *checksum* is a native type (not implemented in PDL). See your generator documentation 291for more information on how to use it. 292 293The [integer](#integer) following the name specify the bit size of the checksum value. 294The [string](#string) following the size is a value defined by the generator implementation. 295 296``` 297checksum CRC16: 16 "crc16" 298``` 299 300### Custom Field 301 302> custom_field_declaration:\ 303> `custom_field` [IDENTIFIER](#identifier) (`:` [INTEGER](#integer))? [STRING](#string) 304 305A *custom field* is a native type (not implemented in PDL). See your generator documentation for more 306information on how to use it. 307 308If present, the [integer](#integer) following the name specify the bit size of the value. 309The [string](#string) following the size is a value defined by the generator implementation. 310 311``` 312custom_field URL "url" 313``` 314 315### Test 316 317> test_declaration:\ 318> `test` [IDENTIFIER](#identifier) `{`\ 319> test_case_list\ 320> `}` 321> 322> test_case_list:\ 323> test_case (`,` test_case)* `,`? 324> 325> test_case:\ 326> [STRING](#string) 327 328A *test* declares a set of valid octet representations of a packet identified by its name. 329The generator implementation defines how to use the test data. 330 331A test passes if the packet parser accepts the input; if you want to test 332the values returned for each field, you may specify a derived packet with field values enforced using 333constraints. 334 335``` 336packet Brew { 337 pot: 8, 338 addition: CoffeeAddition 339} 340 341test Brew { 342 "\x00\x00", 343 "\x00\x04" 344} 345 346// Fully Constrained Packet 347packet IrishCoffeeBrew: Brew(pot = 0, additions_list = Whisky) {} 348 349test IrishCoffeeBrew { 350 "\x00\x04" 351} 352``` 353 354## Constraints 355 356> constraint:\ 357> [IDENTIFIER](#identifier) `=` [IDENTIFIER](#identifier) | [INTEGER](#integer) 358> 359> constraint_list:\ 360> constraint (`,` constraint)* `,`? 361 362A *constraint* defines the value of a parent field. 363The value can either be an [enum](#enum) tag or an [integer](#integer). 364 365``` 366group Additionable { 367 addition: CoffeAddition 368} 369 370packet IrishCoffeeBrew { 371 pot: 8, 372 Additionable { 373 addition = Whisky 374 } 375} 376 377packet Pot0IrishCoffeeBrew: IrishCoffeeBrew(pot = 0) {} 378``` 379 380## Fields 381 382> field_list:\ 383> field (`,` field)* `,`? 384> 385> field:\ 386> [checksum_field](#fields-checksum) |\ 387> [padding_field](#fields-padding) |\ 388> [size_field](#fields-size) |\ 389> [count_field](#fields-count) |\ 390> [payload_field](#fields-payload) |\ 391> [body_field](#fields-body) |\ 392> [fixed_field](#fields-fixed) |\ 393> [reserved_field](#fields-reserved) |\ 394> [array_field](#fields-array) |\ 395> [scalar_field](#fields-scalar) |\ 396> [typedef_field](#fields-typedef) |\ 397> [group_field](#fields-group) 398 399A field is either: 400- a [Scalar](#fields-scalar) field 401- a [Typedef](#fields-typedef) field 402- a [Group](#fields-group) field 403- an [Array](#fields-array) field 404- a [Size](#fields-size) field 405- a [Count](#fields-count) field 406- a [Payload](#fields-payload) field 407- a [Body](#fields-body) field 408- a [Fixed](#fields-fixed) field 409- a [Checksum](#fields-checksum) field 410- a [Padding](#fields-padding) field 411- a [Reserved](#fields-reserved) field 412 413### Scalar {#fields-scalar} 414 415> scalar_field:\ 416> [IDENTIFIER](#identifier) `:` [INTEGER](#integer) 417 418A *scalar* field defines a numeric value with a bit size. 419 420``` 421struct Coffee { 422 temperature: 8 423} 424``` 425 426### Typedef {#fields-typedef} 427 428> typedef_field:\ 429> [IDENTIFIER](#identifier) `:` [IDENTIFIER](#identifier) 430 431A *typedef* field defines a field taking as value either an [enum](#enum), [struct](#struct), 432[checksum](#checksum) or a [custom_field](#custom-field). 433 434``` 435packet LastTimeModification { 436 coffee: Coffee, 437 addition: CoffeeAddition 438} 439``` 440 441### Array {#fields-array} 442 443> array_field:\ 444> [IDENTIFIER](#identifier) `:` [INTEGER](#integer) | [IDENTIFIER](#identifier) `[`\ 445> [SIZE_MODIFIER](#size-modifier) | [INTEGER](#integer)\ 446> `]` 447 448An *array* field defines a sequence of `N` elements of type `T`. 449 450`N` can be: 451- An [integer](#integer) value. 452- A [size modifier](#size-modifier). 453- Unspecified: In this case the array is dynamically sized using a 454[*\_size\_*](#fields-size) or a [*\_count\_*](#fields-count). 455 456`T` can be: 457- An [integer](#integer) denoting the bit size of one element. 458- An [identifier](#identifier) referencing an [enum](#enum), a [struct](#struct) 459or a [custom field](#custom-field) type. 460 461The size of `T` must always be a multiple of 8 bits, that is, the array elements 462must start at byte boundaries. 463 464``` 465packet Brew { 466 pots: 8[2], 467 additions: CoffeeAddition[2], 468 extra_additions: CoffeeAddition[], 469} 470``` 471 472### Group {#fields-group} 473 474> group_field:\ 475> [IDENTIFIER](#identifier) (`{` [constraint_list](#constraints) `}`)? 476 477A *group* field inlines all the fields defined in the referenced group. 478 479If a [constraint list](#constraints) constrains a [scalar](#fields-scalar) field 480or [typedef](#fields-typedef) field with an [enum](#enum) type, the field will 481become a [fixed](#fields-fixed) field. 482The [fixed](#fields-fixed) field inherits the type or size of the original field and the 483value from the constraint list. 484 485See [Group Declaration](#group) for more information. 486 487### Size {#fields-size} 488 489> size_field:\ 490> `_size_` `(` [IDENTIFIER](#identifier) | `_payload_` | `_body_` `)` `:` [INTEGER](#integer) 491 492A *\_size\_* field is a [scalar](#fields-scalar) field with as value the size in octet of the designated 493[array](#fields-array), [*\_payload\_*](#fields-payload) or [*\_body\_*](#fields-body). 494 495``` 496packet Parent { 497 _size_(_payload_): 2, 498 _payload_ 499} 500 501packet Brew { 502 pot: 8, 503 _size_(additions): 8, 504 additions: CoffeeAddition[] 505} 506``` 507 508### Count {#fields-count} 509 510> count_field:\ 511> `_count_` `(` [IDENTIFIER](#identifier) `)` `:` [INTEGER](#integer) 512 513A *\_count\_* field is a [*scalar*](#fields-scalar) field with as value the number of elements of the designated 514[array](#fields-array). 515 516``` 517packet Brew { 518 pot: 8, 519 _count_(additions): 8, 520 additions: CoffeeAddition[] 521} 522``` 523 524### Payload {#fields-payload} 525 526> payload_field:\ 527> `_payload_` (`:` `[` [SIZE_MODIFIER](#size-modifier) `]` )? 528 529A *\_payload\_* field is a dynamically sized array of octets. 530 531It declares where to parse the definition of a child [packet](#packet) or [struct](#struct). 532 533A [*\_size\_*](#fields-size) or a [*\_count\_*](#fields-count) field referencing 534the payload induce its size. 535 536If used, a [size modifier](#size-modifier) can alter the octet size. 537 538### Body {#fields-body} 539 540> body_field:\ 541> `_body_` 542 543A *\_body\_* field is like a [*\_payload\_*](#fields-payload) field with the following differences: 544- The body field is private to the packet definition, it's accessible only when inheriting. 545- The body does not accept a size modifier. 546 547### Fixed {#fields-fixed} 548 549> fixed_field:\ 550> `_fixed_` `=` \ 551> ( [INTEGER](#integer) `:` [INTEGER](#integer) ) |\ 552> ( [IDENTIFIER](#identifier) `:` [IDENTIFIER](#identifier) ) 553 554A *\_fixed\_* field defines a constant with a known bit size. 555The constant can be either: 556- An [integer](#integer) value 557- An [enum](#enum) tag 558 559``` 560packet Teapot { 561 _fixed_ = 42: 8, 562 _fixed_ = Empty: CoffeeAddition 563} 564``` 565 566### Checksum {#fields-checksum} 567 568> checksum_field:\ 569> `_checksum_start_` `(` [IDENTIFIER](#identifier) `)` 570 571A *\_checksum_start\_* field is a zero sized field that acts as a marker for the beginning of 572the fields covered by a checksum. 573 574The *\_checksum_start\_* references a [typedef](#fields-typedef) field 575with a [checksum](#checksum) type that stores the checksum value and selects the algorithm 576for the checksum. 577 578``` 579checksum CRC16: 16 "crc16" 580 581packet CRCedBrew { 582 crc: CRC16, 583 _checksum_start_(crc), 584 pot: 8, 585} 586``` 587 588### Padding {#fields-padding} 589 590> padding_field:\ 591> `_padding_` `[` [INTEGER](#integer) `]` 592 593A *\_padding\_* field immediately following an array field pads the array field with `0`s to the 594specified number of **octets**. 595 596``` 597packet PaddedCoffee { 598 additions: CoffeeAddition[], 599 _padding_[100] 600} 601``` 602 603### Reserved {#fields-reserved} 604 605> reserved_field:\ 606> `_reserved_` `:` [INTEGER](#integer) 607 608A *\_reserved\_* field adds reserved bits. 609 610``` 611packet DeloreanCoffee { 612 _reserved_: 2014 613} 614``` 615 616## Tokens 617 618### Integer 619 620> INTEGER:\ 621> HEXVALUE | INTVALUE 622> 623> HEXVALUE:\ 624> `0x` | `0X` HEXDIGIT<sup>+</sup> 625> 626> INTVALUE:\ 627> DIGIT<sup>+</sup> 628> 629> HEXDIGIT:\ 630> DIGIT | [`a`-`f`] | [`A`-`F`] 631> 632> DIGIT:\ 633> [`0`-`9`] 634 635A integer is a number in base 10 (decimal) or in base 16 (hexadecimal) with 636the prefix `0x` 637 638### String 639 640> STRING:\ 641> `"` (!`"` __ANY__)* `"` 642 643A string is sequence of character. It can be multi-line. 644 645### Identifier 646 647> IDENTIFIER: \ 648> ALPHA (ALPHANUM | `_`)* 649> 650> ALPHA:\ 651> [`a`-`z`] | [`A`-`Z`] 652> 653> ALPHANUM:\ 654> ALPHA | DIGIT 655 656An identifier is a sequence of alphanumeric or `_` characters 657starting with a letter. 658 659### Size Modifier 660 661> SIZE_MODIFIER:\ 662> `+` INTVALUE 663 664A size modifier alters the octet size of the field it is attached to. 665For example, `+ 2` defines that the size is 2 octet bigger than the 666actual field size. 667 668### Comment 669 670> COMMENT:\ 671> BLOCK_COMMENT | LINE_COMMENT 672> 673> BLOCK_COMMENT:\ 674> `/*` (!`*/` ANY) `*/` 675> 676> LINE_COMMENT:\ 677> `//` (!\n ANY) `//` 678 679### Whitespace 680 681> WHITESPACE:\ 682> ` ` | `\t` | `\n` 683