# Packet Description Language

[TOC]

## Notation

|    Notation   |            Example           |                        Meaning                       |
|:-------------:|:----------------------------:|:----------------------------------------------------:|
| __ANY__       | __ANY__                      | Any character                                        |
| CAPITAL       | IDENTIFIER, INT              | A token production                                   |
| snake_case    | declaration, constraint      | A syntactical production                             |
| `string`      | `enum`, `=`                  | The exact character(s)                               |
| \x            | \n, \r, \t, \0               | The character represented by this escape             |
| x?            | `,`?                         | An optional item                                     |
| x*            | ALPHANUM*                    | 0 or more of x                                       |
| x+            | HEXDIGIT+                    | 1 or more of x                                       |
| x \| y        | ALPHA \| DIGIT, `0x` \| `0X` | Either x or y                                        |
| [x-y]         | [`a`-`z`]                    | Any of the characters in the range from x to y       |
| !x            | !\n                          | Negative Predicate (lookahead), do not consume input |
| ()            | (`,` enum_tag)               | Groups items                                         |


[WHITESPACE](#Whitespace) and [COMMENT](#Comment) are implicitly inserted between every item
and repetitions in syntactical rules (snake_case).

```
file: endianess declaration*
```
behaves like:
```
file: (WHITESPACE | COMMENT)* endianess (WHITESPACE | COMMENT)* (declaration | WHITESPACE | COMMENT)*
```

## File

> file:\
>    endianess [declaration](#declarations)*
>
> endianess:\
>    `little_endian_packets` | `big_endian_packets`

The structure of a `.pdl`file is:
1. A declaration of the protocol endianess: `little_endian_packets` or `big_endian_packets`. Followed by
2. Declarations describing the structure of the protocol.

```
// The protocol is little endian
little_endian_packets

// Brew a coffee
packet Brew {
  pot: 8, // Output Pot: 8bit, 0-255
  additions: CoffeeAddition[2] // Coffee Additions: array of 2 CoffeeAddition
}
```

## Identifiers

- Identifiers can denote a field; an enumeration tag; or a declared type.

- Field identifiers declared in a [packet](#packet) (resp. [struct](#struct)) belong to the _scope_ that extends
  to the packet (resp. struct), and all derived packets (resp. structs).

- Field identifiers declared in a [group](#group) belong to the _scope_ that
  extends to the packets declaring a [group field](#group_field) for this group.

- Two fields may not be declared with the same identifier in any packet scope.

- Two types may not be declared width the same identifier.

## Declarations

> declaration: {#declaration}\
>    [enum_declaration](#enum) |\
>    [packet_declaration](#packet) |\
>    [struct_declaration](#struct) |\
>    [group_declaration](#group) |\
>    [checksum_declaration](#checksum) |\
>    [custom_field_declaration](#custom-field) |\
>    [test_declaration](#test)

A *declaration* defines a type inside a `.pdl` file. A declaration can reference
another declaration appearing later in the file.

A declaration is either:
- an [Enum](#enum) declaration
- a [Packet](#packet) declaration
- a [Struct](#struct) declaration
- a [Group](#group) declaration
- a [Checksum](#checksum) declaration
- a [Custom Field](#custom-field) declaration
- a [Test](#test) declaration

### Enum

> enum_declaration:\
>    `enum` [IDENTIFIER](#identifier) `:` [INTEGER](#integer) `{`\
>      enum_tag_list\
>    `}`
>
> enum_tag_list:\
>    enum_tag (`,` enum_tag)* `,`?
>
> enum_tag:\
>    [IDENTIFIER](#identifier) `=` [INTEGER](#integer)

An *enumeration* or for short *enum*, is a declaration of a set of named [integer](#integer) constants.

The [integer](#integer) following the name specifies the bit size of the values.

```
enum CoffeeAddition: 3 {
  Empty = 0,
  Cream = 1,
  Vanilla = 2,
  Chocolate = 3,
  Whisky = 4,
  Rum = 5,
  Kahlua = 6,
  Aquavit = 7
}
```

### Packet

> packet_declaration:\
>    `packet` [IDENTIFIER](#identifier)\
>      (`:` [IDENTIFIER](#identifier)\
>        (`(` [constraint_list](#constraints) `)`)?\
>      )?\
>    `{`\
>      [field_list](#fields)?\
>    `}`

A *packet* is a declaration of a sequence of [fields](#fields).

A *packet* can optionally inherit from another *packet* declaration. In this case the packet
inherits the parent's fields and the child's fields replace the
[*\_payload\_*](#fields-payload) or [*\_body\_*](#fields-body) field of the parent.

When inheriting, you can use constraints to set values on parent fields.
See [constraints](#constraints) for more details.

```
packet Error {
  code: 32,
  _payload_
}

packet ImATeapot: Error(code = 418) {
  brand_id: 8
}
```

### Struct

> struct_declaration:\
>    `struct` [IDENTIFIER](#identifier)\
>      (`:` [IDENTIFIER](#identifier)\
>        (`(` [constraint_list](#constraints) `)`)?\
>      )?\
>    `{`\
>      [field_list](#fields)?\
>    `}`

A *struct* follows the same rules as a [*packet*](#packet) with the following differences:
- It inherits from a *struct* declaration instead of *packet* declaration.
- A [typedef](#fields-typedef) field can reference a *struct*.

### Group

> group_declaration:\
>    `group` [IDENTIFIER](#identifier) `{`\
>      [field_list](#fields)\
>    `}`

A *group* is a sequence of [fields](#fields) that expand in a
[packet](#packet) or [struct](#struct) when used.

See also the [Group field](#fields-group).

```
group Paged {
  offset: 8,
  limit: 8
}

packet AskBrewHistory {
  pot: 8, // Coffee Pot
  Paged
}
```
behaves like:
```
packet AskBrewHistory {
  pot: 8, // Coffee Pot
  offset: 8,
  limit: 8
}
```

### Checksum

> checksum_declaration:\
>    `checksum` [IDENTIFIER](#identifier) `:` [INTEGER](#integer) [STRING](#string)

A *checksum* is a native type (not implemented in PDL). See your generator documentation
for more information on how to use it.

The [integer](#integer) following the name specify the bit size of the checksum value.
The [string](#string) following the size is a value defined by the generator implementation.

```
checksum CRC16: 16 "crc16"
```

### Custom Field

> custom_field_declaration:\
>    `custom_field` [IDENTIFIER](#identifier) (`:` [INTEGER](#integer))? [STRING](#string)

A *custom field* is a native type (not implemented in PDL). See your generator documentation for more
information on how to use it.

If present, the [integer](#integer) following the name specify the bit size of the value.
The [string](#string) following the size is a value defined by the generator implementation.

```
custom_field URL "url"
```

### Test

> test_declaration:\
>    `test` [IDENTIFIER](#identifier) `{`\
>      test_case_list\
>    `}`
>
> test_case_list:\
>    test_case (`,` test_case)* `,`?
>
> test_case:\
>    [STRING](#string)

A *test* declares a set of valid octet representations of a packet identified by its name.
The generator implementation defines how to use the test data.

A test passes if the packet parser accepts the input; if you want to test
the values returned for each field, you may specify a derived packet with field values enforced using
constraints.

```
packet Brew {
  pot: 8,
  addition: CoffeeAddition
}

test Brew {
  "\x00\x00",
  "\x00\x04"
}

// Fully Constrained Packet
packet IrishCoffeeBrew: Brew(pot = 0, additions_list = Whisky) {}

test IrishCoffeeBrew {
  "\x00\x04"
}
```

## Constraints

> constraint:\
>    [IDENTIFIER](#identifier) `=` [IDENTIFIER](#identifier) | [INTEGER](#integer)
>
> constraint_list:\
>    constraint (`,` constraint)* `,`?

A *constraint* defines the value of a parent field.
The value can either be an [enum](#enum) tag or an [integer](#integer).

```
group Additionable {
  addition: CoffeAddition
}

packet IrishCoffeeBrew {
  pot: 8,
  Additionable {
    addition = Whisky
  }
}

packet Pot0IrishCoffeeBrew: IrishCoffeeBrew(pot = 0) {}
```

## Fields

> field_list:\
>    field (`,` field)* `,`?
>
> field:\
>    [checksum_field](#fields-checksum) |\
>    [padding_field](#fields-padding) |\
>    [size_field](#fields-size) |\
>    [count_field](#fields-count) |\
>    [payload_field](#fields-payload) |\
>    [body_field](#fields-body) |\
>    [fixed_field](#fields-fixed) |\
>    [reserved_field](#fields-reserved) |\
>    [array_field](#fields-array) |\
>    [scalar_field](#fields-scalar) |\
>    [typedef_field](#fields-typedef) |\
>    [group_field](#fields-group)

A field is either:
- a [Scalar](#fields-scalar) field
- a [Typedef](#fields-typedef) field
- a [Group](#fields-group) field
- an [Array](#fields-array) field
- a [Size](#fields-size) field
- a [Count](#fields-count) field
- a [Payload](#fields-payload) field
- a [Body](#fields-body) field
- a [Fixed](#fields-fixed) field
- a [Checksum](#fields-checksum) field
- a [Padding](#fields-padding) field
- a [Reserved](#fields-reserved) field

### Scalar {#fields-scalar}

> scalar_field:\
>    [IDENTIFIER](#identifier) `:` [INTEGER](#integer)

A *scalar* field defines a numeric value with a bit size.

```
struct Coffee {
  temperature: 8
}
```

### Typedef {#fields-typedef}

> typedef_field:\
>    [IDENTIFIER](#identifier) `:` [IDENTIFIER](#identifier)

A *typedef* field defines a field taking as value either an [enum](#enum), [struct](#struct),
[checksum](#checksum) or a [custom_field](#custom-field).

```
packet LastTimeModification {
  coffee: Coffee,
  addition: CoffeeAddition
}
```

### Array {#fields-array}

> array_field:\
>    [IDENTIFIER](#identifier) `:` [INTEGER](#integer) | [IDENTIFIER](#identifier) `[`\
>      [SIZE_MODIFIER](#size-modifier) | [INTEGER](#integer)\
>    `]`

An *array* field defines a sequence of `N` elements of type `T`.

`N` can be:
- An [integer](#integer) value.
- A [size modifier](#size-modifier).
- Unspecified: In this case the array is dynamically sized using a
[*\_size\_*](#fields-size) or a [*\_count\_*](#fields-count).

`T` can be:
- An [integer](#integer) denoting the bit size of one element.
- An [identifier](#identifier) referencing an [enum](#enum), a [struct](#struct)
or a [custom field](#custom-field) type.

```
packet Brew {
   pots: 8[2],
   additions: CoffeeAddition[2],
   extra_additions: CoffeeAddition[],
}
```

### Group {#fields-group}

> group_field:\
>    [IDENTIFIER](#identifier) (`{` [constraint_list](#constraints) `}`)?

A *group* field inlines all the fields defined in the referenced group.

If a [constraint list](#constraints) constrains a [scalar](#fields-scalar) field
or [typedef](#fields-typedef) field with an [enum](#enum) type, the field will
become a [fixed](#fields-fixed) field.
The [fixed](#fields-fixed) field inherits the type or size of the original field and the
value from the constraint list.

See [Group Declaration](#group) for more information.

### Size {#fields-size}

> size_field:\
>    `_size_` `(` [IDENTIFIER](#identifier) | `_payload_` | `_body_` `)` `:` [INTEGER](#integer)

A *\_size\_* field is a [scalar](#fields-scalar) field with as value the size in octet of the designated
[array](#fields-array), [*\_payload\_*](#fields-payload) or [*\_body\_*](#fields-body).

```
packet Parent {
  _size_(_payload_): 2,
  _payload_
}

packet Brew {
  pot: 8,
  _size_(additions): 8,
  additions: CoffeeAddition[]
}
```

### Count {#fields-count}

> count_field:\
>    `_count_` `(` [IDENTIFIER](#identifier) `)` `:` [INTEGER](#integer)

A *\_count\_* field is a [*scalar*](#fields-scalar) field with as value the number of elements of the designated
[array](#fields-array).

```
packet Brew {
  pot: 8,
  _count_(additions): 8,
  additions: CoffeeAddition[]
}
```

### Payload {#fields-payload}

> payload_field:\
>    `_payload_` (`:` `[` [SIZE_MODIFIER](#size-modifier) `]` )?

A *\_payload\_* field is a dynamically sized array of octets.

It declares where to parse the definition of a child [packet](#packet) or [struct](#struct).

A [*\_size\_*](#fields-size) or a [*\_count\_*](#fields-count) field referencing
the payload induce its size.

If used, a [size modifier](#size-modifier) can alter the octet size.

### Body {#fields-body}

> body_field:\
>    `_body_`

A *\_body\_* field is like a [*\_payload\_*](#fields-payload) field with the following differences:
- The body field is private to the packet definition, it's accessible only when inheriting.
- The body does not accept a size modifier.

### Fixed {#fields-fixed}

> fixed_field:\
>    `_fixed_` `=` \
>      ( [INTEGER](#integer) `:` [INTEGER](#integer) ) |\
>      ( [IDENTIFIER](#identifier) `:` [IDENTIFIER](#identifier) )

A *\_fixed\_* field defines a constant with a known bit size.
The constant can be either:
- An [integer](#integer) value
- An [enum](#enum) tag

```
packet Teapot {
  _fixed_ = 42: 8,
  _fixed_ = Empty: CoffeeAddition
}
```

### Checksum {#fields-checksum}

> checksum_field:\
>    `_checksum_start_` `(` [IDENTIFIER](#identifier) `)`

A *\_checksum_start\_* field is a zero sized field that acts as a marker for the beginning of
the fields covered by a checksum.

The *\_checksum_start\_* references a [typedef](#fields-typedef) field
with a [checksum](#checksum) type that stores the checksum value and selects the algorithm
for the checksum.

```
checksum CRC16: 16 "crc16"

packet CRCedBrew {
  crc: CRC16,
  _checksum_start_(crc),
  pot: 8,
}
```

### Padding {#fields-padding}

> padding_field:\
>    `_padding_` `[` [INTEGER](#integer) `]`

A *\_padding\_* field adds a number of **octet** of padding.

```
packet Padded {
  _padding_[1] // 1 octet/8bit of padding
}
```

### Reserved {#fields-reserved}

> reserved_field:\
>    `_reserved_` `:` [INTEGER](#integer)

A *\_reserved\_* field adds reserved bits.

```
packet DeloreanCoffee {
  _reserved_: 2014
}
```

## Tokens

### Integer

> INTEGER:\
>    HEXVALUE | INTVALUE
>
> HEXVALUE:\
>    `0x` | `0X` HEXDIGIT+
>
> INTVALUE:\
>    DIGIT+
>
> HEXDIGIT:\
>    DIGIT | [`a`-`f`] | [`A`-`F`]
>
> DIGIT:\
>    [`0`-`9`]

A integer is a number in base 10 (decimal) or in base 16 (hexadecimal) with
the prefix `0x`

### String

> STRING:\
>    `"` (!`"` __ANY__)* `"`

A string is sequence of character. It can be multi-line.

### Identifier

> IDENTIFIER: \
>    ALPHA (ALPHANUM | `_`)*
>
> ALPHA:\
>    [`a`-`z`] | [`A`-`Z`]
>
> ALPHANUM:\
>    ALPHA | DIGIT

An identifier is a sequence of alphanumeric or `_` characters
starting with a letter.

### Size Modifier

> SIZE_MODIFIER:\
>    `+` | `-` | `*` | `/` DIGIT | `+` | `-` | `*` | `/`

Part of a arithmetic expression where the missing part is a size

For example:
- `+ 2` defines that the size is 2 octet bigger than the real size
- `* 8` defines that the size is 8 times bigger than the real size

### Comment

> COMMENT:\
>    BLOCK_COMMENT | LINE_COMMENT
>
> BLOCK_COMMENT:\
>    `/*` (!`*/` ANY) `*/`
>
> LINE_COMMENT:\
>    `//` (!\n ANY) `//`

### Whitespace

> WHITESPACE:\
>    ` ` | `\t` | `\n`