变量与数据类型
计算机处理的对象是数据,而数据是以某种特定的形式存在的(例如整数、浮点数、字符等形式)。不同的数据之间往往还存在某些联系(例如由若干个整数组成一个整数数组)。数据结构指的是数据的组织形式。例如,数组就是一种数据结构。不同的计算机语言所允许使用的数据结构是不同的。处理同一类问题,如果数据结构不同,算法也会不同。例如,对10个整数排序和对包含10个元素的整型数组排序的算法是不同的。
1. 数据类型部分
Primitive | 这些是对所有的程序最基本的数字和字符类型;你不需要声明它们 |
Literals | 值的直接表示 |
Dictionary | 字典部分由键-值对组成 |
Exception | 系统提供很多基本的异常;你可以自己定义其它的异常 |
Arrays | 数组 |
1.1基本数据类型(Primitive Type)
Primitive基本数据类型是标准的预定义类型,你可以用来作为变量,record域部分的基础。虽然确切的名字可能会有变化,但是大部分这些类型(比如INT)对于大多数编程语言是通用的。因为它们不基于任何别的类型,所以过程语言经常把这些类型称为“基础项”。Primitive类型包括下面的分类:字符、日期/时间、数字、逻辑、ANY。
字符数据类型
EGL提供大量的单字节,双字节和多字节字符的编码类型。下表是所有EGL语言字符类型定义:nt��gt�!78时间、数字、逻辑、ANY。 Primitive | 大小 | 限制 | 数据类型 | EDT支持情况 |
CHAR | 1字节 | CHAR(32767) | 单字节 | 暂不支持 |
DBCHAR | 2字节 | DBCHAR(16383) | 双字节 | 暂不支持 |
MBCHAR | 1或者2字节 | MBCHAR(32767) | MBCS数据。因为第一个字节属于保留设置被认成双字节字符。在单字节里指定长度。 | 暂不支持 |
STRING | 可变化的 | 16,383 字符字符字符 | Unicode (UTF-16编码)字符 | 支持 |
UNICODE | 2字节 | UNICODE(16383) | Unicode (UTF-16编码)字符 | 暂不支持 |
HEX | 4位 | HEX(65534) | 十六进制数字0-9和A-F | 暂不支持 |
其中String类型是目前EDT全面支持的一种,包括Java Generation和JavaScript Generation。其他类型只在语言内核中支持,并未支持目标语言生成。
下例是字符串类型变量的声明
myVarString STRING;
如果需要用到一些特殊的字符的转义符,请参考下表:
转义符 | 字符名 |
\″ | 双引号 |
\\ | 反斜杠 |
\t | 制表符 |
\r | 回车符 |
\f | 换行 |
\n | 换行 |
\b | 回车 |
下面的例子包括一个换行符:var1 String = "This is a string \n that will appear on two lines."
日期/时间 类型
以下是EGL语言支持的日期/时间基本数据类型,目前EDT支持DATE和TIMESTAMP两种类型:
Primitive | 大小 | 数据格式 | EDT支持情况 |
DATE | 8位 | yyyyMMdd | Java/JavaScript |
INTERVAL | 1到21位 | 基于掩码 | Not Support |
TIME | 6位 | HHmmss | Not Support |
TIMESTAMP | 1到20位 | 基于掩码 | Java/JavaScript |
Date类型示例:
date1 date; date1 = "10/11/1983";
TimeStamp类型示例:
ts1 timeStamp?; ts2 timeStamp("yyyyMMddHHmmssffffff"); ts3 timeStamp("yyyyMM"); ts4 timeStamp("HHmmss"); ts5 timeStamp("MMdd"); ts1 = "1988-10-10 08.32.00.123000"; ts2 = "19881010083200123000"; ts2 = "1988-10-10 08.32.00.123000"; ts3 = "198810"; ts3 = "1988-10"; ts4 = "083200"; ts4 = "08.32.00"; ts5 = "0822"; ts5 = "08-22";数字类型
EGL提供大量的数字数据类型。一些数字类型(比如BIGINT或者FLOAT)自动地表示长度和十进制小数点的位置。一些别的数字类型(DECIMAL),你需要指定长度和十进制小数点后的位数,看看下面的例子:
Primitive | 大小 | 制定长度 | 小数点位置 | 数据类型 | EDT支持情况 |
BIGINT | 18位(8字节) | N | N | 二进制 | 支持 |
BIN | 4,9或者18位(2,4或者8字节) | Y | Y | 二进制 | 不支持 |
DECIMAL | 31位 | Y | Y | 压缩十进制字符 | 支持 |
FLOAT | 18位(8字节) | N | Y | 双倍精度浮点 | 支持 |
INT | 9位(4字节) | N | N | 二进制 | 支持 |
MONEY | 31位 | Y | Y | 压缩十进制字符 | 不支持 |
NUM | 31位 | Y | Y | 数字字符(区位十进) | 不支持 |
NUMC | 18位 | Y | Y | 数字字符(区位十进) | 不支持 |
PACF | 18位 | Y | Y | 压缩十进制字符 | 不支持 |
SMALLFLOAT | 9位(4字节) | N | Y | 单倍精度浮点 | 支持 |
SMALLINT | 4位(2字节) | N | N | 二进制 | 支持 |
其中BIN、MONEY、NUM、NUMC、PACF等类型EDT目前不支持。
DECIMAL类型定义较为复杂,以下是相应的示例代码:
d92 decimal(9, 2); d92 = 2147483.47; d92 = -2147483.48; d92 = 1.23456789; //d92 == 1.23 d92 = 1.23456789e2;//d92 == 123.46 d92 = 2.9999e0;//d92 == 3.00 d92 = 2.231e0;//d92 == 2.23
逻辑类型
这里只有单逻辑数据类型BOOLEAN。BOOLEAN类型识别下面的STRING值作为“literals”,大小为1字节:TRUE、YES、FALSE、NO。 布尔类型相对简单,就不做详细介绍。
ANY类型
当你给一个变量赋值ANY, EGL在值运行的时候知道它的类型,而不是创建它的时候。你可以给ANY类型的变量赋值INT,随后,你还可以给这个变量赋值STRING或者HEX。如果你不用操作符as来把一个ANY类型的变量转换,你就不能在数学表达式里使用ANY变量,看下面的例子:
myInt INT = 42; myAny ANY = myInt; myInt = myAny as INT + 38;1.2 Substrings
在任何的上下文中你去访问一个字符变量,你也可以访问一个substring(在变量里一个顺序的字符子集)。例如,如果一个叫myVar的变量,它的值是”ABCD”,你可以访问“BC”,myVar[2:3]第2个和第3个字符。
定义
textVariable [ fromIndex : toIndex ]
textVariable:string类型数据
fromIndex:子串在变量中的第一个字符。在变量中1代表第一个字符。
ToIndex: 子串在变量中最后一个字符
示例代码:
aString string? = "1234567890"; rString string; rString = "abcde"[1 : 1];// rString == a rString = "abcde"[1 : 5];//rString == abcde rString = "abcde"[1 : 10];//抛出异常 rString = aString[1 : 0];//抛出异常1.3 Date/TimeStamp 掩码与格式
基本数据类型中,我们介绍了Date和TimeStamp类型。在定义这两种数据类型中需要用到格式掩码。本节中我们将详细介绍掩码的生成转换机制。
对一个以月为格式的掩码,你可以使用下面的字符:
y: 用数字0到9来代表年份
M: 用数字0到9来代表月份。如果M不是掩码中的第一个字母,最多只允许2个数字
默认掩码是yyyyMM
对一个以秒为格式的掩码,你可以使用下面的字符:
d: 用数字0到9来代表天
H: 用数字0到9来代表小时。如果H不是掩码中的第一个字母,最多只允许2个数字
m: 用数字0到9来代表分。如果m不是掩码中的第一个字母,最多只允许2个数字
s: 用数字0到9来代表秒。如果m不是掩码中的第一个字母,最多只允许2个数字
f: 用数字0到9来代表秒的小数。第一个代表1/10秒,第二个代表1/100秒,如此推下去。如果f是掩码中的第一个字母,最多只允许6个数字
下面的掩码是有效的:
yyyyyyMM
yyyyyy
MM
ddHHmmssffffff
HHmmssff
mmss
Hhmm
下面的掩码是无效的,因为中间的字符丢失了:
ddmmssffffff
HHssff
你可以使用下面任意系统函数转换date/TimeStamp变量到一个string输出
StrLib.formatDate()
StrLib.formatTimestamp()
1.4 空值和空类型
null值从技术的角度讲是访问内存的空区域(在有些程序里称为指针)。某一个变量为可空,你就可以通过在编程过程中测试它是否为空来判断变量当前值是否为所需要的值。如下示例代码中:
if (myCustomer == null) get myCustomer; end通过判断myCustomer变量是否为空来判断是否需要重新获取Customer信息。此时myCustomer变量为可空就会有相应的编码意义。
为了表示一个值变量是nullable,当你声明它的时候,附加一个问号(?)给这个类型的后面。这在大多数的EGL的类型定义是允许的,看下面的例子:
myNullableInt int?;你可以在下面的任何的定义中使用使用nullable的变量:
1.变量和field声明(包括record)
2.函数声明的参数
3.函数的返回值
4.isa 或者as 操作符右边的操作数
例如:
myArrayNullInts INT?[]; Function getCustomer (custNum int?) returns (string?) ... end myCustomer CustomerRecord?;1.5Dictionary类型
Dictionary部分包括一组key和它们相对应的值value。你不需要定义它。你可以在运行的时间增加或者删除key/value项。Dictionary变量按照你输入的key/value保存它们。Dictionary定义如下:
myRef Dictionary{ ID = 5, lastName = "Twain", firstName = "Mark" }; 增加key/value的例子:myRef.age = 30; myRef["Credit"] = 700;
如果你尝试给一个已经存在的key赋值,新值会覆盖老值,看下面的例子,”Twain”被”Clemens”替代:
myRef.lastname = "Clemens";你也可以用赋值来保留数据,看下面的例子:
lastname string age, credit int; lastname = myRef.lastname; age = myRef["age"]; credit = myRef.credit;key/value的值是ANY类型,你在一个Dictionary里可以用不同的类型来存储不同的信息。你还可以包括下面的值:
1.预定义的record或者别的变量
2.常量或者literal
record类型:
Record ExampleRecord x int; end下面的例子把ExampleRecord放到Dictionary里,然后改变原始的值:
myRecord ExampleRecord; // 设置变量值 myRecord.x = 4; myRef Dictionary{theRecord = myRecord}; // 用新值代替老值 myRecord.x = 700; // 访问dictionary的record复制 // 把4赋给testValue. testValue = myRef.theRecord.x;把一个dictionary赋给另一个dictionary,目标的内容取代了源内容,看下面的例子:
myRef Dictionary { age = 30 }; newRef Dictionary { age = 50 }; newRef = myRef; // newRef.age的值是30,判断返回真 if (newRef.age == 30) ; endDictionary属性
Dictionary类型有相应的属性,他和Key/Value的定义方法是一样的,但是具有相应的含义。请看如下例子:
myRef Dictionary{ // properties caseSensitive = no, ordering = none, // key/value ID = 5, lastName = "Twain", firstName = "Mark", age = 30 }; property | value |
caseSensitive | NO(缺省)/YES |
ordering | None(缺省)/byInsertion/byKey |
如果设置caseSensitive=NO,下面的声明是一样的:
age = row.age; age = row.AGE; age = row["aGe"];如果设置为YES,下面的声明是不同的,即使EGL是大小写不敏感的语言:
age = row.age; age = row.AGE; age = row["aGe"];property的值影响getKeys()和getValues()函数。Ordering定义你提取key的顺序:
ByInsertion:根据你插入的顺序
byKey:根据key的顺序
Dictionary类型函数
假设dictionary的名字是row,用下面的方法调用它的函数:
if (row.containsKey(age)) …… end 函数 | 描述 |
result = dictionaryName.containsKey(key) | 是否在dictionary的key里有一个指定的字符串 |
keys = dictionaryName.getKeys() | 返回包括dictionary里key的一个字符串的数组 |
values = dictionaryName.getValues() | 返回dictionary里key/value所有的值 |
dictionaryName.insertAll(sourceDictionary) | 在dictionary之间复制key/value项 |
dictionaryName.removeAll() | 删除所有的key/value |
dictionaryName.removeElement (key) | 删除指定的key/value项 |
result = dictionaryName.size () | 返回dictionary中的key/value的数量 |
1.6动态类型和动态访问
当EGL只能在运行的时间验证变量的上下文访问的时候,这个变量叫做动态(dynamic)类型。实际上,当你访问下面的field的时候你就在执行动态(dynamic)访问:
1.ANY类型变量的field
2.Dictionary的field
3.用[]语法写的不固定的record的field
你也可以在Dicionary中使用[]语法的ANY类型的变量。访问点号的表达式也是动态的,例如:
myRecord.myField下面的例子展示的是动态访问的各种方法:
//定义Dictionary名字叫做point point Dictionary{x=1, y=1}; // 访问key“x”的值 anInt = point["x"]; // 用普通的数据访问语法访问point anInt = point.x ; // 使用变量的值“x”访问X str String = "x"; anInt = point[ str ];2 变量
变量是在EGL应用程序运行时可改变值的项。通用的变量分类如下:
1.用户定义的基于primitive、DataItems或者Record的变量
2.你可以访问的系统变量
类型是一个限制变量值的修饰符。例如INT是一个数字类型,变量包含的是一个整数。另外,你可以把变量分成值类型或者引用类型。一个值类型包括的是数据;一个引用变量类型包括的是数据的地址。在C语言中叫做指针,这两种是不同的。
2.1声明变量和常量
变量和常量用来在EGL程序中存储相应信息,常量在运行时间是不能改变的。在声明变量时,需要明确的进行声明。也许其他语言中可以使用未事先声明的变量,但是EGL中必须提前声明。
变量/常量的声明语法
varName:提取存储的变量的名字
type:要么是一个primitive类型要么是一个用户自定义类型
property:可选的property/value组合
initializer:指定初始化变量的值
以下是示例代码:
myDictionary Dictionary{ empnum=0005, lastName="Twain", firstName="Mark", birthday="021460" }; 对于变量的定义,EGL使用一些特殊的字符作为类型扩展,参照下表: 字符 | 例子 | 说明 |
anInt INT; | 如果这是一个值类型的变量,它是包括数据的存储命名区域 | |
[] | anIntArray INT[]; | 一个整型数组 |
? | aNullableInt INT?; | 一个变量包含一个null类型 |
下面的例子是给一个变量赋值:
customerBalance DECIMAL(9,2) = 1001.22;什么叫常量?常量就是在运行时间不能改变的值。声明一个常量如下:
const copyrStr String = "Copyright 2007 by CompanyB"; const myArray BIN[] = [36, 49, 64]; const myArray02 BIN[][] = [[1,2,3],[5,6,7]];你不能在一个record或者别的复杂的结构中包括一个常量。可以用逗号来间隔多个的常量或者变量之间的声明,例如:
const myString01, myString02 STRING = "INITIAL"; myVar01, myVar02, myVar03 CHAR(5); myRecord01, myRecord02 ExampleRecord;2.2Literals
Literal 是值的直接表示。字符串值用双引号引起。例如:
myInt INT = 9; myDecimal DECIMAL(6,2) = -256.23; myFloat FLOAT = 2.539E7;下表是数字值的类型
数字的个数 | 小数? | 指数 | 类型 |
1-4 | N | N | SMALLINT |
5-9 | N | N | INT |
10-18 | N | N | BIGINT |
X(x>18) | N | N | NUM(X) (不支持) |
X(y小数) | Y | N | DECIMAL(X,Y) |
any | N或者Y | Y | FLOAT |
2.3引用类型变量
如果变量是一个引用类型变量,包括的是在运行时间值的地址。引用类型变量包括下面的类型:Service、Interface、Array、Dictionary、ExternalType、Delegate、ANY、Exception
你不能使用没有初始化的引用类型变量。默认情况下引用类型变量是一个空值,这样当你使用它去访问一个值,field或者函数的时候会引起NullValueException。用new操作符来初始化引用类型变量,看下面的例子:
// new 初始化变量为非空 myDictionary Dictionary = new Dictionary; // 设置一个空的值 myDictionary Dictionary{};2.4Record
record是叫做field数据元素的集合。它类似于Java中的JavaBean或是C中的结构体。用来存储结构化的数据。Field是在record里的变量定义。变量可以是下面的类型:
primitive 、DataItem、Record、Dictionary
下面是一段Record示例代码:
Record CustomerRecord customerNumber INT; customerName String; customerAddr1 String; customerAddr2 String; customerAddr3 String; customerBalance INT; end这个record只是一个模型,你需要声明一个变量:
myCustomer CustomerRecord;EGL使用点号来指向record的field,赋值:
myCustomer.customerBalance = 0;EmbedRecord类型
EGL embed关键字允许你把一个结构record的fields嵌入到另一个结构record,不用创建子结构。
recordType:record定义的名字
下面的例子把所有的address record的fields嵌入到record1:
Record address streetAddress1 String; streetAddress2 String; city String; end Record record1 person String; embed address; end你也可以用embed关键字声明同样的结构record到两个不同的record。关键字不会用嵌入的record的属性覆盖这些record的属性。
2.5数组
EGL支持定值数组、动态数组。在EGL中,数组的第一个元素的数组下标是1。在很多别的语言中,第一个元素的索引是0,这点要注意。
固定长度数组
定值数组包括一对中括号[],里面包括用逗号分隔开的一系列定值(包括别的数组定值)或者表达式(包括数组变量)。每一个定值数组有一个类型,这个类型需要支持数组内所有定值。下表给出了数组文字的例子:
数组文字 | 类型 |
[ 1, 2, 3 ] | SMALLINT[] |
[ ″hi″, ″Mom″ ] | STRING[] |
[ new myRecord, new myRecord ] | myRecord[] |
[ (myPay < 0), (myPay > 0) ] | BOOLEAN[] |
[ [ 1, 2 ], [ 3, 4 ] ] | SMALLINT[][] |
[ 3, ″cow″, [ 4.5, 6.7 ] ] | ANY[] |
动态数组
你可以使用以下类型的动态数组:primitive、DataItems、Records 、Delegates。你可以用’.’操作符来调用数组函数,你也可以在运行时间增加或者减少数组的元素。在你声明一个数组的时候,你不能指定元素的个数。声明一个数组的语法如下面的例子:
myNames STRING; myDataItemArray myDataItem[]; myRecordArray ExampleRecord[]; position int[] = [1,3,5];你可以用定值来初始化元素个数,但是不可以用变量或者常量。象任何别的引用类型变量,你在没有初始化它的时候不能使用数组变量。
myIntArray INT[]; myIntArray[2] = 13; // 引起 NullValueException你可以按照以下代码声明多维数组:
myIntArray INT[][][];动态数组函数
一些定义的动态数组函数是可以用的。这些函数大多数都是操作在一维数组上或者多维数组的第一位,考虑下面的例子:
myIntArray INT[][] = [ [ 1,2], [3,4,5], [6,7,8,9] ] ; // 删除第三个元素的值2:7 myIntArray[3].removeElement(2); // 删除第二个元素:[3,4,5] myIntArray.removeElement(2);下面列出动态数组函数:
appendAll()
arrayName.appendAll(appendArray Array in)把appendArray的所有的元素都复制到arrayName后面,并且根据appendArray的元素大小增加arrayName的数组大小。这2个数组必须是一样的类型
appendElement()
arrayName.appendElement(content ArrayElement in)这个函数在指定的数组后面追加一个元素,数组的大小加1.你追加的内容可是是兼容的类型的表达式
getMaxSize()
arrayName.getMaxSize ( ) returns (INT)这个函数返回一个整数,这个整数是当前数组允许的最大的元素个数。对于多维数组,这个值只应用到第一维。
getSize()
arrayName.getSize ( ) returns (INT)这个函数返回当前在数组里的元素的个数,是一个整数。对于多维数组,这个值只应用到第一维。
insertElement()
arrayName.insertElement (content ArrayElement in, index INT in)在数组的指定位置上插入一个元素。如果指定的位置大于数组当前元素的位置,那么想当于追加一个元素在当前位置的后面,等同于函数appendElement()。数组的索引加1。如果索引小于1或者大于数组大小加1,EGL抛IndexOutOfBoundsException。
removeAll()
arrayName.removeAll( )从内存里删除数组的所有元素,这个数组可以被使用,大小为0
removeElement()
arrayName.removeElement(index INT in)在指定的位置删除一个数组元素,数组的大小减1.如果索引小于1或者大于数组大小,EGL抛IndexOutOfBoundsException。
resize()
arrayName.resize(size INT in)这个函数增加或者减少当前数组指定的大小的大小。如果size的值大于数组允许的最大的值,EGL抛RuntimeException
reSizeAll()
arrayName.reSizeAll(sizes INT[] in)这个函数增加或者减少多维数组每一维的大小。
setElementsEmpty()
arrayName.setElementsEmpty ()这个函数改变每个元素为它们初始化的状态。
3 属性和Stereotype
EGL编程的过程就是把一个通用的格式转换成特定的需求。EGL提供下面的方法:
1.你可以指定大多数部分的属性,在运行时间使用它们改变部分的行为。
2.你可以指定primitive类型作为DataItems并且限定那个类型可以接受的变量的值。
3.你可以使用stereotypes把通用部分适用成特定的目标。
3.1属性
属性是EGL用来把通用部分,变量或者声明编码成指定的信息的名字-值对。EGL内部用不同的方法定义这些属性。
简单属性会对EGL生成目标代码的过程造成影响,他不能在运行时间被动态的改变。一个简单的属性只有一个域(值)。你可以通过用双引号把属性的名字括起来赋给一个合适的值来缩写来设置这个域。看下面的例子,tagName就是一个简单属性:
body Widget { tagName = "tbody" };复杂属性和简单属性唯一的不同就是有多个域。每个名字-值叫做属性域。
隐域是和属性相似的名字-值对,但是在编译的时候对于编译器来说是不可见的。总的来说这些隐域在运行时间可以被改变。EGL自动地增加它们到特定的stereotype部分,例如exception记录。
3.2Stereotype
语法
Part:一个除了DataItem或者Service的Record,Handler,Program或者别的部分
Name:赋给part的名字
Stereotype:自己定制的stereotype的列表
handler Box type RUIWidget … end上面这个例子中RUIWidget就是一个Stereotype。Stereotype类似于父类的概念,他是EGL定义好的一系列类型。规定了一定的行为模式。
异常Stereotype
在大多数情况下,stereotypes和一个特定的技术相关,比如象SQL的数据访问技术或者UI技术。异常的stereotype本身就是这个规则的异常。这个关键字说明了一个记录,你可以在程序代码里处理或者抛出一种特定类型的异常。
当你stereotype一个记录作为异常的时候,EGL预定义两个隐域:
1.messageID STRING;
2.message STRING;
当你创建一个异常记录的时候,你不需要定义它们就可以自动地访问这些域。在很多情况下,这些是你需要的域。如果你想增加附加的域,在记录定义里声明它们,如下面的例子:Record TransactionException type Exception transType INT; end