在实现解释器的过程中,发现了一个好玩的货色,那就是怎么更好的应用面向对象的思路来编写代码, 想了想能够定义一套模板。再开始前先整顿了两种面向对象的模板。
一种是java格调的模板
(class classname (superclass) (. field) ( func t()( ) func j()( )))
一种是go格调的模板
( (define struct-name (struct (. field))) (func (struct-name) hello () ( )) (func (struct-name) j () ( )))
个人感觉还是go格调的编写起来更轻一些,实现起来破费工夫也短,而后就抉择了go格调的。
实现思路
实现思路有两种一种是通过java代码去编写代码去解析它,尽管简略,但想了想用lisp的宏或lisp函数其实会更好一些,不必在去减少java的代码量,而且自身不过是一种语法糖,用宏也并不难,而后就开始了编码过程,上午花了两个小时写了一个主体,下午又花了两个多小时调试批改,当初这套代码能够失常运行了,那么就让咱们开始吧。
在开始前先确定要实现成什么样子,残缺的对象零碎如 类 父类 导入 导出等,咱们不可能短时间全副实现,而后也没必要全副实现,既然这样那就只实现如下这些指标。
两个模板:
- 构造定义的模板
- 构造的办法定义的模板
三个后果:
- 对象能够创立
- 对象能够调用本人办法
- 对象办法之间能够调用
筹备工作
首先咱们先用宏定义一个 defun 的语法糖
(define-macro defun (lambda (name args . body) ( `( define ,name (lambda ,args ,@body) ))) )
而后思考怎么对数据和过程布局
咱们将 对象 分成head 和 body 两局部
- head是类的信息
- body 是对象外面的字段值
head构造是这样用json示意:
{ id: { type: struct, name: struct-name }, info:{ fieldnames:[...fields], methods:{ method-name: { type: self/func, ref: method } } }}
上面是head 的定义
( (define methods (make-dict)) (define struct-head (cons (cons `struct `struct-name) (cons `field-names-vector methods))))
body用向量实现,同一个个构造的对象共享同一个head,body本人独享。
编码
两个模板
1.struct
(define-macro struct (lambda (. fields) //(set-cdr! (car struct-head) (hack-struct-name fields)) (set-car! (cdr struct-head) (list->vector fields)) (map (lambda (f index) (dict-put! methods (string->symbol (string-append 'get-' (symbol->string f))) (cons `self (lambda (o) (vector-ref (cdr o) index)))) (dict-put! methods (string->symbol (string-append 'set-' (symbol->string f))) (cons `self (lambda (o v) (vector-set! (cdr o) index v)))) ) fields) (lambda (. field-values) (define val (cons struct-head (list->vector field-values))) (define self (lambda (key . msg) ( (define method-info (dict-get methods key)) (if (pair? method-info) ( (define method (cdr method-info)) (define args nil) (if (eqv? `self (car method-info)) ( (set! args (list val)) (list-add-all args msg) ) ( (set! args (list self)) (list-add-all args msg) ) ) (apply method args) ) (error (string-append 'not method ' (symbol->string key))) ) ))) self)))
2.func
(define-macro func (lambda (. data) ( if (exp? (car data)) ( (define v (list->vector data)) (define struct-name (car (vector-ref v 0))) (define func-name (vector-ref v 1)) (define func-params (vector-ref v 2)) (define func-body (cdr (cdr (cdr data)))) (define temp-func-params (list struct-name)) (map (lambda (param) (list-add temp-func-params param)) func-params) (set! func-params temp-func-params) (dict-put! methods func-name (cons `func (apply (`(lambda ,func-params ,@func-body)))) ) ) (`(defun ,@data))))))
两个模板搞定。
测试
先定义构造和办法
;定义构造体(define dog (struct (name age color))); 定义构造体的办法(func (dog) hello (a b c) ( (println (string-append 'hello: ' (dog `get-name) a b c)))); 定义方法(func hello (a b c) ( (println (string-append 'hello:' a b c))))
测试
<= ( (hello 'a' 'b' 'c') ;创立实例 (define dog-obj (dog '狗子' 5 '红色')) (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: 狗子abc''hello: 狗子abc''狗子''狗子0''狗子0'
一切正常,三个后果合乎预期。
咱们还可再封装一个new
(define-macro new (lambda (f . vs) ((apply f) vs)))
而后咱们就能够这样定义了
(define dog-obj0 (new dog('狗子55' 5 '红色')))
测试一下
<= (println (dog-obj0 `get-name))=> '狗子55'
合乎预期。
总结
咱们的对象零碎应用了宏函数作为实现,定义构造返回的是一个函数 如上 dog 构造体返回后果是个函数; 创建对象返回的也是一个函数,这个函数外面咱们定义了一个局部变量val 外面存储了方才传入进来的值,而后创建对象返回的这个函数能够承受两个值, 一个是办法名,一个是参数; 而后依据办法名调用对应的函数 如果是get-set 办法咱们将 val 也就是对象信息和参数组合 传给函数 而后就能够对body 外面存储的值进行批改或获取;如果是自定义的办法 ,咱们把这个函数自身和参数取出进行组合就实现了对象办法之间的能够相互调用,当然如果办法不存在的话就抛出异样。
目前这个对象零碎很不欠缺,如 可见范畴 修饰符 调用形式 利用其余类 包等
也存在诸多问题,如head 和 method 是一个变量 而非每一个构造体都生成一个。