如何使用c语言写脚本解释器,脚本解释器编写思路

[背景]

平时喜欢写游戏脚本,经常用按键精灵,在物理机上用还是很方便好用的,但有时多开挂机,游戏又有驱动保护时,就得在虚拟机里面挂。这时按键的资源占用大问题就很突出。在VM里面运行按键经常是超慢镜头,用他强大的脚本编辑器修改脚本时更是一场噩梦。所以萌生了自己写个功能简单的脚本解释器的想法。在写这个解释器的时候有点小经验,在这和大家分享(或者说共同研究研究)。

事先声明,由于我写程序水平极低,所以解释器只有极少极简单的功能。另外下面只写思路不贴代码。

[正文]

首先是脚本语句怎么区分问题。我比较喜欢C语言的分号结尾,这种表示方式很容易从整个脚本里面拆分出一个个语句。

接下来就是怎么表示一个语句。这里我的做法比较简陋,以一个命令开头,比如

“press",

后面跟上用逗号作间隔的一串参数。于是脚本可以写成形如这样的一段字符串:

press,A,2;click,100,100,2;

(意思是按下A键2次,鼠标双击100,100那个点)

处理这样的脚本很简单,执行时先从第一个字母开始复制,直到遇到“;”停止,

那样就取出一条语句。再加个循环就实现逐句读出。

读出一句后,又从第一个字母开始复制到逗号结束,就可以取出第一个命令。

后面的各参数也是这样处理,把一个语句划分成几个字符串变量。

接着用strcmp来比较第1个字符串变量,假设它是"press“那么就执行sendmessage函数,

把后面几个字符串用类型转换函数转成参数,那样就实现了形如press,a,2;这样一条语句。

但是语句种类一多就会形成一个很大的像下面这样的语句段:

if(!strcmp(str1,"press")){};

else if(!strcmp(str1,"click")){};

else ....

占地方不说,这么多重的strcmp,执行效率会很低。

所以可以先 switch(strlen(str1))

这样根据命令长度预分类一下,再用if结构做2次分类,减少比较次数。

当然switch(str1[0])

以命令的首字母来分类也是一种不错的方法。

那么执行代码应该放在哪?直接放到如onok这样的函数是不行的。因为这些代码的执行都在毫秒级,一下子就执行完。

如果加个while更麻烦,他会吸干CPU的运算能力,电脑像死机一样。

所以,应该把这样的执行函数放到Timer里面去,设置一个全局的指针,指向当前执行到哪条语句,

每次Timer事件发生时执行1条语句,接着指针向下个分号移去。

delay? 用Timer有个比较难处理的命令,就是delay(延时)命令。比如想执行到某行就停下来等10秒,于是写了个delay,10;

这样的语句不能用sleep(10000);来处理,不然执行到时会引起电脑反应迟顿。

我的方法是设置一个全局变量

tDelay =

GetTickCount()+延迟时间 ; 然后Timer里面:

GetTickCount()>tDelay ?

指向下一个命令:什么也不做;

//哈哈!发现用这条C语句表达,可以少打很多字。

补充说下,使用timer还要注意保证onTimer程序段执行时的原子性,所以通常写法是

case(nID):

if(timing==0){

timing =1; //加个锁

.......  ;

timing =0; //解锁

}

break;

到这里简单语句已经没问题了,接着是流程控制方面的语句,如if

... else ... endif  或者 while

... wend

(因为我水平有限,没想到怎么处理变量,所以没有for

这样的语句)

先说while比较简单。这个很容易,执行到while先把当前语句位置存入stack[++pos]数组里面

(提醒,别看漏了pos前面的++哦),记下当前执行到哪行语句。

然后判断一下后面的条件,符合就执行下条命令,不符合就pos--;然后向下查找wend;执行wend后面的命令。

如果符合,并执行到wend时处理也很简单,把命令指针指向stack[pos--](注意减减)就回到了while那里继续判断。

而且这样处理后,while的嵌套也不成问题了。但是(恩,最怕就是看到但是)如果wihe...wend段很长,

每次查找wend还是很花时间的。为了快速跳转,可以在脚本执行前做个小的预处理,先查好每个while对应的wend的位置,

存进一个数组jumptable[maxline]中,以命令所在行数为下标,下次执行到while或者wend就可以直接跳到jumptable[currentline]。

if 和 else

也可以存进相同的跳转表内,加快跳转速度。但要注意的是if

要记下两个位置:else和endif 。

而else 只要记住1个位置endif

。是的else也要记,接着看就知道为什么了。

如果条件成立,先stack[++pos]=1,然后执行下一条命令。

如果条件不成立,stack[++pos]=0,然后跳到else命令。

执行到else判断下

stack[pos]==1 ? 跳转到endif

:执行下一条命令;

执行到endif时只要简单的pos--;

这样同样也处理了嵌套if的问题。

没看懂 ? 慢慢想下 : 接着看下面

循环结构OK了,变量不会弄,咱只好研究下子程序了。

有了上面while和if的基础,子程序也很容易。在脚本预处理时设置一个字符串表,记下子程序名和语句开始位置。

假设子程序用 sub,子程序1;  .........

return;这样的结构。

调用时 call,子程序1;

先把当前命令的下一条命令指针位置存入stack[++pos]里面,备作返回时使用。

然后查子程序名字表,查到“子程序1”时,跳到相应位置执行。

执行完了,遇到return时,跳回stack[pos--]处执行。

以上是自己在写那个简陋的脚本解释器时总结的一点心得。

由于水平太低,变量处理,数值计算一直没想到好办法,如果你有思路或者相关资料,请不吝赐教!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值