(in-package :cl-user)
(defpackage :oo-compiler
(:nicknames :oo)
(:use :common-lisp)
(:export :クラス :コンストラクタ :メソッド :インスタンス :祖先 :この :これ :値))
(in-package :oo-compiler)
(defvar *classes* (make-hash-table))
(defun parent (class) (first (gethash class *classes*)))
(defun assoc-names (alist type) (cdr (assoc type alist)))
(defun class-names (class type)
(assoc-names (second (gethash class *classes*)) type))
(defun instance-vars (class) (class-names class :インスタンス変数))
(defun class-vars (class) (class-names class :クラス変数))
(defun constructor (class) (third (gethash class *classes*)))
(defun (setf constructor) (value class)
(setf (third (gethash class *classes*)) value))
(defun cmethod (class name)
(gethash name (fourth (gethash class *classes*))))
(defun (setf cmethod) (value class name)
(setf (gethash name (fourth (gethash class *classes*))) value))
(defun class-var (class name)
(gethash name (fifth (gethash class *classes*))))
(defun (setf class-var) (value class name)
(setf (gethash name (fifth (gethash class *classes*))) value))
(defmacro クラス (name parent &rest properties)
`(progn
(setf (gethash ',name *classes*)
(list ',parent
(list
(cons :インスタンス変数
(append (class-names ',parent :インスタンス変数)
',(assoc-names properties :インスタンス変数)))
(cons :クラス変数
(append (class-names ',parent :クラス変数)
',(assoc-names properties :クラス変数))))
nil (make-hash-table) (make-hash-table)))
,@(loop for method in (rest (find :メソッド properties :key #'first))
for instance = (gensym) and args = (gensym) collect
`(defun ,method (,instance &rest ,args)
(apply (search-method (instance-class ,instance) ',method)
,instance ,args)))))
(defun instance-class (instance) (first instance))
(defun instance-var (instance name) (gethash name (second instance)))
(defun (setf instance-var) (value instance name)
(setf (gethash name (second instance)) value))
(defun evaluate-var (instance-or-class name)
(cond ((atom instance-or-class)
(cond ((null (gethash instance-or-class *classes*))
(error "No such class: ~a" instance-or-class))
((member name (class-vars instance-or-class))
(class-var instance-or-class name))
(t (error "Cannot find class variable ~a" name))))
((member name (class-vars (instance-class instance-or-class)))
(class-var (instance-class instance-or-class) name))
((member name (instance-vars (instance-class instance-or-class)))
(instance-var instance-or-class name))
(t (error "Cannot find the variable ~a" name))))
(defun (setf evaluate-var) (value instance-or-class name)
(cond ((atom instance-or-class)
(cond ((null (gethash instance-or-class *classes*))
(error "No such class: ~a" instance-or-class))
((member name (class-vars instance-or-class))
(setf (class-var instance-or-class name) value))
(t (error "Cannot find class variable ~a" name))))
((member name (class-vars (instance-class instance-or-class)))
(setf (class-var (instance-class instance-or-class) name) value))
((member name (instance-vars (instance-class instance-or-class)))
(setf (instance-var instance-or-class name) value))
(t (error "Cannot find the variable ~a" name))))
(defmacro 値 (instance-or-class name)
`(evaluate-var ,instance-or-class ',name))
(defun call-parent (instance name &rest args)
(if (eq name :コンストラクタ)
(apply (constructor (parent (instance-class instance)))
instance args)
(apply (cmethod (parent (instance-class instance)) name)
instance args)))
(defun interpret-sexp (instance sexp)
(cond ((atom sexp) (if (eq sexp 'これ) instance sexp))
((eq (first sexp) 'この) `(evaluate-var ,instance ',(second sexp)))
((eq (first sexp) '祖先)
`(call-parent ,instance ,(second sexp) ,@(subseq sexp 2)))
(t (cons (interpret-sexp instance (first sexp))
(interpret-sexp instance (rest sexp))))))
(defmacro コンストラクタ (class args &body body)
(let ((instance (gensym)))
`(setf (constructor ',class)
(lambda (,instance ,@args)
,@(mapcar (lambda (sexp) (interpret-sexp instance sexp))
body)
,instance))))
(defun search-method (class name)
(if class
(or (cmethod class name)
(let ((parent (parent class)))
(and parent (search-method parent name))))
(error "Cannot find the method ~a" name)))
(defmacro メソッド (class name args &body body)
(let ((instance (gensym)))
`(setf (cmethod ',class ',name)
(lambda (,instance ,@args)
,@(mapcar (lambda (sexp) (interpret-sexp instance sexp))
body)))))
(defmacro インスタンス (class &rest args)
`(funcall (constructor ',class)
(list ',class (make-hash-table))
,@args))