blah.lisp (1748 bytes)
1 (defpackage :blah 2 (:use :common-lisp) 3 (:export #:blah #:blaher)) 4 5 (in-package :blah) 6 7 (defun blah (stream template &rest args) 8 (loop for x in (parse-blah template) do (princ (getf args x x) stream))) 9 10 (define-compiler-macro blah (&whole whole stream template &rest args) 11 (if (stringp template) 12 `(funcall (blaher ,template) ,stream ,@args) 13 whole)) 14 15 (defmacro blaher (template) 16 (let* ((template (parse-blah template)) 17 (stream (gensym "STREAM.")) 18 (params (mapcar #'(lambda (x) (list x (gensym))) 19 (remove-duplicates 20 (remove-if-not #'symbolp template))))) 21 `(lambda (,stream &key ,@(mapcar #'list params)) 22 ,@(mapcar (lambda (x) 23 `(princ ,(or (cadr (assoc x params)) x) ,stream)) 24 template)))) 25 26 (defun parse-blah (string &optional (start 0)) 27 (let* ((p1 (search "{{" string :start2 start)) 28 (p2 (and p1 (search "}}" string :start2 p1)))) 29 (cons (subseq string start (and p2 p1)) 30 (and p2 (cons (intern (string-upcase 31 (subseq string (+ p1 2) p2)) 32 :keyword) 33 (parse-blah string (+ p2 2))))))) 34 35 ;; (blaher "Hi {{person}}! I am {{computer-assistant}}, here to help you, {{person}}, with all of your problems.") 36 37 ;; macro expands to: 38 39 ;; (LAMBDA (#:STREAM.6790 &KEY ((:COMPUTER-ASSISTANT #:G6791)) 40 ;; ((:PERSON #:G6792))) 41 ;; (PRINC "Hi " #:STREAM.6790) 42 ;; (PRINC #:G6792 #:STREAM.6790) 43 ;; (PRINC "! I am " #:STREAM.6790) 44 ;; (PRINC #:G6791 #:STREAM.6790) 45 ;; (PRINC ", here to help you, " #:STREAM.6790) 46 ;; (PRINC #:G6792 #:STREAM.6790) 47 ;; (PRINC ", with all of your problems." #:STREAM.6790))