Skip to content

Instantly share code, notes, and snippets.

@sdilts
Created February 7, 2020 23:14
Show Gist options
  • Save sdilts/73a811a633bb0ef3dd7e31b84a138a5a to your computer and use it in GitHub Desktop.
Save sdilts/73a811a633bb0ef3dd7e31b84a138a5a to your computer and use it in GitHub Desktop.
Defmacro implemented in Carp
(defndynamic my-nth [x lst]
(if (not (= (length lst) 0))
(if (= x 0)
(car lst)
(my-nth (- x 1) (cdr lst)))
(macro-error "my-nth: x provided was too big")))
(defndynamic my-nthcdr [x lst]
(if (not (= (length lst) 0))
(if (= x 0)
lst
(my-nthcdr (- x 1) (cdr lst)))
(macro-error "my-nthcdr: No list left to cdr")))
(defndynamic make-keyword [thing]
(if (symbol? thing)
(Dynamic.Symbol.join [': thing])
()))
(defndynamic keywordp [thing]
(if (symbol? thing)
(= (Dynamic.String.char-at (str thing) 0) \:)
false))
(defndynamic build-keyword-list-binding [lambda-list source accum]
(if (not (= (length lambda-list) 0))
(let [item (car lambda-list)]
(if (keywordp item)
(macro-error "Cannot have multiple keyword designators in the same branch")
(let [lookup (make-keyword item)]
(build-keyword-list-binding (cdr lambda-list)
source
(append (list (list 'plist-get source (list 'quote lookup)) item)
accum)))))
accum))
(defndynamic build-body-list-binding [lambda-list source]
(list source (car lambda-list)))
(defndynamic build-keyword-binding [lambda-list source]
(let [item (car lambda-list)]
(if (= ':rest item)
(build-body-list-binding (cdr lambda-list) source)
(if (= ':key item)
(build-keyword-list-binding (cdr lambda-list) source ())
(macro-error (Dynamic.String.join ["Macro lambda-list keyword unrecognized " (str item)]))))))
(defndynamic build-let-binding-inner [lambda-list index source accum]
(if (not (= (length lambda-list) 0))
(let [item (car lambda-list)]
(if (list? item)
(let [nested-loc (gensym-with 'build-let)]
(append (Dynamic.append (build-let-binding-inner item 0 nested-loc ())
(list (list 'my-nth index source) nested-loc))
(build-let-binding-inner (cdr lambda-list) (+ 1 index) source accum)))
(if (keywordp item)
(let [nested-loc (gensym-with 'build-keyword)]
(append (append (build-keyword-binding lambda-list nested-loc)
(list (list 'my-nthcdr index source) nested-loc))
accum))
(build-let-binding-inner (cdr lambda-list) (+ 1 index) source (cons (list 'my-nth index source)
(cons item accum))))))
accum))
;; Using the given lambda-list as a guide, build a list sutible for let* that binds
;; the values in source a la destructuring-bind
(defndynamic build-let-binding [lambda-list source]
(build-let-binding-inner lambda-list 0 source ()))
;; Since we can't use macros in the dynamic context, 'unwind-let` does the same thing as
;; let*
(defndynamic unwind-let [bindings body]
(if (not (= (length bindings) 0))
(let [inital-form (car bindings)
var-name (my-nth 1 bindings)]
(unwind-let (cdr (cdr bindings)) (list (append (list 'let (array var-name inital-form))
body))))
body))
(defndynamic destructuring-bind [lambda-list source body]
(let [source-symb (gensym-with 'source)]
(append (list 'let (array source-symb source))
(unwind-let (build-let-binding lambda-list source-symb) body))))
(defdynamic *my-macros* (list))
(defmacro my-defmacro [name lambda-list :rest body]
(list 'defdynamic '*my-macros*
(list 'Dynamic.plist-set '*my-macros*
(list 'quote name) (list 'fn (array 'form)
;; the input list is the second value in the form:
(destructuring-bind lambda-list '(cdr form)
body)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment