Skip to content

Instantly share code, notes, and snippets.

@ehaliewicz
Last active January 1, 2016 23:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ehaliewicz/8215370 to your computer and use it in GitHub Desktop.
Save ehaliewicz/8215370 to your computer and use it in GitHub Desktop.
A macro that generates enumeration types and functions to parse strings into enumerations.
;; load a regex library
(ql:quickload "cl-ppcre")
;; utility functions
;; concatenate any number of any type of object into a string
(defun mkstr (&rest args)
(with-output-to-string (s)
(dolist (a args) (princ a s))))
;; convert a string into a symbol
(defun symb (&rest args)
(values (intern (apply #'mkstr args))))
;; generates an 'enumeration' type along with code to parse strings into the enumerated symbols,
;; as well as predicate functions to for each enumeration as well as the whole type
(defmacro defenum (name &rest enumerations)
(let ((func-names (mapcar (lambda (enum) (symb enum '-p)) enumerations)))
`(progn
(defun ,(symb name '-p) (sym)
(member sym ',enumerations))
,@(mapcar (lambda (name enum)
`(defun ,name (sym)
(eq sym ',enum)))
func-names
enumerations)
(deftype name () '(or ,@enumerations))
(defgeneric ,(symb 'to- name) (str)
(:documentation ,(format nil "Converts a string to an enumeration of ~a." name)))
(defmethod ,(symb 'to- name) ((string string))
(cond
,@(mapcar
(lambda (enum)
`((funcall (cl-ppcre:create-scanner ,(string enum) :case-insensitive-mode t) string 0 ,(length (string enum))) ,enum))
enumerations)
(t (error (format nil "~s is not one of ~a" string ',enumerations))))))))
(defenum weekday
:monday
:tuesday
:wednesday
:thursday
:friday
:saturday
:sunday)
;; generated code for the above expression
(progn
(defun weekday-p (sym)
(member sym '(:monday :tuesday :wednesday :thursday :friday :saturday :sunday)))
(defun monday-p (sym) (eq sym ':monday))
(defun tuesday-p (sym) (eq sym ':tuesday))
(defun wednesday-p (sym) (eq sym ':wednesday))
(defun thursday-p (sym) (eq sym ':thursday))
(defun friday-p (sym) (eq sym ':friday))
(defun saturday-p (sym) (eq sym ':saturday))
(defun sunday-p (sym) (eq sym ':sunday))
(deftype name ()
'(or :monday :tuesday :wednesday :thursday :friday :saturday :sunday))
(defgeneric to-weekday
(str)
(:documentation "converts a string to an enumeration of weekday."))
(defmethod to-weekday ((string string))
(cond
((funcall (create-scanner "monday" :case-insensitive-mode t) string 0 6)
:monday)
((funcall (create-scanner "tuesday" :case-insensitive-mode t) string 0 7)
:tuesday)
((funcall (create-scanner "wednesday" :case-insensitive-mode t) string 0 9)
:wednesday)
((funcall (create-scanner "thursday" :case-insensitive-mode t) string 0 8)
:thursday)
((funcall (create-scanner "friday" :case-insensitive-mode t) string 0 6)
:friday)
((funcall (create-scanner "saturday" :case-insensitive-mode t) string 0 8)
:saturday)
((funcall (create-scanner "sunday" :case-insensitive-mode t) string 0 6)
:sunday)
(t
(error
(format nil "~s is not one of ~a" string
'(:monday :tuesday :wednesday :thursday :friday :saturday
:sunday)))))))
(weekday-p "wednesday")
=> NIL
(to-weekday "wednesday")
=> :wednesday
(weekday-p (to-weekday "wednesday"))
=> T
;; you can also use the defined type for type declarations
(defun do-something-with-a-weekday (day)
;; will throw an error if it's not one of the above enumerations
;; (or not even compile with a good enough lisp implementation)
(declare (type weekday day))
...
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment