(in-package :cl-user) (defpackage cl-annot.class (:nicknames :annot.class) (:use :cl :annot.util :annot.helper) (:export :metaclass :export-slots :export-accessors :export-constructors :export-structure :export-class) (:import-from :alexandria :ensure-list :curry :compose :if-let :symbolicate)) (in-package :annot.class) (defannotation metaclass (metaclass class-definition-form) (:arity 2) (progn-form-replace-last (lambda (class-definition-form) (if (get-class-option :metaclass class-definition-form) (error ":metaclass is not empty") (append class-definition-form `((:metaclass ,metaclass))))) class-definition-form)) (defmacro export-slots (class-definition-form) (progn-form-replace-last (lambda (class-definition-form) (loop for slot-specifier in (slot-specifiers class-definition-form) for slot = (if (consp slot-specifier) (car slot-specifier) slot-specifier) collect slot into slots finally (return (if slots `(progn (export ',slots) ,class-definition-form) class-definition-form)))) class-definition-form)) (defmacro export-accessors (class-definition-form) (progn-form-replace-last (lambda (class-definition-form) (case (first class-definition-form) (defclass (get-accessors-in-defclass class-definition-form)) (defstruct (get-accessors-in-defstruct class-definition-form)))) class-definition-form)) (defun get-accessors-in-defclass (class-definition-form) (loop for slot-specifier in (slot-specifiers class-definition-form) for slot-options = (when (consp slot-specifier) (cdr slot-specifier)) if slot-options append (plist-get-all slot-options :reader) into accessors and append (plist-get-all slot-options :writer) into accessors and append (plist-get-all slot-options :accessor) into accessors finally (return (if accessors `(progn (export ',accessors) ,class-definition-form) class-definition-form)))) (defun get-conc-name (class-definition-form) (let ((options (ensure-list (second class-definition-form)))) (if-let ((conc-name (find-if (lambda (option) (and (consp option) (eq (first option) :conc-name))) options))) (second conc-name) (if (find :conc-name options) nil (symbolicate (first options) '-))))) (defun get-accessors-in-defstruct (class-definition-form) `(progn (export ',(mapcar (compose (let ((conc-name (get-conc-name class-definition-form))) (if conc-name (curry #'symbolicate conc-name) #'identity)) #'first #'ensure-list) (slot-specifiers class-definition-form))) ,class-definition-form)) (defmacro export-constructors (class-definition-form) (progn-form-replace-last (lambda (class-definition-form) (case (first class-definition-form) (defstruct (if (consp (second class-definition-form)) (let ((constructor-clauses (remove-if-not (lambda (lst) (eq (first lst) :constructor)) (mapcar #'ensure-list (cdr (second class-definition-form)))))) (if (and (= 1 (length constructor-clauses)) (= 2 (length (car constructor-clauses))) (null (cadar constructor-clauses))) class-definition-form `(progn (export ',(or (remove nil (mapcar #'second constructor-clauses)) (list (symbolicate 'make- (first (second class-definition-form)))))) ,class-definition-form))) `(progn (export '(,(symbolicate 'make- (second class-definition-form)))) ,class-definition-form))) (t class-definition-form))) class-definition-form)) (defmacro export-class (class-definition-form) `(annot.std:export* (export-slots (export-accessors ,class-definition-form)))) (defmacro export-structure (class-definition-form) `(annot.std:export* (export-slots (export-accessors (export-constructors ,class-definition-form)))))