Slice语言语法介绍
-
一、介绍
1、Slice定义关注的焦点是对象接口、这些接口所支持的操作,以及操作可能引发的异常。还提供了一些用于对象持久的特性。
二、源文件
文件命名
2、含有Slice定义的文件必须以.ice扩展名结尾,编译器拒绝接受其它扩展名。对于大小写敏感的文件系统,扩展名必须小写。
文件格式
3、Slice是一种形式自由的语言,可以使用空格、横向和纵向制表符、换页,以及换行字符,Slice不会把语义与定义的布局关联起来。
预处理
4、Slice支持#ifndef、#define、#endif,以及#include预处理指令,使用方式有严格的限制:
(1)、只能把#ifndef、#define,以及#endif指令用于创建双包括块。例如:
#ifndef _CLOCK_ICE
#define _CLOCK_ICE
//#incluce directives here….
// ……….
#endif _CLOCK_ICE
(2)、#include指令只能出现在Slice源文件的开头,也就是说必须出现在其它所有的Slice定义的前面,此外,在使用#include指令时,只允许使用< >语法来指定文件名,不能使用” ”。例如:
#incluce <File.ice>
(3)、通过#include指令,Slice定义可以使用其它源文件中定义的类型。Slice编译器会编译解析源文件中的所有代码,包括通过#include包括的文件。但是编译器只为在命令行上指定的顶层文件生成代码,你必须分别编译用#include包括的各个文件。
定义次序
Slice的成分,比如模块、接口、或类型定义,可以按照任何次出现,但标识符在使用之前必须先声明。
三、语法规则
1、注释
在 Slice定义里,既可以使用C的、也可以使用C++的注释风格:
/*
*注释内容
*/
//注释内容
2、关键字
Slice使用了一些关键字,必须以小写的方式拼写。例如:class和dictionary都是关键字,必须按照所示方式拼写。这个规则有两个例外:Object和LocalObject也是关键字,必须按照所示方式让首字母大写。
3、标识符
标识符以一个字母起头,后面可以跟任意数目的字母或数字。Slice标识符被限制在ASCII字符范围内,不能包含非英文字母。Slice标识符不能有下划线。
(1)、大小写敏感
标识符是大小写敏感的,但大小写的拼写方式必须保持一致。
(2)、是关键字的标识符
可以定义在一种或多种实现语言中的关键字的Slice标识符。例如:switch是合法的Slice标识符,但也是C++和Java的关键字。语言映射定义了一些规则来处理这样的标识符。要解决这个问题,通常要用一个前缀来使映射后的标识符不在是关键字。但是建议少使用其它语言的关键字作为Slice的标识符。
(3)、转义的标识符
在关键字的前面加上一个反斜杠,可以把Slice关键字用作标识符。但是建议少使用。
(4)、保留的标识符
Slice为Ice实现保留了标识符Ice及以Ice(任何大小写方式)起头的所有标识符。例如:Icecream,Slice编译器会发出错误警告。
以下面任何一种后缀结尾的Slice标识符也是保留的:Helper、Holder、Prx,以及Ptr。Java和C++语言映射了使用了这些后缀,保留是为了防止冲突。
四、基本的Slice类型
Slice提供了一些内建的基本类型:
bool、byte、short、int、long、float、double、string
1、整数类型
Slice提供了整数类型short、int、long,类型的范围分别是16位、32位、64位。注意,在有些架构上,这些类型有可能映射到更宽的原生类型。还有Slice没有提供无符号类型。
2、浮点类型。
3、串
Slice串使用的是Unicode字符集。唯一一个不能出现在串中的字符是零字符。
Slice数据模型没有null串的概念。
4、布尔值
布尔值只有false和true两种值。
5、字节
Slice的byte类型是一种(至少)8位的类型,当在地址空间之间传递时,它保证不会发生任何改变。这样允许交换二进制数据,在传送过程中这些数据不会被篡改。
五、用户自定义的类型
Slice允许定义复杂的类型:枚举(enumerations)、结构(structures)、序列(sequences)、词典(dictionaries)。
1、枚举
Slice的枚举类型定义看起来像是C++的枚举类型定义:
Enum Fruit { Apple,Pear,Orange};
这个定义引入了一种名为Fruit的类型,是一种拥有自己权利的新类型。关于怎样把顺序值赋给枚举符的问题,Slice没有作出定义。
与C++不同,Slice不允许控制枚举符的顺序值。
与C++里一样,Slice枚举符也会围绕它的名字空间,所以下面的定义是非法的:
enum Fruit { Apple,Pear,Orang};
enum ComputerBrands {Apple,IBM,Sun,HP};
Slice不允许定义空的枚举。
2、结构
Slice支持含有一个或者多个有名称的成员的结构,这些成员可以具有任意类型,包括用户定义的复杂类型。例如:
Struct TimeOfDay {
short hour;
short minute;
short second;
}
在结构内部,只能出现数据成员定义,这些定义必须使用有名字的类型。
3、序列
序列是变长的元素向量:
Sequence<Fruit> FruitPlatter;
序列可以是空的,也就是说可以不包含元素;可以持有任意数量的元素,直到到达你的平台的内存限制。
序列包含的元素自身也可以序列。
4、词典
词典是从键类型到值类型的映射。例如:
struct Employee{
long number;
string firstName;
string lastName;
};
Dictionary<long,Employee> EmployeeMap;
词典的值类型可以是用户定义的任何类型。但词典的键类型只能是以下类型之一:
整型(buyte、short、int、long、bool,以及枚举类型)
string
元素类型为整型或string的序列
数据成员的类型只有整型或string的结构。
5、常量定义与直接量
Slice允许定义常量,类型必须是以下类型中的一种:
整型、float、double、string
例如:
const bool app=true;
const byte low=0X0f;
const string ad=”aa”;
const short th=42;
const double PI=3.1416;
enum Fruit { Apple,Pear,Orange};
const Fruit fa=Pear;
直接量的语法与C++ 和Java的一样(有一些小的两位):
布尔常量只能用关键字false和true初始化(不能用0和1)。
和C++一样,可以用十进制、八进制、十六进制方式来指定整数直接量。
注意:用于指示长常量和无符号常量的后缀是非法的:
const long w=0u;l
const long wt =100000L;
整数直接量的值必须落在其常量类型的范围内。
浮点直接量使用的是C++的语法,除了不能用l或L后缀来表示扩展的浮点常量,但是f和F是合法的(但会被忽略)。值必须落在其常量类型(float或double)的范围内。
串直接量支持与C++相同的转义序列。
注意:Slice没哟null串的概念,因此,在Ice平平台的任何地方它都不能用作合法的串值。
六、接口、操作、异常
1、参数与返回值
操作定义必须包含返回类型,以及0个或更多的参数定义。
一个操作可以有一个或多个输入参数。参数名是必需的,不能缺省参数名。要把值从服务器传到客户,可以使用输出参数,这种参数用out关键字指示。
如果操作既有出入参数,又有输出参数,输出参数必须放在输入参数的后面。
Slice不支持既是输入、又是输出参数的参数(传引用调用)。
2、Slice不支持任何形式的操作重载。
同一个接口中的各个操作必须具有不同的名称,不管参数的类型是什么,数目是多少。之所以存在这个限制,是因为重载函数无法有效的映射到没有内建的重载支持的语言。
3、Nonmutating操作
Nonmutating关键字说明,操作不会改变它的对象的状态。这是有用的,原因有两个:
(1)、语言映射可以利用关于操作行为的这项附件知识。例如:在使用C++时,nonmtating操作会映射到骨架类上的C++ const成员函数。
(2)、知道了某个操作不会修改它的对象的状态,Ice run time就了可以尝试进行更积极的错误恢复,特别地,Ice会保证操作调用的“最多一次”语义。
对于普通的操作, Ice run time 在处理错误时必须保守。例如,如果客户发送一个操作调用给服务器,然后连接断掉了,在这种情况下,客户端run time 无法知道它所发送的请求是否已实际到达服务器。这意味着,客户端run time 不能通过重新建立连接、并再次发送请求来进行错误恢复,因为这可能会造成操作两次调用,从而违反“最多一次”语义;客户端run time 别无选择,只能把错误报告给应用。
而另一方面,对于nonmutating 操作,客户端run time 可以尝试重新建立与服务器的连接,并安全地再次发送先前失败的请求。如果第二次尝试能够联系上服务器,那么万事大吉,应用不会注意到发生过(暂时的)失败。只有在第二次尝试也失败的情况下,客户端run time才需要把错误报告给应用(可以通过Ice 的一个配置参数来增加重试次数)。
(3)、Idempotent操作
如果对某个操作进行两次连续的调用,其效果与一次调用是一样的,这个操作就是idempotent操作。
操作的情况一样, Ice run time 利用这一知识来更积极地进行错误恢复。
一个操作可以有nonmutating 修饰符,也可以有idempotent 修饰符,但不能同时有这两个修饰符(nonmutating 隐含了idempotent)。
4、用户异常
Slice允许定义用户异常,用以向客户指示错误的情况。
exception Error { };
exception RangeError {
TimeOfDay errorTime;
TimeOfDay minTime;
TimeOfDay maxTime;
};
用户异常很像是含有一些数据成员的结构。但是,与结构不同,异常可以有零个数据成员,也就是说,是空的。当操作的实现出错时,异常允许你向客户返回任意数量的出错信息。操作可以使用异常规范来说明可能会有异常返回给客户:
interface Clock {
nonmutating TimeOfDay getTime();
idempotent void setTime(TimeOfDay time)
throws RangeError, Error;
};
操作只能抛出在它的异常规范中列出的那些用户异常。
不能把异常当作参数值传递。
不能把异常用作数据成员的类型。
不能把异常用作序列的元素类型。
不能把异常用作词典的键或值类型。
不能抛出非异常类型的值(比如int 或string 类型的值)。
5、异常继承
异常支持继承,只支持单继承。
注意,如果某个操作的异常规范指明了具体的异常类型,该操作的实现在运行时也可能会抛出派生层次最深的异常。
6、Ice运行时异常
操作的异常规范不能列出任何运行时异常。
所有的Ice 运行时异常和用户异常都处在一个继承层次中。
Ice运行时异常的继承结构:
Ice 运行时异常的完整层次(服务器可能会发出有阴影的异常):
6、接口语义与代理
接口是有资格的类型,可以作为参数传递,*操作符叫作代码操作符。
代理可以为null,代理可以悬空(指向的对象已经不存在),通过代理分派的操作使用的是迟后绑定:如果与代理的类型相比,代理所代表的对象的实际运行时类型派生层次更深,调用的就将是派生层次最深的接口的实现。
注意,在共享语义方面,代理的行为也和指针非常像:如果两个客户都有一个代理,指向相同的对象,一个客户造成的状态变化就会被另一个客户看到。
代理是强类型的。
7、接口继承
(1)、接口继承的局限
如果一个接口使用多重继承,它不能从不止一个基接口那里继承相同的操作名称。
(2)、隐含地继承Object
所有Slice接口最终都派生自Object.
(3)、Null代理
(4)、自引用的接口
代理具有指针语义,所以可以定义自引用的接口:
interface Link {
nonmutating SomeType getValue();
nonmutating Link* next();
};
(5)、空接口
Slice空接口是合法的。
(6)、接口继承\实现继承
Slice继承只是建立在类型兼容性,并没有说出任何与接口的实现方式有关的事情。
七、类
Slice还允许定义类。类像是接口,都有操作;类像是结构,都有数据成员。这样就产生了一种混合的对象,可以把它当作接口,通过引用进行传递,也可以把它当做值,通过值进行传递。类提供了很大的架构灵活性,类允许在客户端实现行为,而接口只允许在服务器端实现行为。
类支持继承,因此是多态的:在运行时,只要实际的类类型是从操作的型构的形参类型派生的,就可以把一个类实例传给一个操作。这使得类能够用作类型安全的联合。
1、 简单类
Slice的类型定义与结构定义类型,但是用关键字class。例如:
class TimeOfDay {
short hour; // 0 - 23
short minute; // 0 - 59
short second; // 0 – 59 };
能使用Slice结构的地方,都能使用Slice类,但是出于性能的上的考虑,结构已经够用,就不应该使用类。与结构不同,类可以使空。
2、 类继承
与结构不同,类支持继承。
3、 类的继承语义
类使用的传值语义和结构一样。如果把一个类实例传给一个操作,这个类和它的所有成员都会被传递。
4、 类用作联合
Slice没有提供专门的联合,可以从共同的基类派生多个类,从而得到与使用联合相同的效果。
浙公网安备 33010602011771号