手把手教你写脚本引擎(二)——命令脚本

这次要实现的是一个形式最简单的脚本。这种脚本仅有命令、标号及跳转构成,看起来就跟汇编一样,不过好是比较好读的。虽然这种脚本语言的语法非常简单,但是最基本的要素还是要有的。

作为一个脚本引擎,为了可以在各种各样的合适的宿主程序中使用,脚本本身最好不要涉及到具体的领域。当然,如果这个脚本被创建的目的仅仅是为了某个领域的话,那就无所谓了。因此,一个脚本引擎需要一个检查和运行代码的机制、运行时环境的维护以及一个功能足够使用的插件系统。一个完整的脚本引擎至少需要如下部件:

1、代码数据结构。代码的数据结构用来存放经过分析的脚本代码。事实上解释型的脚本引擎,也就是边执行边分析代码字符串的脚本引擎是比较难做,而且效率也不高的。脚本代码经过事先分析,可以检查一出一些在运行之前就能够检查的错误。而且我们把脚本的代码重新处理成一个数据结构之后,执行也变得更加容易控制。

2、运行时环境。运行时环境用于存放脚本在运行的过程中产生的数据,譬如堆栈、变量和状态信息等。对于一个已知的代码,不同的运行时环境代表不同的脚本执行流程。为了让脚本可以同时(但不一定是并发)执行,将运行时环境独立出来也就显得必要了。

3、语法分析器。语法分析器用于将代码转换成等价的代码数据结构,并在发现代码出错的时候输出合适的错误信息。

4、插件。插件是脚本与外部环境交互的途径之一。有了插件系统,我们可以为脚本引擎添加额外的、跟脚本引擎无关的功能,譬如文件操作、屏幕输入输出等。如果必要的话,插件系统可以将脚本引擎与领域信息互相隔离,系统将变得更加容易使用。

5、虚拟机。虚拟机用于执行代码并返回相应的结果。我们在使用脚本引擎时直接跟虚拟机进行交互,虚拟机则协调上述4个部件的相互协作。

在知道了这些之后,我们就可以开始开发一个基于命令的脚本引擎了。为了更加详细以及明确地讲述开发过程以及原理,在这里将构造一门简单的基于命令的语言。一门语言至少还是要有分支和循环的。但是为了简化,我们将分支和循环分解成判断与跳转。语言可以自由添加标号,标号将作为跳转的目标而出现。这门语言使用如下语法:

<值>:值可以是整数、小数、字符串或名字。

<名>:名可以是变量名或者标号等,使用字母与下划线开始,后接不定数量的字母、下划线与数字。

<名>::名字后接冒号代表一个标号。这个标号代表着一个指令的位置,用于指定跳转目标。

goto <名>:goto用于直接跳转到一个位置继续执行。

set <名> <值>:set用于将一个值赋值给一个指定名字的变量。这个变量不存在则创建。

opcode <名> <值> <值>:opcode可以是add、minus、mul、div、idiv或mod。这6个命令将两个值进行加、减、乘、除、整除及求余,并将结果赋值给一个指定名字的变量。这个变量不存在则创建。

if <值>[ opcode <值>] goto <名>:if用于判断一个条件并在条件满足被满足的时候跳转到指定的地方。条件可以是一个值,这个值必须是整数,并且在这个值不为0的时候条件被满足。条件也可以是一个比较,这个时候opcode可以是is、is_not、less_than、greater_than、less_equal或greater_equal,分别在第一个值等于、不等于、小于、大于、小于或等于、大于或等于第二个值的时候满足条件。

exit:结束执行

<名> <值>*:如果命令名称不是上面的5种的其中一种的话,那么这个命令将被传递给插件进行执行。这个时候,命令可以有任意的参数。

在这种语法下,我们可以假设宿主程序给了我们write、writeln和read命令用于输入输出,并得到一个判断输入的数字是否质数的程序:

write "请输入一个数字:"

read Number
 if Number less_then 2 goto FAIL
  if Number is 2 goto SUCCESS
 set Divisor 2
LOOP_BEGIN:
 if Number is Divisor goto SUCCESS
 mod Remainder Number Divisor
 if Remainder is 0 goto FAIL
 add Divisor Divisor 1
 goto LOOP_BEGIN
SUCCESS:
 writeln Number "是质数。"
 exit
FAIL:
 writeln Number "不是质数。"

时间: 2025-01-20 09:16:52

手把手教你写脚本引擎(二)——命令脚本的相关文章

手把手教你写脚本引擎(一)——挑选语言的特性

脚本引擎的作用在于增强程序的可配置性.从游戏到管理系统都需要脚本,甚至连工业级产品的Office.3DS Max以及AutoCAD等都添加了属于自己的脚本语言.DHTML的出现让我们可以在网页代码中嵌入脚本语言,PHP和ASP等技术的出现让我们可以将一个应用程序的界面换成网页,而逻辑使用脚本语言编写.现在脚本语言的种类繁多,Python的发展让BOOST库拥有了对Python的支持,Rails框架的出现壮大了Ruby的实力,LUA更是被大量应用在游戏开发中.Windows甚至提供了wscript

比较详细的手把手教你写批处理(willsort题注版)第1/5页_DOS/BAT

另,建议Climbing兄取文不用拘泥于国内,此类技术文章,内外水平相差极大:与其修正国内只言片语,不如翻译国外优秀著述. -------------------------------------------------------- 标题:手把手教你写批处理-批处理的介绍 作者:佚名 编者:Climbing 题注:willsort 日期:2004-09-21 -------------------------------------------------------- 批处理的介绍 扩展名

手把手教你写回调函数

                                        手把手教你写回调函数       手把手教你写回调函数源代码   回调函数的主要功能就是提供统一的接口以及事件的通知.如果你是从事middleware,框架,提供API编程的,那么你肯定少不了要使用回调函数. 所谓提供统一接口或者事件的通知即:当发生某一事件或者出现某一个状态之后必定会进行某种操作.但是这个操作又不能写死,不同的环境不同的程序会有不同的实现,也就是说提供接口的人不想把逻辑写死,而是由调用接口的人来实现

手把手教你写脚本引擎(三)——简单的高级语言(1,基本原理)

这一篇文章开始讲述如何实现一个高级语言的脚本引擎了.由于工程量较为庞大,因此将分开几篇文章讲.学习做脚本还是要从简单的东西做起的.上一篇文章介绍的命令脚本为实现高级语言的原理做了铺垫.首先,高级语言和低级语言脚本的架构是一致的.其次,为了具有较大的优化的空间,我们将把高级语言转换成低级语言,并配合一个低级语言的脚本引擎来实现高级语言的脚本引擎.当然,习惯上,在这种情况下我们把低级语言叫『指令』. 在这个阶段,我们实现的这门语言是非惰性计算的.弱类型的.仅支持基本类型.数组和函数指针的语言.作为扩

只有20行Javascript代码!手把手教你写一个页面模板引擎

AbsurdJS 作者写的一篇教程,一步步教你怎样用 Javascript 实现一个纯客户端的模板引擎.整个引擎实现只有不到 20 行代码.如果你能从头看到尾的话,还能有不少收获的.你甚至可以跟随大牛的脚步也自己动手写一个引擎.以下是全文. 不知道你有木有听说过一个基于Javascript的Web页面预处理器,叫做AbsurdJS.我是它的作者,目前我还在不断地完善它.最初我只是打算写一个CSS的预处理器,不过后来扩展到了CSS和HTML,可以用来把Javascript代码转成CSS和HTML代

手把手教你写Kconfig---基于tiny4412开发板

今天,我就来教大家写写最简单的Kconfig,什么是Kconfig? 我们配置过的信息最终会在这以下的两个文件中找到. 这个文件会被.c包含 include/generated/autoconf.h  这个autoconf.h中对应的就是一些宏,打开看看就明白了  这个文件根据.config生成,而.config是根据make menuconfig来进行生成,内核中的makefile包含了这个文件inlcude/config/auto.conf    简单的说吧,它是一个Linux驱动的配置文件

手把手教你写Kconfig---基于tiny4412开发板(增强版)

Kconfig怎么写的在上节就已经教大家写了. 这节我们来写写增强版的,因为Kconfig有太多太多可以配置的,所以这里我就不给出图片演示了,请参考上节的文章,再来看这节大家就会大彻大悟,然后自己去尝试吧. 基本上最常见的配置就是以下的这些. 文章链接如下: http://blog.csdn.net/morixinguan/article/details/54744237 在make menuconfig添加Kconfig配置简单的选项有很多的Kconfig组成./scripts/Kconfig

手把手教你写脚本引擎(四)——简单的高级语言(2,处理语法)

首先是词法分析器.我们仍然能够使用<构造可配置语法分析器>前半部分的方法人脑画出一张合适的DFA,这个时候我们可以手工来实现.用于词法分析器的DFA只有两种状态,一种是普通状态,另一种是终结状态.所以我们可以很机械地将DFA用C++写出来. 我们要为状态编号.编号要连续,而且要从0开始,这样的话C++的编译器一般都会为switch-case的代码生成一张表,用于快速跳转.然后用下面的方法. 1:将输入的指针Input复制出一个副本,叫Current:给出一个同类型的指针Last,将其赋值为NU

手把手教你写脚本引擎(五)——简单的高级语言(3,符号表)

符号表的结构的复杂度跟语言的语义规则的复杂度有关.对于C#来说,每一个符号都附带了一大堆信息,譬如位置啦,所在的namespace啦,类型啦什么的.对于JavaScript来说,符号表几乎是不需要的,因为东西都动态了,编译时几乎不检查内容.语义分析的输出是符号表,代码生成的输入是符号表和语法树.因此语法树除了放语法相关的内容,语义相关的内容最好放到符号表里面(譬如说表达式的类型啦,语句的scope结果啦).关于一个现实中的符号表组织可以看CMinus的语义分析结果. 首先我们要解决类型的表达问题