Recently Written · git

blaher

Gilbert Baumann's blaher

git clone https://github.com/equwal/blaher

Log | Files | Refs


commit 890ea7f6c2894ae370964aff70f3a1549fe5e87b
Spenser Truex <truex@equwal.com>
2023-05-27 18:31:25 -0300

blah v1

 blah.lisp | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)
diff --git a/blah.lisp b/blah.lisp
new file mode 100644
index 0000000..787b0ff
--- /dev/null
+++ b/blah.lisp
@@ -0,0 +1,63 @@
+(defun blah (stream template &rest args)
+  (multiple-value-bind (format keys) (parse-blah template)
+    (apply #'format stream format (loop for key in keys
+                                        collect (getf args key)))))
+
+(define-compiler-macro blah (&whole whole stream template &rest args)
+  (cond ((stringp template)             ;constantp?
+         `(funcall (blaher ,template) ,stream ,@args))
+        (t
+         whole)))
+
+(defmacro blaher (template)
+  (multiple-value-bind (format keys) (parse-blah template)
+    (let ((stream (gensym "STREAM."))
+          (args (gensym "ARGS."))
+          (params (mapcar #'(lambda (x) (list x (gensym)))
+                          (remove-duplicates keys))))
+      `(lambda (,stream &key ,@(mapcar #'list params))
+         (format ,stream ,format ,@(mapcar #'(lambda (key)
+                                               (cadr (assoc key params)))
+                                           keys))))))
+
+(defun parse-blah (string &optional (start 0) (end (length string)))
+  (labels ((format-escape (string)
+             (with-output-to-string (bag)
+               (loop for c across string do
+                     (when (eql c #\~) (write-char c bag))
+                     (write-char c bag)))))
+    (let* ((p1 (search "{{" string :start2 start :end2 end))
+           (p2 (and p1 (search "}}" string :start2 p1 :end2 end))))
+      (cond ((and p1 p2)
+             (multiple-value-bind (format args)
+                 (parse-blah string (+ p2 2) end)
+               (values (format nil "~A~~A~A"
+                               (format-escape (subseq string start p1))
+                               format)
+                       (cons (intern (string-upcase
+                                      (subseq string (+ p1 2) p2))
+                                     :keyword)
+                             args))))
+            (t
+             (values (format-escape (subseq string start end)) nil))))))
+
+;; (blah nil "Hi {{person}}!" :person "Joe") => "Hi Joe!"
+
+;; (blah nil "Hi {{person}}! I am {{computer-assistant}}, here to help you, {{person}}, with all of your problems."
+;;      :person "Fred"
+;;      :computer-assistant "Eliza")
+
+;; => "Hi Fred! I am Eliza, here to help you, Fred, with all of your problems."
+
+;; (blaher "Hi {{person}}! I am {{computer-assistant}}, here to help you, {{person}}, with all of your problems.")
+
+;; macro expands to:
+
+;; (LAMBDA (#:STREAM.5900 &KEY ((:COMPUTER-ASSISTANT #:G5902))
+;;          ((:PERSON #:G5903)))
+;;   (FORMAT #:STREAM.5900
+;;           "Hi ~A! I am ~A, here to help you, ~A, with all of your problems."
+;;           #:G5903
+;;           #:G5902
+;;           #:G5903))
+