Skip to content

Instantly share code, notes, and snippets.

@commander-trashdin
Created October 16, 2020 22:26
Show Gist options
  • Save commander-trashdin/4b547fdfba08c1de0a795917b29698a3 to your computer and use it in GitHub Desktop.
Save commander-trashdin/4b547fdfba08c1de0a795917b29698a3 to your computer and use it in GitHub Desktop.
Let's force people to write docs.
(defparameter *type-policy* :docs)
(defparameter *doc-info* (make-hash-table :test #'equal))
(defun validate-ftype (ftype)
(labels ((check-symbols (list)
(every (lambda (token)
(if (atom token)
(symbolp token)
(check-symbols token)))
list)))
(when (and ftype (listp ftype))
(when (or (eql (car ftype) 'function)
(eql (car ftype) '->))
(setf (car ftype) 'function)
(when (check-symbols ftype)
(when (listp (second ftype))
(when (= 3 (length ftype))
ftype)))))))
(defun get-docs (name &optional (stream *standard-output*))
(if (gethash name *doc-info*)
(destructuring-bind (_ type __ documentation) (gethash name *doc-info*)
(declare (ignore _ __))
(format stream "Type:~%->~% ~a~% ~a~%" (second type) (third type))
(format stream "Documentation:~%~a~%" documentation)
(values type documentation))
(error 'simple-error :format-control "Function is not known to defun/doc.")))
(define-condition malformed-type (warning)
((function-name
:initarg :name
:reader name)
(function-type
:initarg :type
:reader function-type))
(:report (lambda (condition stream)
(format stream "The functional type ~a provided for ~a is not a valid ftype specifier."
(function-type condition) (name condition)))))
(define-condition no-type (style-warning)
((function-name
:initarg :name
:reader name))
(:report (lambda (condition stream)
(format stream "No type was provided for function ~a. Please consider adding it for clarity."
(name condition)))))
(define-condition bad-docs (warning)
((function-docs
:initarg :doc
:reader function-doc))
(:report (lambda (condition stream)
(if (function-doc condition)
(cond ((not (stringp (function-doc condition)))
(format stream "Only string documentation is allowed."))
((>= 15 (length (function-doc condition)))
(format stream "Such short documentation probably will not be enough for user. Consider a more detailed description.")))
(format stream "No documentation is provided. Consider adding it.")))))
(defmacro defun/doc (name lambda-list body &key type documentation)
(if type
(let ((transformed-type (validate-ftype type)))
(if transformed-type
(setf type transformed-type)
(warn 'malformed-type :name name :type type)))
(warn 'no-type :name name))
(unless (and documentation (stringp documentation) (< 15 (length documentation)))
(warn 'bad-docs :doc documentation))
(unless documentation
(setf documentation "No further documentation has been provided"))
(setf (gethash name *doc-info*) (list :type type :documentation documentation))
(ecase *type-policy*
(:declare
`(progn
(declaim ,type)
(defun ,name ,lambda-list
,documentation
,@body)))
(:docs
`(defun ,name ,lambda-list
,documentation
,@body))))
(defun/doc add (a b)
((+ a b))
:documentation "Literally just adds two numbers, that's it"
:type (-> (number number) (values number &optional)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment