Last active
January 1, 2016 23:18
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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