流逝的时光
总有一天我们都会离去 email: zzxhang@gmail.com
posts - 21,comments - 111,trackbacks - 0
   经过这段时间的努力,LuckyScript终于初步具备了我所希望它具有的一些特性,发文庆贺下,虽然总体来说还是个比较简陋的东西,但作为本人本命年的吉祥物,我不想对它再要求太多了,由于还没有经过仔细测试,Bug还比较多,所以暂时不会发布,现在只是把它所已经具有的一些特性介绍下,没什么意外的话,不会再有改动了,以后改进的方向主要是效率方面,说到这里,我得表达下对Lua开发组的崇敬之情,前几天同事发给我一个专业测试网站的报告http://shootout.alioth.debian.org/u32/benchmark.php?test=all&lang=all&box=1,里面列出了世界上各种比较流行的语言的效率测试,看过Lua跟c的效率对比后,自己偷偷测试了下LuckyScript..结果可想而知,很简单的测试,虽然我还没有优化,虽然我用面象对象的方法组织工程,但这绝不该成为程序效率差别人一个数量级的理由,所以今后会把对LuckyScript效率的改进作为重点.

语法基本上与C++保持一致,没太多好说的
一.数据定义
关键字:var,class,func,new
就如你猜想的那样,var用于定义变量,class用于定义类,new 用于创建类,在LuckyScript中变量可以是所有东西,无论是数值,函数还是类,都可以赋给一个变量,除非显式指定需要值复制,不然所有指向一个对象的变量间的赋值都只是传递引用,稍后你会看到这样做的作用,下面的一段LuckyScript代码能很好的告诉你数据定义的方式.
class Test
{
}


func TestFunc()
{

}


func main
{
    var t 
= new Test();
    var array[
3= {2,"str",3};
    var b 
= t;
}


二、宏定义
LuckyScript支持带参数的宏定义,如:

#define add(a,b) \
(a 
+ b)

func Main()
{
    
var sum = add(2,3);
}

三、语句
关键字:if,else,while,switch,case,default,for,break,continue
不要怀疑,跟C++完全一致,用法没有任何区别,恩..也许是有一点区别的,还是以代码说明问题
var k = 0;

for(var i = 0;i < 10;i ++)
{
    
for(var j = 0;j < 1000000;j ++)
    
{
        k 
= k + 1;
    }

}
上面的代码被我用来测试自己脚本跟Lua的效率,循环1000万次加法操作,恩..测试结果是lua花了0.369秒,luckyScript花了8.902秒,so,相差30倍左右..言归正传,在这段代码中,不要认为i跟j的作用域就是在一个for循环内,我可以付责任地告诉你,在函数返回前,它都是有效的,所以如果你想在这个函数内接下来的代码中copy这段代码继续Happy的循环的话,就会得到i跟j重复定义的错误,同理,while跟if也是一样,另外,为了保持代码的整洁,LuckyScript强制规定if,while,for后面必须跟着配对的大括号,同时操作数跟操作符之间必须隔开,也就是说,如果你把i ++换成i++也是会得到一个语法错误滴

四、跟OO有关的一些东西
关键字:class,public,private,protected
1.类定义
在LuckyScript中,你不可以象C++那样定义自己的构造函数,或者说,LuckyScript中根本就没有构造函数的概念,但你仍然可以指定类中变量的初始值,如下面代码所示:
class Test
{
public:
    var a;
    var b;
}
;

main
{
    var t 
= new Test(2,3);
}
2、3分别被赋给了Test中的a、b,赋值的顺序跟定义的顺序保持一致,public,private,protected声明的作用跟你想的一样
2、类嵌套
在一个类中允许包含另一个类作为成员变量,可以多层嵌套
class Test1
{
public:
    var a;
}
;
class Test2
{
public:
    var b 
= Test1();
}
;
class Test3
{
public:
    var c 
= Test2();
}
;

func main()
{
    var t 
= new Test3();

    t.c.b.a 
= 2;//合法
}

3.继承
LuckyScript支持单继承方式,在类成员变量的作用域方面LuckyScript仍然跟C++保持一致,子类可以引用父类的protected成员,但不可以引用父类的private成员,恩..照例贴上一段代码
class Base
{
public:
    var a;
protected:
    var b;
private:
    var c;
}
;

class Test : public Base
{
public:
    func test()
    
{
        a 
= 2;\\合法
        b 
= 2;\\合法
        c 
= 2;\\错误
    }

}
;

4、操作符重载
LuckyScript支持重载操作符,但这种重载类似于函数调用,在表达式中可能不能得到我们期望的结果,如:
class TestObj
{
public:
    func operator 
+ (var otherObj)
    
{
        
var retObj = new TestObj();
        retObj.mVal 
= mVal + otherObj.mVal;

        
return retObj;
    }


    func operator 
* (var otherObj)
    
{
        
var retObj = new TestObj();
        retObj.mVal 
= mVal * otherObj.mVal;

        
return retObj;
    }



private:
    
var mVal;
}
;

func Main()
{
    
var t1 = new TestObj(2);
    
var t2 = new TestObj(3);

    
var t3 = t1 + t2;//相当于t1.operator + (t2),是正确的
    var t4 = t1 * t2 + t3;//相当于t1.operator * (t2.operator + (t3)),这大概不是我们想要的结果
}
在对象参与的表达式中,优先级是从右到左的,跟操作符优先级没关系,你可以认为这是个BUG,但修改它会带来更多BUG,所以我就让它这样了。。有点讽刺的是,主程序对象的操作符重载却是正常的,也可以作为正常的因子加入表达式中。

5、多态
看到这个标题,你可能会觉得有点奇怪,刚刚似乎没看到有virtual之类的关键字啊..你没看错,但在LuckyScript中,这种声明是多余的,让我们考虑下面这样一段代码:

class Dog
{
public:
    func doSomething()
    
{
        print(
"Wang Wang Wang");
        newLine();
    }

}
;

func doSomething(var animal)
 { 
    
 animal.doSomething();
 
}


func main
{
    var d 
= new Dog();
    doSomething(d);
}
在一门号称无类型的语言中,上面的代码是不是看起来很自然,很合理,既然是无类型,那就应该就可以传递任何东西,遗憾的是,虽然似乎很合理,但大多数情况下我们都会得到一个函数参数类型不匹配的错误,这是一个相当令人郁闷的错误(至少在我看来如此),既然无类型,又何来类型不匹配呢?本着为用户服务的精神,我让上面这段代码在LuckyScript中是合法的,既然所有对象都可以传递,那么要实现多态的特性也是轻而易举的了,如:
class Dog
{
public:
    func doSomething()
    
{
        print(
"Wang Wang Wang");
        newLine();
        
return 0;
    }

}
;

class Cat
{
public:
    func doSomething()
    
{
        print(
"Miao Miao Miao");
        newLine();
        
return 0;
    }

}
;

class Value
{
public:
    func doSomething()
    
{
        
return 10;
    }

}
;

class Test
{
public:
    func doSomething(var animal)
    

        value 
= animal.doSomething();
        
        
return value;
    }

private:
    var value;   
}
;

func Main()
{
   var cat 
= new Cat();
   var dog 
= new Dog();
   var value 
= new Value();
   var animal 
= new Test();

   animal.doSomething(cat);
   animal.doSomething(dog);

   print(animal.doSomething(value));
   newLine();
}
得到运行结果:

如你所想的那样,实现这个特性并不像脚本代码本身看起来那么轻松自然,因为在函数调用前,我们都无法知道animal到底是个什么东西,自然也就无从判断animal.doSomething()的合法性,所以,一个很直观的办法就是为每个不同的调用生成一个版本的函数代码,就象模板一样,我就是这样做的,是的,这个方法既不高效也不巧妙,或许你有更好的idea?

五、一些预定义的命令
__Pause命令,当遇到这个命令时,脚本会无条件保留现场并返回,下次再执行的时候会从暂停的地方开始执行。
__Exit命令,跟C的exit一样,会直接结束脚本的运行。

六、与宿主程序的交互
基本思想跟lua差不多,用户可以往脚本添加自己的数据,以栈来互相传递数据,这块还有很多东西在考虑中,所以暂时不写,留待以后补充

PS:LuckyScript是支持垃圾回收的,所以别奇怪为什么有new没有delete
posted on 2009-03-13 18:08 清風 阅读(1903) 评论(7)  编辑 收藏 引用 所属分类: LuckyScript

FeedBack:
# re: LuckyScript目前的样子
2009-03-14 19:11 | 陈梓瀚(vczh)
更好的idea是,运行时查找doSomething。不过我看你之前的指令似乎不可能支持这个……  回复  更多评论
  
# re: LuckyScript目前的样子[未登录]
2009-03-14 22:06 | 清风
@陈梓瀚(vczh)
可否说得详细点?  回复  更多评论
  
# re: LuckyScript目前的样子
2009-03-15 00:46 | 陈梓瀚(vczh)
@清风
如果你不打算提供反射api的话,你可以把程序中的identifier用的字符串都映射到一个整数去。int很大,不怕用不完。然后你就可以在对象里面保留一个int -> function结构了。

譬如说你要运行a.doSomeThing,你的编译器已经实现整理好了,譬如说doSomeThing被分配到的ID是123,于是你可以在a里面查找一个ID是123的名字(这张表也是编译器给的),然后执行之。就变成invoke(a,123,...)了。

至于一个字符串被分配到的ID是什么,这很容易处理的。因为编译期就能决定了。如果你要支持反射api的话,这张表也要留,但是只好同时加一张string -> function表了。  回复  更多评论
  
# re: LuckyScript目前的样子
2009-03-15 00:51 | 陈梓瀚(vczh)
@清风
话说你那种泛化的想法在将动态语言编译进x86的时候是很有用的。  回复  更多评论
  
# re: LuckyScript目前的样子[未登录]
2009-03-15 14:33 | 清风
@陈梓瀚(vczh)
是个办法,不过这样做如果a中并不存在doSomething的话就没办法在编译期就抛给用户一个错误了,而且在运行时查找,效率上恐怕也是个问题  回复  更多评论
  
# re: LuckyScript目前的样子
2009-03-19 00:06 | 陈梓瀚(vczh)
@清风
没人会用脚本去做计算密集型任务的,你看javascript不也够用了。  回复  更多评论
  
# re: LuckyScript目前的样子[未登录]
2009-03-19 09:20 | 清风
@陈梓瀚(vczh)
是的,其实对效率的追求源自本人的偏好,有人说是偏执  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理