Created
February 7, 2020 23:14
-
-
Save sdilts/73a811a633bb0ef3dd7e31b84a138a5a to your computer and use it in GitHub Desktop.
Defmacro implemented in Carp
This file contains 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
(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