在实现解释器的过程中,我发现了一个有趣的事情,那就是如何更好的运用面向对象的思想来写代码。想了想,自己可以自己定义一套模板。在我们开始之前,我们组织了两个面向对象的模板。一个是java风格的模板(classclassname(superclass)(.field)(funct()()funcj()()))另一个是go风格的模板((definestruct-name(struct(.然后我选择了go风格。实现思路实现思路有两种。一种是自己写代码通过java代码解析。虽然简单,但想了想还是用lisp宏或者lisp函数会好一些。不需要增加java代码量,只是一种语法糖,用宏也不难。然后我开始了编码过程。上午花了两个小时写body,下午花了两个多小时调试修改。现在这组代码可以正常运行了。那行得通,所以让我们开始吧。在开始之前,确定要实施的内容。类、父类、导入、导出等完整的对象系统,我们短时间内不可能全部实现,以后也没有必要全部实现。在这种情况下,我们只能实现以下目标。二模板:结构定义模板结构方法定义模板三结果:对象可以被创建对象可以调用自己的方法对象方法可以在方法之间调用准备工作首先我们定义一个defun语法糖用宏(define-macrodefun(lambda(nameargs.body)(`(define,name(lambda,args,@body)))))然后想想怎么布局数据和流程我们把对象分成两部分head和bodyhead是类信息body是对象中字段值头部结构在json中是这样表示的:{id:{type:struct,name:struct-name},info:{fieldnames:[...fields],methods:{method-name:{type:self/func,ref:method}}}}下面是head的定义((definemethods(make-dict))(definestruct-head(cons(cons`struct`struct-name)(cons`field-names-vectormethods))))body是用vector实现的,相同结构的对象共享同一个head,body本身是独占的。代码两个模板1.struct(define-macrostruct(lambda(.fields)//(set-cdr!(carstruct-head)(hack-struct-namefields))(set-car!(cdrstruct-head)(list->vectorfields))(map(lambda(findex)(dict-put!methods(string->symbol(string-append'get-'(symbol->stringf)))(cons`self(lambda(o)(vector-ref(cdro)index))))(dict-put!methods(string->symbol(string-append'set-'(symbol->stringf)))(cons`self(lambda(ov)(vector-set!(cdro)indexv)))))fields)(lambda(.field-values)(defineval(consstruct-head(list->vectorfield-values)))(defineself(lambda(key.msg)((definemethod-info(dict-getmethodskey))(if(pair?method-info)((definemethod(cdrmethod-info))(defineargsnil)(如果(eqv?`self(carmethod-info))((set!args(listval))(list-add-allargsmsg))((set!args(listself))(list-add-allargsmsg)))(applymethodargs))(error(string-append'notmethod'(符号->stringkey)))))))self)))2.func(define-macrofunc(lambda(.data)(if(exp?(cardata))((definev(list->vectordata))(definestruct-name(car(vector-refv0)))(definefunc-name(vector-refv1))(definefunc-params(vector-refv2))(definefunc-body(cdr(cdr(cdr数据))))(definetemp-func-params(liststruct-name))(map(lambda(param)(list-addtemp-func-paramsparam))func-params)(set!func-paramstemp-func-params)(dict-put!methodsfunc-name(cons`func(apply(`(lambda,func-params,@func-body)))))(`(defun,@data))))))两个获取测试的模板首先定义结构和方法;定义结构(definedog(struct(nameagecolor)));定义结构体的方法(func(dog)hello(abc)((println(string-append'hello:'(dog`get-name)abc))));definemethod(funchello(abc)((println(string-append'hello:'abc))))test<=((hello'a''b''c');创建实例(definedog-obj(dog'狗子'5'white'))(dog-obj`hello(`('a''b''c')));在方法之间调用(dog-obj`hello'a''b''c');调用你自己的方法(println(dog-obj`get-name))(dog-obj`set-name'狗子0')(println(dog-obj`get-name))(println(dog-obj`get-name))(dog-obj`set-name('狗子0')))=>'hello:abc''hello:dogabc''hello:狗子abc',狗子',狗子0',Gouzi0'一切正常,三个结果符合预期。我们还可以再封装一个new(define-macronew(lambda(f.vs)((applyf)vs)))然后我们可以这样定义它(definedog-obj0(newdog('狗子55'5'White')))Test<=(println(dog-obj0`get-name))=>'狗子55'符合预期。综上所述,我们的对象系统使用宏函数作为实现。定义的结构返回一个函数。上面dog结构的返回结果是一个函数;创建的对象返回一个函数。在这个函数中,我们定义了一个局部变量val,用来存储刚才的值。传入传入值,然后创建对象返回的函数,可以接受两个值,一个是方法名,一个是参数;然后根据方法名调用相应的函数。如果是get-set方法,我们会使用val,它是将对象信息和参数组合传递给函数,然后可以修改或获取body中存储的值;如果是自定义方法,我们将函数本身和参数结合起来,实现对象方法之间的相互调用。当然,如果方法不存在,则会抛出异常。目前这个对象体系很不完善,比如可见范围,修饰符,调用方法,其他类,包的应用等。还有很多问题,比如head和method是一个变量,而不是每个结构体都生成一个.
