# Заметки по синтаксису
[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")
```