设计一种面向对象脚本语言

  1. 云栖社区>
  2. 博客>
  3. 正文

设计一种面向对象脚本语言

异步社区 2018-09-03 13:31:03 浏览1117
展开阅读全文

​​​有没有感觉设计一门语言实在是太有意思了,可以自定义语法规则,我的“地盘听我的”。

脚本语言的功能

本书设计一门纯粹的面向对象脚本语言,任何语言都有个名词,这里给这个语言起个名字——sparrow(麻雀)。它支持的功能如下。

1 变量

支持局部变量和局部变量的定义。

变量可引用、赋值。

内部复合数据类型以大写字符开头,如System.print()

2 基本数据类型

数值:包括整数和浮点数。

字符串:包括普通字符和unicode。

list:列表,如Python中的list。

支持字面量创建,如['a', 'b']和new方法创建。

元素通过下标list'[索引]'获得。

map:哈希数组,如Python中的字典。支持字面量创建和new创建。字面量创建如:

{

    'k1':v1
    "k2":v2
}

key可以是任何数据类型。同样支持new方法创建。

value通过下标map'[key]'获得。

range:用以确定一段整数范围,用符号..表示。range包括from和to两个成员,分别表示这段范围的起和始,用区间表示[from, to],即包括from和to。如range“2..6”,2就是from,6就是to,“2..6”表示2、3、4、5、6。“..”类似于Python中的分片操作符“:”,只不过我们包括了结尾的to,而Python不包括,若用区间表示则to后面的是右小括号“)”。

3 运算

数值:+、−、*、/、%。

逻辑:>、<、==、!=、||、&&、?:、|、&等。

位运算:>>、<<。

方法调用:.。

索引:[]。

字符串:+、%(字符串内的表达式)

4 控制结构

支持if-else选择。

支持while循环。

支持for循环。

支持break退出循环。

支持continue,跳过本次循环体后面的部分,继续下一轮循环。

支持return返回。

5 函数

尽管这是一门面向对象语言,但也支持传统意义上的函数,用关键字fun实现函数定义。

函数也是用类实现。

支持函数重载。

6 类

就是传统意义上的class,包括类定义和类实例,静态类。

实现继承,所有类都是object类的子类。

类成员(也称域,或字段)必须先声明再引用。

方法包括method、getter、setter、subscript、subscriptSetter和构造函数。支持块参数,块参数的参数是用“||”括起来的参数列表,以逗号分隔。

支持静态方法。

7 线程

支持线程创建及调度。

8 模块

支持执行模块和模块内模块变量的单独导入。

9 注释

行注释://

块注释:/* 块注释 */

以上列举若有遗漏则以实际代码为主。

关键字

有以下关键字被提前征用了。

var:用于变量定义。

fun:用于函数定义。

if:用于条件判断。

else:用于条件判断的else分支。

true:bool值真。

false:bool值假。

while:用于while循环。

for:用于for循环。

break:用于退出循环。

continue:用于结束本次循环并进入下一轮循环。

return:用于从函数返回。

null:空值。

class:用于类定义。

is:用于判断类是否为某类的子类,即“is a”。

static:用于设置静态文法。

this:用于指向本实例。

super:用于指向父类。

import:用于导入模块。

脚本的执行方式

我们采用传统的虚拟机作为执行方式,即要实现一个虚拟机。编译器先把源码编译为opcode,再让虚拟机执行opcode。

opcode即操作码,是自定义的一套专供虚拟机执行的指令,后面我们在实现虚拟机时会详细介绍。 

“纯手工”的开发环境

既然本着教学的目的我觉得应该拿出教学的诚意,因此这里所说的“纯手工”是指编码中不想借助STL或其他类似的泛型语言,没有第三方库,一切以最基础最原始的形式展现语言的奥秘,因此选择了C语言,确定地说是C89,并不是较新的C99标准,诚意满满,让我们纯手工去编码吧。

基础开发环境是:

宿主系统是Linux,采用CentOS release 6.8 (Final);

编译器是gcc,版本是gcc version 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC)。

定义sparrow语言的文法

在之前介绍的文法中,我们采用的是大写字母表示非终结符,小写字母表示终结符,然而我们也说过了,在现实中为了便于编程,一般都用正规文法来定义语言,正规文法说白了就是用正则表达式定义的文法,因此本小节的基础是正则表达式,为保证容易看懂,我会用最简单的方式书写正则表达式。

值得注意的是,正规文法与第0章中介绍的文法有很大的差别,主要是涉及终结符是用''表示,原因是正规文法中会涉及()、[]和{},这些在正规文法中都是元字符,有其特殊含义:()表示成为一组,[]表示范围,{}表示重复。

但在实际语言它们只是字符串字面量(即终结符),比如在语言中()表示函数名后面的括号,也可表示表达式中的小括号,[]表示下标索引,因此为避免冲突,正规文法中用单引号括起的是终结符。

其实语法和传统语言差不多,只是用文法来描述就显得生涩了。注意,正规文法中的[]与EBNF中的意义不同,在此表示范围,其中可以用-表示一段连续的范围,比如0-9就表示0至9之间(包括0和9)的任何数;a-z同理,表示字母a到字母z之间的任何字母。[]后面一般会接量词,当然量词不一定只用在[]之后,但它一定是用在某个字符之后以表示该字符的数量,其前不能没有字符。

按照数量级别划分有3种量词,+表示重复出现1次以上,*表示重复出现0次以上,?表示出现0次或1次,比如可用[\t]*表示0个或多个空白字符,其中\t是tab。

注意此处[]中的空白是空格,为了突显这里有个空格就写了两个。.表示任意字符,包括控制字符比如回车等,|表示或者、任意其一,比如a|b,表示a和b两者取其一,注意,|是对两边的整体有效,并不是只对紧邻|的有效。

比如对于ab|cd的意思是ab或者cd,如果想表达abd或acd,可以用分组符号(),就是小括号对儿。()表示作为一组考虑,使相应的正则符号应用于整个组成员。

初次接触文法的读者可能对递归定义感到“消化不良”,比如非终结符exp是用于定义表达式,exp是由infixExp等非终结符组成,而infixExp又是由exp组成,看上去有点死循环出不来了,但你不要忘了,infixExp只是exp的其中一个组成部分,exp还可以由num、id等指代,num和id下面再无递归定义,这就是递归终止的条件。因此infixExp的组成部分exp也会是num或id等。

下面是具体的样本。

006aYAbngy1fuw7zjmhvaj30ep0nrmy0.jpg

006aYAbngy1fuw7zq8y3nj30eo0p6jse.jpg

006aYAbngy1fuw7zvmgr4j30el0p275g.jpg

以下是上述文件的执行结果,其中的spr是最终的脚本解释器(包括编译器及虚拟机),spr是sparrow的缩写。

006aYAbngy1fuw802pzl7j30ex0g274i.jpg

以上./spr manager.sp就是执行脚本文件manager.sp,这与任何脚本语言的运行方法都是一致的,执行过就是脚本的输出,大家有兴趣可以核对一下结果,除了System.clock返回的时间戳是动态变化的外,其他不变。

8b5ec8cd208f122b875534d4a77a1352

《自制编程语言》

郑钢  著

本书全面从脚本语言和虚拟机介绍开始,讲解了词法分析的实现、一些底层数据结构的实现、符号表及类的结构符号表,常量存储,局部变量,模块变量,方法存储、虚拟机原理、运行时栈实现、编译的实现、语法分析和语法制导自顶向下算符优先构造规则、调试、查看指令流、查看运行时栈、给类添加更多的方法、垃圾回收实现、添加命令行支持命令行接口。

小福利

你认为程序员存在的最大价值是什么,参与话题有机会获奖哦。截止时间9月7日17时,留言+转发本活动到朋友圈,小编将抽奖选出3名读者赠送纸书1本

024672d802e680abd21a0a6ced0cb3b4

扫码关注我们

点击阅读原文,直接购买《自制编程语言》

阅读原文​​​​

网友评论

登录后评论
0/500
评论
异步社区
+ 关注