这篇文章是Tiger Dong今年3月份写的,较完整的比较了CSE与Python之间异同,转贴出来供大家参考。
Python是一门优秀的脚本语言,CSE无疑也是一门优秀语言,要不,本文不会将它们相提并论的作比较。
事实上,要准确比确两门语言的特性差异不是一件容易的事。因为,既然一门语言能够存在,就有她存在的理由,她总有一些特殊的应用定位。由于定位不同,导致语言比较很难在对等的层面上展开,有谁尝试比较汇编语言与C#的异同吗?----所以,本文尝试比较Python与CSE,首先因为这两门语言的应用范围与表现特征有较高的重合度,可比性有了,其次,从两者的发展渊源来看,CSE是新生语言,从Python继承较多的风格,当然也摈弃了不少东西,比较这两者,不难管窥现今语言的承递脉络与发展趋势。
CSE从推出第一个版本到现在还不到一年,打个比方,她还是一个婴儿,而Pyhton有数十年的发展历史,已经是个中年人了。两者在语言成熟度、应用广泛度、拥有资源丰富程度上,都存在很大差距,这是显而易见,不是我们比较的重点,本文更多的从核心语法语义上对比,也即重点比较两门语言的内涵,而非外延,新生语言的外延若假以时日,外部条件也具备了,总能很快抺平与他人差距的。而内涵相对稳定,如果核心语法有了很大改变,那可能演生出一门新语言,语言比较所假定的基础不存在了。
现在比较CSE与Python就可能存在这个风险,还不是因为CSE尚处于快速发展中,更重要的是,CSE将适应语言发展变化当作她固有的一个特性,她好比是一部强大的进化机器,核心语法语义演化不只被认为可能,而且被认为是应该,或者说是必然。本人时隔3个月再次试用CSE新版本就大吃一惊,prop属性、yield产生式、测试脚本自动生成等令人惊异的特性好似一夜之间突然冒出来的。所以,本文不只比较CSE与Python的语法差异(否则一两年后这篇文章就成垃圾了),也比较两者的行事风格、运作模式、商业定位等方面的异同,有赖于与CSE缔造者wayne chan多年朋友关系,本文获得一些独家“内幕消息”,希望能给大家提供一点帮助。
CSE具备两面特性,她的第一面是脚本语言,另一面是编译语言,某种程度上说,前者是软语言,后者是硬语言,前者更富动态变化,或者说具备有机体特征,而后者是命令式架构,缺失一些变化,但具有更强的能力与更高的运行性能。拿wayne chan本人的话来说,他希望CSE是狮身人面像,在古埃及,狮身人面像被认为是智慧与权力的象权,CSE具备LISP语言特征,展现“拥有人的智慧”一面,同时还具备命令式语言特征,展现“如狮子般敏捷与强大”的另一面。
这种两面性是CSE区别于其它语言的本质特征,可以说,CSE相比于Python,多数差异都源于此,把握住这一点就能很好理解什么是CSE了。CSE的“人面”已揭开面纱了,遗憾的是,“狮身”还无缘谋面,CSE编译器还没开发,我们无从揣测其特性。
CSE的脚本表述方式与Python比较接近,函数、类、模块的组织方式很类似,一些命名风格,如系统保留命名__init__、__del__、__str__等都是一致。两者的外在脚本表现较接近,但内在的体系架构却存在天壤之别,可以说,CSE相比Python,外在表述的创新远逊于其内核组织机制的创新,外部创新甚至有点归于平淡。另外,试用过CSE后不难感觉她的行事风格与Python还是有很大差别的。
比如,CSE的GUI界面是与生俱来的,第一个CSE版本发布时,IDE(集成开发界面)就已经比较完整,CSE的扩展模块还很少,但单元测试框架、跨进程MVC布署框架却已具备了,CSE的IDE还完整的支持单步调试功能。Python是另一种风格,先有命令行界面,集成IDE是后加的,而且其风格相对于内核比较另类,随正式版本发布的Python GUI(即IDLE)是用TCL/TK做的。这些差异,我们还不能简单的将之归结于初创者个人风格的差别,更多的,我们应理解为时代的差异,有人将前者称为Windows风格,使用风格倾向于终端用户,而后者称为Unix风格,使用风格倾向于开发者。毕竟时代变了,Python产生于上世纪80年代,那是Unix与Dos盛行的年代,CSE产生于21世纪初,是一个强调个性与客户体验的时代。
再如,CSE还专注于为软件研发过程提供配套服务,而不只是提供一个执行器,Python软件缺少这种工程化思路。CSE推出第一个版本就宣称支持第4代白盒测试方法(4GWM,The 4th Generate White-box-testing Methodology),CSE后续版本还将内嵌XML解析器,注意,不是简单扩展出一个能解析XML的模块,而是直接使用CSE内核封一门可解析XML的语言,本质上说,CSE语言也是CSE内核的一种实例化实现,有点类似于.NET平台,CLR是核心平台,C#、VB.NET等是都是它的实例化实现。当CSE体系有了CSE执行器与XML执行器这两根支柱后,在IT工程服务方面必能大展身手,.Net号称是基于XML信息集成的系统,它的XML支持仍属外在扩展,当然这也没有太多坏处,只是某些内嵌紧耦合的应用无法支持,比方,CSE集成XML后,实际模糊了代码、文档、规格化数据三者之间的界限,无论对代码文档化,或文档代码化,还是UML表述及MDA转换支持,都是非常便利的。所以,仅此一点而论,CSE比.NET具有更高的起点,.NET使用XML保证了静态集成性,而CSE除静态集成性,还拥有更多的动态(或在线)集成特性。
以上两点是本人初次接触CSE就有深刻体会的,除此之外,我们形而上的概括,CSE比Python更加FP(Functional Programming),Python的语法分析与字节码机制中尚保留强烈的命令式语言特征,CSE语法分析及解释执行增加不少生命气息(这个提法较抽象,不要紧,下文还有叙述),操作符与命令字(或称保留字)都是在线可定义的,不是语法分析固定规定的。
由于这个原因,CSE语法表达显得非常灵活,当然,过于灵活不见得都是好事,一些不合规范的CSE脚本,解释器并不认为它是错的,这导致一些错误引入后不容易被发现。不合语法规则的脚本,许多时候CSE解释器只是忽略它,或者直接返回一个缺省值,并不报错。Python具备更多命令式风格,比较明确的限定哪些语句是合法的,哪些非法,能够检测的语法错误也就更多。比如下面2条CSE语句:
raise EMyError, 'Here raise error.';
raise(EMyError, 'Here raise error.');
这两个语句都是合法的,第一条CSE把它看成两个表达式,等效于“raise(EMyError),'Here raise error.';”,第二条CSE能正确解析成raise调用,很显然,这两个语句的执行效果不同的。
CSE核心语法过于灵活,相比其它语言更容易误用,比如:
iValue = 0;
if iValue == nil:
print('OK');
end;
>>> executing script...
ESyntaxError:Error at line 586, LNotator(if) not closed.
上述报错多少有点让人摸不到头脑,分析一下,错误发生在nil误用了,因为nil注册为变元操作,变元操作可以有如下书写格式:
>>> nil # same to nil()
0
>>> nil()
0
>>> nil(0) # careful, nil not same to 0, but "nil == 0" is true
0
>>> nil: 0; end;
0
因为变元操作可以写成以“:”开头、“end”结尾的语句块,所以前面报错是因为nil吃掉其后从“:”到“end”的语句块,使得if语句无法正常结束。上面例子若按CSE推荐格式,应写成:
iValue = 0;
if nil(iValue): # or, you can write "if nil == iValue:" as you like, but it is not same meaning
print('OK');
end;
>>> executing script...
0
与nil类似用法的还有dummy关键字。
CSE与Python一样,都是一门解释执行、动态交互、面向对象的编程语言,集合了模块化结构、异常处理、动态类型、高度抽象动态数据类型、类与类继承等特性。下面我们重点比较两者异性,相同之处只稍微提及,不展开说明。我们分如下五个方面展开叙述:
² 编码风格
² 数据类型
² 函数定义
² 类与模块
² 扩展接口
先讲编码风格,两者的指令风格、保留字设置比较近似,他们的注释风格完全相同,都以“#”作为注释起始标记。编码风格差异主要表现在以下几个方面:
1. 行缩进,Python采用行缩进风格,CSE无此要求
Python代码:
if isTrue:
print 'OK'
else:
print 'fail'
CSE代码:
if isTrue: print('OK'); end else print('fail');
换行符及每行缩进字符在Python是有语法含义的,在CSE中不具语法含义,换行符在Python中作为语句自然结束标记,CSE中各语句或表达式必须以逗号或分号分隔。两者均以“:”作为语句块起始标记,但Python以行缩进指示语块何处结束,CSE必须明确用end关键字指示。
2. 前后缀风格
Python使用"_"前缀指示变量或函数是否私有,CSE除了使用前缀指示变量类型,还使用“_”后缀指示变量或函数是否私有。之所以要用前缀标识数据类型,是因为CSE要兼容解释执行与编译执行两种运行模式。
3. 构词法
Python与CSE均可用单引号或双引号构造字串,不同的是,Python使用单引号或双引号构造字串不可以跨行,CSE可以跨行。Python要构造跨行字串应使用longstring标记,即起止为连续3个单引号(或双引号)。
Python支持迭代方式构造List数据,比如:[i for i in range(5)],CSE不支持。
另外,CSE中的分号(;)与冒号(:)只是分隔符,语法含义与“,”等同。比如:Python构造字典数据“{key1:value1,key2:value2}”,CSE也支持这种书写格式,另外还可以写成:“{key1,value1,key2,value2}”,再如:
tt = (1,2,3);
print(tt);
将上面两行CSE脚本的逗号与分号互换,写成如下样子也是可以的:
tt = (1;2;3), print(tt),
接下来我们看看CSE与Python的数据类型差异,CSE中的int、float、string、buff、dict类型与python中相关数据类型一一对应,其中Python中的list在CSE中改叫buff了,个人揣测:“l”前缀比较难看,容易与数字1弄混,CSE干脆改称list为buff,用“b”作前缀了。CSE与Python主要数据类型差异如下:
1. CSE放弃Python中的tuple类型,而增加了Tier类型
Python中的tuple与buff都能保存任意类型的数据,两者功能重复了,至少表面上如此(实际上tuple在Python中还来表达immutable数据、函数调用参数等)。CSE认为tuple类型并非必须,而且,用户面对tuple与list,到底选择哪一个常有困扰,这个类型毫无悬念的被喀嚓掉了。
同时,Python中的string实际上是字符数组,list实际上是任意类型的数组,int数据最常用,却没有int数组,只能用list去构造。CSE认为这是不充分的,尤其在增加编译支持后,应尽量用int数组代替list数据,int数组直译为数据块,无论加快运行速度还是节约内存空间,都是很有益的,所以CSE语言新增设了Tier类型。
2. CSE的WideInt对应Python的long,但CSE的WideInt宽度限定于int的2倍,而Python的long是不限宽度的
3. CSE新增了ID与Notator数据类型
一个变量名或函数名在表达式中计算时,它是变量或函数,但它在描述一种指示体时,是ID类型,ID被计算1次就是变量或函数,或者这么认为,ID是“域名查找”计算0次时的值,而变量或函数是“域名查找”计算1次的值。
CSE还使用Notator类型表达操作运算,比如“3+5”被描述成<+:3,5>,“print(3+5)”被描述成:
Notator与ID反映了程序代码的静态特性,把常见的表达式也描述成一种数据,所CSE的表达式是可以自由传递的。Python也提供类似机制,CodeType就是这种运行代码的数据形式表达,不同的是,Python封装这种“执行体数据”仅让用户可以从中读取一些简单信息,并没有很好的抽象其组成要素并开放出去,即,Python的CodeType还不是完整意义上的可读写的数据类型。
有了ID与Notator类型,CSE更象一个可以自我成长的有机体了。下面举一个例子,先定义TRemoteModule:
uses:
'duality.cse';
'bases.cse';
'rpc.cse';
end;
class TRemoteModule:
sPeer_ := '';
iWait_ := 5000; # 5 seconds
func __init__(me,sPeer,iWait=5000):
me.sPeer_ = sPeer;
me.iWait_ = iWait;
end;
func __get__:
declare(me,$var);
if str($var)[-1] == '_':
setRet(me.$var@1);
end else setRet(rpc.evalEx(local.(bArg_[0]),[$var],
me.iWait_,me.sPeer_));
end;
func __set__:
declare(me,$var,value);
setRet(value);
if str($var)[-1] == '_':
me.$var@1 = value;
end else rpc.evalEx(local.(bArg_[0])=bArg_[1],[$var,value],
me.iWait_,me.sPeer_);
end;
end;
TRemoteModule用于将本机其它CSE进程映射为本程序的一个类对象。比如当前程注册RPC端口为CseWin,启动本机另一个进程,注册RPC端口为CseClient,假定CseClient程序已定义一个变量sValue,其值为“I am at CseClient.”,之后我们在当前CseWin程序中远读写CseClient中变量sValue:
>>> CseClient = TRemoteModule('CseClient')
>>> CseClient.sValue
I am at CseClient.
>>> CseClient.sValue = 'Yes, I know.'
Yes, I know.
>>> CseClient.sValue # the value should changed
Yes, I know.
这个例子中,读写对端变量的表达式很方便的透传到另一个程序执行了,而且,应用接口封装可以不留痕迹,就象读写远程变量就像操作本地的变量一样。
4. CSE未支持unicode字串
准确来讲,CSE内核未支持针对unicode字串的相关操作。因为unicode直接面向应用,CSE仅将它理解为字串的特定应用,比如取字串长度调用函数“len(s)”,如果支持unicode或许应增加一个函数“uniStrLen(s)”,应该在扩展库中实现这些定义。Python则是另一思路,把unicode支持合到内核去了。基于相同理由,CSE也没象Python那样支持复数类型。
从中我们不难看出,CSE与Python遵循了两种行事风格,CSE尽力维持精简的内核,把内核当成DNA,然后在外围将DNA组装成细胞,根据不同功能制造不同细胞,比方能运载氧气的红细脑,能抵御外敌的白细胞,具有记忆功能的脑细胞等,最后才组合各类细胞制造有机体。所以,CSE内核之上可以封装新语言,不只形成CSE,完全可以仿真C、Python等语言。
Python内核处于更高一个层面,它已经是细胞了,库模块type.py记录了Python内嵌的数据类型,除了int、float、string、tuple、list、dict等基本类型外,还有Unicode、函数、类、类实例、类方法、lambda对象、文件对象、产生式等30多种类型,这已经是种类齐全的细胞库了。
5. Python用中括号([])操作字典,CSE用花括号({})操作字典,如:
Python字典操作:
>>> dd = {1:'One',2:'Two'}
>>> dd[2]
'One'
>>> dd[3] = 'Three'
CSE字典操作:
>>> dd = {1:'One',2:'Two'}
{1:'One',2:'Two'}
>>> dd{2}
One
>>> dd{3} = 'Three'
Three
>>> dd{4:'Unknow'} # if key(4) not exist, return default value('Unknow')
Unknow
CSE的函数定义要比Python复杂,因为CSE函数还同时承担Iter迭代功能,Python的迭代器是后期版本追加的,如果算上迭代功能,CSE函数定义附加的规则与Python相当。这两者都支持缺省参数与变长参数,他们主要差别如下:
1. 调用CSE固定格式函数时,可用“:=”赋值指示函数返回,Python无类似功能
该功能可心让代码书写更加紧凑,比如:
s = 'Coverage:%s, rate:%d';
strJoin(s,',Test passed.'); # return new string length
print(s % ['LICC',95]);
可以改写为:
print(strJoin(s := 'Coverage:%s, rate:%d',',Test passed.') % ['LICC',95]);
2. Python支持Documentation Strings,CSE无此功能
Python中Documentation Strings常用于函数接口提示,如:
def my_function():
"""Do nothing, but document it."""
pass
>>> print my_function.__doc__
Do nothing, but document it.
3. CSE的IterScript函数可以传递表达式的原始数据,Python无此功能
比如:
func showVar:
declare($var);
print($var, ":", $var @ -1);
end;
>>> iValue = 55;
55
>>> showVar(iValue)
iValue : 55
4. 产生式定义
CSE与Python的产生式都按无堆栈计算方式实现,两者都以yield与return指定产生值,用法也相同,两者都支持在异常处理语句中(try..except..finally)返回产生值。两者也都支持在for语句及迭代器对象(CSE是TIter类,Python中是generator对象)中调用产生函数。
CSE中函数未尾的yield并非最后一次返回,而在python中是最后一次返回,如CSE中:
func test:
declare(i);
yield i;
i = i + 1;
setRet(i);
yield i + 1;
end;
>>> for i in test(3): print(i); end;
3
5
4
在Python中:
def test(i):
yield i
i = i + 1
yield i + 1
end;
>>> for i in test(3): print(i)
3
5
在for语句之外取产生值的书写方式两者有差异,在CSE中:
>>> test(3)
[3,5,4]
>>> IterObj = TIter(test,3)
>>> IterObj.next()
3
>>> IterObj.next()
5
>>> IterObj.next()
4
>>> assert( dummy(IterObj.next()) )
1
CSE中以TIter类实例包装迭代器,而Python采用一次调用赋值得到迭代器,CSE中一次调用产生式函数得到产生值列表,在Python中要用list函数调用得到产生值列表。另外,CSE的迭代器对象在迭代调用结束后,再取产生值得到的始终是dummy,Python在迭代结束后再取产生值引发StopIteration异常。如:
>>> list(test(3))
[3,5]
>>> IterObj = test(3)
>>> IterObj.next()
3
>>> IterObj.next()
5
>>> IterObj.next()
Traceback (most recent call last):
File "
StopIteration
5. 操作符与保留字定义
在CSE中定义一个函数后,可以将该函数注册为一元、二元或多元操作,也可以注册该函数为编程语言的保留字(如if、else、return等),Python没有这种动态定义新语法的功能。我们取duality.cse中的代码作说明:
func `%`(v1,v2):
iType1 = type(v1);
if equal(iType1,iIntType):
setRet(intMod(v1,v2));
end else if equal(iType1,iStrType):
setRet(strFormat(v1,v2));
end else raise(ETypeError,'Type mismatch in operator:%');
end;
registDuality('%',65);
这里按常规语法义定义一个取余函数,再将“%”注册为二元操作符,其优先级为65。然后,下面语句就可以正常执行了:
>>> 5 % 3
2
>>> 'Coverage:%s, rate:%d' % ['LICC',95]
Coverage:LICC, rate:95
定义一元操作符,如:
>>> registUnary('print',1)
0
>>> print 'Coverage:%s, rate:%d' % ['LICC',95]
Coverage:LICC, rate:95
0
上面将print注册为一元操作,设为优先级为1,因print函数已存在,这里不须重复定义它。之后,我们就可以象Python的print语句一样使用了,不必非得按函数方式。
定义Notator命令字,如:
>>> registVariety('lambda',0)
0
>>> i1 = 3; i2 = 4
4
>>> lambda: i1,i2; (i1 < 3) or i2; end
4
前面例子我们演示的lambda是普通函数调用,这里,将lambda注册为变元操作符后,我们既可以象普通函数一样调用它,也可以用类似if、else、while等句式,即,以“:”开启语句块、以end结束语句块的方式调用它,lambda成为编程语言的关键字了。
介绍过函数定义的差别后,我们再来分析两门语言在类与模块方面的差异。尽管Python支持类与类继承,但对完整的类封装概念来说,是不够完整的。CSE尝试提供完整的类封装与继承,对比这两者,除了Python支持多重继承,CSE只支持单继承外,Python在类特性可以说只是CSE的子集,下面我们罗列主要差异:
1. CSE解决了Python中“可见性倒置”问题。
如下Python代码:
class T1:
def getName(self):
return 'George'
def test(self):
return self.getName() + ' is wonderful.'
class T2(T1):
def getName(self):
return 'Peter'
def show(self):
print self.test()
>>> v1 = T1()
>>> v1.test()
'George is wonderful.'
>>> v2 = T2()
>>> v2.show()
'Peter is wonderful.'
所谓可见性倒置,是指基类中函数定义被继承类下同名函数定义覆盖,如上例,基类T1下的方法test调用后打印“George is wonderful.”,该方法被T2继承后再调用,打印结果却不幸被篡改成“Peter is wonderful.”。也就是说,设计T1的test函数时,设计师并不假定他当前调用T1.getName方法会发生意外,而事实上T2类定义时因不小心因同名将基类中函数屏蔽了。
可见性倒置破坏了类对象封装性,基类已固化的函数不应看到(或调用)继承类中的函数。如果程序逻辑需要这么做,那是回调函数的设计思路,规划基类时就应预留接口了,比如:
class T1:
def __init__(self):
self.getName = self.getBaseName;
def getBaseName(self):
return 'George'
def test(self):
return self.getName() + ' is wonderful.'
class T2(T1):
def __init__(self):
self.getName = self.getNewName;
def getNewName(self):
return 'Peter'
def show(self):
print self.test()
CSE的类方法域名查找总与定义它的class类相关联,解决了这个问题。
2. CSE支持可持久化的类属性,Python无此内容
CSE提供一种函数化的属性操作,类似于Pascal中的“property xxx read getXX write setXX”,如下:
class TT:
iValue_ = 0;
sValue_ = 'example';
prop iValue(me,value=nil):
if nil(iValue):
setRet(me.iValue_);
end else:
me.iValue_ = value;
setRet(value);
end;
end;
prop sValue(me):
setRet(me.sValue_);
end;
end;
>>> v = TT()
>>> v.iValue
0
>>> v.iValue = 3
3
>>> v.sValue
example
>>> v.sValue = 'abc' # TT.sValue is readonly
EArgsError:sValue expect 1 arguments, but 2 passed.
把类属性定义成函数调用方式,可以方便的为属性存取过程添加特定控制,同时,也便于操作接口变得持久,维护的需求可反映在prop函数定义上,接口能一直维持稳定,JAVA持久化技术也采用类似方案,JAVA持久化类包含特定的getXX与setXX方法,用于属性读写控制。
从上面例子还看出,CSE的prop定义可用于只读属性控制。
3. CSE提供protected方法继承,Python未支持,如:
class T1:
intValue_ := 95;
strValue_ := '';
prop showMsg_(me,sName):
print('%s got score:%d' % [sName,me.intValue_]);
end;
prop sValue_(me):
setRet(me.strValue_);
end;
end;
class T2(T1):
func test(me):
me.showMsg_('George');
end;
end;
>>> v = T2()
>>> v.test()
George got score:95
>>> v.showMsg_('george') # should raise error
EAttrError:Attribute not exist:showMsg_
上面showMsg_函数在T1及其继承类T2类定义中可见,但在外部不可见。大家或许会问,私有命名的prop函数在继承类可见,是不是破坏了封装原则,如上例sValue_希望在继承类不可见,遇到这种情况只需将prop函数改成常规函数,丝毫不影响其功能实现。
4. CSE解决了Python中保留方法__get__、__set__嵌套循环的问题
拿前面TRemoteModule的例子,__get__函数定义如下:
class TRemoteModule:
sPeer_ := '';
iWait_ := 5000; # 5 seconds
func __init__(me,sPeer,iWait=5000):
me.sPeer_ = sPeer;
me.iWait_ = iWait;
end;
func __get__:
declare(me,$var);
s = str($var)
if s == 'sPeer_':
setRet(me.sPeer_);
end else if s == 'iWait_':
setRet(me.iWait_);
end else setRet(rpc.evalEx(local.(bArg_[0]),[$var],
me.iWait_,me.sPeer_));
end;
end;
__get__函数截获TRemoteModule类对象取成员的操作,但如果__get__函数中也有取自身成员的操作呢?如上面脚本中,“me.sPeer_”与“me.iValue_”属于这种情况。是不是再次调用__get__函数呢?若是,就进入无限递归了,Python是按无限递归处理的,CSE附加特别控制,__get__函数尚在运行中是不会重复进入__get__递归调用的。
Python的__set__调用也存在类似问题,CSE规避了此情况。
5. 模块与类之间的关系
Python中的模块与类是按两套机制实现的,两者之间不能自由转换,CSE中的类与模块按同一机制实现,如:
class TT:
ValueInTT = 100;
func testFunc():
print('function testFunc() in TT');
end;
end;
>>> uses TT
{ValueInTT:100,testFunc:
>>> testFunc()
function testFunc() in TT
一个类定义可以改用作模块,这一特性使CSE具备更优良的可组合特性,比方打包编译,多个脚本模块可以无歧义的并成成一个模块,原模块定义归并到新模块的一个类定义下。
最后,我们再介绍CSE与Python的扩展接口差异。CSE也像Python一样,提供多种语言扩展接口,既可以用C/C++实现API扩展,也可以用Delphi或其它高级语言做扩展(CSE有计划要支持C#与VBA的接口扩展),CSE与Python都既可以将扩展命令嵌入到脚本系统中使用,也可以将脚本系统内嵌到其它由C/C++/Delphi等开发的系统中。总体而言,CSE的可扩展性要优于Python,表现在如下方面:
1. CSE扩展函数中多线程处理比Python要简单
CSE与Python都使用全局锁保证同一时刻仅一个脚本线程在执行,比如在C语言程序开发中,创建一个新线程要调用脚本解释器,Python要求初始化该解释器的线程控制块,比较复杂。而在CSE,只须调用一个API函数,如:
Cse_lock(1);
// do something call script system
Cse_lock(-1);
在这两个Cse_lock之间的语句运算CSE脚本都是线程安全的。CSE释放解释器控制权也用到这个API,比如:
i = Cse_leave();
// do something out of script system
Cse_lock(i);
Cse_leave用于释放当前线程对脚本解释器的占用,Cse_lock用于重新获得对解释器的控制权。
2. 使用C/C++扩展一个Python内嵌模块非常复杂,而CSE简单多了
这是两门语言采用了不同机制决定的,Python抽象出不少操作在基础类型定义(PyTypeObject)必须处理,而且Python的模块结构与类结构是两套机制,用户要深入了解其类型格式才能胜任扩展工作。CSE的类与模块使用同一套机制,而且都以字典类型保存,定义一个类对象并不附加特别类型结构。所以,在CSE中用编译语言扩展一个模块非常容易,根据实际经验估算,开发一个规模不大的扩展模块,在CSE中要比Python中快出四、五倍。
举个例子,如下脚本类定义:
class TTest:
func test(me):
print('OK');
end;
end;
若改用Delphi定义它,框架代码如下:
function TTest_test(me:PCseObj):integer; stdcall;
begin
// to do: define TTest.test
end;
Class_TTest := Cse_NewClass('TTest',CseNilValue); // class TTest: end;
Cse_RegistApi(Class_TTest,'test(me)',@TTest_test,ctFrmtStdcall);
再把TTest类定义放到某模块下(比如MyModule.csb):
MyModule := Cse_NewClass('MyModule.csb',CseNilValue);
Cse_SetRefSpace(Class_TTest,CseBltinModule); // uses '__bltin__.csb'
key := Class_TTest.entry^[1].value;
CseDict_set(_CompilerClass,key,Class_TTest,key.count);
大家感觉一下,是不是很简单。
到这里,CSE与Python核心语法的主要差异已介绍完毕,需要强调的是,CSE相对Python,对语言要素的改良多过创新,除了类封装与继承有较多改进外,其余基本上是对Python做优化。毕竞Python产生于上世纪80年代,其体系架构成形后就很难推倒重来了,CSE没有历史负担,可以站在更高起点大胆往前走出一步。实际上,就编程语言的表现形式而言,很难再有多少创新了,C#是一门伟大的语言,也只是将现有数种语言的优点揉合一起而已。
CSE最大亮点应在内核架构上,从词法分析到语法分析,再到语义分析,在常规编程语言中是不可分隔的整体,牵一发而动全身,很少有语言新增关键字而不重写语法分析的。毫无疑问CSE做到了,其内核具有化腐朽为神奇的力量----所有CSE控制结构,包括if、else、while、for、try、except等,都是外部扩展的API,立体式的语法分析过程被改造成平面式条条块块,各个语法要素可以象积木一样组装。大家不妨阅读CseCompiler的源码,所有控制语句都在BltinFlow.pas中定义,区区数百行代码就搞掂了。
CSE似乎理顺了“鸡生蛋,蛋生鸡”自我进化的最佳路径,常规编程语言是看管着长大的,在外力作用下,可以推倒重来。但如果改成自我进化,就是另一种要求了,首要一点是:进化的演变过程不能中断。1.5亿年前的始祖鸟只能在树间跳跃滑翔,而现在的北极燕鸥来回迁徙一次,飞行4万公里,能绕地球一周了。进化的力量如此强大,但我们不能因为始祖鸟的羽翼比较原始,把它杀了重造一个,或者,把蛋拿过来改一改让它变凤凰。破坏性重构不是有机体的生存之道,CSE最大的创新主是:她很好的解构了编程基本要素,把语言重构需求限定在局部范围。
Python从2.1版本开始完全采用与GPL相兼容的开源协议,这是近乎公共域的授权许可,允许免费修改、免费二次发布,而且对商业应用比较友好。CSE除解释器内核(CseKernel.csd)外采用MPL授权,也允许自由获取源码,允许免费修改、免费二次发布,但要求修改后的代码版权归CSE共享软件基金会,CSE的授权形式对商业应用是友好的,甚至,CSF基金组织鼓励商业机构使用CSE。
MPL中对“发布”的定义是“以源代码方式发布的文件”,意味着MPL允许一个企业在自己现有的代码库上追加一个接口,以这个接口作阻隔引入开源代码,除了接口程序的源代码要以MPL形式对外授权外,产品自身代码就不必按MPL要求强制对外开放。所以,商业应用CSE软件是安全的,即便是CSE内核,由《CSF软件最终用户许可协议(ELUA)》规定,内核模块是免费使用、可自由传播的,因为CseKernel.csd在REDIST列表之中。
Python的版权形式比CSE要宽松,CSE采取多种授权的策略,为开发者保留了更多权益。其中,CSE内核以共享软件形式提供,用户得不到源码,按作者的话来说,这是为了维护CSE核心语义纯粹性,我们不妨解读为:作者保障大家自由使用CSE的权利,但不希望商业机构重写内核,掺和搅局。
业界开源组织采用MPL授权的,许多情况是因为还没想清楚以后如何发展,CSE似乎有此考虑,她没打算从CSE语言(包括内核)获得收益,但不排除要从CSE的附属产品(比方相关工具软件)获取回报。另外,CSF全称是“CSE Shareware Foundation”,而不是“CSE Opensource Foundation”,也喻示着CSF组织对依赖社会捐赠的模式并不抱十足信心,一方面因为国内开源氛围没法乐观,另一方面,CSE是一个庞大的系统工程,没有充足资金支持是很难发展的。
与Python不同,Python完全依赖社会捐赠,CSE走的是一条“具有中国特色”的开源之路,目前CSE扩展模块还很匮乏,CSF并不急着把应用库丰富起来,而是采取稳扎稳打的策略,在良好规划系统框架的前提下,由实实在在的商业应用带动内容建设,用到什么就先开发什么。WAYNE工作室作为CSE发展策略的载体,不只自己开发一些基于CSE的应用,也协助业界开发上层应用,借此方式逐步丰富CSE的内涵。