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