In the process of implementing the interpreter, I found an interesting thing, that is, how to better use the object-oriented idea to write code. After thinking about it, you can define a set of templates. Two object-oriented templates have been sorted out before we start.
One is a java-style template
(class classname (superclass) (. field) (
func t()(
)
func j()(
)
))
One is a go-style template
(
(define struct-name (struct (. field)))
(func (struct-name) hello () (
))
(func (struct-name) j () (
))
)
Personally, I feel that the go style is lighter to write and takes less time to implement, so I chose the go style.
Realize ideas
There are two ways to realize it. One is to write code to parse it through java code. Although it is simple, it is actually better to use lisp macros or lisp functions. It is a kind of syntactic sugar. It is not difficult to use macros, and then the coding process begins. It took two hours in the morning to write a main body, and in the afternoon, it took more than two hours to debug and modify it. Now the code can run normally. , so let's get started.
Before starting, determine what it will be like to achieve. The complete object system, such as import and export of class parent classes, etc., we cannot realize all of them in a short time, and then there is no need to realize them all. In this case, only the following goals can be achieved.
Two templates:
- Template for structure definition
- struct method definition template
Three results:
- objects can be created
- Objects can call their own methods
- Object methods can be called between
Preparation
First, let's define a syntactic sugar for defun with macros
(define-macro defun (lambda (name args . body) (
`(
define ,name (lambda ,args ,@body)
)
)) )
Then think about how to lay out the data and process
We divide the object into two parts head and body
- head is the class information
- body is the field value in the object
The head structure is expressed in json like this:
{
id: {
type: struct,
name: struct-name
},
info:{
fieldnames:[...fields],
methods:{
method-name: {
type: self/func,
ref: method
}
}
}
}
Here is the definition of head
(
(define methods (make-dict))
(define struct-head
(cons (cons `struct `struct-name)
(cons `field-names-vector methods)))
)
The body is implemented with a vector. Objects of the same structure share the same head, and the body itself enjoys it exclusively.
coding
Two templates
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)))
)))
Two templates are done.
test
First define the structure and methods
;定义结构体
(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))
))
test
<=
(
(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'
Everything works fine and the three results are as expected.
We can also encapsulate a new
(define-macro new (lambda (f . vs)
((apply f) vs)
))
Then we can define it like this
(define dog-obj0 (new dog('狗子55' 5 '白色')))
have a test
<= (println (dog-obj0 `get-name))
=> '狗子55'
In line with expectations.
Summarize
Our object system uses macro functions as the implementation. The defined structure returns a function. As above, the result of the dog structure is a function; the object created is also a function. In this function, we define a local variable val, which stores the data passed just now. Enter the value, and then create the object. The returned function can accept two values, one is the method name and the other is the parameter; then call the corresponding function according to the method name. If it is the get-set method, we will val, which is the object information and parameters. The combination is passed to the function and then the value stored in the body can be modified or obtained; if it is a custom method, we can combine the function itself and the parameters to realize that the object methods can call each other, of course, if the method Throws an exception if it does not exist.
At present, this object system is very imperfect. For example, the visible range modifier is called by applying other class packages, etc.
There are also many problems, such as head and method being a variable instead of generating one for each struct.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。