北京理工大学 20981 陈罡
好了,既然Squirrel简介说得那么好,那么自然要来看看语法的定义啥的有没有
什么特殊的地方。
先把看到的语法要点给记录一下,备查:
(1)变量命名规则,跟c/c++一致,字母数字下划线,首字符不得为数字。
(2)大小写敏感,"Foo"与"foo"是两个不同的变量。
(3)关键字列表:
break case catch class clone continue
const default delegate delete else enum
extends for function if in local
null resume return switch this throw
try typeof while parent yield constructor
vargc vargv instanceof true false static
嗯,除了几个比较特殊以外,其余的确实跟普通的c++非常类似,具体的如果
有与传统c++不一样需要特别记录下来的地方,偶会标记一下的。
(4)支持的运算符:
! != || == && <= => >
+ += - -= / /= * *=
% %= ++ -- <- = & ^
| ~ >> << >>>
(5)其它标记:
{ } [ ] . : :: ' ; " @"
(6)一些常量的表示方法:
34 --->十进制的整数
0xFF00A120 --->十六进制的整数
0753 --->八进制的整数
'a' --->整数,表示字母A所对应的ascii码
1.52 --->浮点数
1.e2 --->科学技术法表示的浮点数
1.e-2 --->也是科学计数法表示的浮点数
"I'm a string" --->常规的字符串
@"I'm a verbatim string" --->另外一种表达形式的字符串
@" I'm a multiline verbatim string
"
这种是可以写多行的字符串表示方法。
(7)注释的方法,跟c/c++一样,既支持块注释“/* xxx */”,也支持行注释“//”。
(8)基本数据类型:integer, float, string, null, table, array,
function, generator,class, instance, bool, thread and userdata。
看上去倒是不多,不过确实比传统的c语言要多了一些吧。
(9)Integer类型,表示32位的整数,使用示例:
local a = 123 // 十进制
local b = 0x0012 // 十六进制
local c = 075 // 八进制
local d = 'w' // 把'w'的ascii码的数值附给d
(10)Float类型,表示32位的浮点数,使用示例:
local a=1.0
local b=0.234
(11)String数据类型,看上去跟c/c++的非常类似,支持格式符:
(\t,\a,\b,\n,\r,\v,\f,\\,\",\',\0,\xhhhh)
字符串这东西,必须要真到真枪的玩玩才知道具体的使用方法。
好,现在插入一段关于运行Squirrel的方法:
首先,下载Squirrel的稳定版,毕竟学习这东西,如果基础就是有问题(教科书就是错的话),
会让我们走不少弯路。偶选择的是Squirrel 2.2号称是稳定版,下载完毕以后;就是编译了,
编译过程非常容易,装好vs2005,找到SQUIRREL2目录下面的一个叫做squirrel.dsw双击它,
然后,会弹出几个工程转换的对话框,一律选择yes即可。然后会看到3个项目,sq, sqstdlib,
以及squirrel。这里squirrel是核心的脚本引擎,这个sqstdlib是为了让脚本引擎可以调用类似
stdio,stdlib,stdmath之类的库而做的一个c/c++的扩展,这主要是为了在pc上使用方便而
编写的;最后就是这个sq项目了,它相当于是一个入口,所有我们编写的squirrel脚本都可以
用它编译执行。
好闲话少叙,一个个地编译吧。可以先右键单击squirrel项目,然后选择“Set as Startup
Project”,然后再在它上面单击右键,选择“Build...”,然后就是编译过程了,因为Squirrel
脚本引擎很小,总共只有6000行代码左右,所以应该很快就可以编译完成。
下一步,就是依葫芦画瓢,依次编译sqstdlib以及sq这两个项目。编译完毕后,就会在SQUIRREL2
目录下面的bin目录里面找到叫做sq.exe的一个程序,它就是支持c语言std库的脚本引擎了。
写几个演示程序玩玩,首先需要明确一点的是,在Squirrel脚本中负责输出的函数是::print()
a) t1.nut,第一步,当然了,hello world!。
进入SQUIRREL2/bin/目录,然后用记事本创建一个文件,另存为t1.nut(Squirrel是英文松鼠的
意思,是松鼠嘛自然喜欢吃坚果了,这也是为什么后缀要起名叫做nut——英语意思是坚果)
输入下面的代码看看:
::print("hello world!\n") ;
然后,在windows的“开始”菜单中选择“运行”,在对话框里面输入“cmd”,结果就可以看到
一个黑色的命令行窗口,在命令行里面进入/SQUIRREL2/BIN目录,然后输入如下命令:
sq -h
应该能够看到如下所示的解释:
Squirrel 2.2.2 stable Copyright (C) 2003-2008 Alberto Demichelis (32 bits)
usage: sq
.
Available options are:
-c compiles the file to bytecode(default output 'out.cnut')
-o specifies output file for the -c option
-c compiles only
-d generates debug infos
-v displays version infos
-h prints help
这些就是那个叫做sq.exe给出来的帮助信息了,果然短小精悍。
我们刚刚的t1.nut就可以试试了,脚本引擎嘛自然可以解释执行了。使用方法是:
sq t1.nut [回车]
应该就可以看到一个叫做hello world!的输出了。
ok这是第一步,看看这个sq.exe还有什么用法。
似乎-c是编译的意思,-o是指定编译以后的文件名,我们来实验一下。
sq -c t1.nut -o t1.cnut [回车]
输出了一个out.cnut,这个很类似于gcc a.c,最后输出一个叫做a.out的东西,看上去似乎
-o t1.cnut这个参数没有起作用。再试一次,删掉out.cnut,然后运行:
sq -c t1.nut [回车]
这次跟上次的结果一样,这就证明了-o t1.cnut没有起作用。
靠,什么意思?明明按照它的help做的啊,好追踪到sq.c的代码中去,记得《深入浅出MFC》
这本书的作者侯杰曾经说过“源码之下了无秘密”,有了代码还怕这东西没法工作?!
单步运行一下,首先把t1.nut拷贝到SQUIRREL2/sq目录下面,然后把sq项目设置成"Startup"
的项目,然后左键单击sq项目,然后在菜单上选择“Project”->“sq Properties”,在弹出
的这个对话框的左侧选择Debugging,然后在“Command Arguments”后面的文本框里面输入:
-c t1.nut -o t1.cnut
然后按确定保存,然后单步运行,然后很快发现这个地方出了问题:
//<> this func is a mess ---> 呵呵,从这行注释上来看,作者确实有自知之明,
知道这个地方写的有问题了。
int getargs(HSQUIRRELVM v,int argc, char* argv[])
{
int i;
int compiles_only = 0;
static SQChar temp[500];
const SQChar *ret=NULL;
char * output = NULL;
int lineinfo=0;
if(argc>1)
{
int arg=1,exitloop=0;
while(arg < argc && !exitloop)
{
if(argv[arg][0]=='-')
{
switch(argv[arg][1])
{
case 'd': //DEBUG(debug infos)
sq_enabledebuginfo(v,1);
break;
case 'c':
compiles_only = 1;
break;
case 'o':
if(arg < argc) {
arg++;
output = argv[arg];
}
break;
case 'v':
PrintVersionInfos();
return _DONE;
case 'h':
PrintVersionInfos();
PrintUsage();
return _DONE;
default:
PrintVersionInfos();
scprintf(_SC("unknown prameter '-%c'\n"),argv[arg][1]);
PrintUsage();
return _DONE;
}
}else break; // <--- 就是在这个地方出的问题了
arg++;
}
...然后下面就是处理编译和文件保存方面的代码了。
在这里发现,如果-c先处理的话,argv到了输入参数是t1.nut以后:
if(argv[arg][0]=='-') { // <---这个地方的'-'判断一定无法通过,于是乎。。。
......
} else break ; // <---就跑到了这个地方了,然后就break,跳出了argv参数的处理模块
// 这么做的直接后果就是-o t1.cnut这两个参数就不会被处理了。
这就解释了为什么刚刚的命令:sq -c t1.nut -o t1.cnut不可以正常工作。
我晕,作者连这么强大的squirrel都能写得出来,怎么这个小bug也不处理一下。
呵呵,可能是太简单了他自己懒得弄,只是加了个FixMe,看谁有心情就处理一下吧。。。
好了既然知道了问题的位置解决起来就方便多了,最简单的方法就是直接改用另外一种
格式输入命令:
sq -o t1.nut -c t1.cnut [回车]
把-o t1.nut放在前面,就不会有这个问题了,毕竟-o t1.cnut的处理代码是这样的:
case 'o':
if(arg < argc) {
arg++; // <--- 这里调整了arg的数组下标
output = argv[arg];
}
break;
呵呵,这次就可以正常工作没有问题了,看来sq.exe是不可以像刚刚那样sq -c xxx -o xxx
来使用的(上面提到的问题)。看来跟gcc这种编译工具的使用上来比较还是有区别的
(很低级的bug啊,汗一个)。看到有一个叫做t1.cnut的文件生成出来了,很兴奋,首先
确认一下,是否可以直接执行。
来,实验一下:sq t1.cnut [回车]
ok没问题,看到hello world了。从这个角度上来看,编译以后的nut脚本文件应该是可以直接
在squirrel的虚拟机上运行,不需要squirrel编译啥的就能直接工作,这一点跟JVM虚拟机很类似
生成中间代码,执行的时候可以直接运行加快运行速度。嗯,很不错的开局。
再来看看这个文件的大小。
t1.nut 26个字节
t1.cnut 日!竟然242个字节,怎么越编译越大啦?!
看来得到比较高的运行效率是有代价的啊,打开看看这个t1.cnut,用十六进制看看到底是些
什么东西那么大。。。
可以看到如下所示的东西(用winhex这种工具就可以看到):
确实是有print在里面,似乎还多了很多乱七八糟的东西在里面,还保存了文件名之类的东西,
或许这是为了以后编译多个nut文件用来做一些区别用得吧。
总之,很郁闷,看来以后真的要用这个引擎做点有意义的事情的话,这个内置的compiler需要
重写,或者用更加“经济”的方法来压缩。毕竟手机上是来不得半点铺张浪费的,要勤俭节约。
乎乎,有点跑题了,总之,算是大概看了一下sq.c这个文件,对于调用squirrel的引擎有了一些
体会。
还需要继续深入下去。。。
回到刚刚的String数据类型,有两种,可以做几个实验玩玩:
t2.nut:
local s1 = "hello, this is wayne speaking\n" ;
local s2 = "hello, this is \"wayne\" speaking\n" ;
local s3 = @"hello, this is wayne speaking\n" ;
::print(s1) ;
::print(s2) ;
::print(s3) ;
嗯,输出的结果如下:
hello, this is wayne speaking
hello, this is "wayne" speaking
hello, this is wayne speaking\n
从这样看来,用双引号处理的字符串,跟c/c++里面的printf的格式化字符串基本上差不多,特殊
字符是需要用反斜杠"\"来处理一下的,而如果在双引号前面加一个“@”,就不一样了,应该是
表示一定要把双引号内的所有字符原原本本地输出出来。例如这个“\n”也被原样输出出来了。
今天进度缓慢啊,只看了一个数据类型,郁闷。。。明天继续