说明: 《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     表示类型head3个数据成员wVersioncCmdwLen

 

根据定义的形式,类型定义可以分为:“静态定义”(不含条件逻辑)、“动态定义”(含条件逻辑)、“别名定义”(类型的别名)。

1.2 类型定义

1.2.1 静态定义

静态定义可以是:

1)    另一个类型名称,如:
head = cCmd  表示head类型里只有1个成员cCmd(cCmdhead的子节点)

 

静态定义也可以包含其他类型定义的组合:

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