SRC Language

更新至版本1.1

 SRCSend Recv Cmds)语言主要用于快速编写网络测试程序,用户在SRC源文件里列出每个命令的每个字段,然后使用SRC编译器读取源文件,自动发送或接收命令。如果需要测试的命令比较多,或者有多个相关功能需要系列测试,使用SRC语言能够大大节省时间和精力,因为用户不需要编写任何测试程序,所有的工作都是写文本配置文件(SRC源文件),并且可以在以后随时修改。

此外,SRC源文件与命令文档结构类似,所以为命令编写的SRC测试源文件,可以直接作为命令解释文档使用。甚至SRC源文件拥有比一般命令文档更多、更准确的信息,例如采用的连接类型、字节序约定、加密协议、HTTP包装等等,因为这些都是SRC源代码里必须明确指定的信息。

SRC源代码结构

SRC的源代码主要包括:变量声明,函数调用,和命令结构。下面是一个简单的例子:

 

STR server_ip := "127.0.0.1"    //变量声明

U16 server_port := 9527

TCP conn(server_ip,server_port)

 

NET_BYTE_ORDER                     //函数调用

 

COMMAND QueryCommand              //命令结构

SEND(conn)

U16 ver := 100

U16 cmd_type := 161

U32 seq := 1234

U32 len

BEGIN(len)

STR file_hash := "http://www.google.com"

STR client_hash := UNHEX("1A2B3C4D5E6F")

STR peer_id := "ABCDEF1234567890"

END COMMAND

 

上面的SRC代码定义了一个名为“QueryCommand”的命令结构,并准备发送给本机的9531端口。

数据类型

SRC语言支持的基本数据类型有:

分类

关键字

字节数

意义

数值类型

U8

1

无符号8位整数

S8

1

有符号8位整数

U16

2

无符号16位整数

S16

2

有符号16位整数

U32

4

无符号32位整数

S32

4

有符号32位整数

U64

8

无符号64位整数

S64

8

有符号64位整数

字符串类型

STR

可变

字符串

RAW

可变

字符串

网络连接类型

TCP

N/A

TCP连接

UDP

N/A

UDP连接

1  基本数据类型

数值类型的变量,编码和解码时直接写入和读取真正的数据。

STR类型的变量,编码时首先写入U32类型的长度字段,然后写入真正的数据;在解码时,STR类型的变量首先读取U32类型的长度,然后读取真正的数据;

RAW类型的变量,编码时直接写入真正的数据;在解码时,RAW类型的变量直接读取真正的数据,但是必须和其他条件配合才能确定读取的结束位置。

TCPUDP属于网络连接类型,不能被编码或解码,

 

函数调用

SRC语言里,函数调用的格式为:

FUNCTION ( arg_list )

FUNCTION

其中,FUNCTION可以是任意内置函数,或者是任意数据类型关键字。arg_list是用‘,’分隔的参数列表,可以为空。对于内置函数,当参数列表为空的时候,可以省略‘(’和‘)’。

 

内置函数

SRC语言提供了如下的内置函数:

l  HBOHOST_BYTE_ORDER

参数:无

意义:设置以本地字节序编码或解码

说明:由于HBO函数没有参数,调用时采用下面2种形式均可:

HBO

HBO()

HOST_BYTE_ORDER

HOST_BYTE_ORDER()

 

l  NBONET_BYTE_ORDER

参数:无

意义:设置以网络字节序编码或解码

说明:由于NBO函数没有参数,调用时采用下面2种形式均可:

NBO

NBO()

NET_BYTE_ORDER

NET_BYTE_ORDER()

在默认情况下,SRC编译器采用网络字节序进行编码和解码。

 

l  HEX

参数:字符串

意义:将参数字符串的每个字符以16进制表示生成新字符串,作为函数的返回值

说明:采用如下方式调用:

STR hex_1 = HEX(“abc”)    // hex_1 = “616263”

 

l  UNHEX

参数:字符串

意义:将参数字符串的每2个字符作为16进制数值,生成新字符串,作为函数的返回值

说明:要求参数字符串为连续的16进制字符(0-9A-Fa-f),例如:

STR origin = UNHEX(“616263”)   // origin = “abc”

 

l  SEND

参数:若干连接变量(TCPUDP

意义:将命令发送给指定的连接

说明:如果SEND函数没有参数,那么编译器会选择整个源文件里定义的第一个连接变量作为参数。SEND函数的参数个数没有限制,如果参数里有重复的连接变量,那么同一个命令会多次发送给这个连接。调用示例:

SEND        // 自动选择第一个连接变量作为参数

SEND()

SEND(conn1,conn2,…)

 

l  RECV

参数:若干连接变量(TCPUDP

意义:从指定的连接接收命令

说明:如果RECV函数没有参数,那么编译器会选择整个源文件里定义的第一个连接变量作为参数。虽然从语法上,RECV函数的参数个数没有限制,但运行时只从RECV函数的第一个参数接收命令,这是目前编译器的实现限制。调用示例:

RECV        // 自动选择第一个连接变量作为参数

RECV()

RECV(conn1,conn2,…)   // 运行时只从conn1接收命令

 

l  BEGIN

参数:1个或以上的变量名

意义:为计算数据段长度的变量指定起始偏移位置

说明:在命令里总有一些字段表示某个数据段的长度。在数据段开始的地方,调用BEGIN函数,并传入记录该数据段长度的变量的名字。

BEGIN函数的参数必须是变量名,并且这些变量必须是数值类型的(UxxSxx)。调用示例:

U32 body_len

BEGIN(body_len)

 

l  END

参数:1个或以上的变量名

意义:为计算数据段长度的变量指定结束偏移位置

说明:在数据段结束的地方,调用END函数,并传入记录该数据段长度的变量的名字。END函数应该与BEGIN函数成对出现,并且END函数的位置应在后面。在一个命令结构结束的时候,如果存在调用了BEGIN函数却没有调用END函数的变量,编译器会自动为其调用END函数,位置就是整个命令的结尾。

END函数的参数必须是变量名,并且这些变量必须是数值类型的(UxxSxx)。调用示例:

U32 body_len

BEGIN(body_len)

……  // 任意字段

END(body_len)

 

l  FUNFUNCTION

参数:自定义函数名字 + 长度数值

意义:对命令数据调用指定的用户自定义函数

说明:有些命令除了写入所有字段,还需要进行一些特殊处理,比如加密、计算校验码等等;同样,有些命令除了读取所有字段,需要进行上述操作的逆处理。SRC语言没有从语法上支持这些复杂的操作,但是允许用户对命令数据进行自定义的操作,方式就是通过FUN函数调用。调用示例:

FUN(my_encrypt_fun)

FUN(my_decrypt_fun,30)   //保证后续至少有30字节数据的前提下调用指定函数

 

l  IP NBO

参数:1个字符串或1个数值

意义:把参数字符串表示的IPv4地址转化为网络字节序U32数值;或者将网络字节序U32数值转化成为IPv4字符串

说明:注意这里是2个关键字组合起来,表示一个函数。采用如下方式调用:

STR ip_str = IP NBO(0x505AA8C0)       // ip_str = “192.168.90.80”

STR ip_val = IP NBO(“192.168.90.80”) // ip_val = 0x505AA8C0

 

l  IP HBO

参数:1个字符串或1个数值

意义:把参数字符串表示的IPv4地址转化为本地字节序U32数值;或者将本地字节序U32数值转化成为IPv4字符串

说明:同上面一样,这里是2个关键字组合起来,表示一个函数。采用如下方式调用:

STR ip_str = IP HBO(0xC0A85A50)       // ip_str = “192.168.90.80”

U32 ip_val = IP HBO(“192.168.90.80”) // ip_val = 0xC0A85A50

 

l  PRINT

参数:1个或以上的可打印变量

意义:在标准输出打印指定的内容

说明:可用于在代码任何地方,打印出任何信息,包括调试信息,提示信息,等等。PRINT函数要求至少有一个参数,没有上限。目前,所有SRC语言的基本类型都是可打印的。调用示例:

U32 val = 100

STR str = “Hello”

TCP conn(“127.0.0.1”,9531)

PRINT(“val = “,val)

PRINT(“str = “,str)

PRINT(“conn = “,conn)

会在标准输出上打印:

val = 100

str = Hello

conn = (3,127.0.0.1:9531)    //打印格式为 (fd , IP : Port)

 

l  BEGIN ARRAY

参数:0个或1个的数值变量

意义:指定一个数组的起始位置,参数数值表示元素个数

说明:有些命令并非只是简单字段的组合,而是有内部的逻辑结构,甚至有一些复杂结构的数组。“BEGIN ARRAY”函数用于定义这种结构体数组的起始位置。

调用示例:

BEGIN ARRAY(3)          //指定数组起始位置,并指定元素个数为3

U32 val = 100

STR str = “abc”

END ARRAY                //数组结构结束,每个元素包含U32STR两个字段

该函数只能出现在命令结构体内。

如果在发送命令里:

1.       如果函数没有参数,SRC编译器报告“数组长度未知”错误;

2.       如果函数有参数,SRC编译器把元素的每个字段依次编码,并重复参数指定的次数。

例如上面的代码,由于指定了元素个数为3,编译器会把:

编码U32变量val

编码STR变量str

重复做3次。这就相当于编码了3个结构体,每个结构体包括U32STR两个字段。

如果在接收命令里:

1.       如果函数没有参数,SRC编译器会先读取U32元素个数字段,然后依次读取每个元素的每个字段;

2.       如果函数有参数,SRC编译器会假定元素个数为参数数值,直接依次读取每个元素的每个字段。

值得注意的是,“BEGIN ARRAY”函数可以嵌套调用,即在一个数组结构体内部,可以定义另一个数组结构体,这样就允许定义任意复杂的命令,例如:

BEGIN ARRAY      //第一层数组结构体

  U32 val_1

  BEGIN ARRAY    //第二层数组结构体

    U8 val_2

    BEGIN ARRAY  //第三层数组结构体

      STR str_3

      STR id_3

    END ARRAY    //第三层数组结构体结束

    U64 sz_2

  END ARRAY      //第二层数组结构体结束

  STR str_1

END ARRAY        //第一层数组结构体结束

实际中可能很难找到如此复杂的命令,但是有些命令的确会用到第二层数组结构体。

 

l  END ARRAY

参数:无

意义:指定一个数组的结束位置

说明:“END ARRAY”函数必须与BEGIN ARRAY”函数成对出现。函数的使用请见上面的介绍。

 

构造函数

SRC语言里,每一种数据类型都对应有若干个构造函数,用于初始化相应的变量。下面是各种数据类型的构造函数的介绍:

l  U8S8U16S16U32S32U64S64

参数:0个或1个数值或变量名

意义:用参数值或0初始化对应类型的变量

说明:如果参数列表为空,则用0初始化变量。调用示例:

U32 v1 = U32(100)

U32 v2 = U32(v1)

U8 v3 = U8()       //使用0初始化变量

数值类型的变量可以直接相互赋值,但是当出现溢出或者符号不匹配时,SRC编译器会报告一个运行时错误,此时用户应该确认转化的正确意义。

 

l  STRRAW

参数:0个或1个字符串或变量名

意义:用参数字符串初始化对应类型的变量

说明:如果参数列表为空,则用空字符串初始化变量。调用示例:

STR s1 = STR(“abc”)

RAW v2 = RAW(s1)

RAW v3 = RAW()       //默认使用空字符串初始化变量

STRRAW类型的变量,可以直接相互赋值。

 

l  TCPUDP

参数:以下几种组合都是可以的:

s  1个相同类型的连接对象

s  字符串(IP地址)+ 字符串(端口)+ 数值(超时,可省略)

s  字符串(IP地址)+ 数值(端口)+ 数值(超时,可省略)

意义:建立新连接,或指向一个已有的连接

说明:如果参数是另一个连接对象,那么只是复制了指向连接的指针,而没有建立新的连接;其他情况下会建立一个新的连接。“超时”参数暂时没有任何作用,并且可以省略,因为当前的SRC编译器采用阻塞模式发送和接收数据。

调用示例:

UDP(“127.0.0.1”,9527)

TCP tcp1 = TCP(“127.0.0.1”,”9527”)

TCP tcp2 = TCP(tcp1)   //只是复制了连接指针,没有建立新的连接

TCPUDP变量之间不能相互转化,并且也不能参与命令编码与解码,所以无论是构造函数,还是变量声明,都只能出现在命令结构之外,即全局空间里。

整个SRC源文件里,最先建立的连接(无论是通过变量声明,还是构造函数),会作为默认连接,如果调用SENDRECV函数而没有传递参数,SRC编译器会选择默认连接作为参数。

 

变量声明

变量声明是SRC语言里最重要、最复杂的语句,也是使用最多的语句。SRC语言的变量声明分为普通声明和自由变量声明。首先介绍普通声明:

数组变量声明

数组变量的声明格式为:

TYPE [] name

TYPE [ expression ] name

其中,TYPE是数据类型关键字;expression是任意可求值的表达式,并且是数值类型。例如:

U32 [] u32_array;

STR [] str_array;

U8[12] char_array_size_12;

但是以下类型不能声明为数组:

s   RAW

s   TCP

s   UDP

数组类型的变量不能被编码,因为数组声明无法给元素赋初值。在解码时,没有指定长度的数组变量,会先读取U32类型的元素个数字段,然后依次读取每个元素;指定了长度的数组变量,直接读取每个元素。

数组变量声明只允许出现在接收命令结构里。

上面第一种声明方式通过读取到的命令数据内容来决定元素的个数,第二种方式指定了数组的元素个数。其实这2种方式在某些情况下是等价的,例如:

STR[] str_array   //先读取U32元素个数,再读取每个元素

等价于

U32 array_sz      //先读取U32元素个数

STR[array_sz] str_array  //再读取每个元素

 

常量声明

SRC语言里,常量是指在声明的时候可以立刻求值,并且在以后都不会改变的对象,相当于C++里的const常量。常量的声明方式如下:

TYPE name := expression

TYPE name :( arg_list )

其中,expression表示任何可求值的表达式,而arg_list表示用‘,’分隔的参数列表,可以为空。例如:

U32 ver := 100       //定义值为100U32常量

U32 ver:(100)        //与上面等价

TCP conn := TCP(“127.0.0.1”,9527) //定义一个TCP常量,连接本机的9527端口

TCP conn:(“127.0.0.1”,9527)         //与上面等价

TCP conn1 := conn   //定义另一个TCP常量conn1,与conn表示同一个连接

TCP conn1:(conn)    //与上面等价

常量声明只能出现在全局空间或发送命令结构里。

 

普通变量声明

SRC语言里,普通变量是指在声明的时候无法求值,或者求出的不一定是最终值的对象。普通变量的声明方式如下:

TYPE name

TYPE name = expression

TYPE name ( arg_list )

其中expressionarg_list的意义与常量的相同。例如:

U32 body_len      //定义初值为0U32变量,可能在后面被改变

U32 body_len = 0 //与上面等价

U32 body_len(0)   //与上面等价

普通变量声明可以出现在任何地方。

对于TCPUDP连接类型,普通变量声明和常量声明是等价的,因为连接总是会在变量声明的地方开始建立,并且无法改变变量所代表的连接对象。

对于STRRAW类型,因为除了在普通变量声明的时候赋值,没有其他方法改变值,所以普通变量和常量的声明也是等价的。

 

断言变量声明

断言变量声明是SRC语言引入的新语法,用于在读取命令字段的同时,检查字段值的有效性。在接收命令里,为了尽早发现错误的数据,可以为一些字段加上限制条件,例如版本号应该大于或等于100,数据包长度应该小于32K字节,等等。这些限制条件可以通过断言变量方便的实现。断言变量的声明方式如下:

TYPE name OP expression

其中,OP是下列判断操作符之一:

s   <

 

s   >

 

s   <=

s   >=

s   ==

s   !=

expression的意义与前面的相同。例如:

U32 ver >= 100      //变量ver的值必须大于或等于100

U32 body_len < 32K //变量body_len的值必须小于32×1024

RAW str == “HTTP header”  //字符串str必须等于“HTTP header

断言变量的声明只能出现在接收命令里,因为在其他地方进行断言,本身就是没有意义的。

如果断言返回值为真,SRC编译器继续进行后面的操作;如果断言返回值为假,SRC编译器会报告错误,并终止操作。

 

流操作变量声明

流操作变量是SRC语言的又一特色,用于字符串与数值类型变量之间的相互转化。流操作变量有2种:流输出变量,流输入变量。

l  流输出变量声明

流输出变量声明用于把其他类型的值,转化成字符串类型的变量,声明方式如下:

STRING name << expression

其中,STRING表示STRRAWexpression是任意可求值的表达式,并且返回值能够转化成字符串。实际上,除了TCPUDP连接类型,SRC语言支持的其他类型都可以转化成字符串。使用示例:

U32 value = 100

STR str << value     //str = “100”

流输出变量声明只能出现在发送命令里。

 

l  流输入变量声明

流输入变量声明能把字符串转化成数值类型,声明方式如下:

STRING name >> expression

STRING name >> TYPE

其中,STRING表示STRRAWexpression是任意可求值的表达式,并且返回值必须是数值类型;TYPE表示任意数值类型关键字(UxxSxx)。使用示例:

DEF U32 value

STR str >> value     //str字符串转化成U32值,赋给value变量

STR str >> U32       //str字符串转化成U32

流输入变量声明只能出现在接收命令里。

 

自由变量声明

只要在普通声明的前面加上“DEF”或“DEFINE”关键字,就是自由变量的声明。

在命令结构体内,使用普通方式定义的任意变量,都会作为该命令的字段,并需要进行编码或解码。而自由变量则不作为命令的字段,也不参与编码或解码,但是仍像普通变量那样求值。自由变量在某些场合下有不可替代的作用,例如:

DEF U32 def_total_len          // 使用自由变量计算整个数据包的长度

BEGIN(def_total_len)

……     //命令头的字段

U32 total_len = def_total_len // 实际编码的是total_len字段

……     //命令体的字段

END(def_total_len)

上例中,普通变量total_len需要计算整个数据包的长度,这就需要在声明total_len之前调用BEGIN(total_len),而在函数内使用未定义的变量是SRC语言所不允许的,所以需要使用自由变量def_total_len完成计算任务,最后将值赋给普通变量total_len,并最终编码进数据包。

 

断言表达式

断言表达式用于在接收命令里判断数据的合理性,它比断言变量声明更好的地方在于:可以随时随地进行判断,不一定非在变量定义的时候。

SRC语言的断言表达式格式如下:

OP1 expression

expression OP2 expression

其中,OP1代表一元判断操作符,目前支持的有:

s   !

OP2代表二元判断操作符,目前支持:

s   <

 

s   >

 

s   <=

s   >=

s   ==

s   !=

expression的意义与前面的相同,不过要求返回值类型可以进行相应的判断操作。具体来说就是,对于一元操作符“!”,要求expression的返回值是数值类型;对于二元操作符,要求左右expression的返回值要么都是数值类型,要么都是字符串类型。例如:

U32 ver

ver >= 100   //二元判断操作符

!ver          //一元判断操作

STR str

str == “abc”

 

命令结构

SRC语言主要是用来定义各种各样的命令结构,而SRC源文件一般就是若干个命令结构的集合。

命令结构的定义通过“CMD”或“COMMAND”关键字,格式如下:

CMD CmdName    //命令结构开始,CmdName是命令的名字,可以为空

……    //任意字段

END CMD         //命令结构结束

命令的名字CmdName可以为空,但是不允许与其他全局变量重名,不同的命令也不允许使用相同的名字。

命令结构有2种类型:发送命令和接收命令。

 

发送命令

发送命令的标志,是在所有命令字段声明之前,有SEND函数调用,例如:

CMD QueryCommand

  SEND

……    //命令的其他字段

END CMD

SEND函数可以被多次调用,也可以传递任意个数的参数,SRC编译器会自动把命令数据发送给SEND函数的每个参数,无论是否有重复。

在发送命令里不允许出现的语句包括:

s   TCPUDP类型的变量声明和构造函数

s   数组变量声明

s   断言变量声明

s   流输入变量声明

s   RECV函数调用

s   断言表达式

s   无参数的BEGIN ARRAY函数

下面是一个简单的发送命令的示例:

 

COMMAND QueryCommand        //命令名字为“QueryCommand

SEND                         //在所有字段声明之前调用SEND函数

U16 ver := 100             //对于可以立即求值的字段,请使用常量声明

U16 cmd_type := 161

U32 seq := 1234

U32 len                      //对于需要延后求值的字段,请使用普通变量声明

BEGIN(len)                  //调用BEGIN函数

STR file_hash := "http://www.google.com"

STR client_hash := UNHEX("1A2B3C4D5E6F")

STR peer_id := "ABCDEF1234567890"

END COMMAND                   //命令结束时,自动为len字段调用END函数

 

这个命令只是简单的把每个字段打包,最后发送出去。

有一些命令,还需要加上HTTP头,而HTTP头里有一个“Content-Length:”字段,表示HTTP数据体的长度,为了正确填写这个字段,需要使用自由变量和流输出变量:

 

CMD QueryCommand SEND       //SEND函数还可以写在这个位置

RAW http1 := "POST http://127.0.0.1:12345/ HTTP/1.1/r/n/

Content-Length: "          //使用转义字符/’,将较长的字符串分行表示

DEF U32 pack_len           //使用自由变量记录命令包的总长度

RAW pack_len_str << pack_len  //pack_len流输出到字符串

RAW http2 := "/r/nContent-Type: application/octet-stream/r/n/

Connection:Close/r/n/r/n"

BEGIN(pack_len)             //从此处开始计算数据包长度

……                             //其他字段与上例相同

END CMD  //QueryCommand      //命令结束时,自动为pack_len调用END函数

 

现在,这个发送命令加入了HTTP头,通过SRC编译器的处理,最后发送出去的会是如下数据:

 

0000h: 50 4F 53 54 20 68 74 74 70 3A 2F 2F 31 32 37 2E ; POST http://127.

0010h: 30 2E 30 2E 31 3A 31 32 33 34 35 2F 20 48 54 54 ; 0.0.1:12345/ HTT

0020h: 50 2F 31 2E 31 0D 0A 43 6F 6E 74 65 6E 74 2D 4C ; P/1.1..Content-L

0030h: 65 6E 67 74 68 3A 20 35 36 0D 0A 43 6F 6E 74 65 ; ength: 56..Conte

0040h: 6E 74 2D 54 79 70 65 3A 20 61 70 70 6C 69 63 61 ; nt-Type: applica

0050h: 74 69 6F 6E 2F 6F 63 74 65 74 2D 73 74 72 65 61 ; tion/octet-strea

0060h: 6D 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 43 ; m..Connection: C

0070h: 6C 6F 73 65 0D 0A 0D 0A 00 64 00 A1 00 00 04 D2 ; lose.....d......

0080h: 00 00 00 2C 00 00 00 14 68 74 74 70 3A 2F 2F 77 ; ...,....http://w

0090h: 77 77 2E 62 61 69 64 75 2E 63 6F 6D 00 00 00 06 ; ww.baidu.com....

00a0h: 1A 2B 3C 4D 5E 6F 00 00 00 06 41 42 43 44 45 46 ; .+<M^o....ABCDEF

 

可以看到,HTTP数据体的长度为“56”,被正确的写入了HTTP头信息里。

除了加HTTP头信息,还可以对命令进行更加复杂的处理,例如加密解密,计算校验和等等,这些操作可以通过FUN函数来实现,具体请参考“Using SRC Language”。

 

接收命令

接收命令的标志,是在所有命令字段声明之前,有RECV函数调用,例如:

CMD RespCommand

  RECV

……    //命令的其他字段

END CMD

RECV函数可以被多次调用,也可以传递任意个数的参数,但是由于实现限制,目前SRC编译器只会从第一个RECV调用的第一个参数接收数据。

在接收命令里不允许出现的语句包括:

s   TCPUDP类型的变量声明和构造函数

s   常量声明

s   流输出变量声明

s   SEND函数调用

下面是一个简单的接收命令的示例:

CMD RespCommand RECV    //RECV函数还可以写在这个位置

U16 ver >= 100

U16 cmd_type == 162

U32 seq

U32 len < 32K           //32K = 32×1024 = 32768

U8  result

!result

STR fileHash_ == "http://www.baidu.com";

STR clientHash_;

STR[] peerId_;       //array

END CMD

 

这个接收命令只需解码每个字段即可,但实际中有些命令会使用HTTP头回复,例如上面带HTTP头的发送命令的回复。为了正确的接收HTTP头信息,需要对RAW类型的变量使用断言声明和流输入声明,示例如下:

 

CMD RespCommand RECV

RAW http1 == "HTTP/1.1 200 OK/r/nContent-Length: "

                              //使用“==断言声明,既判断内容的正确性,又提示RAW

//字符串的长度

RAW pack_len_str >> U32  //使用流输入声明,提示RAW字符串的内容

RAW http2 == "/r/nContent-Type: Application/octet-stream/

/r/nConnection: Close/r/n/r/n"

……                           //其他字段与上例相同

END CMD

 

在接收命令里读取RAW字符串字段的最大问题,是无法知道读取操作的结束位置,因为RAW字符串没有长度字段。所以必须与其他条件配置,例如上面的“==”断言,限制字符串的内容,同时也限制了RAW字符串的长度;而“>>”流输入操作符,可以限制字符串的内容必须是数字字符“0-9”,当读取到其他字符的时候,SRC编译器就知道读取结束了。

 

其他语法

下面的内容介绍了SRC语言里比较基础的一些语法。

 

语法常量

SRC语言的语法常量包括数值常量和字符串常量。数值常量的表达方式遵循C++语法,例如:

 

S32 int_v := 10              //10”表示整型常量10

S32 long_v := 100L          //100L”表示长整型常量100

S64 long_long_v := 1000LL  //1000LL”表示64位长整型常量1000

S64 i64_v := 1000i64        //同上

U32 uint_v := 10U            //10U”表示无符号整型常量10

U32 long_v := 100UL          //100UL”表示无符号长整型常量100

U64 long_long_v := 1000ULL  //1000ULL”表示64位无符号长整型常量1000

S8 char_v := ‘a’              //’a’”表示字符‘a’的ASCII码值

 

关于数值常量表示语法的详细信息,请参考C++语言的语法说明。

字符串常量的表达方式同样遵循C++语法,例如:

 

STR str_v := “abc”              //使用引号表示字符串常量“abc

STR long_str_v := “this is a very long/

string that needs return new line”   //使用转义字符‘/’使字符串换行

STR odd_str_v := “O/rD/aD/tS/032T/xaeR”

  //这也是合法的字符串常量,不信用C++试试!

 

关于字符串常量表示语法的详细信息,请参考C++语言的语法说明。

目前SRC语言不支持宽字符和字符串,即以“L”前导表示的字符和字符串。在C++语言里宽字符的类型是wchar_t,在SRC语言里没有对应的类型。

 

注释

SRC语言的注释以“#”或“//”开始,到行尾结束,可以与有效代码同行,例如:

# This is shell style comments

//This is C++ style comments

STR str_v    //comments after statement

C++语言里,使用“//”的注释可以通过转义字符‘/’延伸到下一行,但是在SRC语言里,这是不允许的,即注释里的‘/’字符没有转行的意义。

 

语句分隔符

语句分隔符是指一条完整语句的结束标志,例如C++语言里的‘;’字符。在SRC语言里,‘;’字符和换行符都可以作为语句分隔符,例如下面的写法都是合法的:

U32 len ; BEGIN(len)   //使用‘;’分隔语句

STR cid                   //使用换行符分割语句

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章