---
# System prepended metadata

title: Заметки по синтаксису

---

# Заметки по синтаксису

[TOC]

## Блок `data`

Каждому блоку `data` сопоставляется один тип `Ty::Struct` (т.е. `StructTy`). Вариант `Ty::Bytes` становится "встроенным".

У блока `data` есть *параметры* и *поля*.

```ebnf
Data = "data" ID [ "<" { Param }+ ">" ] "{" Body "}" ;
```

Ссылке на тип по его имени (`ID`) с указанными параметрами (или без параметров, если их нет), соответствует CompletedType — один `TyInstanceCtor`.

## Параметры

Параметры аннотируются типом. Из этого типа определяется `TyArgKind`.

```ebnf
Param = ParamType ID ;
ParamType = ("const", "data", "endianness") ;
```

Параметры можно в дальнейшем использовать в полях, передавая их в параметры стоящим там типам (встроенный тип `bytes` принимает параметры `const N` и `endianness E`). Параметры типа `data` можно самих передавать как value поля.

## Поля

Поля отображаются в `Relation`-ы, следовательно их синтаксис должен позволять манипулировать всеми параметрами `Relation`:

* `name`
* `ty_instance_ctor`
* `kind`
* `condition`
* `location`:
  * `offset`
  * `anchor`
  * `origin`

```ebnf
(* Без условий и location, примитивно *)
Body = { Field }* ;
Field = Name ":" Value ;
Name = ID ;
Value = CompletedType ;
CompletedType = ID ["<" ParamValues ">"] ;
ParamValues = BoundExpr | CompletedType | Endianness ;
```

Здесь Name соответствует `name`, Value соответствует `ty_instance_ctor`.

Возможны **конфликты имен** с параметрами:

```
data MyData<const param>{
    param: bytes<4, little>,
    body: bytes<param, little>  // который param ???
}
```

По идее должна быть синтаксическая ошибка: что-то в духе `"ID 'param' already exists"`

## Ключевые слова

* `data`
* `alias`
* `little`, `big`
* `bytes`
* `endianness`, `const`
* `if`

## Тип bytes

Единственный встроенный тип — массив байтов известного размера. Сигнатура `bytes<const N, endianness E>`:

`N` — число 10-base, 16-base, 2-base (`42`, `0x2A`, `0b101010`)

`E` — `little` или `big`

```c
data MyData<s>{...}

MyData<bytes<42, little>>
```

Краткая запись: **???**

* `'42<little>`, `42<big>`
* `'42le`, `42be`
* `little<42>`, `big<42>`

## Псевдоним

Удобно создавать псевдонимы для конкретных типов:

```
alias u4 = bytes<4, little>
```

Проблема — порядок аргументов:

```
data T<const a, const b, const c> {}

alias TT = T<'1> // ???
alias TT = T<b: '1> // так надо?
```

## Преобразование в объекты `datamodel`

* Тип без параметров

    ```
    data MyData {
        ...
    }
    ```

    -> 
    ```rust
    my_data = Ty::Struct(...)
    my_data_ctor = TyInstanceCtor::Ty(my_data, [])
    ```

* Тип с параметрами

    ```
    data MyData<const p1, data p2, ...> {
        ...
    }
    ```

    -> 

    ```rust
    my_data = Ty::Struct(args=[Const, TyInstance, ...])
    my_data_ctor = TyInstanceCtor::Ty(my_data, args=[42, AnotherData, ...])
    ```

* Поле со ссылкой на другой тип
    ```
    data T {
        field: F<'8>
        ...
    }
    ```

    -> 

    ```rust
    add_field("field", TyInstanceCtor::Ty(F, ![TyArgCtor::Const("8")]))
    ```

## Элементы синтаксиса

Многие распространенные типы и псевдонимы можно вынести в некую "стандартную библиотеку", чтобы не нагружать синтаксис.

```
alias u32le = bytes<4, little>

data seq<T, N> {
    ...
}
```

### Magic values

Используем BitVecLiteral

```
data PNG {
    magic: 0x89504E470D0A1A0A'64
    ...
}
```

### Bit fields

Представляются как именнованные маски.

Например, флаги в DNS:

```
data DnsPacket {
    ...
    // 16 bits, 10 flags
    // use BitPatBinding
    flags: (qr: '1,
            opcode: '4,
            aa: '1,
            tc: '1,
            ...),
    ...
}
```

### Длина тела + тело

```
data F {
    len: bytes<8, little>, // ty = bytes, args = [lambda: 8, little]
    body: bytes<len, little>, // ty = bytes, args [lambda len: len, little]
}
```

Тоже самое:

```
alias byte = bytes<1, little>;
alias u64le = bytes<8, little>;

data seq<T, L> {
    ...
}

data F {
    len: u64le,
    body: seq<byte, len>,
}
```

Соответствие объектам `datamodel`:

```
seq                // <-- Ty(args=[TyArgKind::Const,
                                  TyArgKind::TyInstance])
seq<'10, u64le>    // <-- TyInstanceCtor(
                   //        Array, [TyArgCtor::Const("10"),
                   //                TyArgCtor::TyInstance(u64le)])
```

### Terminal sequence

Поле — все байты до тех пор, пока не встретится определенное значение (или одно из определенных значений). Например: строка читается до `0x00` или до `\n\n`.

### if statement (`Relation::condition`)

```
data T {
    cond: bytes<1, little>,
    field: bytes<8, little> if 'eq(cond, 1'8),
    rest: bytes<4, little>
}
```

->

```rust
cond = Relation {...}
field = Relation {
        name: "field",
        kind: RelationKind::Internal,
        TyInstanceCtor::Ty(bytes, ...),
        condition: BoundExpr{"lambda x: x == 1'8", [cond_relation_id]},
        location: mem::replace(
            &mut self.next_field_location,
            Location {
                origin: LocationOrigin::new_field(id),
                anchor: LocationAnchor::End,
                offset: None,
            },
        ),
    };
```

### XXX statement (`Relation::location`)


## ELFhdr

```clike
enum Class: byte {
    ELFCLASS32 = 1,
    ELFCLASS64 = 2,
}

enum Data: byte {
    ELFDATA2LSB = 1,
    ELFDATA2MSB = 2,
}

enum Version: byte {
    EV_CURRENT = 1,
}

data Ident {
    mag: seq<byte, 4>,
    class: Class,
    data: Data,
    version: Version,
    pad: ...,
}

data Elf32_Half<E: endianness> = u16<E>;
data Elf32_Word<E: endianness> = u32<E>;
data Elf32_Addr<E: endianness> = u32<E>;
data Elf32_Off<E: endianness> = u32<E>;

data Ehdr<c: Class, E: endianness> {
    if 'eq(c, ELFCLASS32) {
        e_type: Elf32_Half at self,
        e_machine: Elf32_Half,
        e_version: Elf32_Word,
        e_entry: Elf32_Addr,
        e_phoff: Elf32_Off,
        e_shoff: Elf32_Off,
    },

    if 'eq(c, ELFCLASS64) {
        e_type: Elf64_Half at self,
    },
}
```

## DNS


```
data DnsPacket {
    transaction_id: u2,

    flags: (qr: '1,
            opcode: '4,
            aa: '1,
            tc: '1,
            rd: '1,
            ra: '1,
            z: '1,
            ad: '1,
            cd: '1,
            rcode: '4),

    qdcount: u2,
    ancount: u2,
    nscount: u2,
    arcount: u2,

    queries: seq<Query, qdcount>,
    answers: seq<Answer, ancount>
}
```

```
data Query {
    name: DomainName,
    type: Type_type,
    class: Class_type,
}

data Answer {
    name: DomainName,
    type: Type_type,
    answer_class: Class_type,
    ttl: u4,
    rdlength: u2,
    if (type == Type_type::ptr) {
        ptrdname: DomainName,
    }
    if (type == Type_type::a) {
        address: Address,
    }
}
```

```
DomainName {
    name: seq<Label, until Label.length == 0 or 0b1100_0000>
    ...
}

Label {
    length: u1,
    ...
}

Address {
    ...
}
```

```
PacketFlags {

}
```

```
enum Type_type: u2 {
    a = 1
    ns = 2
    md = 3
    ... (13 more)
}

enum Class_type: u2 {
    in_class = 1
    cs = 2
    ch = 3
    hs = 4
}
```

## Грамматика

```ebnf
Data = "data" ID [ "<" { Param }+ ">" ] "{" Body "}" ;
Param = ParamType ID ;
ParamType = ("const", "data", "endianness") ;
Body = { Field }* ;
Field = Name ":" Value ;
Name = ID ;
Value = CompletedType ;
CompletedType = ID ["<" ParamValues ">"] ;
ParamValues = SmtExpr | CompletedType | Endianness ;
Endianness = ("little", "big")
```
