说明: 《DTK Guide》的完整内容,请查附件pdf(带目录)。
基础篇
1 DTK语言
1.1 数据类型
在DTK里,基本类型的数据不需要专门的类型定义,其命名规则即隐含了类型定义,如:
cRet 表示char型数据,名字为Ret。
即:小写的前缀表示类型,以大写字母开头的是名称,二者构成了完整的类型命名。
DTK的前缀表
前缀 |
SIZE(bytes) |
说明 |
举例 |
c |
1 |
int8_t |
cRet |
uc |
1 |
uint8_t |
ucType |
s |
1 |
int8_t |
sBuf[10] |
sh |
2 |
int16_t |
shId |
w |
2 |
uint16_t |
wSeq |
i |
4 |
int32_t |
iCode |
dw |
4 |
uint32_t |
dwTime |
ll |
8 |
int64_t |
llAddr |
qw |
8 |
uint64_t |
qwKey |
不满足的“基本类型”命名规则的数据类型,都是复杂类型,必须有明确的类型定义。
其描述格式如下:
类型名称 = 类型定义
其中,类型定义必须同在1行,可以通过“\”行连接符来连接多行定义。
举例: head = wVersion + cCmd + wLen 表示类型head有3个数据成员wVersion、cCmd和wLen。
根据定义的形式,类型定义可以分为:“静态定义”(不含条件逻辑)、“动态定义”(含条件逻辑)、“别名定义”(类型的别名)。
1.2 类型定义
1.2.1 静态定义
静态定义可以是:
1) 另一个类型名称,如:
head = cCmd 表示head类型里只有1个成员cCmd。(cCmd是head的子节点)
静态定义也可以包含其他类型定义的组合:
1) 简单组合
即用”+” 把不同的类型定义组合起来,如:
head = wVersion + cCmd
2) 按数目重复组合(repeated by count),如:
head = wLen + sBuf [ wLen ]
其中,“[]”里面的数目可以是DTK表达式的值(参见1.6)
3) 按长度重复组合(repeated by length),如:
head = wLen + item { wLen }
其中,item{wLen} 表示wLen个字节里全部都是item类型的数据。具体有多少个,要根据item的定义确定。其中,“{}”里面的长度可以是DTK表达式的值(参见1.6)
事实上,以上的组合形式可以任意混合使用,比如:
head = cCnt + ( wLen + ( ucCmd + wVersion { wLen } ) [ cCnt ]
其中,通过“()”将数据元素封装成一个单元。
1.2.2 别名定义
别名定义类似于C语言中的typedef。其完×××式如下:
类型名称1 == 类型名称2
表示“类型1”拥有“类型2”完全一样的定义,只是名字不同。
举例:
msg_0x1 = cCmd + wVer
msg_0x2 == msg_0x1
表示msg_0x2拥有和msg_0x1完全一样的格式
1.2.3 动态定义
动态定义是包含条件逻辑的定义形式:
类型名称 = {
条件语句
}
DTK目前支持“if/elif/else”、“switch”语句,以及C语言形式的“表达式”(参见1.6)
“if/elif/else”式定义举例:
类型名称 = {
if 表达式1 : 类型定义1
elif 表达式2 : 类型定义2
else : 类型定义3
}
“switch”式定义举例:
类型名称 = {
switch 表达式
取值1 : 类型定义1
取值2 : 类型定义2
* : 类型定义3
}
其中“*”相当于default分支。
也可以指定取值的连续范围或取值列表,如:
1~5 : 定义4 # 使用 “~”表示连续范围(左右数值都包含)
1,3,6 : 定义5 # 使用 “,”分隔数值列表的不同数值
其中,以上类型定义可以是任意类型的“类型定义”(无论是静态定义、动态定义还是别名定义)。
举例:
======samples/basic/pay.def
# 支付信息,包括用户ID、支付方式、以及支付的具体信息
payInfo = dwUserId + cType + info
# 支付的具体信息
info = {
if dwUserId < 50000000 : { # VIP会员,有多种支付方式
switch cType
1 : =virtualMoney # 虚拟货币,这里以“别名”的形式使用virtualMoney的定义
2 : wTicket # 优惠券
* : dwMoney # 实际货币
}
else : dwMoney # 普通会员,只支持“实际货币”购买
}
virtualMoney = dwAmount + dwValidTime # 数目、有效时间
1.3 文件包含
DTK的类型定义可以通过@include宏进行文件级复用。其形式如下:
@include “filePath”
举例:@include “../lib/comm.def”
1.4 如何引用类型
类型的全名形式:文件名.类型名,如:
fileA.def中定义了 head 类型,则其全名是:fileA.head
当要引用某类型时,可以:
1) 使用类型的全名,这样可以唯一确定类型。
2) 使用类型的本名(类型名),DTK会自动推测和提示。
举例:
===samples/basic/product.def
product = dwId + wType + info
=== samples/basic/pay.def
payInfo = dwUserId + cType + info
=== samples/basic/order.def
@include "product.def"
@include "pay.def"
order = wCount + product[ wCount ] + detail
detail == pay.payInfo # 这里“pay.”前缀实际可用省略,DTK会自动推测是pay.def里的payInfo
更多:
DTK如何自动推测类型?
如何在同一文件里定义同名类型?
如果include的不同文件里有同名类型,是否冲突?
如果include的文件路径不同,但文件名相同,造成名字冲突,如何解决?
以及在这些情况下“如何引用类型”,参见“中级篇”第1.1节“类型的名字空间”
1.5 数据对象
数据对象即数据以某种数据类型的形式解析之后的结果。
数据对象的“全名”,并不等同于其类型名称,而是要根据它在“根对象”的层次位置决定。
举例:
head = wCmd + dwVer
req = head + dwAddr
resp = head + dwPhone
当数据以req的形式解析,得到以下数据对象(下面是全名):
req #根对象(只有根对象的全名和其类型名称一样)
req.head
req.head.wCmd
req.head.dwVer
req.dwAddr
当数据以resp的形式解析,得到以下数据对象(下面是全名):
resp #根对象
resp.head
resp.head.wCmd
resp.head.dwVer
resp.dwPhone
数据对象的“全名”是明确标识某对象的唯一方式。
1.6 表达式
DTK的表达式支持C语言形式的绝大部门的常用算术表达式、逻辑表达式及其组合(除了”++”和”–“ )。参与运算的运算数可以是:数字、数据对象、DTK的内建函数(参见附录)。
DTK的表达式用于:
1) 指定重复元素的数目和最大长度,参见本篇1.2.1
2) 条件语句的条件判断,参见本篇1.2.3
举例:
req = head + (dwSN + order){ head.wLen – sizeof(head) }
head = wVersion + cCmd + wLen
以及本篇1.2.3中例子
1.7 如何引用对象
1) 可以使用数据对象的“全名”(参见1.5)明确指明
2) 也可以简化,使用数据对象的本名或部分全名,DTK会自动进行推测。(推荐)
以本篇1.6的例子来说,head.wLen 和 head都是简化的引用形式。若以全名引用,则是:
req = head + (dwSN + order){ req.head.wLen – sizeof(req.head) }
如果DTK的推测结果并不符合要求,请使用“全名”来引用对象。
更多:
DTK如何根据自动推测具体对象?
参见中级篇1.1.4
1.8 其他语言特性
1.8.1 注释
DTK使用” # ” 来表示注释开始。和shell的注释风格相同。
1.8.2 宏
DTK目前支持的一些宏命令,以@开始,如@include,具体参见附录。
1.8.3 内建函数
DTK目前支持的内建函数参见附录。
1.8.4 __testcases
针对动态定义(即有多个分支),如何进行遍历的语法检查,或生成不同条件下的解码样本, 可以使用__testcases。参见“中级篇”第4节。
1.8.5 event
DTK允许在某数据对象解码前和解码后进行自定义的python脚本处理,即可以定义__begin和__end事件处理逻辑,参见“高级篇”第1节。
转载于:https://blog.51cto.com/vincentchen/867024