From 22686584f103d163a1e5f370aa06905ec5c3b4a4 Mon Sep 17 00:00:00 2001 From: colin Date: Mon, 17 Jun 2024 21:13:41 -0700 Subject: Inital Commit --- def.lisp | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 def.lisp (limited to 'def.lisp') diff --git a/def.lisp b/def.lisp new file mode 100644 index 0000000..9944505 --- /dev/null +++ b/def.lisp @@ -0,0 +1,101 @@ +;;;; def.lisp + +(in-package #:def) + +(defmacro var (name &key doc (init nil initp)) + "DEFVAR with optional documentation and optional initform. Lets you +easily document uninitialized special variables." + (assert (good-muffed-var-name-p name) (name) + "Special variable ~s is not wearing earmuffs :(") + `(progn + (defvar ,name) + ,@(when initp + (list `(setf ,name ,init))) + ,@(when doc + (list `(setf (documentation ',name 'cl:variable) ,doc))))) + +(defmacro const (name value &optional doc) + "Define a constant in a way that is redefinable whenever the form is +reevaluated." + (assert (good-muffed-var-name-p name :muffer "+")) + `(progn + (handler-bind ((error + (lambda (&rest ignore) + (declare (ignore ignore)) + (invoke-restart 'cl:continue)))) + (makunbound ',name)) + (defconstant ,name ,value ,@(when doc (list doc))))) + +(defmacro class (name (&rest supers) &body slots-and-options) + "Define a class. + +SLOTS-AND-OPTIONS := (SLOT-SPEC1 ... SLOT-SPECN . CLASS-OPTIONS) + +Each SLOT-SPEC is a list of slot names followed by keyword slot +options, E.g: + +(X Y Z :type real :initform 0) + +Would expand into three slot definitions +(X :accessor X :initarg :X :type real :initform 0) +(Y :accessor Y :initarg :Y :type real :initform 0) +(Z :accessor Z :initarg :Z :type real :initform 0) + +There are a few flag style slot definition arguments. Flags do not +have an value after them, all flags must come before other options. Flags are: + :prefix - prefix the accessor by the class name + :ro - only define a reader + :wo - only define a writer + +By default an accessor is defined. + +CLASS-OPTIONS is a PLIST of class options. + +E.g. + +(def:class pt (thing) + (x y z :prefix :type real :initform 0) + (label :type :string) +" + (labels + ((make-accessor-name (slot &optional prefix?) + (if prefix? + (intern (format nil "~a-~a" name slot)) + slot)) + (singlep (x) + (find x '(:prefix :ro :wo) :test #'eq)) + (parse-slot-spec-expr (expr) + " (names ... &key kwargs)" + (multiple-value-bind (slot-names kwargs) (take-until #'keywordp expr) + (multiple-value-bind (singles kwargs) (partition #'singlep kwargs) + (let ((prefix-accessor? (find ':prefix singles :test #'eq)) + (read-only? (find ':ro singles :test #'eq)) + (write-only? (find ':wo singles :test #'eq))) + (assert (not (and read-only? write-only?)) () + "A slot cannot be both read-only (:ro) and write-only (:wo).") + (loop + :with accessor-type := (cond (read-only? :reader) + (write-only? :writer) + (t :accessor)) + :for slot :in slot-names + :collect `(,slot + ,accessor-type ,(make-accessor-name slot prefix-accessor?) + :initarg ,(alexandria:make-keyword slot) + ,@kwargs)))))) + + (parse-class-options (kwargs) + (loop :for (key val . more) :on kwargs :by #'cddr + :collect (list key val)))) + + (let* ((slot-defs (loop :for expr :in slots-and-options + :while (listp expr) + :append (parse-slot-spec-expr expr))) + (options (parse-class-options + (nth-value 1 (take-until #'keywordp slots-and-options))))) + + `(defclass ,name ,supers + (,@slot-defs) + ,@options)))) + + + -- cgit v1.2.3