共计 3297 个字符,预计需要花费 9 分钟才能阅读完成。
在实现解释器的过程中,发现了一个好玩的货色,那就是怎么更好的应用面向对象的思路来编写代码,想了想能够定义一套模板。再开始前先整顿了两种面向对象的模板。
一种是 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 是一个变量 而非每一个构造体都生成一个。