- point."
-
- (let ((inst-var (gensym "INST"))
- (class-name (symbolicate code '-inst))
- (keys (mapcar (lambda (arg) (intern (symbol-name arg) :keyword))
- args)))
- `(progn
- (defclass ,class-name (inst)
- ,(mapcar (lambda (arg key)
- `(,arg :initarg ,key :reader ,(symbolicate 'inst- arg)))
- args keys))
- (defun ,(symbolicate 'make- code '-inst) (,@args)
- (make-instance ',class-name ,@(mappend #'list keys args)))
- (defmethod inst-metric ((,inst-var ,class-name))
- (with-slots (,@args) ,inst-var
- (+ 1 ,@(mapcar (lambda (arg) `(inst-metric ,arg)) args))))
- (defmethod print-object ((,inst-var ,class-name) ,streamvar)
- (with-slots (,@args) ,inst-var
- (if *print-escape*
- (print-unreadable-object (,inst-var ,streamvar :type t)
- (format stream "~@<~@{~S ~@_~S~^ ~_~}~:>"
- ,@(mappend #'list keys args)))
- (progn ,@body)))))))
-
-;; Important instruction classes.
-
-(export '(block-inst make-block-inst var-inst make-var-inst
- function-inst make-function-inst set-inst make-set-inst
- return-inst make-return-inst expr-inst make-expr-inst
- inst-decls inst-body inst-name inst-type inst-init inst-var
- inst-expr))
-
-(definst var (stream) (name type init)
- (pprint-c-type type stream name)
- (when init
- (format stream " = ~A" init)))
-(definst set (stream) (var expr)
- (format stream "~@<~A = ~@_~2I~A;~:>" var expr))
-(definst return (stream) (expr)
- (format stream "return~@[ (~A)~];" expr))
-(definst expr (stream) (expr)
- (format stream "~A;" expr))
-(definst block (stream) (decls body)
- (format stream "{~:@_~@< ~2I~@[~{~A;~:@_~}~:@_~]~{~A~^~:@_~}~:>~:@_}"
- decls body))
-(definst function (stream) (name type body)
- (pprint-logical-block (stream nil)
- (princ "static " stream)
- (pprint-c-type type stream name)
- (format stream "~:@_~A~:@_~:@_" body)))
+ point.
+
+ The ARGS are an ordinary lambda-list, with the following quirks:
+
+ * Where an argument-name symbol is expected (as opposed to a list), a
+ list (ARG SLOT) may be written instead. This allows the slots to be
+ named independently of the argument names, which is handy if they'd
+ otherwise conflict with exported symbol names.
+
+ * If an argument name begins with a `%' character, then the `%' is
+ stripped off, except when naming the actual slot. Hence, `%FOO' is
+ equivalent to a list `(FOO %FOO)', except that a `%'-symbol can be
+ used even where the lambda-list syntax permits a list.
+
+ If EXPORT is non-nil, then export the `CODE-inst' and `make-CODE-inst'
+ symbols."
+
+ (multiple-value-bind (bvl public private)
+ (let ((state :mandatory)
+ (bvl (make-list-builder))
+ (public (make-list-builder))
+ (private (make-list-builder)))
+ (labels ((recurse-arg (arg path)
+ (cond ((symbolp arg)
+ (let ((name (symbol-name arg)))
+ (if (and (plusp (length name))
+ (char= (char name 0) #\%))
+ (let ((public (intern (subseq name 1))))
+ (values public public arg))
+ (values arg arg arg))))
+ ((atom arg)
+ (error "Unexpected item ~S in lambda-list." arg))
+ ((null path)
+ (multiple-value-bind (public private)
+ (if (cdr arg) (values (car arg) (cadr arg))
+ (values (car arg) (car arg)))
+ (values public public private)))
+ (t
+ (let* ((step (car path))
+ (mine (nthcdr step arg)))
+ (multiple-value-bind (full public private)
+ (recurse-arg (car mine) (cdr path))
+ (values (append (subseq arg 0 step)
+ full
+ (cdr mine))
+ public
+ private))))))
+ (hack-arg (arg maxdp)
+ (multiple-value-bind (full public-name private-name)
+ (recurse-arg arg maxdp)
+ (lbuild-add bvl full)
+ (lbuild-add public public-name)
+ (lbuild-add private private-name))))
+ (dolist (arg args)
+ (cond ((or (eq arg '&optional)
+ (eq arg '&rest)
+ (eq arg '&key)
+ (eq arg '&aux))
+ (setf state arg)
+ (lbuild-add bvl arg))
+ ((eq arg '&allow-other-keys)
+ (lbuild-add bvl arg))
+ ((or (eq state :mandatory)
+ (eq state '&rest))
+ (hack-arg arg '()))
+ ((or (eq state '&optional)
+ (eq state '&aux))
+ (hack-arg arg '(0)))
+ ((eq state '&key)
+ (hack-arg arg '(0 1)))
+ (t
+ (error "Confusion in ~S!" 'definst)))))
+ (values (lbuild-list bvl)
+ (lbuild-list public)
+ (lbuild-list private)))
+ (let* ((inst-var (gensym "INST"))
+ (class-name (symbolicate code '-inst))
+ (constructor-name (symbolicate 'make- code '-inst))
+ (keys (mapcar (lambda (arg) (intern (symbol-name arg) :keyword))
+ public)))
+ `(progn
+ (defclass ,class-name (inst)
+ ,(mapcar (lambda (public-slot private-slot key)
+ `(,private-slot :initarg ,key
+ :reader ,(symbolicate 'inst- public-slot)))
+ public private keys))
+ (defun ,constructor-name (,@bvl)
+ (make-instance ',class-name ,@(mappend #'list keys public)))
+ (defmethod inst-metric ((,inst-var ,class-name))
+ (with-slots (,@private) ,inst-var
+ (+ 1 ,@(mapcar (lambda (slot) `(inst-metric ,slot)) private))))
+ (defmethod print-object ((,inst-var ,class-name) ,streamvar)
+ (with-slots ,(mapcar #'list public private) ,inst-var
+ (if *print-escape*
+ (print-unreadable-object (,inst-var ,streamvar :type t)
+ (format stream "~@<~@{~S ~@_~S~^ ~_~}~:>"
+ ,@(mappend #'list keys public)))
+ (block ,code ,@body))))
+ ,@(and export `((export '(,class-name ,constructor-name
+ ,@(mapcar (lambda (slot)
+ (symbolicate 'inst- slot))
+ public)))))
+ ',code))))