changelog shortlog graph tags branches files raw help

Mercurial > core / changeset: init std

changeset 5: 6ce26a70a11e
parent 4: 7e5b3feb2c38
child 6: 32ed719c5189
author: ellis <ellis@rwest.io>
date: Mon, 16 Oct 2023 19:33:42 -0400
files: .hgsub .hgsubstate lisp/ffi/rocksdb/rocksdb.lisp lisp/lib/cli/tests.lisp lisp/lib/rt/tests.lisp lisp/std.asd lisp/std/alien.lisp lisp/std/all.lisp lisp/std/ana.lisp lisp/std/cond.lisp lisp/std/fmt.lisp lisp/std/fs.lisp lisp/std/fu.lisp lisp/std/list.lisp lisp/std/log.lisp lisp/std/named-readtables.lisp lisp/std/pan.lisp lisp/std/pkg.lisp lisp/std/str.lisp lisp/std/sym.lisp lisp/std/tests.lisp lisp/std/thread.lisp
description: init std
     1.1--- a/.hgsub	Sun Oct 15 22:00:24 2023 -0400
     1.2+++ b/.hgsub	Mon Oct 16 19:33:42 2023 -0400
     1.3@@ -1,3 +1,2 @@
     1.4-lisp/macs = ssh://git@lab.rwest.io/ellis/macs
     1.5 lisp/lib/skel = ssh://git@lab.rwest.io/ellis/skel
     1.6 lisp/lib/organ = ssh://git@lab.rwest.io/ellis/organ
     1.7\ No newline at end of file
     2.1--- a/.hgsubstate	Sun Oct 15 22:00:24 2023 -0400
     2.2+++ b/.hgsubstate	Mon Oct 16 19:33:42 2023 -0400
     2.3@@ -1,3 +1,2 @@
     2.4 fed991c01753ff72fe66954786e85ba813138521 lisp/lib/organ
     2.5 2917dbd4a30bd52fe8ca84f8395105f2bdd6da6c lisp/lib/skel
     2.6-f3f22d17b55b23a50be8b5b97aa1c80d21547bee lisp/macs
     3.1--- a/lisp/ffi/rocksdb/rocksdb.lisp	Sun Oct 15 22:00:24 2023 -0400
     3.2+++ b/lisp/ffi/rocksdb/rocksdb.lisp	Mon Oct 16 19:33:42 2023 -0400
     3.3@@ -136,7 +136,7 @@
     3.4 (define-alien-type rocksdb-errptr (* (* t)))
     3.5 
     3.6 ;;; Cache
     3.7-(define-alien-routine rocksdb-cache-create-lru (* rocksdb) (capacity unsigned-int))
     3.8+(define-alien-routine rocksdb-cache-create-lru (* rocksdb) (capacity u32))
     3.9 
    3.10 ;;; Options
    3.11 
    3.12@@ -152,43 +152,43 @@
    3.13   (val c-string))
    3.14 
    3.15 ;;;; db
    3.16-(define-alien-routine rocksdb-options-create rocksdb-options)
    3.17+(define-alien-routine rocksdb-options-create (* rocksdb-options))
    3.18 (define-alien-routine rocksdb-options-destroy void 
    3.19   (options rocksdb-options))
    3.20 (define-alien-routine rocksdb-options-increase-parallelism void 
    3.21-  (opt rocksdb-options) (total-threads int))
    3.22+  (opt (* rocksdb-options)) (total-threads int))
    3.23 (define-alien-routine rocksdb-options-optimize-level-style-compaction void 
    3.24-  (opt rocksdb-options) 
    3.25-  (memtable_memory_budget (unsigned 4)))
    3.26+  (opt (* rocksdb-options))
    3.27+  (memtable-memory-budget u64))
    3.28 (define-alien-routine rocksdb-options-set-create-if-missing void 
    3.29-  (opt rocksdb-options) 
    3.30+  (opt (* rocksdb-options))
    3.31   (val boolean))
    3.32 (define-alien-routine rocksdb-options-set-block-based-table-factory void
    3.33-  (opt rocksdb-options)
    3.34-  (table-options rocksdb-block-based-table-options))
    3.35+  (opt (* rocksdb-options))
    3.36+  (table-options (* rocksdb-block-based-table-options)))
    3.37 ;;;; write
    3.38-(define-alien-routine rocksdb-writeoptions-create rocksdb-writeoptions)
    3.39+(define-alien-routine rocksdb-writeoptions-create (* rocksdb-writeoptions))
    3.40 (define-alien-routine rocksdb-writeoptions-destroy void
    3.41-  (opt rocksdb-writeoptions))
    3.42+  (opt (* rocksdb-writeoptions)))
    3.43 ;;;; read
    3.44-(define-alien-routine rocksdb-readoptions-create rocksdb-readoptions)
    3.45+(define-alien-routine rocksdb-readoptions-create (* rocksdb-readoptions))
    3.46 (define-alien-routine rocksdb-readoptions-destroy void
    3.47-  (opt rocksdb-readoptions))
    3.48+  (opt (* rocksdb-readoptions)))
    3.49 
    3.50 ;;; DB
    3.51-(define-alien-routine rocksdb-open rocksdb 
    3.52-  (opt rocksdb-options) 
    3.53+(define-alien-routine rocksdb-open (* rocksdb)
    3.54+  (opt (* rocksdb-options))
    3.55   (name c-string) 
    3.56   (errptr rocksdb-errptr))
    3.57 (define-alien-routine rocksdb-close void 
    3.58-  (db rocksdb))
    3.59+  (db (* rocksdb)))
    3.60 (define-alien-routine rocksdb-cancel-all-background-work void 
    3.61-  (db rocksdb) 
    3.62+  (db (* rocksdb))
    3.63   (wait boolean))
    3.64 
    3.65 (define-alien-routine rocksdb-put void 
    3.66-  (db rocksdb) 
    3.67-  (options rocksdb-writeoptions) 
    3.68+  (db (* rocksdb))
    3.69+  (options (* rocksdb-writeoptions))
    3.70   (key (* char))
    3.71   (keylen size-t) 
    3.72   (val (* char))
    3.73@@ -196,40 +196,40 @@
    3.74   (errptr rocksdb-errptr))
    3.75 
    3.76 (define-alien-routine rocksdb-get (* char)
    3.77-  (db rocksdb) 
    3.78-  (options rocksdb-readoptions) 
    3.79+  (db (* rocksdb))
    3.80+  (options (* rocksdb-readoptions))
    3.81   (key (* char))
    3.82   (keylen size-t) 
    3.83   (vallen (* size-t))
    3.84   (errptr rocksdb-errptr))
    3.85 
    3.86 (define-alien-routine rocksdb-delete void
    3.87-  (db rocksdb)
    3.88-  (options rocksdb-writeoptions)
    3.89+  (db (* rocksdb))
    3.90+  (options (* rocksdb-writeoptions))
    3.91   (key (* char))
    3.92   (keylen size-t)
    3.93   (errptr rocksdb-errptr))
    3.94 
    3.95 ;;; Iterators
    3.96-(define-alien-routine rocksdb-create-iterator rocksdb-iterator 
    3.97-  (db rocksdb) 
    3.98-  (opt rocksdb-readoptions))
    3.99+(define-alien-routine rocksdb-create-iterator (* rocksdb-iterator)
   3.100+  (db (* rocksdb))
   3.101+  (opt (* rocksdb-readoptions)))
   3.102 (define-alien-routine rocksdb-iter-destroy void 
   3.103-  (iter rocksdb-iterator))
   3.104+  (iter (* rocksdb-iterator)))
   3.105 (define-alien-routine rocksdb-iter-seek-to-first void 
   3.106-  (iter rocksdb-iterator))
   3.107+  (iter (* rocksdb-iterator)))
   3.108 (define-alien-routine rocksdb-iter-valid boolean 
   3.109-  (iter rocksdb-iterator))
   3.110+  (iter (* rocksdb-iterator)))
   3.111 (define-alien-routine rocksdb-iter-next void 
   3.112-  (iter rocksdb-iterator))
   3.113+  (iter (* rocksdb-iterator)))
   3.114 (define-alien-routine rocksdb-iter-prev void 
   3.115-  (iter rocksdb-iterator))
   3.116+  (iter (* rocksdb-iterator)))
   3.117 (define-alien-routine rocksdb-iter-key (* char)
   3.118-  (iter rocksdb-iterator) 
   3.119+  (iter (* rocksdb-iterator))
   3.120   (klen-ptr (* size-t)))
   3.121 (define-alien-routine rocksdb-iter-value (* char) 
   3.122-  (iter rocksdb-iterator) (vlen-ptr (* size-t)))
   3.123+  (iter (* rocksdb-iterator)) (vlen-ptr (* size-t)))
   3.124 (define-alien-routine rocksdb-destroy-db void
   3.125-  (options rocksdb-options)
   3.126+  (options (* rocksdb-options))
   3.127   (name c-string) 
   3.128   (errptr rocksdb-errptr))
     4.1--- a/lisp/lib/cli/tests.lisp	Sun Oct 15 22:00:24 2023 -0400
     4.2+++ b/lisp/lib/cli/tests.lisp	Mon Oct 16 19:33:42 2023 -0400
     4.3@@ -0,0 +1,46 @@
     4.4+;; we should be able to call this from the body of the test, but we
     4.5+;; get an undefined-function error for 'MACS.RT::MAKE-PROMPT!'
     4.6+(defpkg :cli/tests
     4.7+    (:use :cl :rt :cli))
     4.8+
     4.9+(defsuite :cli)
    4.10+(in-suite :cli)
    4.11+(unless *compile-tests*
    4.12+  (deftest cli-prompt ()
    4.13+    "Test MACS.CLI prompts"
    4.14+    (make-prompt! tpfoo "testing: ")
    4.15+    (defvar tcoll nil)
    4.16+    (defvar thist nil)
    4.17+    (let ((*standard-input* (make-string-input-stream 
    4.18+			     (format nil "~A~%~A~%" "foobar" "foobar"))))
    4.19+      ;; prompts 
    4.20+      (is (string= (tpfoo-prompt) "foobar"))
    4.21+      (is (string= "foobar"
    4.22+		   (cli:completing-read "nothing: " tcoll :history thist :default "foobar"))))))
    4.23+
    4.24+(defparameter *opts* (cli:make-opts (:name foo :global t :description "bar")
    4.25+			    (:name bar :description "foo")))
    4.26+
    4.27+(defparameter *cmd1* (make-cli :cmd :name "holla" :opts *opts* :description "cmd1 description"))
    4.28+(defparameter *cmd2* (make-cli :cmd :name "ayo" :cmds #(*cmd1*) :opts *opts* :description "cmd1 description"))
    4.29+(defparameter *cmds* (cli:make-cmds (:name "baz" :description "baz" :opts *opts*)))
    4.30+
    4.31+(defparameter *cli* (make-cli t :opts *opts* :cmds *cmds* :description "test cli"))
    4.32+
    4.33+(deftest cli ()
    4.34+  "test MACS.CLI OOS."
    4.35+  (let ((cli *cli*))
    4.36+    (is (eq (make-shorty "test") #\t))
    4.37+    (is (equalp (proc-args cli '("-f" "baz" "--bar" "fax")) ;; not eql
    4.38+		(make-cli-ast 
    4.39+		 (list (make-cli-node 'opt (find-short-opt cli #\f))
    4.40+		       (make-cli-node 'cmd (find-cmd cli "baz"))
    4.41+		       (make-cli-node 'opt (find-opt cli "bar"))
    4.42+		       (make-cli-node 'arg "fax")))))
    4.43+    (is (parse-args cli '("--bar" "baz" "-f" "yaks")))
    4.44+    (is (stringp
    4.45+	 (with-output-to-string (s)
    4.46+	   (print-version cli s)
    4.47+	   (print-usage cli s)
    4.48+	   (print-help cli s))))
    4.49+    (is (string= "foobar" (parse-str-opt "foobar")))))
     5.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2+++ b/lisp/lib/rt/tests.lisp	Mon Oct 16 19:33:42 2023 -0400
     5.3@@ -0,0 +1,17 @@
     5.4+;;; rt/tests.lisp
     5.5+(defpkg :rt/tests
     5.6+ (:use :cl :rt))
     5.7+(defsuite :rt)
     5.8+(in-suite :rt)
     5.9+(deftest rt (:bench 100 :profile t :persist nil)
    5.10+  (is (typep (make-fixture-prototype :empty nil) 'fixture-prototype))
    5.11+  (with-fixture (fx (make-fixture ((a 1) (b 2)) 
    5.12+		      (:+ () (+ (incf a) (incf b)))
    5.13+		      (:- () (- (decf a) (decf b)))
    5.14+		      (t () 0)))
    5.15+    (is (= 5 (funcall fx :+)))
    5.16+    (is (= 7 (funcall fx :+)))
    5.17+    (is (= 5 (funcall fx :-)))
    5.18+    (is (= 0 (funcall fx))))
    5.19+  (signals (error t) (test-form (make-instance 'test-result))))
    5.20+
     6.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2+++ b/lisp/std.asd	Mon Oct 16 19:33:42 2023 -0400
     6.3@@ -0,0 +1,7 @@
     6.4+;;; std.asd --- standard library
     6.5+(defsystem :std
     6.6+  :pathname "std"
     6.7+  :class :package-inferred-system
     6.8+  :depends-on (:std/all)
     6.9+  :in-order-to ((test-op (test-op "std/tests")))
    6.10+  :perform (test-op (o c) (symbol-call :rt :do-tests :std)))
     7.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2+++ b/lisp/std/alien.lisp	Mon Oct 16 19:33:42 2023 -0400
     7.3@@ -0,0 +1,106 @@
     7.4+;;; std/alien.lisp --- foreign alien friends
     7.5+
     7.6+;;; Commentary:
     7.7+
     7.8+;; FFI in Lisp is somewhat different than FFI in other host langs. As
     7.9+;; such, we usually refer to our Lispy FFI interfaces inline with the
    7.10+;; CMUCL terminology: alien interfaces.
    7.11+
    7.12+;; ref: https://www.sbcl.org/manual/#Foreign-Function-Interface for details
    7.13+
    7.14+;; sb-alien is a high-level interface which automatically converts C
    7.15+;; memory pointers to lisp objects and back, but this can be slow for
    7.16+;; large or complex objects.
    7.17+
    7.18+;; The lower-level interface is based on System Area Pointers (or
    7.19+;; SAPs), which provide untyped access to foreign memory.
    7.20+
    7.21+;; Objects which can't be automatically converted into Lisp values are
    7.22+;; represented by objects of type ALIEN-VALUE.
    7.23+
    7.24+;;; Code:
    7.25+(pkg:defpkg :std/alien
    7.26+  (:nicknames :alien)
    7.27+  (:use :cl :sb-vm :sb-ext :sb-c :str :sym :fu)
    7.28+  (:use-reexport :sb-alien)
    7.29+  (:export
    7.30+   :copy-c-string
    7.31+   :clone
    7.32+   :foreign-int-to-integer :foreign-int-to-bool :bool-to-foreign-int
    7.33+   :defbytes
    7.34+   :u1 :u2 :u3 :u4 :u8 :u16 :u24 :u32 :u64 :u128
    7.35+   :i2 :i3 :i4 :i8 :i16 :i24 :i32 :i64 :i128
    7.36+   :f16 :f24 :f32 :f64 :f128))
    7.37+
    7.38+(in-package :alien)
    7.39+
    7.40+;; (reexport-from :sb-vm
    7.41+;; 	       :include
    7.42+;; 	       '(:with-pinned-objects :with-pinned-object-iterator :with-code-pages-pinned
    7.43+;; 		 :sanctify-for-execution))
    7.44+
    7.45+(defun copy-c-string (src dest &aux (index 0))
    7.46+  (loop (let ((b (sb-sys:sap-ref-8 src index)))
    7.47+          (when (= b 0)
    7.48+            (setf (fill-pointer dest) index)
    7.49+            (return))
    7.50+          (setf (char dest index) (code-char b))
    7.51+          (incf index))))
    7.52+
    7.53+(defun clone-strings (list)
    7.54+  (with-alien ((x (* (* char))
    7.55+                  (make-alien (* char) (length list))))
    7.56+    (unwind-protect
    7.57+         (labels ((populate (list index function)
    7.58+                    (if list
    7.59+                        (let ((array (sb-ext:string-to-octets (car list) :null-terminate t)))
    7.60+                          (sb-sys:with-pinned-objects (array)
    7.61+                            (setf (deref x index) (sap-alien (sb-sys:vector-sap array) (* char)))
    7.62+                            (populate (cdr list) (1+ index) function)))
    7.63+                        (funcall function))))
    7.64+           (populate list 0
    7.65+                     (lambda ()
    7.66+                       (loop for i below (length list)
    7.67+                             do (print (cast (deref x i) c-string))))))
    7.68+      (free-alien x))))
    7.69+
    7.70+(defun foreign-int-to-integer (buffer size)
    7.71+  "Check SIZE of int BUFFER. return BUFFER."
    7.72+  (assert (= size (sb-alien:alien-size sb-alien:int :bytes)))
    7.73+  buffer)
    7.74+
    7.75+(defun foreign-int-to-bool (x size)
    7.76+  (if (zerop (foreign-int-to-integer x size))
    7.77+      nil
    7.78+      t))
    7.79+
    7.80+(defun bool-to-foreign-int (val)
    7.81+  (if val 1 0))
    7.82+
    7.83+;;; Bytes
    7.84+(defmacro defbytes (&body bitsets)
    7.85+  "For each cons-cell in BITSETS, define a new CAR-byte type for each
    7.86+member of CDR."
    7.87+  `(loop for set in ',bitsets
    7.88+	 collect
    7.89+	 (let* ((ty (car set))
    7.90+		(pfx
    7.91+		  (cond
    7.92+		    ((eq 'signed-byte ty) "I")
    7.93+		    ((eq 'unsigned-byte ty) "U")
    7.94+		    ((eq 'float ty) "F")
    7.95+		    (t (subseq (symbol-name ty) 0 1))))
    7.96+		(nums (cdr set))
    7.97+		r) ;result
    7.98+	   (setf r
    7.99+		 (mapc
   7.100+		  (lambda (x)
   7.101+		    `(deftype ,(symbolicate pfx (format 'nil "~a" x)) ()
   7.102+		       (cons ,ty ,x)))
   7.103+		       nums))
   7.104+	   (cons ty r))))
   7.105+
   7.106+(defbytes
   7.107+  (unsigned-byte 1 2 3 4 8 16 24 32 64 128)
   7.108+  (signed-byte 2 3 4 8 16 24 32 64 128)
   7.109+  (float 16 24 32 64 128))
     8.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2+++ b/lisp/std/all.lisp	Mon Oct 16 19:33:42 2023 -0400
     8.3@@ -0,0 +1,14 @@
     8.4+(defpkg :std/all
     8.5+  (:nicknames :std)
     8.6+  (:use-reexport
     8.7+   :pkg
     8.8+   :named-readtables
     8.9+   :alien
    8.10+   :ana
    8.11+   :pan
    8.12+   :log
    8.13+   :str
    8.14+   :sym
    8.15+   :list
    8.16+   :fmt
    8.17+   :fs))
     9.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2+++ b/lisp/std/ana.lisp	Mon Oct 16 19:33:42 2023 -0400
     9.3@@ -0,0 +1,82 @@
     9.4+;;; ana.lisp --- anaphoric macros
     9.5+
     9.6+;;; Code:
     9.7+(defpackage :std/ana
     9.8+  (:use :cl :readtables :fu)
     9.9+  (:nicknames :ana)
    9.10+  (:export
    9.11+   #:alambda
    9.12+   #:nlet-tail
    9.13+   #:alet%
    9.14+   #:alet
    9.15+   #:acond2
    9.16+   #:it
    9.17+   #:aif
    9.18+   #:this
    9.19+   #:self))
    9.20+
    9.21+(in-package :macs.ana)
    9.22+
    9.23+(in-readtable :std)
    9.24+
    9.25+;; Graham's alambda
    9.26+(defmacro alambda (parms &body body)
    9.27+  `(labels ((self ,parms ,@body))
    9.28+     #'self))
    9.29+
    9.30+;; Graham's aif
    9.31+(defmacro aif (test then &optional else)
    9.32+  `(let ((it ,test))
    9.33+     (if it ,then ,else)))
    9.34+
    9.35+;; ;; TODO 2023-09-05: wrap, document, optimize, hack
    9.36+;; (reexport-from :sb-int :include '(:awhen :acond))
    9.37+
    9.38+(defmacro! nlet-tail (n letargs &body body)
    9.39+  (let ((gs (loop for i in letargs
    9.40+               collect (gensym))))
    9.41+    `(macrolet
    9.42+         ((,n ,gs
    9.43+            `(progn
    9.44+               (psetq
    9.45+                ,@(apply #'nconc
    9.46+                         (mapcar
    9.47+                          #'list
    9.48+                          ',(mapcar #'car letargs)
    9.49+                          (list ,@gs))))
    9.50+               (go ,',g!n))))
    9.51+       (block ,g!b
    9.52+         (let ,letargs
    9.53+           (tagbody
    9.54+              ,g!n (return-from
    9.55+                    ,g!b (progn ,@body))))))))
    9.56+
    9.57+(defmacro alet% (letargs &rest body)
    9.58+  `(let ((this) ,@letargs)
    9.59+     (setq this ,@(last body))
    9.60+     ,@(butlast body)
    9.61+     this))
    9.62+
    9.63+(defmacro alet (letargs &rest body)
    9.64+  `(let ((this) ,@letargs)
    9.65+     (setq this ,@(last body))
    9.66+     ,@(butlast body)
    9.67+     (lambda (&rest params)
    9.68+       (apply this params))))
    9.69+
    9.70+;; swiped from fiveam. This is just like acond except it assumes that
    9.71+;; the TEST in each element of CLAUSES returns two values as opposed
    9.72+;; to one.
    9.73+(defmacro acond2 (&rest clauses)
    9.74+  (if (null clauses)
    9.75+      nil
    9.76+      (with-gensyms (val foundp)
    9.77+        (destructuring-bind ((test &rest progn) &rest others)
    9.78+            clauses
    9.79+          `(multiple-value-bind (,val ,foundp)
    9.80+               ,test
    9.81+             (if (or ,val ,foundp)
    9.82+                 (let ((it ,val))
    9.83+                   (declare (ignorable it))
    9.84+                   ,@progn)
    9.85+                 (acond2 ,@others)))))))
    10.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2+++ b/lisp/std/cond.lisp	Mon Oct 16 19:33:42 2023 -0400
    10.3@@ -0,0 +1,184 @@
    10.4+;;; cond.lisp --- Conditions
    10.5+
    10.6+;;; Code:
    10.7+(defpackage :std/cond
    10.8+  (:use :cl)
    10.9+  (:nicknames :cond)
   10.10+  (:export
   10.11+   #:nyi!
   10.12+   #:required-argument
   10.13+   #:ignore-some-conditions
   10.14+   #:simple-style-warning
   10.15+   #:simple-reader-error
   10.16+   #:simple-parse-error
   10.17+   #:simple-program-error
   10.18+   #:circular-dependency
   10.19+   #:circular-dependency-items
   10.20+   #:unknown-argument
   10.21+   #:unknown-argument-name
   10.22+   #:unknown-argument-kind
   10.23+   #:unknown-argument-p
   10.24+   #:missing-argument
   10.25+   #:missing-argument-command
   10.26+   #:missing-argument-p
   10.27+   #:invalid-argument
   10.28+   #:invalid-argument-item
   10.29+   #:invalid-argument-reason
   10.30+   #:invalid-argument-p
   10.31+   #:unwind-protect-case))
   10.32+
   10.33+(in-package :cond)
   10.34+
   10.35+(defmacro nyi! (&optional comment)
   10.36+  `(prog1
   10.37+       (error "Not Yet Implemented!")
   10.38+     (when ',comment (print ',comment))))
   10.39+
   10.40+(defun required-argument (&optional name)
   10.41+  "Signals an error for a missing argument of NAME. Intended for
   10.42+use as an initialization form for structure and class-slots, and
   10.43+a default value for required keyword arguments."
   10.44+  (error "Required argument ~@[~S ~]missing." name))
   10.45+
   10.46+(define-condition simple-style-warning (simple-warning style-warning)
   10.47+  ())
   10.48+
   10.49+(defun simple-style-warning (message &rest args)
   10.50+  (warn 'simple-style-warning :format-control message :format-arguments args))
   10.51+
   10.52+;; We don't specify a :report for simple-reader-error to let the
   10.53+;; underlying implementation report the line and column position for
   10.54+;; us. Unfortunately this way the message from simple-error is not
   10.55+;; displayed, unless there's special support for that in the
   10.56+;; implementation. But even then it's still inspectable from the
   10.57+;; debugger...
   10.58+(define-condition simple-reader-error
   10.59+    (sb-int:simple-reader-error)
   10.60+  ())
   10.61+
   10.62+(defun simple-reader-error (stream message &rest args)
   10.63+  (error 'simple-reader-error
   10.64+         :stream stream
   10.65+         :format-control message
   10.66+         :format-arguments args))
   10.67+
   10.68+(define-condition simple-parse-error (simple-error parse-error)
   10.69+  ())
   10.70+
   10.71+(defun simple-parse-error (message &rest args)
   10.72+  (error 'simple-parse-error
   10.73+         :format-control message
   10.74+         :format-arguments args))
   10.75+
   10.76+(define-condition simple-program-error (simple-error program-error)
   10.77+  ())
   10.78+
   10.79+(defun simple-program-error (message &rest args)
   10.80+  (error 'simple-program-error
   10.81+         :format-control message
   10.82+         :format-arguments args))
   10.83+
   10.84+(define-condition circular-dependency (simple-error)
   10.85+  ((items
   10.86+    :initarg :items
   10.87+    :initform (error "Must specify items")
   10.88+    :reader circular-dependency-items))
   10.89+  (:report (lambda (condition stream)
   10.90+             (declare (ignore condition))
   10.91+             (format stream "Circular dependency detected")))
   10.92+  (:documentation "A condition which is signalled when a circular dependency is encountered."))
   10.93+
   10.94+(define-condition unknown-argument (error)
   10.95+  ((name
   10.96+    :initarg :name
   10.97+    :initform (error "Must specify argument name")
   10.98+    :reader unknown-argument-name)
   10.99+   (kind
  10.100+    :initarg :kind
  10.101+    :initform (error "Must specify argument kind")
  10.102+    :reader unknown-argument-kind))
  10.103+  (:report (lambda (condition stream)
  10.104+             (format stream "Unknown argument ~A of kind ~A"
  10.105+                     (unknown-argument-name condition)
  10.106+                     (unknown-argument-kind condition))))
  10.107+  (:documentation "A condition which is signalled when an unknown argument is encountered."))
  10.108+
  10.109+(defun unknown-argument-p (value)
  10.110+  (typep value 'unknown-argument))
  10.111+
  10.112+(define-condition missing-argument (simple-error)
  10.113+  ((item
  10.114+    :initarg :item
  10.115+    :initform (error "Must specify argument item")
  10.116+    :reader missing-argument-item)
  10.117+   (command
  10.118+    :initarg :command
  10.119+    :initform (error "Must specify command")
  10.120+    :reader missing-argument-command))
  10.121+  (:report (lambda (condition stream)
  10.122+             (declare (ignore condition))
  10.123+             (format stream "Missing argument")))
  10.124+  (:documentation "A condition which is signalled when an option expects an argument, but none was provided"))
  10.125+
  10.126+(defun missing-argument-p (value)
  10.127+  (typep value 'missing-argument))
  10.128+
  10.129+(define-condition invalid-argument (simple-error)
  10.130+  ((item
  10.131+    :initarg :item
  10.132+    :initform (error "Must specify argument item")
  10.133+    :reader invalid-argument-item
  10.134+    :documentation "The argument which is identified as invalid")
  10.135+   (reason
  10.136+    :initarg :reason
  10.137+    :initform (error "Must specify reason")
  10.138+    :reader invalid-argument-reason
  10.139+    :documentation "The reason why this argument is invalid"))
  10.140+  (:report (lambda (condition stream)
  10.141+             (format stream "Invalid argument: ~A~%Reason: ~A" (invalid-argument-item condition) (invalid-argument-reason condition))))
  10.142+  (:documentation "A condition which is signalled when an argument is identified as invalid."))
  10.143+
  10.144+(defmacro ignore-some-conditions ((&rest conditions) &body body)
  10.145+  "Similar to CL:IGNORE-ERRORS but the (unevaluated) CONDITIONS
  10.146+list determines which specific conditions are to be ignored."
  10.147+  `(handler-case
  10.148+       (progn ,@body)
  10.149+     ,@(loop for condition in conditions collect
  10.150+             `(,condition (c) (values nil c)))))
  10.151+
  10.152+(defmacro unwind-protect-case ((&optional abort-flag) protected-form &body clauses)
  10.153+  "Like CL:UNWIND-PROTECT, but you can specify the circumstances that
  10.154+the cleanup CLAUSES are run.
  10.155+
  10.156+  clauses ::= (:NORMAL form*)* | (:ABORT form*)* | (:ALWAYS form*)*
  10.157+
  10.158+Clauses can be given in any order, and more than one clause can be
  10.159+given for each circumstance. The clauses whose denoted circumstance
  10.160+occured, are executed in the order the clauses appear.
  10.161+
  10.162+ABORT-FLAG is the name of a variable that will be bound to T in
  10.163+CLAUSES if the PROTECTED-FORM aborted preemptively, and to NIL
  10.164+otherwise.
  10.165+
  10.166+Examples:
  10.167+
  10.168+  (unwind-protect-case ()
  10.169+       (protected-form)
  10.170+     (:normal (format t \"This is only evaluated if PROTECTED-FORM executed normally.~%\"))
  10.171+     (:abort  (format t \"This is only evaluated if PROTECTED-FORM aborted preemptively.~%\"))
  10.172+     (:always (format t \"This is evaluated in either case.~%\")))
  10.173+
  10.174+  (unwind-protect-case (aborted-p)
  10.175+       (protected-form)
  10.176+     (:always (perform-cleanup-if aborted-p)))
  10.177+"
  10.178+  (check-type abort-flag (or null symbol))
  10.179+  (let ((gflag (gensym "FLAG+")))
  10.180+    `(let ((,gflag t))
  10.181+       (unwind-protect (multiple-value-prog1 ,protected-form (setf ,gflag nil))
  10.182+	 (let ,(and abort-flag `((,abort-flag ,gflag)))
  10.183+	   ,@(loop for (cleanup-kind . forms) in clauses
  10.184+		   collect (ecase cleanup-kind
  10.185+			     (:normal `(when (not ,gflag) ,@forms))
  10.186+			     (:abort  `(when ,gflag ,@forms))
  10.187+			     (:always `(progn ,@forms)))))))))
    11.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2+++ b/lisp/std/fmt.lisp	Mon Oct 16 19:33:42 2023 -0400
    11.3@@ -0,0 +1,168 @@
    11.4+;;; std/fmt.lisp --- printer and format utils
    11.5+
    11.6+;;; Code:
    11.7+(defpackage :std/fmt
    11.8+  (:nicknames :fmt)
    11.9+  (:use :cl :str :fu :list)
   11.10+  (:import-from :uiop :println)
   11.11+  (:export :printer-status :fmt-row :fmt-sxhash :iprintln :fmt-tree))
   11.12+
   11.13+(in-package :fmt)
   11.14+
   11.15+(defun iprintln (x &optional (n 2) stream)
   11.16+  (println (format nil "~A~A" (make-string n :initial-element #\Space) x) stream))
   11.17+
   11.18+(defun printer-status ()
   11.19+  (format t ";;           *print-array* = ~a~%" *print-array*)
   11.20+  (format t ";;            *print-base* = ~a~%" *print-base*)
   11.21+  (format t ";;            *print-case* = ~a~%" *print-case*)
   11.22+  (format t ";;          *print-circle* = ~a~%" *print-circle*)
   11.23+  (format t ";;          *print-escape* = ~a~%" *print-escape*)
   11.24+  (format t ";;          *print-gensym* = ~a~%" *print-gensym*)
   11.25+  (format t ";;          *print-length* = ~a~%" *print-length*)
   11.26+  (format t ";;           *print-level* = ~a~%" *print-level*)
   11.27+  (format t ";;           *print-lines* = ~a~%" *print-lines*)
   11.28+  (format t ";;     *print-miser-width* = ~a~%" *print-miser-width*)
   11.29+  (format t ";; *print-pprint-dispatch* = ~a~%" *print-pprint-dispatch*)
   11.30+  (format t ";;          *print-pretty* = ~a~%" *print-pretty*)
   11.31+  (format t ";;           *print-radix* = ~a~%" *print-radix*)
   11.32+  (format t ";;        *print-readably* = ~a~%" *print-readably*)
   11.33+  (format t ";;    *print-right-margin* = ~a~%" *print-right-margin*))
   11.34+
   11.35+;;; Tables
   11.36+(defun fmt-row (data)
   11.37+  (format nil "| ~{~A~^ | ~} |~%" data))
   11.38+
   11.39+;;; IDs
   11.40+(defun fmt-sxhash (code)
   11.41+  "Turn the fixnum value CODE into a human-friendly string. CODE should
   11.42+be produced by `sxhash'."
   11.43+  (let (r)
   11.44+    (dotimes (i 8 r)
   11.45+      (push (ldb (byte 8 (* i 8)) code) r))
   11.46+    (format
   11.47+     nil
   11.48+     "~{~A~^-~}"
   11.49+     (mapcar
   11.50+      (lambda (x) (format nil "~{~(~2,'0x~)~}" x))
   11.51+      (group r 2)))))
   11.52+
   11.53+;;; ASCII
   11.54+
   11.55+;;;; Trees
   11.56+
   11.57+;; from https://gist.github.com/WetHat/9682b8f70f0241c37cd5d732784d1577
   11.58+
   11.59+;; Example:
   11.60+
   11.61+;; (let ((tree '(A B1 B2 (B3 C1) C2)))
   11.62+;;     ; enumerate all layout options and draw the tree for each one.
   11.63+;;     (dolist (layout '(:up :centered :down))
   11.64+;;         (format t "Layout = :~A~%" layout)
   11.65+;;         (fmt-tree t tree :layout layout)))
   11.66+
   11.67+;; Layout = :UP
   11.68+;;  ╭─ C2
   11.69+;;  │   ╭─ C1
   11.70+;;  ├─ B3
   11.71+;;  ├─ B2
   11.72+;;  ├─ B1
   11.73+;;  A
   11.74+;; Layout = :CENTERED
   11.75+;;  ╭─ B2
   11.76+;;  ├─ B1
   11.77+;;  A
   11.78+;;  ├─ B3
   11.79+;;  │   ╰─ C1
   11.80+;;  ╰─ C2
   11.81+;; Layout = :DOWN
   11.82+;;  A
   11.83+;;  ├─ B1
   11.84+;;  ├─ B2
   11.85+;;  ├─ B3
   11.86+;;  │   ╰─ C1
   11.87+;;  ╰─ C2
   11.88+
   11.89+;;                       Unicode    plain ASCII representation
   11.90+(defvar *space*      "    ")
   11.91+(defvar *upper-knee* " ╭─ ") ; " .- "
   11.92+(defvar *pipe*       " │  ") ; " |  "
   11.93+(defvar *tee*        " ├─ ") ; " +- "
   11.94+(defvar *lower-knee* " ╰─ ") ; " '- "
   11.95+
   11.96+(defun format-tree-segments (node &key (layout :centered)
   11.97+                                       (node-formatter #'write-to-string))
   11.98+  (unless node
   11.99+    (return-from format-tree-segments nil)) ; nothing to do here
  11.100+  (setq node (ensure-cons node))
  11.101+  (flet ((prefix-node-strings (child-node &key layout node-formatter
  11.102+                                                 (upper-connector *pipe*)
  11.103+                                                 (root-connector  *tee*)
  11.104+                                                 (lower-connector *pipe*))
  11.105+                "A local utility to add connectors to a string representation
  11.106+                 of a tree segment to connect it to other tree segments."
  11.107+                (multiple-value-bind (u r l)
  11.108+                    (format-tree-segments child-node
  11.109+                        :layout         layout
  11.110+                        :node-formatter node-formatter)
  11.111+                    ; prefix tree segment with connector glyphs to connect it to
  11.112+                    ; other segments.
  11.113+                    (nconc
  11.114+                        (mapcar
  11.115+                            (lambda (str) (concatenate 'string upper-connector str))
  11.116+                            u)
  11.117+                         (list (concatenate 'string root-connector r))
  11.118+                         (mapcar
  11.119+                             (lambda (str) (concatenate 'string lower-connector str))
  11.120+                             l)))))
  11.121+        (let* ((children (rest node))
  11.122+              (pivot (case layout ; the split point of the list of children
  11.123+                         (:up   (length children)) ; split at top
  11.124+                         (:down 0)                 ; split at bottom
  11.125+                         (otherwise (round (/ (length children) 2))))) ; bisect
  11.126+              (upper-children (reverse (subseq children 0 pivot))) ; above root
  11.127+              (lower-children (subseq children pivot))) ; nodes below root
  11.128+        (values ; compile multiple value return of upper-children root lower children
  11.129+            (when upper-children
  11.130+                (loop with top = (prefix-node-strings (first upper-children)
  11.131+                                     :layout layout
  11.132+                                     :node-formatter node-formatter
  11.133+                                     :upper-connector *space*
  11.134+                                     :root-connector  *upper-knee*) ; top node has special connectors
  11.135+                    for child-node in (rest upper-children)
  11.136+                    nconc (prefix-node-strings child-node
  11.137+                              :layout layout
  11.138+                              :node-formatter node-formatter)
  11.139+                    into strlist
  11.140+                    finally (return (nconc top strlist))))
  11.141+            (let ((root-name (funcall node-formatter (car node)))) ; root node
  11.142+                (if (= 1 (length root-name))
  11.143+                    (concatenate 'string " " root-name) ; at least 2 chars needed
  11.144+                 ;else
  11.145+                     root-name))
  11.146+            (when lower-children
  11.147+                (loop for (head . tail) on lower-children
  11.148+                    while tail ; omit the last child
  11.149+                    nconc (prefix-node-strings head
  11.150+                              :layout layout
  11.151+                              :node-formatter node-formatter)
  11.152+                    into strlist
  11.153+                    finally (return
  11.154+                                (nconc
  11.155+                                    strlist
  11.156+                                    ; bottom node has special connectors
  11.157+                                    (prefix-node-strings head
  11.158+                                        :layout layout
  11.159+                                        :node-formatter  node-formatter
  11.160+                                        :root-connector  *lower-knee*
  11.161+                                        :lower-connector *space*)))))))))
  11.162+
  11.163+(defun fmt-tree (stream root &key 
  11.164+			       (plist nil)
  11.165+			       (layout :centered)
  11.166+                               (node-formatter #'write-to-string))
  11.167+    (multiple-value-bind (u r l)
  11.168+        (format-tree-segments (if plist (cons (car root) (group (cdr root) 2)) root)
  11.169+                              :layout layout
  11.170+                              :node-formatter node-formatter)
  11.171+        (format stream "~{~A~%~}" (nconc u (list r) l))))
    12.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2+++ b/lisp/std/fs.lisp	Mon Oct 16 19:33:42 2023 -0400
    12.3@@ -0,0 +1,16 @@
    12.4+;;; std/fs.lisp --- Filesystem utils
    12.5+
    12.6+;; TODO
    12.7+
    12.8+;;; Commentary:
    12.9+
   12.10+;; I think PN has a 'portable pathname library' in PAICL or
   12.11+;; something. oldie but goodie for reference.
   12.12+
   12.13+;;; Code:
   12.14+(defpackage :std/fs
   12.15+  (:nicknames :fs)
   12.16+  (:use :cl :str :cond :fu)
   12.17+  (:export))
   12.18+
   12.19+(in-package :fs)
    13.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2+++ b/lisp/std/fu.lisp	Mon Oct 16 19:33:42 2023 -0400
    13.3@@ -0,0 +1,968 @@
    13.4+;;; fu.lisp --- Function utilities
    13.5+
    13.6+;;; Code:
    13.7+(defpackage :std/fu
    13.8+  (:nicknames :fu)
    13.9+  (:use :cl :sb-mop :sb-c :named-readtables :sym :list :cond)
   13.10+  (:export
   13.11+   :until
   13.12+   #:mkstr
   13.13+   #:symb
   13.14+   #:group
   13.15+   #:flatten
   13.16+   #:fact
   13.17+   #:choose
   13.18+   #:g!-symbol-p
   13.19+   #:defmacro/g!
   13.20+   #:o!-symbol-p
   13.21+   #:o!-symbol-to-g!-symbol
   13.22+   #:defmacro!
   13.23+   #:defun!
   13.24+   #:|#"-reader|
   13.25+   #:|#`-reader|
   13.26+   #:|#f-reader|
   13.27+   #:|#$-reader|
   13.28+   #:segment-reader
   13.29+   #:match-mode-ppcre-lambda-form
   13.30+   #:subst-mode-ppcre-lambda-form
   13.31+   #+cl-ppcre #:|#~-reader|
   13.32+   #:dlambda
   13.33+   #:make-tlist
   13.34+   #:tlist-left
   13.35+   #:tlist-right
   13.36+   #:tlist-empty-p
   13.37+   #:tlist-add-left
   13.38+   #:tlist-add-right
   13.39+   #:tlist-rem-left
   13.40+   #:tlist-update
   13.41+   #:build-batcher-sn
   13.42+   #:sortf
   13.43+   #:dollar-symbol-p
   13.44+   #:if-match
   13.45+   #:when-match
   13.46+   #:once-only
   13.47+   #:destructuring-case
   13.48+   #:destructuring-ccase
   13.49+   #:destructuring-ecase
   13.50+   #:when-let
   13.51+   #:when-let*
   13.52+   #:if-let
   13.53+   #:if-let*
   13.54+   :define-constant
   13.55+   :def!
   13.56+   :eval-always
   13.57+   :merge! :sort!
   13.58+   :list-slot-values-using-class :list-class-methods :list-class-slots :list-indirect-slot-methods))
   13.59+
   13.60+(in-package :fu)  
   13.61+
   13.62+;;; Misc
   13.63+(defmacro until (condition &body body)
   13.64+  (let ((block-name (gensym)))
   13.65+    `(block ,block-name
   13.66+       (loop
   13.67+	 (if ,condition
   13.68+	     (return-from ,block-name nil)
   13.69+	     (progn ,@body))))))
   13.70+
   13.71+;;; From LOL
   13.72+
   13.73+(defun group (source n)
   13.74+  (when (zerop n) (error "zero length"))
   13.75+  (labels ((rec (source acc)
   13.76+             (let ((rest (nthcdr n source)))
   13.77+               (if (consp rest)
   13.78+                   (rec rest (cons
   13.79+                               (subseq source 0 n)
   13.80+                               acc))
   13.81+                   (nreverse
   13.82+                     (cons source acc))))))
   13.83+    (if source (rec source nil) nil)))
   13.84+
   13.85+(eval-when (:compile-toplevel :execute :load-toplevel)
   13.86+  (defun mkstr (&rest args)
   13.87+    (with-output-to-string (s)
   13.88+      (dolist (a args) (princ a s))))
   13.89+
   13.90+  (defun symb (&rest args)
   13.91+    (values (intern (apply #'mkstr args))))
   13.92+
   13.93+  (defun flatten (x)
   13.94+    (labels ((rec (x acc)
   13.95+                  (cond ((null x) acc)
   13.96+                        #+sbcl
   13.97+                        ((typep x 'sb-impl::comma) (rec (sb-impl::comma-expr x) acc))
   13.98+                        ((atom x) (cons x acc))
   13.99+                        (t (rec
  13.100+                             (car x)
  13.101+                             (rec (cdr x) acc))))))
  13.102+      (rec x nil)))
  13.103+
  13.104+  (defun g!-symbol-p (s)
  13.105+    (and (symbolp s)
  13.106+         (> (length (symbol-name s)) 2)
  13.107+         (string= (symbol-name s)
  13.108+                  "G!"
  13.109+                  :start1 0
  13.110+                  :end1 2)))
  13.111+
  13.112+  (defun o!-symbol-p (s)
  13.113+    (and (symbolp s)
  13.114+         (> (length (symbol-name s)) 2)
  13.115+         (string= (symbol-name s)
  13.116+                  "O!"
  13.117+                  :start1 0
  13.118+                  :end1 2)))
  13.119+
  13.120+  (defun o!-symbol-to-g!-symbol (s)
  13.121+    (symb "G!"
  13.122+          (subseq (symbol-name s) 2))))
  13.123+
  13.124+(defmacro defmacro/g! (name args &rest body)
  13.125+  (let ((syms (remove-duplicates
  13.126+               (remove-if-not #'g!-symbol-p
  13.127+                              (flatten body)))))
  13.128+    (multiple-value-bind (body declarations docstring)
  13.129+        (parse-body body :documentation t)
  13.130+      `(defmacro ,name ,args
  13.131+         ,@(when docstring
  13.132+             (list docstring))
  13.133+         ,@declarations
  13.134+         (let ,(mapcar
  13.135+                (lambda (s)
  13.136+                  `(,s (gensym ,(subseq
  13.137+                                 (symbol-name s)
  13.138+                                 2))))
  13.139+                syms)
  13.140+           ,@body)))))
  13.141+
  13.142+(defmacro defmacro! (name args &rest body)
  13.143+  (let* ((os (remove-if-not #'o!-symbol-p (flatten args)))
  13.144+         (gs (mapcar #'o!-symbol-to-g!-symbol os)))
  13.145+    (multiple-value-bind (body declarations docstring)
  13.146+        (parse-body body :documentation t)
  13.147+      `(defmacro/g! ,name ,args
  13.148+         ,@(when docstring
  13.149+            (list docstring))
  13.150+         ,@declarations
  13.151+         `(let ,(mapcar #'list (list ,@gs) (list ,@os))
  13.152+            ,(progn ,@body))))))
  13.153+
  13.154+(defmacro defun! (name args &body body)
  13.155+  (let ((syms (remove-duplicates
  13.156+               (remove-if-not #'g!-symbol-p
  13.157+                              (flatten body)))))
  13.158+    (multiple-value-bind (body declarations docstring)
  13.159+        (parse-body body :documentation t)
  13.160+      `(defun ,name ,args
  13.161+         ,@(when docstring
  13.162+             (list docstring))
  13.163+         ,@declarations
  13.164+         (let ,(mapcar (lambda (s)
  13.165+                         `(,s (gensym ,(subseq (symbol-name s)
  13.166+                                               2))))
  13.167+                       syms)
  13.168+           ,@body)))))
  13.169+
  13.170+(eval-when (:compile-toplevel :execute :load-toplevel)
  13.171+  (defun |#`-reader| (stream sub-char numarg)
  13.172+    (declare (ignore sub-char))
  13.173+    (unless numarg (setq numarg 1))
  13.174+    `(lambda ,(loop for i from 1 to numarg
  13.175+                 collect (symb 'a i))
  13.176+       ,(funcall
  13.177+         (get-macro-character #\`) stream nil)))
  13.178+
  13.179+  (defun |#f-reader| (stream sub-char numarg)
  13.180+    (declare (ignore stream sub-char))
  13.181+    (setq numarg (or numarg 3))
  13.182+    (unless (<= numarg 3)
  13.183+      (error "Bad value for #f: ~a" numarg))
  13.184+    `(declare (optimize (speed ,numarg)
  13.185+                        (safety ,(- 3 numarg)))))
  13.186+
  13.187+  (defun |#$-reader| (stream sub-char numarg)
  13.188+    "Switch on the shell reader, parsing STREAM and returning a
  13.189+POSIX-compliant shell program as a string. In other words, this is an
  13.190+implementation of the lazy version of SHCL's #$-reader.
  13.191+
  13.192+Similar to shcl, we add some reader extensions to enable embedding
  13.193+lisp forms and other goodies.
  13.194+
  13.195+#$ x=,(* 2 2) 
  13.196+echo $x
  13.197+$#
  13.198+;; => 4"
  13.199+    (declare (ignore sub-char numarg))
  13.200+    (let (chars (state 'sh))
  13.201+      (loop do
  13.202+	(let ((c (read-char stream)))
  13.203+	  (cond 
  13.204+	    ((eq state 'sh)
  13.205+	     (when (char= c #\$) (setq state 'dolla))
  13.206+	     (push c chars))
  13.207+	    ((eq state 'dolla)
  13.208+	     (cond
  13.209+	       ((char= c #\#)
  13.210+		;; remove trailing '$'
  13.211+		(pop chars)
  13.212+		(return))
  13.213+	       (t (setq state 'sh) (push c chars)))))))
  13.214+      (coerce (nreverse chars) 'string))))
  13.215+
  13.216+;; Nestable suggestion from Daniel Herring
  13.217+(eval-when (:compile-toplevel :load-toplevel :execute)
  13.218+ (defun |#"-reader| (stream sub-char numarg)
  13.219+   (declare (ignore sub-char numarg))
  13.220+   (let (chars (state 'normal) (depth 1))
  13.221+     (loop do
  13.222+          (let ((curr (read-char stream)))
  13.223+            (cond ((eq state 'normal)
  13.224+                   (cond ((char= curr #\#)
  13.225+                          (push #\# chars)
  13.226+                          (setq state 'read-sharp))
  13.227+                         ((char= curr #\")
  13.228+                          (setq state 'read-quote))
  13.229+                         (t
  13.230+                          (push curr chars))))
  13.231+                  ((eq state 'read-sharp)
  13.232+                   (cond ((char= curr #\")
  13.233+                          (push #\" chars)
  13.234+                          (incf depth)
  13.235+                          (setq state 'normal))
  13.236+                         (t
  13.237+                          (push curr chars)
  13.238+                          (setq state 'normal))))
  13.239+                  ((eq state 'read-quote)
  13.240+                   (cond ((char= curr #\#)
  13.241+                          (decf depth)
  13.242+                          (if (zerop depth) (return))
  13.243+                          (push #\" chars)
  13.244+                          (push #\# chars)
  13.245+                          (setq state 'normal))
  13.246+                         (t
  13.247+                          (push #\" chars)
  13.248+                          (if (char= curr #\")
  13.249+                              (setq state 'read-quote)
  13.250+                              (progn
  13.251+                                (push curr chars)
  13.252+                                (setq state 'normal)))))))))
  13.253+     (coerce (nreverse chars) 'string))))
  13.254+
  13.255+; This version is from Martin Dirichs
  13.256+(eval-when (:compile-toplevel :load-toplevel :execute)
  13.257+  (defun |#>-reader| (stream sub-char numarg)
  13.258+    (declare (ignore sub-char numarg))
  13.259+    (let (chars)
  13.260+      (do ((curr (read-char stream)
  13.261+                 (read-char stream)))
  13.262+          ((char= #\newline curr))
  13.263+        (push curr chars))
  13.264+      (let ((pattern (nreverse chars))
  13.265+            output)
  13.266+        (labels ((match (pos chars)
  13.267+                   (if (null chars)
  13.268+                       pos
  13.269+                       (if (char= (nth pos pattern) (car chars))
  13.270+                           (match (1+ pos) (cdr chars))
  13.271+                           (match 0 (cdr (append (subseq pattern 0 pos) chars)))))))
  13.272+          (do (curr
  13.273+               (pos 0))
  13.274+              ((= pos (length pattern)))
  13.275+            (setf curr (read-char stream)
  13.276+                  pos (match pos (list curr)))
  13.277+            (push curr output))
  13.278+          (coerce
  13.279+           (nreverse
  13.280+            (nthcdr (length pattern) output))
  13.281+           'string))))))
  13.282+
  13.283+; (set-dispatch-macro-character #\# #\> #'|#>-reader|)
  13.284+
  13.285+(defun segment-reader (stream ch n)
  13.286+  (if (> n 0)
  13.287+    (let ((chars))
  13.288+      (do ((curr (read-char stream)
  13.289+                 (read-char stream)))
  13.290+          ((char= ch curr))
  13.291+        (push curr chars))
  13.292+      (cons (coerce (nreverse chars) 'string)
  13.293+            (segment-reader stream ch (- n 1))))))
  13.294+
  13.295+#+cl-ppcre
  13.296+(defmacro! match-mode-ppcre-lambda-form (o!args o!mods)
  13.297+ ``(lambda (,',g!str)
  13.298+     (cl-ppcre:scan-to-strings
  13.299+       ,(if (zerop (length ,g!mods))
  13.300+          (car ,g!args)
  13.301+          (format nil "(?~a)~a" ,g!mods (car ,g!args)))
  13.302+       ,',g!str)))
  13.303+
  13.304+#+cl-ppcre
  13.305+(defmacro! subst-mode-ppcre-lambda-form (o!args)
  13.306+ ``(lambda (,',g!str)
  13.307+     (cl-ppcre:regex-replace-all
  13.308+       ,(car ,g!args)
  13.309+       ,',g!str
  13.310+       ,(cadr ,g!args))))
  13.311+
  13.312+#+cl-ppcre
  13.313+(eval-when (:compile-toplevel :load-toplevel :execute)
  13.314+  (defun |#~-reader| (stream sub-char numarg)
  13.315+    (declare (ignore sub-char numarg))
  13.316+    (let ((mode-char (read-char stream)))
  13.317+      (cond
  13.318+        ((char= mode-char #\m)
  13.319+         (match-mode-ppcre-lambda-form
  13.320+          (segment-reader stream
  13.321+                          (read-char stream)
  13.322+                          1)
  13.323+          (coerce (loop for c = (read-char stream)
  13.324+                     while (alpha-char-p c)
  13.325+                     collect c
  13.326+                     finally (unread-char c stream))
  13.327+                  'string)))
  13.328+        ((char= mode-char #\s)
  13.329+         (subst-mode-ppcre-lambda-form
  13.330+          (segment-reader stream
  13.331+                          (read-char stream)
  13.332+                          2)))
  13.333+        (t (error "Unknown #~~ mode character"))))))
  13.334+
  13.335+#+cl-ppcre (set-dispatch-macro-character #\# #\~ #'|#~-reader|)
  13.336+
  13.337+(eval-when (:compile-toplevel :load-toplevel :execute)
  13.338+(defreadtable :std
  13.339+  (:merge :modern)
  13.340+  (:dispatch-macro-char #\# #\" #'|#"-reader|)
  13.341+  (:dispatch-macro-char #\# #\> #'|#>-reader|)
  13.342+  #+cl-ppcre (:dispatch-macro-char #\# #\~ #'|#~-reader|)
  13.343+  (:dispatch-macro-char #\# #\` #'|#`-reader|)
  13.344+  (:dispatch-macro-char #\# #\f #'|#f-reader|)
  13.345+  (:dispatch-macro-char #\# #\$ #'|#$-reader|)))
  13.346+
  13.347+(defmacro! dlambda (&rest ds)
  13.348+  "Dynamic dispatch lambda."
  13.349+  `(lambda (&rest ,g!args)
  13.350+     (case (car ,g!args)
  13.351+       ,@(mapcar
  13.352+           (lambda (d)
  13.353+             `(,(if (eq t (car d))
  13.354+                  t
  13.355+                  (list (car d)))
  13.356+               (apply (lambda ,@(cdr d))
  13.357+                      ,(if (eq t (car d))
  13.358+                         g!args
  13.359+                         `(cdr ,g!args)))))
  13.360+           ds))))
  13.361+
  13.362+(declaim (inline make-tlist tlist-left
  13.363+                 tlist-right tlist-empty-p))
  13.364+
  13.365+(defun make-tlist () (cons nil nil))
  13.366+(defun tlist-left (tl) (caar tl))
  13.367+(defun tlist-right (tl) (cadr tl))
  13.368+(defun tlist-empty-p (tl) (null (car tl)))
  13.369+
  13.370+(declaim (inline tlist-add-left
  13.371+                 tlist-add-right))
  13.372+
  13.373+(defun tlist-add-left (tl it)
  13.374+  (let ((x (cons it (car tl))))
  13.375+    (if (tlist-empty-p tl)
  13.376+      (setf (cdr tl) x))
  13.377+    (setf (car tl) x)))
  13.378+
  13.379+(defun tlist-add-right (tl it)
  13.380+  (let ((x (cons it nil)))
  13.381+    (if (tlist-empty-p tl)
  13.382+      (setf (car tl) x)
  13.383+      (setf (cddr tl) x))
  13.384+    (setf (cdr tl) x)))
  13.385+
  13.386+(declaim (inline tlist-rem-left))
  13.387+
  13.388+(defun tlist-rem-left (tl)
  13.389+  (if (tlist-empty-p tl)
  13.390+    (error "Remove from empty tlist")
  13.391+    (let ((x (car tl)))
  13.392+      (setf (car tl) (cdar tl))
  13.393+      (if (tlist-empty-p tl)
  13.394+        (setf (cdr tl) nil)) ;; For gc
  13.395+      (car x))))
  13.396+
  13.397+(declaim (inline tlist-update))
  13.398+
  13.399+(defun tlist-update (tl)
  13.400+  (setf (cdr tl) (last (car tl))))
  13.401+
  13.402+(defun build-batcher-sn (n)
  13.403+  (let* (network
  13.404+         (tee (ceiling (log n 2)))
  13.405+         (p (ash 1 (- tee 1))))
  13.406+    (loop while (> p 0) do
  13.407+      (let ((q (ash 1 (- tee 1)))
  13.408+            (r 0)
  13.409+            (d p))
  13.410+        (loop while (> d 0) do
  13.411+          (loop for i from 0 to (- n d 1) do
  13.412+            (if (= (logand i p) r)
  13.413+              (push (list i (+ i d))
  13.414+                    network)))
  13.415+          (setf d (- q p)
  13.416+                q (ash q -1)
  13.417+                r p)))
  13.418+      (setf p (ash p -1)))
  13.419+    (nreverse network)))
  13.420+
  13.421+(in-readtable :std)
  13.422+
  13.423+(defmacro! sortf (comparator &rest places)
  13.424+  (if places
  13.425+    `(tagbody
  13.426+       ,@(mapcar
  13.427+           #`(let ((,g!a #1=,(nth (car a1) places))
  13.428+                   (,g!b #2=,(nth (cadr a1) places)))
  13.429+               (if (,comparator ,g!b ,g!a)
  13.430+                 (setf #1# ,g!b
  13.431+                       #2# ,g!a)))
  13.432+           (build-batcher-sn (length places))))))
  13.433+
  13.434+#+cl-ppcre
  13.435+(defun dollar-symbol-p (s)
  13.436+  (and (symbolp s)
  13.437+       (> (length (symbol-name s)) 1)
  13.438+       (string= (symbol-name s)
  13.439+                "$"
  13.440+                :start1 0
  13.441+                :end1 1)
  13.442+       (ignore-errors (parse-integer (subseq (symbol-name s) 1)))))
  13.443+
  13.444+
  13.445+#+cl-ppcre
  13.446+(defmacro! if-match ((match-regex str) then &optional else)
  13.447+  (let* ((dollars (remove-duplicates
  13.448+                   (remove-if-not #'dollar-symbol-p
  13.449+                                  (flatten then))))
  13.450+         (top (or (car (sort (mapcar #'dollar-symbol-p dollars) #'>))
  13.451+                  0)))
  13.452+    `(multiple-value-bind (,g!matches ,g!captures) (,match-regex ,str)
  13.453+       (declare (ignorable ,g!matches ,g!captures))
  13.454+       (let ((,g!captures-len (length ,g!captures)))
  13.455+         (declare (ignorable ,g!captures-len))
  13.456+         (symbol-macrolet ,(mapcar #`(,(symb "$" a1)
  13.457+                                       (if (< ,g!captures-len ,a1)
  13.458+                                           (error "Too few matchs: ~a unbound." ,(mkstr "$" a1))
  13.459+                                           (aref ,g!captures ,(1- a1))))
  13.460+                                   (loop for i from 1 to top collect i))
  13.461+           (if ,g!matches
  13.462+               ,then
  13.463+               ,else))))))
  13.464+
  13.465+#+cl-ppcre
  13.466+(defmacro when-match ((match-regex str) &body forms)
  13.467+  `(if-match (,match-regex ,str)
  13.468+     (progn ,@forms)))
  13.469+
  13.470+(defmacro once-only (specs &body forms)
  13.471+  "Constructs code whose primary goal is to help automate the handling of
  13.472+multiple evaluation within macros. Multiple evaluation is handled by introducing
  13.473+intermediate variables, in order to reuse the result of an expression.
  13.474+
  13.475+The returned value is a list of the form
  13.476+
  13.477+  (let ((<gensym-1> <expr-1>)
  13.478+        ...
  13.479+        (<gensym-n> <expr-n>))
  13.480+    <res>)
  13.481+
  13.482+where GENSYM-1, ..., GENSYM-N are the intermediate variables introduced in order
  13.483+to evaluate EXPR-1, ..., EXPR-N once, only. RES is code that is the result of
  13.484+evaluating the implicit progn FORMS within a special context determined by
  13.485+SPECS. RES should make use of (reference) the intermediate variables.
  13.486+
  13.487+Each element within SPECS is either a symbol SYMBOL or a pair (SYMBOL INITFORM).
  13.488+Bare symbols are equivalent to the pair (SYMBOL SYMBOL).
  13.489+
  13.490+Each pair (SYMBOL INITFORM) specifies a single intermediate variable:
  13.491+
  13.492+- INITFORM is an expression evaluated to produce EXPR-i
  13.493+
  13.494+- SYMBOL is the name of the variable that will be bound around FORMS to the
  13.495+  corresponding gensym GENSYM-i, in order for FORMS to generate RES that
  13.496+  references the intermediate variable
  13.497+
  13.498+The evaluation of INITFORMs and binding of SYMBOLs resembles LET. INITFORMs of
  13.499+all the pairs are evaluated before binding SYMBOLs and evaluating FORMS.
  13.500+
  13.501+Example:
  13.502+
  13.503+  The following expression
  13.504+
  13.505+  (let ((x '(incf y)))
  13.506+    (once-only (x)
  13.507+      `(cons ,x ,x)))
  13.508+
  13.509+  ;;; =>
  13.510+  ;;; (let ((#1=#:X123 (incf y)))
  13.511+  ;;;   (cons #1# #1#))
  13.512+
  13.513+  could be used within a macro to avoid multiple evaluation like so
  13.514+
  13.515+  (defmacro cons1 (x)
  13.516+    (once-only (x)
  13.517+      `(cons ,x ,x)))
  13.518+
  13.519+  (let ((y 0))
  13.520+    (cons1 (incf y)))
  13.521+
  13.522+  ;;; => (1 . 1)
  13.523+
  13.524+Example:
  13.525+
  13.526+  The following expression demonstrates the usage of the INITFORM field
  13.527+
  13.528+  (let ((expr '(incf y)))
  13.529+    (once-only ((var `(1+ ,expr)))
  13.530+      `(list ',expr ,var ,var)))
  13.531+
  13.532+  ;;; =>
  13.533+  ;;; (let ((#1=#:VAR123 (1+ (incf y))))
  13.534+  ;;;   (list '(incf y) #1# #1))
  13.535+
  13.536+  which could be used like so
  13.537+
  13.538+  (defmacro print-succ-twice (expr)
  13.539+    (once-only ((var `(1+ ,expr)))
  13.540+      `(format t \"Expr: ~s, Once: ~s, Twice: ~s~%\" ',expr ,var ,var)))
  13.541+
  13.542+  (let ((y 10))
  13.543+    (print-succ-twice (incf y)))
  13.544+
  13.545+  ;;; >>
  13.546+  ;;; Expr: (INCF Y), Once: 12, Twice: 12"
  13.547+  (let ((gensyms (make-gensym-list (length specs) "ONCE-ONLY"))
  13.548+        (names-and-forms (mapcar (lambda (spec)
  13.549+                                   (etypecase spec
  13.550+                                     (list
  13.551+                                      (destructuring-bind (name form) spec
  13.552+                                        (cons name form)))
  13.553+                                     (symbol
  13.554+                                      (cons spec spec))))
  13.555+                                 specs)))
  13.556+    ;; bind in user-macro
  13.557+    `(let ,(mapcar (lambda (g n) (list g `(gensym ,(string (car n)))))
  13.558+                   gensyms names-and-forms)
  13.559+       ;; bind in final expansion
  13.560+       `(let (,,@(mapcar (lambda (g n)
  13.561+                           ``(,,g ,,(cdr n)))
  13.562+                         gensyms names-and-forms))
  13.563+          ;; bind in user-macro
  13.564+          ,(let ,(mapcar (lambda (n g) (list (car n) g))
  13.565+                         names-and-forms gensyms)
  13.566+             ,@forms)))))
  13.567+
  13.568+;;;; DESTRUCTURING-*CASE
  13.569+
  13.570+(defun expand-destructuring-case (key clauses case)
  13.571+  (once-only (key)
  13.572+    `(if (typep ,key 'cons)
  13.573+         (,case (car ,key)
  13.574+           ,@(mapcar (lambda (clause)
  13.575+                       (destructuring-bind ((keys . lambda-list) &body body) clause
  13.576+                         `(,keys
  13.577+                           (destructuring-bind ,lambda-list (cdr ,key)
  13.578+                             ,@body))))
  13.579+                     clauses))
  13.580+         (error "Invalid key to DESTRUCTURING-~S: ~S" ',case ,key))))
  13.581+
  13.582+(defmacro destructuring-case (keyform &body clauses)
  13.583+  "DESTRUCTURING-CASE, -CCASE, and -ECASE are a combination of CASE and DESTRUCTURING-BIND.
  13.584+KEYFORM must evaluate to a CONS.
  13.585+
  13.586+Clauses are of the form:
  13.587+
  13.588+  ((CASE-KEYS . DESTRUCTURING-LAMBDA-LIST) FORM*)
  13.589+
  13.590+The clause whose CASE-KEYS matches CAR of KEY, as if by CASE, CCASE, or ECASE,
  13.591+is selected, and FORMs are then executed with CDR of KEY is destructured and
  13.592+bound by the DESTRUCTURING-LAMBDA-LIST.
  13.593+
  13.594+Example:
  13.595+
  13.596+ (defun dcase (x)
  13.597+   (destructuring-case x
  13.598+     ((:foo a b)
  13.599+      (format nil \"foo: ~S, ~S\" a b))
  13.600+     ((:bar &key a b)
  13.601+      (format nil \"bar: ~S, ~S\" a b))
  13.602+     (((:alt1 :alt2) a)
  13.603+      (format nil \"alt: ~S\" a))
  13.604+     ((t &rest rest)
  13.605+      (format nil \"unknown: ~S\" rest))))
  13.606+
  13.607+  (dcase (list :foo 1 2))        ; => \"foo: 1, 2\"
  13.608+  (dcase (list :bar :a 1 :b 2))  ; => \"bar: 1, 2\"
  13.609+  (dcase (list :alt1 1))         ; => \"alt: 1\"
  13.610+  (dcase (list :alt2 2))         ; => \"alt: 2\"
  13.611+  (dcase (list :quux 1 2 3))     ; => \"unknown: 1, 2, 3\"
  13.612+
  13.613+ (defun decase (x)
  13.614+   (destructuring-case x
  13.615+     ((:foo a b)
  13.616+      (format nil \"foo: ~S, ~S\" a b))
  13.617+     ((:bar &key a b)
  13.618+      (format nil \"bar: ~S, ~S\" a b))
  13.619+     (((:alt1 :alt2) a)
  13.620+      (format nil \"alt: ~S\" a))))
  13.621+
  13.622+  (decase (list :foo 1 2))        ; => \"foo: 1, 2\"
  13.623+  (decase (list :bar :a 1 :b 2))  ; => \"bar: 1, 2\"
  13.624+  (decase (list :alt1 1))         ; => \"alt: 1\"
  13.625+  (decase (list :alt2 2))         ; => \"alt: 2\"
  13.626+  (decase (list :quux 1 2 3))     ; =| error
  13.627+"
  13.628+  (expand-destructuring-case keyform clauses 'case))
  13.629+
  13.630+(defmacro destructuring-ccase (keyform &body clauses)
  13.631+  (expand-destructuring-case keyform clauses 'ccase))
  13.632+
  13.633+(defmacro destructuring-ecase (keyform &body clauses)
  13.634+  (expand-destructuring-case keyform clauses 'ecase))
  13.635+
  13.636+(dolist (name '(destructuring-ccase destructuring-ecase))
  13.637+  (setf (documentation name 'function) (documentation 'destructuring-case 'function)))
  13.638+
  13.639+;;; *-let --- control-flow let-binding macros
  13.640+;; based on https://stevelosh.com/blog/2018/07/fun-with-macros-if-let/
  13.641+
  13.642+(defmacro when-let (bindings &body body)
  13.643+  "Bind `bindings` in parallel and execute `body`, short-circuiting on `nil`.
  13.644+
  13.645+  This macro combines `when` and `let`.  It takes a list of bindings and
  13.646+  binds them like `let` before executing `body`, but if any binding's value
  13.647+  evaluates to `nil` the process stops and `nil` is immediately returned.
  13.648+
  13.649+  Examples:
  13.650+
  13.651+    (when-let ((a (progn (print :a) 1))
  13.652+               (b (progn (print :b) 2))
  13.653+      (list a b))
  13.654+    ; =>
  13.655+    :A
  13.656+    :B
  13.657+    (1 2)
  13.658+
  13.659+    (when-let ((a (progn (print :a) nil))
  13.660+               (b (progn (print :b) 2)))
  13.661+      (list a b))
  13.662+    ; =>
  13.663+    :A
  13.664+    NIL
  13.665+
  13.666+  "
  13.667+  (with-gensyms (block)
  13.668+    `(block ,block
  13.669+       (let ,(loop :for (symbol value) :in bindings
  13.670+                   :collect `(,symbol (or ,value
  13.671+                                          (return-from ,block nil))))
  13.672+         ,@body))))
  13.673+
  13.674+(defmacro when-let* (bindings &body body)
  13.675+  "Bind `bindings` serially and execute `body`, short-circuiting on `nil`.
  13.676+
  13.677+  This macro combines `when` and `let*`.  It takes a list of bindings
  13.678+  and binds them like `let*` before executing `body`, but if any
  13.679+  binding's value evaluates to `nil` the process stops and `nil` is
  13.680+  immediately returned.
  13.681+
  13.682+  Examples:
  13.683+
  13.684+    (when-let* ((a (progn (print :a) 1))
  13.685+                (b (progn (print :b) (1+ a)))
  13.686+      (list a b))
  13.687+    ; =>
  13.688+    :A
  13.689+    :B
  13.690+    (1 2)
  13.691+
  13.692+    (when-let* ((a (progn (print :a) nil))
  13.693+                (b (progn (print :b) (1+ a))))
  13.694+      (list a b))
  13.695+    ; =>
  13.696+    :A
  13.697+    NIL
  13.698+
  13.699+  "
  13.700+  (with-gensyms (block)
  13.701+    `(block ,block
  13.702+       (let* ,(loop :for (symbol value) :in bindings
  13.703+                    :collect `(,symbol (or ,value
  13.704+                                           (return-from ,block nil))))
  13.705+         ,@body))))
  13.706+
  13.707+(defmacro if-let (bindings &body body)
  13.708+  "Bind `bindings` in parallel and execute `then` if all are true, or `else` otherwise.
  13.709+
  13.710+  `body` must be of the form `(...optional-declarations... then else)`.
  13.711+
  13.712+  This macro combines `if` and `let`.  It takes a list of bindings and
  13.713+  binds them like `let` before executing the `then` branch of `body`, but
  13.714+  if any binding's value evaluates to `nil` the process stops there and the
  13.715+  `else` branch is immediately executed (with no bindings in effect).
  13.716+
  13.717+  If any `optional-declarations` are included they will only be in effect
  13.718+  for the `then` branch.
  13.719+
  13.720+  Examples:
  13.721+
  13.722+    (if-let ((a (progn (print :a) 1))
  13.723+             (b (progn (print :b) 2)))
  13.724+      (list a b)
  13.725+      'nope)
  13.726+    ; =>
  13.727+    :A
  13.728+    :B
  13.729+    (1 2)
  13.730+
  13.731+    (if-let ((a (progn (print :a) nil))
  13.732+             (b (progn (print :b) 2)))
  13.733+      (list a b)
  13.734+      'nope)
  13.735+    ; =>
  13.736+    :A
  13.737+    NOPE
  13.738+
  13.739+  "
  13.740+  (with-gensyms (outer inner)
  13.741+    (multiple-value-bind (body declarations) (parse-body body)
  13.742+      (destructuring-bind (then else) body
  13.743+        `(block ,outer
  13.744+           (block ,inner
  13.745+             (let ,(loop :for (symbol value) :in bindings
  13.746+                         :collect `(,symbol (or ,value
  13.747+                                                (return-from ,inner nil))))
  13.748+               ,@declarations
  13.749+               (return-from ,outer ,then)))
  13.750+           ,else)))))
  13.751+
  13.752+(defmacro if-let* (bindings then else)
  13.753+  "Bind `bindings` serially and execute `then` if all are true, or `else` otherwise.
  13.754+
  13.755+  This macro combines `if` and `let*`.  It takes a list of bindings and
  13.756+  binds them like `let*` before executing `then`, but if any binding's
  13.757+  value evaluates to `nil` the process stops and the `else` branch is
  13.758+  immediately executed (with no bindings in effect).
  13.759+
  13.760+  Examples:
  13.761+
  13.762+    (if-let* ((a (progn (print :a) 1))
  13.763+              (b (progn (print :b) (1+ a)))
  13.764+      (list a b)
  13.765+      'nope)
  13.766+    ; =>
  13.767+    :A
  13.768+    :B
  13.769+    (1 2)
  13.770+
  13.771+    (if-let* ((a (progn (print :a) nil))
  13.772+              (b (progn (print :b) (1+ a))))
  13.773+      (list a b)
  13.774+      'nope)
  13.775+    ; =>
  13.776+    :A
  13.777+    NOPE
  13.778+
  13.779+  "
  13.780+  (with-gensyms (outer inner)
  13.781+    `(block ,outer
  13.782+       (block ,inner
  13.783+         (let* ,(loop :for (symbol value) :in bindings
  13.784+                      :collect `(,symbol (or ,value
  13.785+                                             (return-from ,inner nil))))
  13.786+           (return-from ,outer ,then)))
  13.787+       ,else)))
  13.788+
  13.789+
  13.790+(defmacro def! (name &body body)
  13.791+  "`defun' without args."
  13.792+  `(defun ,name () ,@body))
  13.793+
  13.794+(defmacro eval-always (&body body)
  13.795+  `(eval-when (:compile-toplevel :load-toplevel :execute) ,@body))
  13.796+
  13.797+;;; TODO 2023-09-04: Env
  13.798+
  13.799+;;; Introspection
  13.800+;; (eval-always (require :sb-introspect))
  13.801+
  13.802+;; (reexport-from :sb-introspect
  13.803+;; 	       :include '(:function-lambda-list :lambda-list-keywords :lambda-parameters-limit
  13.804+;; 			  :method-combination-lambda-list :deftype-lambda-list
  13.805+;; 			  :primitive-object-size :allocation-information
  13.806+;; 			  :function-type
  13.807+;; 			  :who-specializes-directly :who-specializes-generally
  13.808+;; 			  :find-function-callees :find-function-callers))
  13.809+
  13.810+;; ;;; Compiler
  13.811+
  13.812+;; (reexport-from :sb-c
  13.813+;; 	       :include '(:define-source-transformation
  13.814+;; 			  :parse-eval-when-situations
  13.815+;; 			  :source-location))
  13.816+;;; Definitions
  13.817+(defun %reevaluate-constant (name value test)
  13.818+  (if (not (boundp name))
  13.819+      value
  13.820+      (let ((old (symbol-value name))
  13.821+            (new value))
  13.822+        (if (not (constantp name))
  13.823+            (prog1 new
  13.824+              (cerror "Try to redefine the variable as a constant."
  13.825+                      "~@<~S is an already bound non-constant variable ~
  13.826+                       whose value is ~S.~:@>" name old))
  13.827+            (if (funcall test old new)
  13.828+                old
  13.829+                (restart-case
  13.830+                    (error "~@<~S is an already defined constant whose value ~
  13.831+                              ~S is not equal to the provided initial value ~S ~
  13.832+                              under ~S.~:@>" name old new test)
  13.833+                  (ignore ()
  13.834+                    :report "Retain the current value."
  13.835+                    old)
  13.836+                  (continue ()
  13.837+                    :report "Try to redefine the constant."
  13.838+                    new)))))))
  13.839+
  13.840+(defmacro define-constant (name initial-value &key (test ''eql) documentation)
  13.841+  "Ensures that the global variable named by NAME is a constant with a value
  13.842+that is equal under TEST to the result of evaluating INITIAL-VALUE. TEST is a
  13.843+/function designator/ that defaults to EQL. If DOCUMENTATION is given, it
  13.844+becomes the documentation string of the constant.
  13.845+
  13.846+Signals an error if NAME is already a bound non-constant variable.
  13.847+
  13.848+Signals an error if NAME is already a constant variable whose value is not
  13.849+equal under TEST to result of evaluating INITIAL-VALUE."
  13.850+  `(defconstant ,name (%reevaluate-constant ',name ,initial-value ,test)
  13.851+     ,@(when documentation `(,documentation))))
  13.852+
  13.853+;;; Named Lambdas
  13.854+;; (reexport-from :sb-int :include '(:make-macro-lambda :parse-lambda-list))
  13.855+
  13.856+;;; Sexp utils
  13.857+;; (reexport-from :uiop :include '(read-file-form read-file-forms slurp-stream-forms))
  13.858+
  13.859+;;; cl-bench utils
  13.860+;; Destructive merge of two sorted lists.
  13.861+;; From Hansen's MS thesis.
  13.862+(defun merge! (a b predicate)
  13.863+  (labels ((merge-loop (r a b)
  13.864+             (cond ((funcall predicate (car b) (car a))
  13.865+                    (setf (cdr r) b)
  13.866+                    (if (null (cdr b))
  13.867+                        (setf (cdr b) a)
  13.868+                        (merge-loop b a (cdr b))))
  13.869+                   (t ; (car a) <= (car b)
  13.870+                    (setf (cdr r) a)
  13.871+                    (if (null (cdr a))
  13.872+                        (setf (cdr a) b)
  13.873+                        (merge-loop a (cdr a) b))))))
  13.874+    (cond ((null a) b)
  13.875+          ((null b) a)
  13.876+          ((funcall predicate (car b) (car a))
  13.877+           (if (null (cdr b))
  13.878+               (setf (cdr b) a)
  13.879+               (merge-loop b a (cdr b)))
  13.880+           b)
  13.881+          (t                           ; (car a) <= (car b)
  13.882+           (if (null (cdr a))
  13.883+               (setf (cdr a) b)
  13.884+               (merge-loop a (cdr a) b))
  13.885+           a))))
  13.886+
  13.887+;; Stable sort procedure which copies the input list and then sorts
  13.888+;; the new list imperatively.  On the systems we have benchmarked,
  13.889+;; this generic list sort has been at least as fast and usually much
  13.890+;; faster than the library's sort routine.
  13.891+;; Due to Richard O'Keefe; algorithm attributed to D.H.D. Warren.
  13.892+(defun sort! (seq predicate)
  13.893+  (labels ((astep (n)
  13.894+             (cond ((> n 2)
  13.895+                    (let* ((j (truncate n 2))
  13.896+                           (a (astep j))
  13.897+                           (k (- n j))
  13.898+                           (b (astep k)))
  13.899+                      (merge! a b predicate)))
  13.900+                   ((= n 2)
  13.901+                    (let ((x (car seq))
  13.902+                          (y (cadr seq))
  13.903+                          (p seq))
  13.904+                      (setf seq (cddr seq))
  13.905+                      (when (funcall predicate y x)
  13.906+                        (setf (car p) y)
  13.907+                        (setf (cadr p) x))
  13.908+                      (setf (cddr p) nil)
  13.909+                      p))
  13.910+                   ((= n 1)
  13.911+                    (let ((p seq))
  13.912+                      (setf seq (cdr seq))
  13.913+                      (setf (cdr p) nil)
  13.914+                      p))
  13.915+                   (t nil))))
  13.916+    (astep (length seq))))
  13.917+
  13.918+;;; CLOS/MOP
  13.919+(defun list-indirect-class-methods (class)
  13.920+  "List all indirect methods of CLASS."
  13.921+  (remove-duplicates (mapcan #'specializer-direct-generic-functions (compute-class-precedence-list class))))
  13.922+
  13.923+(defun list-class-methods (class methods &optional indirect)
  13.924+  "List all methods specializing on CLASS modulo METHODS. When INDIRECT is
  13.925+non-nil, also include indirect (parent) methods."
  13.926+  (if (eq methods t)
  13.927+      (if indirect
  13.928+	  (list-indirect-class-methods class)
  13.929+	  (specializer-direct-generic-functions class))
  13.930+      (mapcar
  13.931+       (lambda (s)
  13.932+	 (car (member s (specializer-direct-generic-functions class) :key #'generic-function-name)))
  13.933+       methods)))
  13.934+
  13.935+;; FIX 2023-09-13: need exclude param
  13.936+(defun list-class-slots (class slots &optional exclude)
  13.937+  ;; should probably convert slot-definition-name here
  13.938+  (let ((cs (remove-if
  13.939+	     (lambda (s)
  13.940+	       (or
  13.941+		(null s)
  13.942+		(member t (mapcar
  13.943+			   (lambda (x)
  13.944+			     (string= (slot-definition-name s) x))
  13.945+			   exclude))))
  13.946+	     (class-slots class))))
  13.947+    (if (eq slots t)
  13.948+	cs
  13.949+	(loop for s in slots
  13.950+	      with sn = (symb s)
  13.951+	      for c in cs
  13.952+	      with cn = (symb (slot-definition-name c))
  13.953+	      when (eq sn cn)
  13.954+		collect c))))
  13.955+
  13.956+;; TODO 2023-09-09: slot exclusion from dynamic var
  13.957+(defun list-slot-values-using-class (class obj slots &optional nullp unboundp)
  13.958+  (remove-if
  13.959+   #'null
  13.960+   (mapcar
  13.961+    (lambda (s)
  13.962+      (let ((n (slot-definition-name s)))
  13.963+	(let ((ns (make-keyword (symbol-name n))))
  13.964+	  (if (slot-boundp-using-class class obj s)
  13.965+	      (let ((v (slot-value-using-class class obj s)))
  13.966+		(if nullp
  13.967+		    `(,ns ,v)
  13.968+		    (unless (null v)
  13.969+		      `(,ns ,v))))
  13.970+	      (when unboundp (list ns))))))
  13.971+    slots)))
    14.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2+++ b/lisp/std/list.lisp	Mon Oct 16 19:33:42 2023 -0400
    14.3@@ -0,0 +1,40 @@
    14.4+;;; std/list.lisp --- List utils
    14.5+
    14.6+;;; Code:
    14.7+(defpackage :std/list
    14.8+  (:nicknames :list)
    14.9+  (:use :cl)
   14.10+  (:export
   14.11+   #:ensure-car
   14.12+   #:ensure-cons
   14.13+   :let-binding-transform))
   14.14+
   14.15+(in-package :list)
   14.16+
   14.17+;; (reexport-from :sb-int
   14.18+;; 	       :include '(:recons :memq :assq :ensure-list :proper-list-of-length-p :proper-list-p
   14.19+;; 			  :singleton-p))
   14.20+
   14.21+(defun ensure-car (thing)
   14.22+  "If THING is a CONS, its CAR is returned. Otherwise THING is returned."
   14.23+  (if (consp thing)
   14.24+      (car thing)
   14.25+      thing))
   14.26+
   14.27+(defun ensure-cons (cons)
   14.28+  "If CONS is a cons, it is returned. Otherwise returns a fresh cons with CONS
   14.29+  in the car, and NIL in the cdr."
   14.30+  (if (consp cons)
   14.31+      cons
   14.32+      (cons cons nil)))
   14.33+
   14.34+(defun let-binding-transform (bs)
   14.35+  (if bs
   14.36+    (cons
   14.37+      (cond ((symbolp (car bs))
   14.38+              (list (car bs)))
   14.39+            ((consp (car bs))
   14.40+              (car bs))
   14.41+            (t
   14.42+              (error "Bad let bindings")))
   14.43+      (let-binding-transform (cdr bs)))))
    15.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2+++ b/lisp/std/log.lisp	Mon Oct 16 19:33:42 2023 -0400
    15.3@@ -0,0 +1,90 @@
    15.4+;;; log.lisp --- logging facade for lisp
    15.5+
    15.6+;; this package contains a simple logging facade for lisp applications
    15.7+;; and libraries.
    15.8+
    15.9+;;; Commentary:
   15.10+
   15.11+;; Use `*log-level*' to set the current level of logging. Value is
   15.12+;; either a bool or one of the following keywords: :warn :debug :info
   15.13+;; :trace.
   15.14+
   15.15+;; top-level macros: info! trace! warn! debug!
   15.16+
   15.17+;; inspired by rust-lang/log https://crates.io/crates/log
   15.18+
   15.19+;; I intend to keep things simple for a while and then work out a DSL
   15.20+;; for configuring logging. The DSL will be embedded in skelfiles.
   15.21+
   15.22+;;; Code:
   15.23+(defpackage :std/log
   15.24+  (:nicknames :log)
   15.25+  (:use :cl :str :fmt :sym :fu)
   15.26+  (:export :*log-level* :log-level-designator :log-timestamp-source 
   15.27+   :log! :warn! :info! :debug! :trace!  :dbg!
   15.28+   :debug-p))
   15.29+
   15.30+(in-package :log)
   15.31+
   15.32+(deftype log-level-designator () '(member :warn :debug :info :trace))
   15.33+(declaim (type (or boolean log-level-designator) *log-level*))
   15.34+(defparameter *log-level* nil)
   15.35+(defparameter *logger* nil)
   15.36+(defparameter *log-router* nil)
   15.37+(declaim (type (or boolean function) *log-timestamp*))
   15.38+(defparameter *log-timestamp* t 
   15.39+  "If non-nil, print a timestamp with log output. The value may be a
   15.40+function in which case it is used as the function value of
   15.41+`log-timestamp-source'.")
   15.42+
   15.43+;; TODO 2023-09-20: (declaim (inline log-timestamp-source)) ;; this
   15.44+;; probably shouldn't be inlined.. bench it
   15.45+(defun log-timestamp-source ()
   15.46+  (if (functionp *log-timestamp*)
   15.47+      (funcall *log-timestamp*)
   15.48+      (format nil "~f" (/ (get-internal-real-time) internal-time-units-per-second))))
   15.49+
   15.50+;; the purpose of this struct is to route log messages to the
   15.51+;; appropriate output stream. It should be configured and bound to
   15.52+;; *LOG-ROUTER*.
   15.53+(defstruct log-router
   15.54+  info error debug trace)
   15.55+
   15.56+;; TODO 2023-09-20: make-synonym-stream, make-synonym-stream-symbol 
   15.57+(defvar *default-log-router* 
   15.58+  (make-log-router :info *terminal-io* 
   15.59+		   :error *error-output* 
   15.60+		   :debug *debug-io*
   15.61+		   :trace *trace-output*))
   15.62+
   15.63+(defstruct logger
   15.64+  (active nil :type boolean)
   15.65+  (timestamp *log-timestamp* :type (or boolean function))
   15.66+  (router *default-log-router* :type log-router))
   15.67+   
   15.68+(defmacro info! (opts &rest args))
   15.69+
   15.70+(defmacro trace! (opts &rest args))
   15.71+
   15.72+(defmacro warn! (opts &rest args))
   15.73+
   15.74+(defun debug-p () (eq *log-level* :debug))
   15.75+
   15.76+(defun debug-log-line ()
   15.77+  (format t ":DEBUG~A~%"
   15.78+	  (if *log-timestamp*
   15.79+	      (format nil " @ ~A ~t" (log-timestamp-source))
   15.80+	      "")))
   15.81+
   15.82+;; TODO 2023-08-31: single format control string
   15.83+(defun debug! (&rest args)
   15.84+  (when (debug-p)
   15.85+    (debug-log-line)
   15.86+    ;; RESEARCH 2023-08-31: what's better here.. loop, do, mapc+nil?
   15.87+    (map nil (lambda (x) (format t "; ~X~%" x)) args))
   15.88+  args)
   15.89+
   15.90+(defun dbg! (&rest args)
   15.91+  (when (debug-p)
   15.92+    (debug-log-line)
   15.93+    (apply #'describe args)))
    16.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2+++ b/lisp/std/named-readtables.lisp	Mon Oct 16 19:33:42 2023 -0400
    16.3@@ -0,0 +1,1111 @@
    16.4+;;; named-readtables.lisp --- named-readtables
    16.5+
    16.6+;; The standard readtable is controlled by the Lisp implementation and
    16.7+;; generally shouldn't be touched. There can be problems with
    16.8+;; 'stacking' multiple read-macros as can be seen in this SO post:
    16.9+;; https://stackoverflow.com/questions/73346051/how-can-i-modify-the-and-readtable-macros-in-lisp
   16.10+
   16.11+;; Instead, if you really want to change standard readtable behavior,
   16.12+;; it is better to define your own readtables and be aware of the
   16.13+;; context in which they are enabled. For example, loading a system
   16.14+;; definition before enabling the readtable may cause divergent
   16.15+;; behavior (using standard) versus your source code (custom).
   16.16+
   16.17+;;; Code:
   16.18+(pkg:defpkg :std/named-readtables
   16.19+  (:nicknames :named-readtables)
   16.20+  (:use :cl)
   16.21+  (:export
   16.22+   #:defreadtable
   16.23+   #:in-readtable
   16.24+   #:make-readtable
   16.25+   #:merge-readtables-into
   16.26+   #:find-readtable
   16.27+   #:ensure-readtable
   16.28+   #:rename-readtable
   16.29+   #:readtable-name
   16.30+   #:register-readtable
   16.31+   #:unregister-readtable
   16.32+   #:copy-named-readtable
   16.33+   #:list-all-named-readtables
   16.34+   ;; Types
   16.35+   #:named-readtable-designator
   16.36+   ;; Conditions
   16.37+   #:readtable-error
   16.38+   #:reader-macro-conflict
   16.39+   #:readtable-does-already-exist
   16.40+   #:readtable-does-not-exist
   16.41+   #:parse-body))
   16.42+
   16.43+(in-package :named-readtables)
   16.44+(pushnew :named-readtables *features*)
   16.45+
   16.46+(defmacro without-package-lock ((&rest package-names) &body body)
   16.47+  `(sb-ext:with-unlocked-packages (,@package-names) ,@body))
   16.48+
   16.49+;;; Taken from SWANK (which is Public Domain.)
   16.50+
   16.51+(defmacro destructure-case (value &body patterns)
   16.52+  "Dispatch VALUE to one of PATTERNS.
   16.53+A cross between `case' and `destructuring-bind'.
   16.54+The pattern syntax is:
   16.55+  ((HEAD . ARGS) . BODY)
   16.56+The list of patterns is searched for a HEAD `eq' to the car of
   16.57+VALUE. If one is found, the BODY is executed with ARGS bound to the
   16.58+corresponding values in the CDR of VALUE."
   16.59+  (let ((operator (gensym "op-"))
   16.60+        (operands (gensym "rand-"))
   16.61+        (tmp (gensym "tmp-")))
   16.62+    `(let* ((,tmp ,value)
   16.63+            (,operator (car ,tmp))
   16.64+            (,operands (cdr ,tmp)))
   16.65+       (case ,operator
   16.66+         ,@(loop for (pattern . body) in patterns collect
   16.67+                   (if (eq pattern t)
   16.68+                       `(t ,@body)
   16.69+                       (destructuring-bind (op &rest rands) pattern
   16.70+                         `(,op (destructuring-bind ,rands ,operands
   16.71+                                 ,@body)))))
   16.72+         ,@(if (eq (caar (last patterns)) t)
   16.73+               '()
   16.74+               `((t (error "destructure-case failed: ~S" ,tmp))))))))
   16.75+
   16.76+;;; Taken from Alexandria (which is Public Domain, or BSD.)
   16.77+
   16.78+(define-condition simple-style-warning (simple-warning style-warning)
   16.79+  ())
   16.80+
   16.81+(defun simple-style-warn (format-control &rest format-args)
   16.82+  (warn 'simple-style-warning
   16.83+	 :format-control format-control
   16.84+	 :format-arguments format-args))
   16.85+
   16.86+(define-condition simple-program-error (simple-error program-error)
   16.87+  ())
   16.88+
   16.89+(defun simple-program-error (message &rest args)
   16.90+  (error 'simple-program-error
   16.91+         :format-control message
   16.92+         :format-arguments args))
   16.93+
   16.94+(defun required-argument (&optional name)
   16.95+  "Signals an error for a missing argument of NAME. Intended for
   16.96+use as an initialization form for structure and class-slots, and
   16.97+a default value for required keyword arguments."
   16.98+  (error "Required argument ~@[~S ~]missing." name))
   16.99+
  16.100+(defun ensure-list (list)
  16.101+  "If LIST is a list, it is returned. Otherwise returns the list
  16.102+designated by LIST."
  16.103+  (if (listp list)
  16.104+      list
  16.105+      (list list)))
  16.106+
  16.107+(declaim (inline ensure-function))	; to propagate return type.
  16.108+(declaim (ftype (function (t) (values function &optional))
  16.109+                ensure-function))
  16.110+(defun ensure-function (function-designator)
  16.111+  "Returns the function designated by FUNCTION-DESIGNATOR:
  16.112+if FUNCTION-DESIGNATOR is a function, it is returned, otherwise
  16.113+it must be a function name and its FDEFINITION is returned."
  16.114+  (if (functionp function-designator)
  16.115+      function-designator
  16.116+      (fdefinition function-designator)))
  16.117+
  16.118+(eval-when (:compile-toplevel :load-toplevel :execute)
  16.119+(defun parse-body (body &key documentation whole)
  16.120+  "Parses BODY into (values remaining-forms declarations doc-string).
  16.121+Documentation strings are recognized only if DOCUMENTATION is true.
  16.122+Syntax errors in body are signalled and WHOLE is used in the signal
  16.123+arguments when given."
  16.124+  (let ((doc nil)
  16.125+        (decls nil)
  16.126+        (current nil))
  16.127+    (tagbody
  16.128+     :declarations
  16.129+       (setf current (car body))
  16.130+       (when (and documentation (stringp current) (cdr body))
  16.131+         (if doc
  16.132+             (error "Too many documentation strings in ~S." (or whole body))
  16.133+             (setf doc (pop body)))
  16.134+         (go :declarations))
  16.135+       (when (and (listp current) (eql (first current) 'declare))
  16.136+         (push (pop body) decls)
  16.137+         (go :declarations)))
  16.138+    (values body (nreverse decls) doc)))
  16.139+
  16.140+(defun parse-ordinary-lambda-list (lambda-list)
  16.141+  "Parses an ordinary lambda-list, returning as multiple values:
  16.142+
  16.143+ 1. Required parameters.
  16.144+ 2. Optional parameter specifications, normalized into form (NAME INIT SUPPLIEDP)
  16.145+    where SUPPLIEDP is NIL if not present.
  16.146+ 3. Name of the rest parameter, or NIL.
  16.147+ 4. Keyword parameter specifications, normalized into form ((KEYWORD-NAME NAME) INIT SUPPLIEDP)
  16.148+    where SUPPLIEDP is NIL if not present.
  16.149+ 5. Boolean indicating &ALLOW-OTHER-KEYS presence.
  16.150+ 6. &AUX parameter specifications, normalized into form (NAME INIT).
  16.151+
  16.152+Signals a PROGRAM-ERROR is the lambda-list is malformed."
  16.153+  (let ((state :required)
  16.154+        (allow-other-keys nil)
  16.155+        (auxp nil)
  16.156+        (required nil)
  16.157+        (optional nil)
  16.158+        (rest nil)
  16.159+        (keys nil)
  16.160+        (aux nil))
  16.161+    (labels ((simple-program-error (format-string &rest format-args)
  16.162+               (error 'simple-program-error
  16.163+                      :format-control format-string
  16.164+                      :format-arguments format-args))
  16.165+             (fail (elt)
  16.166+               (simple-program-error "Misplaced ~S in ordinary lambda-list:~%  ~S"
  16.167+                                     elt lambda-list))
  16.168+             (check-variable (elt what)
  16.169+               (unless (and (symbolp elt) (not (constantp elt)))
  16.170+                 (simple-program-error "Invalid ~A ~S in ordinary lambda-list:~%  ~S"
  16.171+                                       what elt lambda-list)))
  16.172+             (check-spec (spec what)
  16.173+               (destructuring-bind (init suppliedp) spec
  16.174+                 (declare (ignore init))
  16.175+                 (check-variable suppliedp what)))
  16.176+             (make-keyword (name)
  16.177+               "Interns the string designated by NAME in the KEYWORD package."
  16.178+               (intern (string name) :keyword)))
  16.179+      (dolist (elt lambda-list)
  16.180+        (case elt
  16.181+          (&optional
  16.182+           (if (eq state :required)
  16.183+               (setf state elt)
  16.184+               (fail elt)))
  16.185+          (&rest
  16.186+           (if (member state '(:required &optional))
  16.187+               (setf state elt)
  16.188+               (progn
  16.189+                 (break "state=~S" state)
  16.190+                 (fail elt))))
  16.191+          (&key
  16.192+           (if (member state '(:required &optional :after-rest))
  16.193+               (setf state elt)
  16.194+               (fail elt)))
  16.195+          (&allow-other-keys
  16.196+           (if (eq state '&key)
  16.197+               (setf allow-other-keys t
  16.198+                     state elt)
  16.199+               (fail elt)))
  16.200+          (&aux
  16.201+           (cond ((eq state '&rest)
  16.202+                  (fail elt))
  16.203+                 (auxp
  16.204+                  (simple-program-error "Multiple ~S in ordinary lambda-list:~%  ~S"
  16.205+                                        elt lambda-list))
  16.206+                 (t
  16.207+                  (setf auxp t
  16.208+                        state elt))
  16.209+                 ))
  16.210+          (otherwise
  16.211+           (when (member elt '#.(set-difference lambda-list-keywords
  16.212+                                                '(&optional &rest &key &allow-other-keys &aux)))
  16.213+             (simple-program-error
  16.214+              "Bad lambda-list keyword ~S in ordinary lambda-list:~%  ~S"
  16.215+              elt lambda-list))
  16.216+           (case state
  16.217+             (:required
  16.218+              (check-variable elt "required parameter")
  16.219+              (push elt required))
  16.220+             (&optional
  16.221+              (cond ((consp elt)
  16.222+                     (destructuring-bind (name &rest tail) elt
  16.223+                       (check-variable name "optional parameter")
  16.224+                       (if (cdr tail)
  16.225+                           (check-spec tail "optional-supplied-p parameter")
  16.226+                           (setf elt (append elt '(nil))))))
  16.227+                    (t
  16.228+                     (check-variable elt "optional parameter")
  16.229+                     (setf elt (cons elt '(nil nil)))))
  16.230+              (push elt optional))
  16.231+             (&rest
  16.232+              (check-variable elt "rest parameter")
  16.233+              (setf rest elt
  16.234+                    state :after-rest))
  16.235+             (&key
  16.236+              (cond ((consp elt)
  16.237+                     (destructuring-bind (var-or-kv &rest tail) elt
  16.238+                       (cond ((consp var-or-kv)
  16.239+                              (destructuring-bind (keyword var) var-or-kv
  16.240+                                (unless (symbolp keyword)
  16.241+                                  (simple-program-error "Invalid keyword name ~S in ordinary ~
  16.242+                                                         lambda-list:~%  ~S"
  16.243+                                                        keyword lambda-list))
  16.244+                                (check-variable var "keyword parameter")))
  16.245+                             (t
  16.246+                              (check-variable var-or-kv "keyword parameter")
  16.247+                              (setf var-or-kv (list (make-keyword var-or-kv) var-or-kv))))
  16.248+                       (if (cdr tail)
  16.249+                           (check-spec tail "keyword-supplied-p parameter")
  16.250+                           (setf tail (append tail '(nil))))
  16.251+                       (setf elt (cons var-or-kv tail))))
  16.252+                    (t
  16.253+                     (check-variable elt "keyword parameter")
  16.254+                     (setf elt (list (list (make-keyword elt) elt) nil nil))))
  16.255+              (push elt keys))
  16.256+             (&aux
  16.257+              (if (consp elt)
  16.258+                  (destructuring-bind (var &optional init) elt
  16.259+                    (declare (ignore init))
  16.260+                    (check-variable var "&aux parameter"))
  16.261+                  (check-variable elt "&aux parameter"))
  16.262+              (push elt aux))
  16.263+             (t
  16.264+              (simple-program-error "Invalid ordinary lambda-list:~%  ~S" lambda-list)))))))
  16.265+    (values (nreverse required) (nreverse optional) rest (nreverse keys)
  16.266+            allow-other-keys (nreverse aux)))))
  16.267+
  16.268+(defmacro define-api (name lambda-list type-list &body body)
  16.269+  (flet ((parse-type-list (type-list)
  16.270+           (let ((pos (position '=> type-list)))
  16.271+             (assert pos () "You forgot to specify return type (`=>' missing.)")
  16.272+             (values (subseq type-list 0 pos)
  16.273+                     `(values ,@(nthcdr (1+ pos) type-list) &optional)))))
  16.274+    (multiple-value-bind (body decls docstring)
  16.275+        (parse-body body :documentation t :whole `(define-api ,name))
  16.276+      (multiple-value-bind (arg-typespec value-typespec)
  16.277+          (parse-type-list type-list)
  16.278+        (multiple-value-bind (reqs opts rest keys)
  16.279+            (parse-ordinary-lambda-list lambda-list)
  16.280+          (declare (ignorable reqs opts rest keys))
  16.281+          `(progn
  16.282+             (declaim (ftype (function ,arg-typespec ,value-typespec) ,name))
  16.283+             (locally
  16.284+                 ;; Muffle the annoying "&OPTIONAL and &KEY found in
  16.285+                 ;; the same lambda list" style-warning
  16.286+                 #+sbcl (declare (sb-ext:muffle-conditions style-warning))
  16.287+               (defun ,name ,lambda-list
  16.288+                 ,docstring
  16.289+                 ,@decls
  16.290+                 (locally
  16.291+                     #+sbcl (declare (sb-ext:unmuffle-conditions style-warning))
  16.292+                     ;; SBCL will interpret the ftype declaration as
  16.293+                     ;; assertion and will insert type checks for us.
  16.294+                     ,@body)))))))))
  16.295+
  16.296+(defmacro define-cruft (name lambda-list &body (docstring . alternatives))
  16.297+  (assert (typep docstring 'string) (docstring) "Docstring missing!")
  16.298+  (assert (not (null alternatives)))
  16.299+  `(progn
  16.300+     (declaim (inline ,name))
  16.301+     (defun ,name ,lambda-list ,docstring ,(first alternatives))))
  16.302+
  16.303+(eval-when (:compile-toplevel :execute)
  16.304+  #+sbcl (when (find-symbol "ASSERT-NOT-STANDARD-READTABLE"
  16.305+                            (find-package "SB-IMPL"))
  16.306+           (pushnew :sbcl+safe-standard-readtable *features*)))
  16.307+
  16.308+
  16.309+
  16.310+;;;; Mapping between a readtable object and its readtable-name.
  16.311+
  16.312+(defvar *readtable-names* (make-hash-table :test 'eq))
  16.313+
  16.314+(define-cruft %associate-readtable-with-name (name readtable)
  16.315+  "Associate READTABLE with NAME for READTABLE-NAME to work."
  16.316+  #+ :common-lisp (setf (gethash readtable *readtable-names*) name))
  16.317+
  16.318+(define-cruft %unassociate-readtable-from-name (name readtable)
  16.319+  "Remove the association between READTABLE and NAME."
  16.320+  #+ :common-lisp (progn (assert (eq name (gethash readtable *readtable-names*)))
  16.321+                         (remhash readtable *readtable-names*)))
  16.322+
  16.323+(define-cruft %readtable-name (readtable)
  16.324+  "Return the name associated with READTABLE."
  16.325+  #+ :common-lisp (values (gethash readtable *readtable-names*)))
  16.326+
  16.327+(define-cruft %list-all-readtable-names ()
  16.328+  "Return a list of all available readtable names."
  16.329+  #+ :common-lisp (list* :standard :current :modern
  16.330+                         (loop for name being each hash-value of *readtable-names*
  16.331+                               collect name)))
  16.332+
  16.333+;;;; Mapping READTABLE objects to docstrings.
  16.334+
  16.335+(defvar *readtable-to-docstring* (make-hash-table :test 'eq))
  16.336+
  16.337+(defun %associate-docstring-with-readtable (readtable docstring)
  16.338+  (setf (gethash readtable *readtable-to-docstring*) docstring))
  16.339+
  16.340+(defun %unassociate-docstring-from-readtable (readtable)
  16.341+  (prog1 (gethash readtable *readtable-to-docstring*)
  16.342+    (remhash readtable *readtable-to-docstring*)))
  16.343+
  16.344+;;;; Specialized DOCUMENTATION for named readtables.
  16.345+
  16.346+;;; Lispworks, at least, forbids defining methods on DOCUMENTATION.
  16.347+;;; Wrapping these forms with WITHOUT-PACKAGE-LOCK (as for PRINT-OBJECT,
  16.348+;;; see below) allows this to compile on Lispworks.
  16.349+
  16.350+(without-package-lock (:common-lisp #+lispworks :implementation)
  16.351+
  16.352+  (defmethod documentation ((name symbol) (doc-type (eql 'readtable)))
  16.353+    (let ((readtable (find-readtable name)))
  16.354+      (and readtable (gethash readtable *readtable-to-docstring*))))
  16.355+
  16.356+  (defmethod documentation ((readtable readtable) (doc-type (eql 'readtable)))
  16.357+    (gethash readtable *readtable-to-docstring*))
  16.358+
  16.359+  (defmethod (setf documentation) (docstring (name symbol)
  16.360+                                             (doc-type (eql 'readtable)))
  16.361+    (let ((readtable (find-readtable name)))
  16.362+      (unless readtable
  16.363+        (error 'readtable-does-not-exist :readtable-name name))
  16.364+      (setf (gethash readtable *readtable-to-docstring*) docstring)))
  16.365+
  16.366+  (defmethod (setf documentation) (docstring (readtable readtable)
  16.367+                                             (doc-type (eql 'readtable)))
  16.368+    (setf (gethash readtable *readtable-to-docstring*) docstring)))
  16.369+
  16.370+
  16.371+;;;; Mapping between a readtable-name and the actual readtable object.
  16.372+
  16.373+;;; On Allegro we reuse their named-readtable support so we work
  16.374+;;; nicely on their infrastructure.
  16.375+
  16.376+(defvar *named-readtables* (make-hash-table :test 'eq))
  16.377+
  16.378+(define-cruft %associate-name-with-readtable (name readtable)
  16.379+  "Associate NAME with READTABLE for FIND-READTABLE to work."
  16.380+  #+ :common-lisp (setf (gethash name *named-readtables*) readtable))
  16.381+
  16.382+(define-cruft %unassociate-name-from-readtable (name readtable)
  16.383+  "Remove the association between NAME and READTABLE"
  16.384+  #+ :common-lisp (progn (assert (eq readtable (gethash name *named-readtables*)))
  16.385+                         (remhash name *named-readtables*)))
  16.386+
  16.387+(define-cruft %find-readtable (name)
  16.388+  "Return the readtable named NAME."
  16.389+  #+ :common-lisp (values (gethash name *named-readtables* nil)))
  16.390+
  16.391+
  16.392+;;;; Reader-macro related predicates
  16.393+
  16.394+;;; CLISP creates new function objects for standard reader macros on
  16.395+;;; each readtable copy.
  16.396+(define-cruft function= (fn1 fn2)
  16.397+  "Are reader-macro function-designators FN1 and FN2 the same?"
  16.398+  (let ((fn1 (ensure-function fn1))
  16.399+        (fn2 (ensure-function fn2)))
  16.400+    (or (eq fn1 fn2)
  16.401+        ;; After SBCL 1.1.18, for dispatch macro characters
  16.402+        ;; GET-MACRO-CHARACTER returns closures whose name is:
  16.403+        ;;
  16.404+        ;; (LAMBDA (STREAM CHAR) :IN SB-IMPL::%MAKE-DISPATCH-MACRO-CHAR)
  16.405+        ;;
  16.406+        ;; Treat all these closures equivalent.
  16.407+        (flet ((internal-dispatch-macro-closure-name-p (name)
  16.408+                 (find "SB-IMPL::%MAKE-DISPATCH-MACRO-CHAR" name
  16.409+                       :key #'prin1-to-string :test #'string-equal)))
  16.410+          (let ((n1 (sb-impl::%fun-name fn1))
  16.411+                (n2 (sb-impl::%fun-name fn2)))
  16.412+            (and (listp n1) (listp n2)
  16.413+                 (internal-dispatch-macro-closure-name-p n1)
  16.414+                 (internal-dispatch-macro-closure-name-p n2))))))
  16.415+  #+ :common-lisp
  16.416+  (eq (ensure-function fn1) (ensure-function fn2)))
  16.417+
  16.418+(define-cruft dispatch-macro-char-p (char rt)
  16.419+  "Is CHAR a dispatch macro character in RT?"
  16.420+  #+ :common-lisp
  16.421+  (handler-case (locally
  16.422+                  (get-dispatch-macro-character char #\x rt)
  16.423+                  t)
  16.424+    (error () nil)))
  16.425+
  16.426+;; (defun macro-char-p (char rt)
  16.427+;;   (let ((reader-fn (%get-macro-character char rt)))
  16.428+;;     (and reader-fn t)))
  16.429+
  16.430+;; (defun standard-macro-char-p (char rt)
  16.431+;;   (multiple-value-bind (rt-fn rt-flag) (get-macro-character char rt)
  16.432+;;     (multiple-value-bind (std-fn std-flag) (get-macro-character char *standard-readtable*)
  16.433+;;       (and (eq rt-fn std-fn)
  16.434+;; 	   (eq rt-flag std-flag)))))
  16.435+
  16.436+;; (defun standard-dispatch-macro-char-p (disp-char sub-char rt)
  16.437+;;   (flet ((non-terminating-p (ch rt) (nth-value 1 (get-macro-character ch rt))))
  16.438+;;     (and (eq (non-terminating-p disp-char rt)
  16.439+;; 	     (non-terminating-p disp-char *standard-readtable*))
  16.440+;; 	 (eq (get-dispatch-macro-character disp-char sub-char rt)
  16.441+;; 	     (get-dispatch-macro-character disp-char sub-char *standard-readtable*)))))
  16.442+
  16.443+
  16.444+;;;; Readtables Iterators
  16.445+
  16.446+(defmacro with-readtable-iterator ((name readtable) &body body)
  16.447+  (let ((it (gensym)))
  16.448+    `(let ((,it (%make-readtable-iterator ,readtable)))
  16.449+       (macrolet ((,name () `(funcall ,',it)))
  16.450+         ,@body))))
  16.451+
  16.452+(defun funcall-or (package-and-name-list &rest args)
  16.453+  (loop for (package name) in package-and-name-list
  16.454+        do (let ((symbol (find-symbol (string name) package)))
  16.455+             (when symbol
  16.456+               (return-from funcall-or (apply symbol args))))))
  16.457+
  16.458+(defun %make-readtable-iterator (readtable)
  16.459+  (let ((char-macro-array (funcall-or '((sb-impl base-char-macro-array)
  16.460+                                        (sb-impl character-macro-array))
  16.461+                                      readtable))
  16.462+        (char-macro-ht (funcall-or '((sb-impl extended-char-table)
  16.463+                                     (sb-impl character-macro-hash-table))
  16.464+                                   readtable))
  16.465+        (dispatch-tables (sb-impl::dispatch-tables readtable))
  16.466+        (char-code 0))
  16.467+    (with-hash-table-iterator (ht-iterator char-macro-ht)
  16.468+      (labels ((grovel-base-chars ()
  16.469+                 (if (>= char-code sb-int:base-char-code-limit)
  16.470+                     (grovel-unicode-chars)
  16.471+                     (let ((reader-fn (svref char-macro-array char-code))
  16.472+                           (char (code-char (shiftf char-code (1+ char-code)))))
  16.473+                       (if reader-fn
  16.474+                           (yield char)
  16.475+                           (grovel-base-chars)))))
  16.476+               (grovel-unicode-chars ()
  16.477+                 (multiple-value-bind (more? char) (ht-iterator)
  16.478+                   (if (not more?)
  16.479+                       (values nil nil nil nil nil)
  16.480+                       (yield char))))
  16.481+               (yield (char)
  16.482+                 (let ((disp-fn (get-macro-character char readtable))
  16.483+                       (disp-ht))
  16.484+                   (cond
  16.485+                     ((setq disp-ht (cdr (assoc char dispatch-tables)))
  16.486+                      (let ((sub-char-alist))
  16.487+                        (maphash (lambda (k v)
  16.488+                                   (push (cons k v) sub-char-alist))
  16.489+                                 disp-ht)
  16.490+                        (values t char disp-fn t sub-char-alist)))
  16.491+                     (t
  16.492+                      (values t char disp-fn nil nil))))))
  16.493+        #'grovel-base-chars))))
  16.494+
  16.495+(defmacro do-readtable ((entry-designator readtable &optional result)
  16.496+                        &body body)
  16.497+  "Iterate through a readtable's macro characters, and dispatch macro characters."
  16.498+  (destructuring-bind (char &optional reader-fn non-terminating-p disp? table)
  16.499+      (if (symbolp entry-designator)
  16.500+          (list entry-designator)
  16.501+          entry-designator)
  16.502+    (let ((iter (gensym "ITER+"))
  16.503+          (more? (gensym "MORE?+"))
  16.504+          (rt (gensym "READTABLE+")))
  16.505+      `(let ((,rt ,readtable))
  16.506+         (with-readtable-iterator (,iter ,rt)
  16.507+           (loop
  16.508+             (multiple-value-bind (,more?
  16.509+                                   ,char
  16.510+                                   ,@(when reader-fn (list reader-fn))
  16.511+                                   ,@(when disp? (list disp?))
  16.512+                                   ,@(when table (list table)))
  16.513+                 (,iter)
  16.514+               (unless ,more? (return ,result))
  16.515+               (let ,(when non-terminating-p
  16.516+                       ;; FIXME: N-T-P should be incorporated in iterators.
  16.517+                       `((,non-terminating-p
  16.518+                          (nth-value 1 (get-macro-character ,char ,rt)))))
  16.519+                 ,@body))))))))
  16.520+
  16.521+;;;; Misc
  16.522+
  16.523+;;; This should return an implementation's actual standard readtable
  16.524+;;; object only if the implementation makes the effort to guard against
  16.525+;;; modification of that object. Otherwise it should better return a
  16.526+;;; copy.
  16.527+(define-cruft %standard-readtable ()
  16.528+  "Return the standard readtable."
  16.529+  #+ :sbcl+safe-standard-readtable sb-impl::*standard-readtable*
  16.530+  #+ :common-lisp                  (copy-readtable nil))
  16.531+
  16.532+;;; On SBCL, SET-SYNTAX-FROM-CHAR does not get rid of a
  16.533+;;; readtable's dispatch table properly.
  16.534+;;; Same goes for Allegro but that does not seem to provide a
  16.535+;;; setter for their readtable's dispatch tables. Hence this ugly
  16.536+;;; workaround.
  16.537+(define-cruft %clear-readtable (readtable)
  16.538+  "Make all macro characters in READTABLE be constituents."
  16.539+  (prog1 readtable
  16.540+    (do-readtable (char readtable)
  16.541+      (set-syntax-from-char char #\A readtable))
  16.542+    (setf (sb-impl::dispatch-tables readtable) nil))
  16.543+  #+ :common-lisp
  16.544+  (do-readtable (char readtable readtable)
  16.545+    (set-syntax-from-char char #\A readtable)))
  16.546+
  16.547+(define-cruft %get-dispatch-macro-character (char subchar rt)
  16.548+  "Ensure ANSI behaviour for GET-DISPATCH-MACRO-CHARACTER."
  16.549+  #+ :common-lisp (get-dispatch-macro-character char subchar rt))
  16.550+
  16.551+(define-cruft %get-macro-character (char rt)
  16.552+  "Ensure ANSI behaviour for GET-MACRO-CHARACTER."
  16.553+  #+ :common-lisp (get-macro-character char rt))
  16.554+
  16.555+
  16.556+;;;; Specialized PRINT-OBJECT for named readtables.
  16.557+
  16.558+;;; As per #19 in CLHS 11.1.2.1.2 defining a method for PRINT-OBJECT
  16.559+;;; that specializes on READTABLE is actually forbidden. It's quite
  16.560+;;; likely to work (modulo package-locks) on most implementations,
  16.561+;;; though.
  16.562+
  16.563+(without-package-lock (:common-lisp)
  16.564+  (defmethod print-object :around ((rt readtable) stream)
  16.565+    (let ((name (readtable-name rt)))
  16.566+      (if name
  16.567+          (print-unreadable-object (rt stream :type nil :identity t)
  16.568+            (format stream "~A ~S" :named-readtable name))
  16.569+          (call-next-method)))))
  16.570+
  16.571+;;;
  16.572+;;;  ``This is enough of a foothold to implement a more elaborate
  16.573+;;;    facility for using readtables in a localized way.''
  16.574+;;;
  16.575+;;;                               (X3J13 Cleanup Issue IN-SYNTAX)
  16.576+;;;
  16.577+
  16.578+;;;;;; DEFREADTABLE &c.
  16.579+(defmacro defreadtable (name &body options)
  16.580+  "Define a new named readtable, whose name is given by the symbol NAME.
  16.581+  Or, if a readtable is already registered under that name, redefine
  16.582+  that one.
  16.583+
  16.584+  The readtable can be populated using the following OPTIONS:
  16.585+
  16.586+  - If the first element of OPTIONS is a string then it is associated
  16.587+    with the readtable as in `(SETF (DOCUMENTATION NAME 'READTABLE)
  16.588+    DOCSTRING)`.
  16.589+
  16.590+  - `(:MERGE READTABLE-DESIGNATORS+)`
  16.591+
  16.592+      Merge the macro character definitions from the readtables
  16.593+      designated into the new readtable being defined as per
  16.594+      MERGE-READTABLES-INTO. The copied options are
  16.595+      :DISPATCH-MACRO-CHAR, :MACRO-CHAR and :SYNTAX-FROM, but not
  16.596+      READTABLE-CASE.
  16.597+
  16.598+      If no :MERGE clause is given, an empty readtable is used. See
  16.599+      MAKE-READTABLE.
  16.600+
  16.601+  - `(:FUSE READTABLE-DESIGNATORS+)`
  16.602+
  16.603+      Like :MERGE except:
  16.604+
  16.605+      Error conditions of type READER-MACRO-CONFLICT that are signaled
  16.606+      during the merge operation will be silently _continued_. It
  16.607+      follows that reader macros in earlier entries will be
  16.608+      overwritten by later ones. For backward compatibility, :FUZE is
  16.609+      accepted as an alias of :FUSE.
  16.610+
  16.611+  - `(:DISPATCH-MACRO-CHAR MACRO-CHAR SUB-CHAR FUNCTION)`
  16.612+
  16.613+      Define a new sub character `SUB-CHAR` for the dispatching macro
  16.614+      character `MACRO-CHAR`, per SET-DISPATCH-MACRO-CHARACTER. You
  16.615+      probably have to define `MACRO-CHAR` as a dispatching macro
  16.616+      character by the following option first.
  16.617+
  16.618+  - `(:MACRO-CHAR MACRO-CHAR FUNCTION [NON-TERMINATING-P])`
  16.619+
  16.620+      Define a new macro character in the readtable, per
  16.621+      SET-MACRO-CHARACTER. If [FUNCTION][argument] is the keyword
  16.622+      :DISPATCH, `MACRO-CHAR` is made a dispatching macro character,
  16.623+      per MAKE-DISPATCH-MACRO-CHARACTER.
  16.624+
  16.625+  - `(:SYNTAX-FROM FROM-READTABLE-DESIGNATOR FROM-CHAR TO-CHAR)`
  16.626+
  16.627+      Set the character syntax of TO-CHAR in the readtable being
  16.628+      defined to the same syntax as FROM-CHAR as per
  16.629+      SET-SYNTAX-FROM-CHAR.
  16.630+
  16.631+  - `(:CASE CASE-MODE)`
  16.632+
  16.633+      Defines the _case sensitivity mode_ of the resulting readtable.
  16.634+
  16.635+  Any number of option clauses may appear. The options are grouped by
  16.636+  their type, but in each group the order the options appeared
  16.637+  textually is preserved. The following groups exist and are executed
  16.638+  in the following order: :MERGE and :FUSE (one group), :CASE,
  16.639+  :MACRO-CHAR and :DISPATCH-MACRO-CHAR (one group), finally
  16.640+  :SYNTAX-FROM.
  16.641+
  16.642+  Notes:
  16.643+
  16.644+  The readtable is defined at load-time. If you want to have it
  16.645+  available at compilation time -- say to use its reader-macros in the
  16.646+  same file as its definition -- you have to wrap the DEFREADTABLE
  16.647+  form in an explicit EVAL-WHEN.
  16.648+
  16.649+  On redefinition, the target readtable is made empty first before
  16.650+  it's refilled according to the clauses.
  16.651+
  16.652+  NIL, :STANDARD, :COMMON-LISP, :MODERN, and :CURRENT are
  16.653+  preregistered readtable names."
  16.654+  (check-type name symbol)
  16.655+  (when (reserved-readtable-name-p name)
  16.656+    (error "~A is the designator for a predefined readtable. ~
  16.657+            Not acceptable as a user-specified readtable name." name))
  16.658+  (flet ((process-option (option var)
  16.659+           (destructure-case option
  16.660+             ((:merge &rest readtable-designators)
  16.661+	      `(merge-readtables-into ,var ,@(mapcar #'(lambda (x) `',x)
  16.662+                                                     readtable-designators)))
  16.663+             ((:fuse &rest readtable-designators)
  16.664+	      `(handler-bind ((reader-macro-conflict #'continue))
  16.665+                 (merge-readtables-into ,var ,@(mapcar #'(lambda (x) `',x)
  16.666+                                                       readtable-designators))))
  16.667+             ;; alias for :FUSE
  16.668+             ((:fuze &rest readtable-designators)
  16.669+	      `(handler-bind ((reader-macro-conflict #'continue))
  16.670+                 (merge-readtables-into ,var ,@(mapcar #'(lambda (x) `',x)
  16.671+                                                       readtable-designators))))
  16.672+             ((:dispatch-macro-char disp-char sub-char function)
  16.673+              `(set-dispatch-macro-character ,disp-char ,sub-char
  16.674+                                             ,function ,var))
  16.675+             ((:macro-char char function &optional non-terminating-p)
  16.676+	      (if (eq function :dispatch)
  16.677+		  `(make-dispatch-macro-character ,char ,non-terminating-p ,var)
  16.678+		  `(set-macro-character ,char ,function
  16.679+                                        ,non-terminating-p ,var)))
  16.680+	     ((:syntax-from from-rt-designator from-char to-char)
  16.681+	      `(set-syntax-from-char ,to-char ,from-char
  16.682+				     ,var (find-readtable ,from-rt-designator)))
  16.683+	     ((:case mode)
  16.684+	      `(setf (readtable-case ,var) ,mode))))
  16.685+	 (remove-clauses (clauses options)
  16.686+	   (setq clauses (if (listp clauses) clauses (list clauses)))
  16.687+	   (remove-if-not #'(lambda (x) (member x clauses))
  16.688+			  options :key #'first)))
  16.689+    (let* ((docstring (when (stringp (first options))
  16.690+                        (pop options)))
  16.691+           (merge-clauses (remove-clauses '(:merge :fuze :fuse) options))
  16.692+	   (case-clauses (remove-clauses :case  options))
  16.693+	   (macro-clauses (remove-clauses '(:macro-char :dispatch-macro-char)
  16.694+                                          options))
  16.695+	   (syntax-clauses (remove-clauses :syntax-from options))
  16.696+	   (other-clauses
  16.697+             (set-difference options
  16.698+                             (append merge-clauses case-clauses
  16.699+                                     macro-clauses syntax-clauses))))
  16.700+      (cond
  16.701+	((not (null other-clauses))
  16.702+	 (error "Bogus DEFREADTABLE clauses: ~/PPRINT-LINEAR/" other-clauses))
  16.703+	(t
  16.704+	 `(eval-when (:load-toplevel :execute)
  16.705+            ;; The (FIND-READTABLE ...) is important for proper
  16.706+            ;; redefinition semantics, as redefining has to modify the
  16.707+            ;; already existing readtable object.
  16.708+            (let ((readtable (find-readtable ',name)))
  16.709+              (cond ((not readtable)
  16.710+                     (setq readtable (make-readtable ',name)))
  16.711+                    (t
  16.712+                     (setq readtable (%clear-readtable readtable))
  16.713+                     (simple-style-warn
  16.714+                      "Overwriting already existing readtable ~S."
  16.715+                      readtable)))
  16.716+              (setf (documentation readtable 'readtable) ,docstring)
  16.717+              ,@(loop for option in merge-clauses
  16.718+                      collect (process-option option 'readtable))
  16.719+              ,@(loop for option in case-clauses
  16.720+                      collect (process-option option 'readtable))
  16.721+              ,@(loop for option in macro-clauses
  16.722+                      collect (process-option option 'readtable))
  16.723+              ,@(loop for option in syntax-clauses
  16.724+                      collect (process-option option 'readtable))
  16.725+              readtable)))))))
  16.726+
  16.727+(defmacro in-readtable (name)
  16.728+  "Set *READTABLE* to the readtable referred to by the symbol NAME.
  16.729+  Return the readtable."
  16.730+  (check-type name symbol)
  16.731+  `(eval-when (:compile-toplevel :load-toplevel :execute)
  16.732+     ;; NB. The :LOAD-TOPLEVEL is needed for cases like (DEFVAR *FOO*
  16.733+     ;; (GET-MACRO-CHARACTER #\"))
  16.734+     (setf *readtable* (ensure-readtable ',name))
  16.735+     (when (find-package :swank)
  16.736+       (%frob-swank-readtable-alist *package* *readtable*))
  16.737+     *readtable*))
  16.738+
  16.739+;;; KLUDGE: [interim solution]
  16.740+;;;
  16.741+;;;   We need support for this in Slime itself, because we want IN-READTABLE
  16.742+;;;   to work on a per-file basis, and not on a per-package basis.
  16.743+;;;
  16.744+(defun %frob-swank-readtable-alist (package readtable)
  16.745+  (let ((readtable-alist (find-symbol (string '#:*readtable-alist*)
  16.746+				      (find-package :swank))))
  16.747+    (when (boundp readtable-alist)
  16.748+      (let ((new-item (cons (package-name package) readtable)))
  16.749+        (setf (symbol-value readtable-alist)
  16.750+              (cons
  16.751+               new-item
  16.752+               (remove new-item (symbol-value readtable-alist)
  16.753+                       :test (lambda (entry1 entry2)
  16.754+                               (string= (car entry1) (car entry2))))))))))
  16.755+
  16.756+(deftype readtable-designator ()
  16.757+  `(or null readtable))
  16.758+
  16.759+(deftype named-readtable-designator ()
  16.760+  "Either a symbol or a readtable itself."
  16.761+  `(or readtable-designator symbol))
  16.762+
  16.763+;;;;; Compiler macros
  16.764+
  16.765+;;; Since the :STANDARD readtable is interned, and we can't enforce
  16.766+;;; its immutability, we signal a style-warning for suspicious uses
  16.767+;;; that may result in strange behaviour:
  16.768+
  16.769+;;; Modifying the standard readtable would, obviously, lead to a
  16.770+;;; propagation of this change to all places which use the :STANDARD
  16.771+;;; readtable (and thus rendering this readtable to be non-standard,
  16.772+;;; in fact.)
  16.773+(eval-when (:compile-toplevel :load-toplevel :execute)
  16.774+  (defun constant-standard-readtable-expression-p (thing)
  16.775+    (or (null thing)
  16.776+        (eq thing :standard)
  16.777+        (and (consp thing)
  16.778+             (find thing
  16.779+                   '((find-readtable nil)
  16.780+                     (find-readtable :standard)
  16.781+                     (ensure-readtable nil)
  16.782+                     (ensure-readtable :standard))
  16.783+                   :test #'equal))))
  16.784+
  16.785+  (defun signal-suspicious-registration-warning (name-expr readtable-expr)
  16.786+    (when (constant-standard-readtable-expression-p readtable-expr)
  16.787+      (simple-style-warn
  16.788+       "Caution: ~<You're trying to register the :STANDARD readtable ~
  16.789+    under a new name ~S. As modification of the :STANDARD readtable ~
  16.790+    is not permitted, subsequent modification of ~S won't be ~
  16.791+    permitted either. You probably want to wrap COPY-READTABLE ~
  16.792+    around~@:>~%             ~S"
  16.793+       (list name-expr name-expr) readtable-expr))))
  16.794+
  16.795+(define-compiler-macro register-readtable (&whole form name readtable)
  16.796+  (signal-suspicious-registration-warning name readtable)
  16.797+  form)
  16.798+
  16.799+(define-compiler-macro ensure-readtable (&whole form name &optional
  16.800+                                                (default nil default-p))
  16.801+  (when default-p
  16.802+    (signal-suspicious-registration-warning name default))
  16.803+  form)
  16.804+
  16.805+(declaim (special *standard-readtable* *empty-readtable*))
  16.806+
  16.807+(define-api make-readtable
  16.808+    (&optional (name nil name-supplied-p) &key merge)
  16.809+    (&optional named-readtable-designator &key (:merge list) => readtable)
  16.810+  "Creates and returns a new readtable under the specified
  16.811+  NAME.
  16.812+
  16.813+  MERGE takes a list of NAMED-READTABLE-DESIGNATORs and specifies the
  16.814+  readtables the new readtable is created from. (See the :MERGE clause
  16.815+  of DEFREADTABLE for details.)
  16.816+
  16.817+  If MERGE is NIL, an empty readtable is used instead.
  16.818+
  16.819+  If NAME is not given, an anonymous empty readtable is returned.
  16.820+
  16.821+  Notes:
  16.822+
  16.823+  An empty readtable is a readtable where each character's syntax is
  16.824+  the same as in the _standard readtable_ except that each macro
  16.825+  character has been made a constituent. Basically: whitespace stays
  16.826+  whitespace, everything else is constituent."
  16.827+  (cond ((not name-supplied-p)
  16.828+         (copy-readtable *empty-readtable*))
  16.829+        ((reserved-readtable-name-p name)
  16.830+         (error "~A is the designator for a predefined readtable. ~
  16.831+                Not acceptable as a user-specified readtable name." name))
  16.832+        ((let ((rt (find-readtable name)))
  16.833+           (and rt (prog1 nil
  16.834+                     (cerror "Overwrite existing entry."
  16.835+                             'readtable-does-already-exist :readtable-name name)
  16.836+                     ;; Explicitly unregister to make sure that we do
  16.837+                     ;; not hold on of any reference to RT.
  16.838+                     (unregister-readtable rt)))))
  16.839+        (t (let ((result (apply #'merge-readtables-into
  16.840+                                ;; The first readtable specified in
  16.841+                                ;; the :merge list is taken as the
  16.842+                                ;; basis for all subsequent
  16.843+                                ;; (destructive!) modifications (and
  16.844+                                ;; hence it's copied.)
  16.845+                                (copy-readtable (if merge
  16.846+                                                    (ensure-readtable
  16.847+                                                     (first merge))
  16.848+                                                    *empty-readtable*))
  16.849+                                (rest merge))))
  16.850+
  16.851+             (register-readtable name result)))))
  16.852+
  16.853+(define-api rename-readtable
  16.854+    (old-name new-name)
  16.855+    (named-readtable-designator symbol => readtable)
  16.856+  "Replaces the associated name of the readtable designated by
  16.857+  OLD-NAME with NEW-NAME. If a readtable is already registered under
  16.858+  NEW-NAME, an error of type READTABLE-DOES-ALREADY-EXIST is
  16.859+  signaled."
  16.860+  (when (find-readtable new-name)
  16.861+    (cerror "Overwrite existing entry."
  16.862+            'readtable-does-already-exist :readtable-name new-name))
  16.863+  (let* ((readtable (ensure-readtable old-name))
  16.864+	 (readtable-name (readtable-name readtable)))
  16.865+    ;; We use the internal functions directly to omit repeated
  16.866+    ;; type-checking.
  16.867+    (%unassociate-name-from-readtable readtable-name readtable)
  16.868+    (%unassociate-readtable-from-name readtable-name readtable)
  16.869+    (%associate-name-with-readtable new-name readtable)
  16.870+    (%associate-readtable-with-name new-name readtable)
  16.871+    (%associate-docstring-with-readtable
  16.872+     readtable (%unassociate-docstring-from-readtable readtable))
  16.873+    readtable))
  16.874+
  16.875+(define-api merge-readtables-into
  16.876+    (result-readtable &rest named-readtables)
  16.877+    (named-readtable-designator &rest named-readtable-designator => readtable)
  16.878+  "Copy macro character definitions of each readtable in
  16.879+  NAMED-READTABLES into RESULT-READTABLE.
  16.880+
  16.881+  If a macro character appears in more than one of the readtables,
  16.882+  i.e. if a conflict is discovered during the merge, an error of type
  16.883+  READER-MACRO-CONFLICT is signaled.
  16.884+
  16.885+  The copied options are :DISPATCH-MACRO-CHAR, :MACRO-CHAR and
  16.886+  :SYNTAX-FROM, but not READTABLE-CASE."
  16.887+  (flet ((merge-into (to from)
  16.888+	   (do-readtable ((char reader-fn non-terminating-p disp? table) from)
  16.889+             (check-reader-macro-conflict from to char)
  16.890+             (cond ((not disp?)
  16.891+                    (set-macro-character char reader-fn non-terminating-p to))
  16.892+                   (t
  16.893+                    (ensure-dispatch-macro-character char non-terminating-p to)
  16.894+                    (loop for (subchar . subfn) in table do
  16.895+                      (check-reader-macro-conflict from to char subchar)
  16.896+                      (set-dispatch-macro-character char subchar
  16.897+                                                    subfn to)))))
  16.898+	   to))
  16.899+    (let ((result-table (ensure-readtable result-readtable)))
  16.900+      (dolist (table (mapcar #'ensure-readtable named-readtables))
  16.901+        (merge-into result-table table))
  16.902+      result-table)))
  16.903+
  16.904+(defun ensure-dispatch-macro-character (char &optional non-terminating-p
  16.905+                                                       (readtable *readtable*))
  16.906+  (if (dispatch-macro-char-p char readtable)
  16.907+      t
  16.908+      (make-dispatch-macro-character char non-terminating-p readtable)))
  16.909+
  16.910+(define-api copy-named-readtable
  16.911+    (named-readtable)
  16.912+    (named-readtable-designator => readtable)
  16.913+  "Like COPY-READTABLE but takes a NAMED-READTABLE-DESIGNATOR as argument."
  16.914+  (copy-readtable (ensure-readtable named-readtable)))
  16.915+
  16.916+(define-api list-all-named-readtables () (=> list)
  16.917+  "Returns a list of all registered readtables. The returned list is
  16.918+  guaranteed to be fresh, but may contain duplicates."
  16.919+  (mapcar #'ensure-readtable (%list-all-readtable-names)))
  16.920+
  16.921+
  16.922+(define-condition readtable-error (error) ())
  16.923+
  16.924+(define-condition readtable-does-not-exist (readtable-error)
  16.925+  ((readtable-name :initarg :readtable-name
  16.926+	           :initform (required-argument)
  16.927+	           :accessor missing-readtable-name
  16.928+                   :type named-readtable-designator))
  16.929+  (:report (lambda (condition stream)
  16.930+             (format stream "A readtable named ~S does not exist."
  16.931+                     (missing-readtable-name condition)))))
  16.932+
  16.933+(define-condition readtable-does-already-exist (readtable-error)
  16.934+  ((readtable-name :initarg :readtable-name
  16.935+                   :initform (required-argument)
  16.936+                   :accessor existing-readtable-name
  16.937+                   :type named-readtable-designator))
  16.938+  (:report (lambda (condition stream)
  16.939+             (format stream "A readtable named ~S already exists."
  16.940+                     (existing-readtable-name condition))))
  16.941+  (:documentation "Continuable."))
  16.942+
  16.943+(define-condition reader-macro-conflict (readtable-error)
  16.944+  ((macro-char
  16.945+    :initarg :macro-char
  16.946+    :initform (required-argument)
  16.947+    :accessor conflicting-macro-char
  16.948+    :type character)
  16.949+   (sub-char
  16.950+    :initarg :sub-char
  16.951+    :initform nil
  16.952+    :accessor conflicting-dispatch-sub-char
  16.953+    :type (or null character))
  16.954+   (from-readtable
  16.955+    :initarg :from-readtable
  16.956+    :initform (required-argument)
  16.957+    :accessor from-readtable
  16.958+    :type readtable)
  16.959+   (to-readtable
  16.960+    :initarg :to-readtable
  16.961+    :initform (required-argument)
  16.962+    :accessor to-readtable
  16.963+    :type readtable))
  16.964+  (:report
  16.965+   (lambda (condition stream)
  16.966+     (format stream "~@<Reader macro conflict while trying to merge the ~
  16.967+                    ~:[macro character~;dispatch macro characters~] ~
  16.968+                    ~@C~@[ ~@C~] from ~A into ~A.~@:>"
  16.969+             (conflicting-dispatch-sub-char condition)
  16.970+             (conflicting-macro-char condition)
  16.971+             (conflicting-dispatch-sub-char condition)
  16.972+             (from-readtable condition)
  16.973+             (to-readtable condition))))
  16.974+  (:documentation "Continuable.
  16.975+
  16.976+  This condition is signaled during the merge process if a reader
  16.977+  macro (be it a macro character or the sub character of a dispatch
  16.978+  macro character) is present in the both source and the target
  16.979+  readtable and the two respective reader macro functions differ."))
  16.980+
  16.981+(defun check-reader-macro-conflict (from to char &optional subchar)
  16.982+  (flet ((conflictp (from-fn to-fn)
  16.983+           (assert from-fn ()
  16.984+                   "Bug in readtable iterators or concurrent access?")
  16.985+           (and to-fn (not (function= to-fn from-fn)))))
  16.986+    (when (if subchar
  16.987+              (conflictp (%get-dispatch-macro-character char subchar from)
  16.988+                         (%get-dispatch-macro-character char subchar to))
  16.989+              (conflictp (%get-macro-character char from)
  16.990+                         (%get-macro-character char to)))
  16.991+      (cerror (format nil "Overwrite ~@C in ~A." char to)
  16.992+              'reader-macro-conflict
  16.993+              :from-readtable from
  16.994+              :to-readtable to
  16.995+              :macro-char char
  16.996+              :sub-char subchar))))
  16.997+
  16.998+
  16.999+;;; Although there is no way to get at the standard readtable in
 16.1000+;;; Common Lisp (cf. /standard readtable/, CLHS glossary), we make
 16.1001+;;; up the perception of its existence by interning a copy of it.
 16.1002+;;;
 16.1003+;;; We do this for reverse lookup (cf. READTABLE-NAME), i.e. for
 16.1004+;;;
 16.1005+;;;   (equal (readtable-name (find-readtable :standard)) "STANDARD")
 16.1006+;;;
 16.1007+;;; holding true.
 16.1008+;;;
 16.1009+;;; We, however, inherit the restriction that the :STANDARD
 16.1010+;;; readtable _must not be modified_ (cf. CLHS 2.1.1.2), although it'd
 16.1011+;;; technically be feasible (as *STANDARD-READTABLE* will contain a
 16.1012+;;; mutable copy of the implementation-internal standard readtable.)
 16.1013+;;; We cannot enforce this restriction without shadowing
 16.1014+;;; CL:SET-MACRO-CHARACTER and CL:SET-DISPATCH-MACRO-FUNCTION which
 16.1015+;;; is out of scope of this library, though. So we just threaten
 16.1016+;;; with nasal demons.
 16.1017+;;;
 16.1018+(defvar *standard-readtable*
 16.1019+  (%standard-readtable))
 16.1020+
 16.1021+(defvar *empty-readtable*
 16.1022+  (%clear-readtable (copy-readtable nil)))
 16.1023+
 16.1024+(defvar *case-preserving-standard-readtable*
 16.1025+  (let ((readtable (copy-readtable nil)))
 16.1026+    (setf (readtable-case readtable) :preserve)
 16.1027+    readtable))
 16.1028+
 16.1029+(defparameter *reserved-readtable-names*
 16.1030+  '(nil :standard :common-lisp :modern :current))
 16.1031+
 16.1032+(defun reserved-readtable-name-p (name)
 16.1033+  (and (member name *reserved-readtable-names*) t))
 16.1034+
 16.1035+;;; In principle, we could DEFREADTABLE some of these. But we do
 16.1036+;;; reserved readtable lookup seperately, since we can't register a
 16.1037+;;; readtable for :CURRENT anyway.
 16.1038+
 16.1039+(defun find-reserved-readtable (reserved-name)
 16.1040+  (cond ((eq reserved-name nil)          *standard-readtable*)
 16.1041+	((eq reserved-name :standard)    *standard-readtable*)
 16.1042+        ((eq reserved-name :common-lisp) *standard-readtable*)
 16.1043+        ((eq reserved-name :modern)      *case-preserving-standard-readtable*)
 16.1044+	((eq reserved-name :current)     *readtable*)
 16.1045+	(t (error "Bug: no such reserved readtable: ~S" reserved-name))))
 16.1046+
 16.1047+(define-api find-readtable
 16.1048+    (name)
 16.1049+    (named-readtable-designator => (or readtable null))
 16.1050+  "Looks for the readtable specified by NAME and returns it if it is
 16.1051+  found. Returns NIL otherwise."
 16.1052+  (cond ((readtablep name) name)
 16.1053+        ((reserved-readtable-name-p name)
 16.1054+         (find-reserved-readtable name))
 16.1055+        ((%find-readtable name))))
 16.1056+
 16.1057+;;; FIXME: This doesn't take a NAMED-READTABLE-DESIGNATOR, but only a
 16.1058+;;; STRING-DESIGNATOR. (When fixing, heed interplay with compiler
 16.1059+;;; macros below.)
 16.1060+(defsetf find-readtable register-readtable)
 16.1061+
 16.1062+(define-api ensure-readtable
 16.1063+    (name &optional (default nil default-p))
 16.1064+    (named-readtable-designator &optional (or named-readtable-designator null)
 16.1065+      => readtable)
 16.1066+  "Looks up the readtable specified by NAME and returns it if it's found.
 16.1067+  If it is not found, it registers the readtable designated by DEFAULT
 16.1068+  under the name represented by NAME; or if no default argument is
 16.1069+  given, it signals an error of type READTABLE-DOES-NOT-EXIST
 16.1070+  instead."
 16.1071+  (cond ((find-readtable name))
 16.1072+        ((not default-p)
 16.1073+         (error 'readtable-does-not-exist :readtable-name name))
 16.1074+        (t (setf (find-readtable name) (ensure-readtable default)))))
 16.1075+
 16.1076+
 16.1077+(define-api register-readtable
 16.1078+    (name readtable)
 16.1079+    (symbol readtable => readtable)
 16.1080+  "Associate READTABLE with NAME. Returns the readtable."
 16.1081+  (assert (typep name '(not (satisfies reserved-readtable-name-p))))
 16.1082+  (%associate-readtable-with-name name readtable)
 16.1083+  (%associate-name-with-readtable name readtable)
 16.1084+  readtable)
 16.1085+
 16.1086+(define-api unregister-readtable
 16.1087+    (named-readtable)
 16.1088+    (named-readtable-designator => boolean)
 16.1089+  "Remove the association of NAMED-READTABLE. Returns T if successfull,
 16.1090+  NIL otherwise."
 16.1091+  (let* ((readtable (find-readtable named-readtable))
 16.1092+	 (readtable-name (and readtable (readtable-name readtable))))
 16.1093+    (if (not readtable-name)
 16.1094+	nil
 16.1095+	(prog1 t
 16.1096+	  (check-type readtable-name
 16.1097+                      (not (satisfies reserved-readtable-name-p)))
 16.1098+          (%unassociate-readtable-from-name readtable-name readtable)
 16.1099+          (%unassociate-name-from-readtable readtable-name readtable)
 16.1100+          (%unassociate-docstring-from-readtable readtable)))))
 16.1101+
 16.1102+(define-api readtable-name
 16.1103+    (named-readtable)
 16.1104+    (named-readtable-designator => symbol)
 16.1105+  "Returns the name of the readtable designated by NAMED-READTABLE,
 16.1106+  or NIL."
 16.1107+   (let ((readtable (ensure-readtable named-readtable)))
 16.1108+    (cond ((%readtable-name readtable))
 16.1109+          ((eq readtable *readtable*) :current)
 16.1110+	  ((eq readtable *standard-readtable*) :common-lisp)
 16.1111+          ((eq readtable *case-preserving-standard-readtable*) :modern)
 16.1112+	  (t nil))))
 16.1113+
 16.1114+(provide :readtables)
    17.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2+++ b/lisp/std/pan.lisp	Mon Oct 16 19:33:42 2023 -0400
    17.3@@ -0,0 +1,98 @@
    17.4+;;; pan.lisp --- Pandoric macros
    17.5+
    17.6+;;; Code:
    17.7+(defpackage :std/pan
    17.8+  (:nicknames :pan)
    17.9+  (:use :cl :named-readtables :fu :ana)
   17.10+  (:export
   17.11+   #:pandoriclet
   17.12+   #:pandoriclet-get
   17.13+   #:pandoriclet-set
   17.14+   #:get-pandoric
   17.15+   #:with-pandoric
   17.16+   #:pandoric-hotpatch
   17.17+   #:pandoric-recode
   17.18+   #:plambda
   17.19+   #:pandoric-eval))
   17.20+
   17.21+(in-package :pan)
   17.22+(in-readtable :std)
   17.23+
   17.24+(defun pandoriclet-get (letargs)
   17.25+  `(case sym
   17.26+     ,@(mapcar #`((,(car a1)) ,(car a1))
   17.27+               letargs)
   17.28+     (t (error
   17.29+          "Unknown pandoric get: ~a"
   17.30+          sym))))
   17.31+
   17.32+(defun pandoriclet-set (letargs)
   17.33+  `(case sym
   17.34+     ,@(mapcar #`((,(car a1))
   17.35+                   (setq ,(car a1) val))
   17.36+               letargs)
   17.37+     (t (error
   17.38+          "Unknown pandoric set: ~a"
   17.39+          sym))))
   17.40+
   17.41+(defmacro pandoriclet (letargs &rest body)
   17.42+  (let ((letargs (cons
   17.43+                   '(this)
   17.44+                   (let-binding-transform
   17.45+                     letargs))))
   17.46+    `(let (,@letargs)
   17.47+       (setq this ,@(last body))
   17.48+       ,@(butlast body)
   17.49+       (dlambda
   17.50+         (:pandoric-get (sym)
   17.51+           ,(pandoriclet-get letargs))
   17.52+         (:pandoric-set (sym val)
   17.53+           ,(pandoriclet-set letargs))
   17.54+         (t (&rest args)
   17.55+           (apply this args))))))
   17.56+
   17.57+(declaim (inline get-pandoric))
   17.58+
   17.59+(defun get-pandoric (box sym)
   17.60+  (funcall box :pandoric-get sym))
   17.61+
   17.62+(defsetf get-pandoric (box sym) (val)
   17.63+  `(progn
   17.64+     (funcall ,box :pandoric-set ,sym ,val)
   17.65+     ,val))
   17.66+
   17.67+(defmacro! with-pandoric (syms o!box &rest body)
   17.68+       `(symbol-macrolet
   17.69+         (,@(mapcar #`(,a1 (get-pandoric ,g!box ',a1))
   17.70+                    syms))
   17.71+         ,@body))
   17.72+
   17.73+(defun pandoric-hotpatch (box new)
   17.74+  (with-pandoric (this) box
   17.75+    (setq this new)))
   17.76+
   17.77+(defmacro pandoric-recode (vars box new)
   17.78+  `(with-pandoric (this ,@vars) ,box
   17.79+     (setq this ,new)))
   17.80+
   17.81+(defmacro plambda (largs pargs &rest body)
   17.82+  (let ((pargs (mapcar #'list pargs)))
   17.83+    `(let (this self)
   17.84+       (setq
   17.85+         this (lambda ,largs ,@body)
   17.86+         self (dlambda
   17.87+                (:pandoric-get (sym)
   17.88+                  ,(pandoriclet-get pargs))
   17.89+                (:pandoric-set (sym val)
   17.90+                  ,(pandoriclet-set pargs))
   17.91+                (t (&rest args)
   17.92+                  (apply this args)))))))
   17.93+
   17.94+(defvar pandoric-eval-tunnel)
   17.95+
   17.96+(defmacro pandoric-eval (vars expr)
   17.97+  `(let ((pandoric-eval-tunnel
   17.98+           (plambda () ,vars t)))
   17.99+     (eval `(with-pandoric
  17.100+              ,',vars pandoric-eval-tunnel
  17.101+              ,,expr))))
    18.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2+++ b/lisp/std/pkg.lisp	Mon Oct 16 19:33:42 2023 -0400
    18.3@@ -0,0 +1,720 @@
    18.4+;;; pkg.lisp @ 2023-10-14.03:28:40 -*- mode: lisp; -*-
    18.5+;;; Code:
    18.6+(defpackage :std/pkg
    18.7+  (:nicknames :pkg)
    18.8+  (:use :cl)
    18.9+  (:export :defpkg
   18.10+   #:find-package* #:find-symbol* #:symbol-call
   18.11+   #:intern* #:export* #:import* #:shadowing-import* #:shadow* #:make-symbol* #:unintern*
   18.12+   #:symbol-shadowing-p #:home-package-p
   18.13+   #:symbol-package-name #:standard-common-lisp-symbol-p
   18.14+   #:reify-package #:unreify-package #:reify-symbol #:unreify-symbol
   18.15+   #:nuke-symbol-in-package #:nuke-symbol #:rehome-symbol
   18.16+   #:ensure-package-unused #:delete-package*
   18.17+   #:package-names #:packages-from-names #:fresh-package-name #:rename-package-away
   18.18+   #:package-definition-form #:parse-defpkg-form
   18.19+   #:ensure-package))
   18.20+
   18.21+(in-package :pkg)
   18.22+
   18.23+(eval-when (:load-toplevel :compile-toplevel :execute)
   18.24+  (defun find-package* (package-designator &optional (error t))
   18.25+    (let ((package (find-package package-designator)))
   18.26+      (cond
   18.27+        (package package)
   18.28+        (error (error "No package named ~S" (string package-designator)))
   18.29+        (t nil))))
   18.30+  (defun find-symbol* (name package-designator &optional (error t))
   18.31+    "Find a symbol in a package of given string'ified NAME;
   18.32+unlike CL:FIND-SYMBOL, work well with 'modern' case sensitive syntax
   18.33+by letting you supply a symbol or keyword for the name;
   18.34+also works well when the package is not present.
   18.35+If optional ERROR argument is NIL, return NIL instead of an error
   18.36+when the symbol is not found."
   18.37+    (block nil
   18.38+      (let ((package (find-package* package-designator error)))
   18.39+        (when package ;; package error handled by find-package* already
   18.40+          (multiple-value-bind (symbol status) (find-symbol (string name) package)
   18.41+            (cond
   18.42+              (status (return (values symbol status)))
   18.43+              (error (error "There is no symbol ~S in package ~S" name (package-name package))))))
   18.44+        (values nil nil))))
   18.45+  (defun symbol-call (package name &rest args)
   18.46+    "Call a function associated with symbol of given name in given package,
   18.47+with given ARGS. Useful when the call is read before the package is loaded,
   18.48+or when loading the package is optional."
   18.49+    (apply (find-symbol* name package) args))
   18.50+  (defun intern* (name package-designator &optional (error t))
   18.51+    (intern (string name) (find-package* package-designator error)))
   18.52+  (defun export* (name package-designator)
   18.53+    (let* ((package (find-package* package-designator))
   18.54+           (symbol (intern* name package)))
   18.55+      (export (or symbol (list symbol)) package)))
   18.56+  (defun import* (symbol package-designator)
   18.57+    (import (or symbol (list symbol)) (find-package* package-designator)))
   18.58+  (defun shadowing-import* (symbol package-designator)
   18.59+    (shadowing-import (or symbol (list symbol)) (find-package* package-designator)))
   18.60+  (defun shadow* (name package-designator)
   18.61+    (shadow (list (string name)) (find-package* package-designator)))
   18.62+  (defun make-symbol* (name)
   18.63+    (etypecase name
   18.64+      (string (make-symbol name))
   18.65+      (symbol (copy-symbol name))))
   18.66+  (defun unintern* (name package-designator &optional (error t))
   18.67+    (block nil
   18.68+      (let ((package (find-package* package-designator error)))
   18.69+        (when package
   18.70+          (multiple-value-bind (symbol status) (find-symbol* name package error)
   18.71+            (cond
   18.72+              (status (unintern symbol package)
   18.73+                      (return (values symbol status)))
   18.74+              (error (error "symbol ~A not present in package ~A"
   18.75+                            (string symbol) (package-name package))))))
   18.76+        (values nil nil))))
   18.77+  (defun symbol-shadowing-p (symbol package)
   18.78+    (and (member symbol (package-shadowing-symbols package)) t))
   18.79+  (defun home-package-p (symbol package)
   18.80+    (and package (let ((sp (symbol-package symbol)))
   18.81+                   (and sp (let ((pp (find-package* package)))
   18.82+                             (and pp (eq sp pp))))))))
   18.83+
   18.84+
   18.85+(eval-when (:load-toplevel :compile-toplevel :execute)
   18.86+  (defun symbol-package-name (symbol)
   18.87+    (let ((package (symbol-package symbol)))
   18.88+      (and package (package-name package))))
   18.89+  (defun standard-common-lisp-symbol-p (symbol)
   18.90+    (multiple-value-bind (sym status) (find-symbol* symbol :common-lisp nil)
   18.91+      (and (eq sym symbol) (eq status :external))))
   18.92+  (defun reify-package (package &optional package-context)
   18.93+    (if (eq package package-context) t
   18.94+        (etypecase package
   18.95+          (null nil)
   18.96+          ((eql (find-package :cl)) :cl)
   18.97+          (package (package-name package)))))
   18.98+  (defun unreify-package (package &optional package-context)
   18.99+    (etypecase package
  18.100+      (null nil)
  18.101+      ((eql t) package-context)
  18.102+      ((or symbol string) (find-package package))))
  18.103+  (defun reify-symbol (symbol &optional package-context)
  18.104+    (etypecase symbol
  18.105+      ((or keyword (satisfies standard-common-lisp-symbol-p)) symbol)
  18.106+      (symbol (vector (symbol-name symbol)
  18.107+                      (reify-package (symbol-package symbol) package-context)))))
  18.108+  (defun unreify-symbol (symbol &optional package-context)
  18.109+    (etypecase symbol
  18.110+      (symbol symbol)
  18.111+      ((simple-vector 2)
  18.112+       (let* ((symbol-name (svref symbol 0))
  18.113+              (package-foo (svref symbol 1))
  18.114+              (package (unreify-package package-foo package-context)))
  18.115+         (if package (intern* symbol-name package)
  18.116+             (make-symbol* symbol-name)))))))
  18.117+
  18.118+(eval-when (:load-toplevel :compile-toplevel :execute)
  18.119+  (defvar *all-package-happiness* '())
  18.120+  (defvar *all-package-fishiness* (list t))
  18.121+  (defun record-fishy (info)
  18.122+    ;;(format t "~&FISHY: ~S~%" info)
  18.123+    (push info *all-package-fishiness*))
  18.124+  (defmacro when-package-fishiness (&body body)
  18.125+    `(when *all-package-fishiness* ,@body))
  18.126+  (defmacro note-package-fishiness (&rest info)
  18.127+    `(when-package-fishiness (record-fishy (list ,@info)))))
  18.128+
  18.129+(eval-when (:load-toplevel :compile-toplevel :execute)
  18.130+  #+(or clisp clozure)
  18.131+  (defun get-setf-function-symbol (symbol)
  18.132+    #+clisp (let ((sym (get symbol 'system::setf-function)))
  18.133+              (if sym (values sym :setf-function)
  18.134+                  (let ((sym (get symbol 'system::setf-expander)))
  18.135+                    (if sym (values sym :setf-expander)
  18.136+                        (values nil nil)))))
  18.137+    #+clozure (gethash symbol ccl::%setf-function-names%))
  18.138+  #+(or clisp clozure)
  18.139+  (defun set-setf-function-symbol (new-setf-symbol symbol &optional kind)
  18.140+    #+clisp (assert (member kind '(:setf-function :setf-expander)))
  18.141+    #+clozure (assert (eq kind t))
  18.142+    #+clisp
  18.143+    (cond
  18.144+      ((null new-setf-symbol)
  18.145+       (remprop symbol 'system::setf-function)
  18.146+       (remprop symbol 'system::setf-expander))
  18.147+      ((eq kind :setf-function)
  18.148+       (setf (get symbol 'system::setf-function) new-setf-symbol))
  18.149+      ((eq kind :setf-expander)
  18.150+       (setf (get symbol 'system::setf-expander) new-setf-symbol))
  18.151+      (t (error "invalid kind of setf-function ~S for ~S to be set to ~S"
  18.152+                kind symbol new-setf-symbol)))
  18.153+    #+clozure
  18.154+    (progn
  18.155+      (gethash symbol ccl::%setf-function-names%) new-setf-symbol
  18.156+      (gethash new-setf-symbol ccl::%setf-function-name-inverses%) symbol))
  18.157+  #+(or clisp clozure)
  18.158+  (defun create-setf-function-symbol (symbol)
  18.159+    #+clisp (system::setf-symbol symbol)
  18.160+    #+clozure (ccl::construct-setf-function-name symbol))
  18.161+  (defun set-dummy-symbol (symbol reason other-symbol)
  18.162+    (setf (get symbol 'dummy-symbol) (cons reason other-symbol)))
  18.163+  (defun make-dummy-symbol (symbol)
  18.164+    (let ((dummy (copy-symbol symbol)))
  18.165+      (set-dummy-symbol dummy 'replacing symbol)
  18.166+      (set-dummy-symbol symbol 'replaced-by dummy)
  18.167+      dummy))
  18.168+  (defun dummy-symbol (symbol)
  18.169+    (get symbol 'dummy-symbol))
  18.170+  (defun get-dummy-symbol (symbol)
  18.171+    (let ((existing (dummy-symbol symbol)))
  18.172+      (if existing (values (cdr existing) (car existing))
  18.173+          (make-dummy-symbol symbol))))
  18.174+  (defun nuke-symbol-in-package (symbol package-designator)
  18.175+    (let ((package (find-package* package-designator))
  18.176+          (name (symbol-name symbol)))
  18.177+      (multiple-value-bind (sym stat) (find-symbol name package)
  18.178+        (when (and (member stat '(:internal :external)) (eq symbol sym))
  18.179+          (if (symbol-shadowing-p symbol package)
  18.180+              (shadowing-import* (get-dummy-symbol symbol) package)
  18.181+              (unintern* symbol package))))))
  18.182+  (defun nuke-symbol (symbol &optional (packages (list-all-packages)))
  18.183+    #+(or clisp clozure)
  18.184+    (multiple-value-bind (setf-symbol kind)
  18.185+        (get-setf-function-symbol symbol)
  18.186+      (when kind (nuke-symbol setf-symbol)))
  18.187+    (loop :for p :in packages :do (nuke-symbol-in-package symbol p)))
  18.188+  (defun rehome-symbol (symbol package-designator)
  18.189+    "Changes the home package of a symbol, also leaving it present in its old home if any"
  18.190+    (let* ((name (symbol-name symbol))
  18.191+           (package (find-package* package-designator))
  18.192+           (old-package (symbol-package symbol))
  18.193+           (old-status (and old-package (nth-value 1 (find-symbol name old-package))))
  18.194+           (shadowing (and old-package (symbol-shadowing-p symbol old-package) (make-symbol name))))
  18.195+      (multiple-value-bind (overwritten-symbol overwritten-symbol-status) (find-symbol name package)
  18.196+        (unless (eq package old-package)
  18.197+          (let ((overwritten-symbol-shadowing-p
  18.198+                  (and overwritten-symbol-status
  18.199+                       (symbol-shadowing-p overwritten-symbol package))))
  18.200+            (note-package-fishiness
  18.201+             :rehome-symbol name
  18.202+             (when old-package (package-name old-package)) old-status (and shadowing t)
  18.203+             (package-name package) overwritten-symbol-status overwritten-symbol-shadowing-p)
  18.204+            (when old-package
  18.205+              (if shadowing
  18.206+                  (shadowing-import* shadowing old-package))
  18.207+              (unintern* symbol old-package))
  18.208+            (cond
  18.209+              (overwritten-symbol-shadowing-p
  18.210+               (shadowing-import* symbol package))
  18.211+              (t
  18.212+               (when overwritten-symbol-status
  18.213+                 (unintern* overwritten-symbol package))
  18.214+               (import* symbol package)))
  18.215+            (if shadowing
  18.216+                (shadowing-import* symbol old-package)
  18.217+                (import* symbol old-package))
  18.218+            #+(or clisp clozure)
  18.219+            (multiple-value-bind (setf-symbol kind)
  18.220+                (get-setf-function-symbol symbol)
  18.221+              (when kind
  18.222+                (let* ((setf-function (fdefinition setf-symbol))
  18.223+                       (new-setf-symbol (create-setf-function-symbol symbol)))
  18.224+                  (note-package-fishiness
  18.225+                   :setf-function
  18.226+                   name (package-name package)
  18.227+                   (symbol-name setf-symbol) (symbol-package-name setf-symbol)
  18.228+                   (symbol-name new-setf-symbol) (symbol-package-name new-setf-symbol))
  18.229+                  (when (symbol-package setf-symbol)
  18.230+                    (unintern* setf-symbol (symbol-package setf-symbol)))
  18.231+                  (setf (fdefinition new-setf-symbol) setf-function)
  18.232+                  (set-setf-function-symbol new-setf-symbol symbol kind))))
  18.233+            #+(or clisp clozure)
  18.234+            (multiple-value-bind (overwritten-setf foundp)
  18.235+                (get-setf-function-symbol overwritten-symbol)
  18.236+              (when foundp
  18.237+                (unintern overwritten-setf)))
  18.238+            (when (eq old-status :external)
  18.239+              (export* symbol old-package))
  18.240+            (when (eq overwritten-symbol-status :external)
  18.241+              (export* symbol package))))
  18.242+        (values overwritten-symbol overwritten-symbol-status))))
  18.243+  (defun ensure-package-unused (package)
  18.244+    (loop :for p :in (package-used-by-list package) :do
  18.245+      (unuse-package package p)))
  18.246+  (defun delete-package* (package &key nuke)
  18.247+    (let ((p (find-package package)))
  18.248+      (when p
  18.249+        (when nuke (do-symbols (s p) (when (home-package-p s p) (nuke-symbol s))))
  18.250+        (ensure-package-unused p)
  18.251+        (delete-package package))))
  18.252+  (defun package-names (package)
  18.253+    (cons (package-name package) (package-nicknames package)))
  18.254+  (defun packages-from-names (names)
  18.255+    (remove-duplicates (remove nil (mapcar #'find-package names)) :from-end t))
  18.256+  (defun fresh-package-name (&key (prefix :%TO-BE-DELETED)
  18.257+                               separator
  18.258+                               (index (random most-positive-fixnum)))
  18.259+    (loop :for i :from index
  18.260+          :for n = (format nil "~A~@[~A~D~]" prefix (and (plusp i) (or separator "")) i)
  18.261+          :thereis (and (not (find-package n)) n)))
  18.262+  (defun rename-package-away (p &rest keys &key prefix &allow-other-keys)
  18.263+    (let ((new-name
  18.264+            (apply 'fresh-package-name
  18.265+                   :prefix (or prefix (format nil "__~A__" (package-name p))) keys)))
  18.266+      (record-fishy (list :rename-away (package-names p) new-name))
  18.267+      (rename-package p new-name))))
  18.268+
  18.269+
  18.270+;;; Communicable representation of symbol and package information
  18.271+
  18.272+(eval-when (:load-toplevel :compile-toplevel :execute)
  18.273+  (defun package-definition-form (package-designator
  18.274+                                  &key (nicknamesp t) (usep t)
  18.275+                                    (shadowp t) (shadowing-import-p t)
  18.276+                                    (exportp t) (importp t) internp (error t))
  18.277+    (let* ((package (or (find-package* package-designator error)
  18.278+                        (return-from package-definition-form nil)))
  18.279+           (name (package-name package))
  18.280+           (nicknames (package-nicknames package))
  18.281+           (use (mapcar #'package-name (package-use-list package)))
  18.282+           (shadow ())
  18.283+           (shadowing-import (make-hash-table :test 'equal))
  18.284+           (import (make-hash-table :test 'equal))
  18.285+           (export ())
  18.286+           (intern ()))
  18.287+      (when package
  18.288+        (loop :for sym :being :the :symbols :in package
  18.289+              :for status = (nth-value 1 (find-symbol* sym package)) :do
  18.290+                (ecase status
  18.291+                  ((nil :inherited))
  18.292+                  ((:internal :external)
  18.293+                   (let* ((name (symbol-name sym))
  18.294+                          (external (eq status :external))
  18.295+                          (home (symbol-package sym))
  18.296+                          (home-name (package-name home))
  18.297+                          (imported (not (eq home package)))
  18.298+                          (shadowing (symbol-shadowing-p sym package)))
  18.299+                     (cond
  18.300+                       ((and shadowing imported)
  18.301+                        (push name (gethash home-name shadowing-import)))
  18.302+                       (shadowing
  18.303+                        (push name shadow))
  18.304+                       (imported
  18.305+                        (push name (gethash home-name import))))
  18.306+                     (cond
  18.307+                       (external
  18.308+                        (push name export))
  18.309+                       (imported)
  18.310+                       (t (push name intern)))))))
  18.311+        (labels ((sort-names (names)
  18.312+                   (sort (copy-list names) #'string<))
  18.313+                 (table-keys (table)
  18.314+                   (loop :for k :being :the :hash-keys :of table :collect k))
  18.315+                 (when-relevant (key value)
  18.316+                   (when value (list (cons key value))))
  18.317+                 (import-options (key table)
  18.318+                   (loop :for i :in (sort-names (table-keys table))
  18.319+                         :collect `(,key ,i ,@(sort-names (gethash i table))))))
  18.320+          `(defpackage ,name
  18.321+             ,@(when-relevant :nicknames (and nicknamesp (sort-names nicknames)))
  18.322+             (:use ,@(and usep (sort-names use)))
  18.323+             ,@(when-relevant :shadow (and shadowp (sort-names shadow)))
  18.324+             ,@(import-options :shadowing-import-from (and shadowing-import-p shadowing-import))
  18.325+             ,@(import-options :import-from (and importp import))
  18.326+             ,@(when-relevant :export (and exportp (sort-names export)))
  18.327+             ,@(when-relevant :intern (and internp (sort-names intern)))))))))
  18.328+
  18.329+(eval-when (:load-toplevel :compile-toplevel :execute)
  18.330+  (defun ensure-shadowing-import (name to-package from-package shadowed imported)
  18.331+    (check-type name string)
  18.332+    (check-type to-package package)
  18.333+    (check-type from-package package)
  18.334+    (check-type shadowed hash-table)
  18.335+    (check-type imported hash-table)
  18.336+    (let ((import-me (find-symbol* name from-package)))
  18.337+      (multiple-value-bind (existing status) (find-symbol name to-package)
  18.338+        (cond
  18.339+          ((gethash name shadowed)
  18.340+           (unless (eq import-me existing)
  18.341+             (error "Conflicting shadowings for ~A" name)))
  18.342+          (t
  18.343+           (setf (gethash name shadowed) t)
  18.344+           (setf (gethash name imported) t)
  18.345+           (unless (or (null status)
  18.346+                       (and (member status '(:internal :external))
  18.347+                            (eq existing import-me)
  18.348+                            (symbol-shadowing-p existing to-package)))
  18.349+             (note-package-fishiness
  18.350+              :shadowing-import name
  18.351+              (package-name from-package)
  18.352+              (or (home-package-p import-me from-package) (symbol-package-name import-me))
  18.353+              (package-name to-package) status
  18.354+              (and status (or (home-package-p existing to-package) (symbol-package-name existing)))))
  18.355+           (shadowing-import* import-me to-package))))))
  18.356+  (defun ensure-imported (import-me into-package &optional from-package)
  18.357+    (check-type import-me symbol)
  18.358+    (check-type into-package package)
  18.359+    (check-type from-package (or null package))
  18.360+    (let ((name (symbol-name import-me)))
  18.361+      (multiple-value-bind (existing status) (find-symbol name into-package)
  18.362+        (cond
  18.363+          ((not status)
  18.364+           (import* import-me into-package))
  18.365+          ((eq import-me existing))
  18.366+          (t
  18.367+           (let ((shadowing-p (symbol-shadowing-p existing into-package)))
  18.368+             (note-package-fishiness
  18.369+              :ensure-imported name
  18.370+              (and from-package (package-name from-package))
  18.371+              (or (home-package-p import-me from-package) (symbol-package-name import-me))
  18.372+              (package-name into-package)
  18.373+              status
  18.374+              (and status (or (home-package-p existing into-package) (symbol-package-name existing)))
  18.375+              shadowing-p)
  18.376+             (cond
  18.377+               ((or shadowing-p (eq status :inherited))
  18.378+                (shadowing-import* import-me into-package))
  18.379+               (t
  18.380+                (unintern* existing into-package)
  18.381+                (import* import-me into-package))))))))
  18.382+    (values))
  18.383+  (defun ensure-import (name to-package from-package shadowed imported)
  18.384+    (check-type name string)
  18.385+    (check-type to-package package)
  18.386+    (check-type from-package package)
  18.387+    (check-type shadowed hash-table)
  18.388+    (check-type imported hash-table)
  18.389+    (multiple-value-bind (import-me import-status) (find-symbol name from-package)
  18.390+      (when (null import-status)
  18.391+        (note-package-fishiness
  18.392+         :import-uninterned name (package-name from-package) (package-name to-package))
  18.393+        (setf import-me (intern* name from-package)))
  18.394+      (multiple-value-bind (existing status) (find-symbol name to-package)
  18.395+        (cond
  18.396+          ((and imported (gethash name imported))
  18.397+           (unless (and status (eq import-me existing))
  18.398+             (error "Can't import ~S from both ~S and ~S"
  18.399+                    name (package-name (symbol-package existing)) (package-name from-package))))
  18.400+          ((gethash name shadowed)
  18.401+           (error "Can't both shadow ~S and import it from ~S" name (package-name from-package)))
  18.402+          (t
  18.403+           (setf (gethash name imported) t))))
  18.404+      (ensure-imported import-me to-package from-package)))
  18.405+  (defun ensure-inherited (name symbol to-package from-package mixp shadowed imported inherited)
  18.406+    (check-type name string)
  18.407+    (check-type symbol symbol)
  18.408+    (check-type to-package package)
  18.409+    (check-type from-package package)
  18.410+    (check-type mixp (member nil t)) ; no cl:boolean on Genera
  18.411+    (check-type shadowed hash-table)
  18.412+    (check-type imported hash-table)
  18.413+    (check-type inherited hash-table)
  18.414+    (multiple-value-bind (existing status) (find-symbol name to-package)
  18.415+      (let* ((sp (symbol-package symbol))
  18.416+             (in (gethash name inherited))
  18.417+             (xp (and status (symbol-package existing))))
  18.418+        (when (null sp)
  18.419+          (note-package-fishiness
  18.420+           :import-uninterned name
  18.421+           (package-name from-package) (package-name to-package) mixp)
  18.422+          (import* symbol from-package)
  18.423+          (setf sp (package-name from-package)))
  18.424+        (cond
  18.425+          ((gethash name shadowed))
  18.426+          (in
  18.427+           (unless (equal sp (first in))
  18.428+             (if mixp
  18.429+                 (ensure-shadowing-import name to-package (second in) shadowed imported)
  18.430+                 (error "Can't inherit ~S from ~S, it is inherited from ~S"
  18.431+                        name (package-name sp) (package-name (first in))))))
  18.432+          ((gethash name imported)
  18.433+           (unless (eq symbol existing)
  18.434+             (error "Can't inherit ~S from ~S, it is imported from ~S"
  18.435+                    name (package-name sp) (package-name xp))))
  18.436+          (t
  18.437+           (setf (gethash name inherited) (list sp from-package))
  18.438+           (when (and status (not (eq sp xp)))
  18.439+             (let ((shadowing (symbol-shadowing-p existing to-package)))
  18.440+               (note-package-fishiness
  18.441+                :inherited name
  18.442+                (package-name from-package)
  18.443+                (or (home-package-p symbol from-package) (symbol-package-name symbol))
  18.444+                (package-name to-package)
  18.445+                (or (home-package-p existing to-package) (symbol-package-name existing)))
  18.446+               (if shadowing (ensure-shadowing-import name to-package from-package shadowed imported)
  18.447+                   (unintern* existing to-package)))))))))
  18.448+  (defun ensure-mix (name symbol to-package from-package shadowed imported inherited)
  18.449+    (check-type name string)
  18.450+    (check-type symbol symbol)
  18.451+    (check-type to-package package)
  18.452+    (check-type from-package package)
  18.453+    (check-type shadowed hash-table)
  18.454+    (check-type imported hash-table)
  18.455+    (check-type inherited hash-table)
  18.456+    (unless (gethash name shadowed)
  18.457+      (multiple-value-bind (existing status) (find-symbol name to-package)
  18.458+        (let* ((sp (symbol-package symbol))
  18.459+               (im (gethash name imported))
  18.460+               (in (gethash name inherited)))
  18.461+          (cond
  18.462+            ((or (null status)
  18.463+                 (and status (eq symbol existing))
  18.464+                 (and in (eq sp (first in))))
  18.465+             (ensure-inherited name symbol to-package from-package t shadowed imported inherited))
  18.466+            (in
  18.467+             (remhash name inherited)
  18.468+             (ensure-shadowing-import name to-package (second in) shadowed imported))
  18.469+            (im
  18.470+             (error "Symbol ~S import from ~S~:[~; actually ~:[uninterned~;~:*from ~S~]~] conflicts with existing symbol in ~S~:[~; actually ~:[uninterned~;from ~:*~S~]~]"
  18.471+                    name (package-name from-package)
  18.472+                    (home-package-p symbol from-package) (symbol-package-name symbol)
  18.473+                    (package-name to-package)
  18.474+                    (home-package-p existing to-package) (symbol-package-name existing)))
  18.475+            (t
  18.476+             (ensure-inherited name symbol to-package from-package t shadowed imported inherited)))))))
  18.477+
  18.478+  (defun recycle-symbol (name recycle exported)
  18.479+    ;; Takes a symbol NAME (a string), a list of package designators for RECYCLE
  18.480+    ;; packages, and a hash-table of names (strings) of symbols scheduled to be
  18.481+    ;; EXPORTED from the package being defined. It returns two values, the
  18.482+    ;; symbol found (if any, or else NIL), and a boolean flag indicating whether
  18.483+    ;; a symbol was found. The caller (DEFPKG) will then do the
  18.484+    ;; re-homing of the symbol, etc.
  18.485+    (check-type name string)
  18.486+    (check-type recycle list)
  18.487+    (check-type exported hash-table)
  18.488+    (when (gethash name exported) ;; don't bother recycling private symbols
  18.489+      (let (recycled foundp)
  18.490+        (dolist (r recycle (values recycled foundp))
  18.491+          (multiple-value-bind (symbol status) (find-symbol name r)
  18.492+            (when (and status (home-package-p symbol r))
  18.493+              (cond
  18.494+                (foundp
  18.495+                 ;; (nuke-symbol symbol)) -- even simple variable names like O or C will do that.
  18.496+                 (note-package-fishiness :recycled-duplicate name (package-name foundp) (package-name r)))
  18.497+                (t
  18.498+                 (setf recycled symbol foundp r)))))))))
  18.499+  (defun symbol-recycled-p (sym recycle)
  18.500+    (check-type sym symbol)
  18.501+    (check-type recycle list)
  18.502+    (and (member (symbol-package sym) recycle) t))
  18.503+  (defun ensure-symbol (name package intern recycle shadowed imported inherited exported)
  18.504+    (check-type name string)
  18.505+    (check-type package package)
  18.506+    (check-type intern (member nil t)) ; no cl:boolean on Genera
  18.507+    (check-type shadowed hash-table)
  18.508+    (check-type imported hash-table)
  18.509+    (check-type inherited hash-table)
  18.510+    (unless (or (gethash name shadowed)
  18.511+                (gethash name imported)
  18.512+                (gethash name inherited))
  18.513+      (multiple-value-bind (existing status)
  18.514+          (find-symbol name package)
  18.515+        (multiple-value-bind (recycled previous) (recycle-symbol name recycle exported)
  18.516+          (cond
  18.517+            ((and status (eq existing recycled) (eq previous package)))
  18.518+            (previous
  18.519+             (rehome-symbol recycled package))
  18.520+            ((and status (eq package (symbol-package existing))))
  18.521+            (t
  18.522+             (when status
  18.523+               (note-package-fishiness
  18.524+                :ensure-symbol name
  18.525+                (reify-package (symbol-package existing) package)
  18.526+                status intern)
  18.527+               (unintern existing))
  18.528+             (when intern
  18.529+               (intern* name package))))))))
  18.530+  (declaim (ftype (function (t t t &optional t) t) ensure-exported))
  18.531+  (defun ensure-exported-to-user (name symbol to-package &optional recycle)
  18.532+    (check-type name string)
  18.533+    (check-type symbol symbol)
  18.534+    (check-type to-package package)
  18.535+    (check-type recycle list)
  18.536+    (assert (equal name (symbol-name symbol)))
  18.537+    (multiple-value-bind (existing status) (find-symbol name to-package)
  18.538+      (unless (and status (eq symbol existing))
  18.539+        (let ((accessible
  18.540+                (or (null status)
  18.541+                    (let ((shadowing (symbol-shadowing-p existing to-package))
  18.542+                          (recycled (symbol-recycled-p existing recycle)))
  18.543+                      (unless (and shadowing (not recycled))
  18.544+                        (note-package-fishiness
  18.545+                         :ensure-export name (symbol-package-name symbol)
  18.546+                         (package-name to-package)
  18.547+                         (or (home-package-p existing to-package) (symbol-package-name existing))
  18.548+                         status shadowing)
  18.549+                        (if (or (eq status :inherited) shadowing)
  18.550+                            (shadowing-import* symbol to-package)
  18.551+                            (unintern existing to-package))
  18.552+                        t)))))
  18.553+          (when (and accessible (eq status :external))
  18.554+            (ensure-exported name symbol to-package recycle))))))
  18.555+  (defun ensure-exported (name symbol from-package &optional recycle)
  18.556+    (dolist (to-package (package-used-by-list from-package))
  18.557+      (ensure-exported-to-user name symbol to-package recycle))
  18.558+    (unless (eq from-package (symbol-package symbol))
  18.559+      (ensure-imported symbol from-package))
  18.560+    (export* name from-package))
  18.561+  (defun ensure-export (name from-package &optional recycle)
  18.562+    (multiple-value-bind (symbol status) (find-symbol* name from-package)
  18.563+      (unless (eq status :external)
  18.564+        (ensure-exported name symbol from-package recycle))))
  18.565+
  18.566+  (defun ensure-package (name &key
  18.567+                                nicknames documentation use
  18.568+                                shadow shadowing-import-from
  18.569+                                import-from export intern
  18.570+                                recycle mix reexport
  18.571+                                unintern)
  18.572+    #+genera (declare (ignore documentation))
  18.573+    (let* ((package-name (string name))
  18.574+           (nicknames (mapcar #'string nicknames))
  18.575+           (names (cons package-name nicknames))
  18.576+           (previous (packages-from-names names))
  18.577+           (discarded (cdr previous))
  18.578+           (to-delete ())
  18.579+           (package (or (first previous) (make-package package-name :nicknames nicknames)))
  18.580+           (recycle (packages-from-names recycle))
  18.581+           (use (mapcar 'find-package* use))
  18.582+           (mix (mapcar 'find-package* mix))
  18.583+           (reexport (mapcar 'find-package* reexport))
  18.584+           (shadow (mapcar 'string shadow))
  18.585+           (export (mapcar 'string export))
  18.586+           (intern (mapcar 'string intern))
  18.587+           (unintern (mapcar 'string unintern))
  18.588+           (shadowed (make-hash-table :test 'equal)) ; string to bool
  18.589+           (imported (make-hash-table :test 'equal)) ; string to bool
  18.590+           (exported (make-hash-table :test 'equal)) ; string to bool
  18.591+           ;; string to list home package and use package:
  18.592+           (inherited (make-hash-table :test 'equal)))
  18.593+      (when-package-fishiness (record-fishy package-name))
  18.594+      #-genera
  18.595+      (when documentation (setf (documentation package t) documentation))
  18.596+      (loop :for p :in (set-difference (package-use-list package) (append mix use))
  18.597+            :do (note-package-fishiness :over-use name (package-names p))
  18.598+                (unuse-package p package))
  18.599+      (loop :for p :in discarded
  18.600+            :for n = (remove-if #'(lambda (x) (member x names :test 'equal))
  18.601+                                (package-names p))
  18.602+            :do (note-package-fishiness :nickname name (package-names p))
  18.603+                (cond (n (rename-package p (first n) (rest n)))
  18.604+                      (t (rename-package-away p)
  18.605+                         (push p to-delete))))
  18.606+      (rename-package package package-name nicknames)
  18.607+      (dolist (name unintern)
  18.608+        (multiple-value-bind (existing status) (find-symbol name package)
  18.609+          (when status
  18.610+            (unless (eq status :inherited)
  18.611+              (note-package-fishiness
  18.612+               :unintern (package-name package) name (symbol-package-name existing) status)
  18.613+              (unintern* name package nil)))))
  18.614+      (dolist (name export)
  18.615+        (setf (gethash name exported) t))
  18.616+      (dolist (p reexport)
  18.617+        (do-external-symbols (sym p)
  18.618+          (setf (gethash (string sym) exported) t)))
  18.619+      (do-external-symbols (sym package)
  18.620+        (let ((name (symbol-name sym)))
  18.621+          (unless (gethash name exported)
  18.622+            (note-package-fishiness
  18.623+             :over-export (package-name package) name
  18.624+             (or (home-package-p sym package) (symbol-package-name sym)))
  18.625+            (unexport sym package))))
  18.626+      (dolist (name shadow)
  18.627+        (setf (gethash name shadowed) t)
  18.628+        (multiple-value-bind (existing status) (find-symbol name package)
  18.629+          (multiple-value-bind (recycled previous) (recycle-symbol name recycle exported)
  18.630+            (let ((shadowing (and status (symbol-shadowing-p existing package))))
  18.631+              (cond
  18.632+                ((eq previous package))
  18.633+                (previous
  18.634+                 (rehome-symbol recycled package))
  18.635+                ((or (member status '(nil :inherited))
  18.636+                     (home-package-p existing package)))
  18.637+                (t
  18.638+                 (let ((dummy (make-symbol name)))
  18.639+                   (note-package-fishiness
  18.640+                    :shadow-imported (package-name package) name
  18.641+                    (symbol-package-name existing) status shadowing)
  18.642+                   (shadowing-import* dummy package)
  18.643+                   (import* dummy package)))))))
  18.644+        (shadow* name package))
  18.645+      (loop :for (p . syms) :in shadowing-import-from
  18.646+            :for pp = (find-package* p) :do
  18.647+              (dolist (sym syms) (ensure-shadowing-import (string sym) package pp shadowed imported)))
  18.648+      (loop :for p :in mix
  18.649+            :for pp = (find-package* p) :do
  18.650+              (do-external-symbols (sym pp) (ensure-mix (symbol-name sym) sym package pp shadowed imported inherited)))
  18.651+      (loop :for (p . syms) :in import-from
  18.652+            :for pp = (find-package p) :do
  18.653+              (dolist (sym syms) (ensure-import (symbol-name sym) package pp shadowed imported)))
  18.654+      (dolist (p (append use mix))
  18.655+        (do-external-symbols (sym p) (ensure-inherited (string sym) sym package p nil shadowed imported inherited))
  18.656+        (use-package p package))
  18.657+      (loop :for name :being :the :hash-keys :of exported :do
  18.658+        (ensure-symbol name package t recycle shadowed imported inherited exported)
  18.659+        (ensure-export name package recycle))
  18.660+      (dolist (name intern)
  18.661+        (ensure-symbol name package t recycle shadowed imported inherited exported))
  18.662+      (do-symbols (sym package)
  18.663+        (ensure-symbol (symbol-name sym) package nil recycle shadowed imported inherited exported))
  18.664+      (map () 'delete-package* to-delete)
  18.665+      package)))
  18.666+
  18.667+(eval-when (:load-toplevel :compile-toplevel :execute)
  18.668+  (defun parse-defpkg-form (package clauses)
  18.669+    (loop
  18.670+      :with use-p = nil :with recycle-p = nil
  18.671+      :with documentation = nil
  18.672+      :for (kw . args) :in clauses
  18.673+      :when (eq kw :nicknames) :append args :into nicknames :else
  18.674+      :when (eq kw :documentation)
  18.675+        :do (cond
  18.676+              (documentation (error "defpkg: can't define documentation twice"))
  18.677+              ((or (atom args) (cdr args)) (error "defpkg: bad documentation"))
  18.678+              (t (setf documentation (car args)))) :else
  18.679+      :when (eq kw :use) :append args :into use :and :do (setf use-p t) :else
  18.680+      :when (eq kw :shadow) :append args :into shadow :else
  18.681+      :when (eq kw :shadowing-import-from) :collect args :into shadowing-import-from :else
  18.682+      :when (eq kw :import-from) :collect args :into import-from :else
  18.683+      :when (eq kw :export) :append args :into export :else
  18.684+      :when (eq kw :intern) :append args :into intern :else
  18.685+      :when (eq kw :recycle) :append args :into recycle :and :do (setf recycle-p t) :else
  18.686+      :when (eq kw :mix) :append args :into mix :else
  18.687+      :when (eq kw :reexport) :append args :into reexport :else
  18.688+      :when (eq kw :use-reexport) :append args :into use :and :append args :into reexport
  18.689+        :and :do (setf use-p t) :else
  18.690+      :when (eq kw :mix-reexport) :append args :into mix :and :append args :into reexport
  18.691+        :and :do (setf use-p t) :else
  18.692+      :when (eq kw :unintern) :append args :into unintern :else
  18.693+        :do (error "unrecognized defpkg keyword ~S" kw)
  18.694+      :finally (return `(,package
  18.695+                         :nicknames ,nicknames :documentation ,documentation
  18.696+                         :use ,(if use-p use '(:common-lisp))
  18.697+                         :shadow ,shadow :shadowing-import-from ,shadowing-import-from
  18.698+                         :import-from ,import-from :export ,export :intern ,intern
  18.699+                         :recycle ,(if recycle-p recycle (cons package nicknames))
  18.700+                         :mix ,mix :reexport ,reexport :unintern ,unintern)))))
  18.701+
  18.702+(defmacro defpkg (package &rest clauses)
  18.703+  "Richard's Robust DEFPACKAGE macro. Based on UIOP:DEFINE-PACKAGE. ymmv.
  18.704+
  18.705+DEFPKG takes a PACKAGE and a number of CLAUSES, of the form (KEYWORD . ARGS).
  18.706+
  18.707+DEFPKG supports the following keywords:
  18.708+USE, SHADOW, SHADOWING-IMPORT-FROM, IMPORT-FROM, EXPORT, INTERN -- as per CL:DEFPACKAGE.
  18.709+
  18.710+DEFPKG also redefines the following extensions:
  18.711+RECYCLE, MIX, REEXPORT, UNINTERN -- as per UIOP/PACKAGE:DEFINE-PACKAGE
  18.712+
  18.713+REEXPORT -- Takes a list of package designators. For each package in
  18.714+the list, export symbols with the same name as those exported from
  18.715+that package. In the case of shadowing, etc. They may not be EQL."
  18.716+  (let ((ensure-form
  18.717+          `(apply 'ensure-package ',(parse-defpkg-form package clauses))))
  18.718+    `(progn
  18.719+       #+(or clasp ecl gcl mkcl) (defpackage ,package (:use))
  18.720+       (eval-when (:compile-toplevel :load-toplevel :execute)
  18.721+         ,ensure-form))))
  18.722+
  18.723+(provide :pkg)
    19.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2+++ b/lisp/std/str.lisp	Mon Oct 16 19:33:42 2023 -0400
    19.3@@ -0,0 +1,36 @@
    19.4+;;; std/str.lisp --- String utilities
    19.5+
    19.6+;;; Code:
    19.7+
    19.8+;; (defvar sb-unicode-syms 
    19.9+;;   '(words lines sentences whitespace-p uppercase lowercase titlecase
   19.10+;;     word-break-class line-break-class sentence-break-class char-block
   19.11+;;     cased-p uppercase-p lowercase-p titlecase-p casefold
   19.12+;;     graphemes grapheme-break-class
   19.13+;;     bidi-mirroring-glyph bidi-class
   19.14+;;     normalize-string normalized-p default-ignorable-p
   19.15+;;     confusable-p hex-digit-p mirrored-p alphabetic-p math-p
   19.16+;;     decimal-value digit-value
   19.17+;;     unicode< unicode> unicode= unicode-equal
   19.18+;;     unicode<= unicode>=))
   19.19+
   19.20+(defpackage :std/str
   19.21+  (:use :cl :uiop/driver :sb-unicode)
   19.22+  (:nicknames :str)
   19.23+  (:export
   19.24+   #:string-designator))
   19.25+
   19.26+(in-package :str)
   19.27+
   19.28+;; (mapc (lambda (s) (export s)) sb-unicode-syms)
   19.29+;; (reexport-from 
   19.30+;;  :sb-unicode
   19.31+;;  :include sb-unicode-syms)
   19.32+
   19.33+(deftype string-designator ()
   19.34+  "A string designator type. A string designator is either a string, a symbol,
   19.35+or a character."
   19.36+  `(or symbol string character))
   19.37+;;;  TODO 2023-08-27: camel snake kebab
   19.38+
   19.39+;;; format recipes
    20.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2+++ b/lisp/std/sym.lisp	Mon Oct 16 19:33:42 2023 -0400
    20.3@@ -0,0 +1,90 @@
    20.4+;;; sym.lisp --- Symbol utils
    20.5+
    20.6+;; inspired by alexandria/symbols.lisp
    20.7+
    20.8+;;; Code:
    20.9+(pkg:defpkg :std/sym
   20.10+  (:use :cl :str :sb-int)
   20.11+  (:nicknames :sym)
   20.12+  (:export
   20.13+   #:ensure-symbol
   20.14+   #:format-symbol
   20.15+   #:make-keyword
   20.16+   #:make-slot-name
   20.17+   #:make-gensym
   20.18+   #:make-gensym-list
   20.19+   #:with-gensyms
   20.20+   #:with-unique-names
   20.21+   #:symbolicate))
   20.22+
   20.23+(in-package :sym)
   20.24+
   20.25+;; (reexport-from :sb-int
   20.26+;; 	       :include '(:with-unique-names :symbolicate :package-symbolicate :keywordicate :gensymify*))
   20.27+
   20.28+;; On SBCL, `with-unique-names' is defined under
   20.29+;; src/code/primordial-extensions.lisp. We use that instead of
   20.30+;; defining our own.
   20.31+(setf (macro-function 'with-gensyms) (macro-function 'with-unique-names))
   20.32+
   20.33+(declaim (inline ensure-symbol))
   20.34+(defun ensure-symbol (name &optional (package *package*))
   20.35+  "Returns a symbol with name designated by NAME, accessible in package
   20.36+designated by PACKAGE. If symbol is not already accessible in PACKAGE, it is
   20.37+interned there. Returns a secondary value reflecting the status of the symbol
   20.38+in the package, which matches the secondary return value of INTERN.
   20.39+
   20.40+Example:
   20.41+
   20.42+  (ensure-symbol :cons :cl) => cl:cons, :external
   20.43+"
   20.44+  (intern (string name) package))
   20.45+
   20.46+(defun maybe-intern (name package)
   20.47+  (values
   20.48+   (if package
   20.49+       (intern name (if (eq t package) *package* package))
   20.50+       (make-symbol name))))
   20.51+
   20.52+(declaim (inline format-symbol))
   20.53+(defun format-symbol (package control &rest arguments)
   20.54+  "Constructs a string by applying ARGUMENTS to string designator CONTROL as
   20.55+if by FORMAT within WITH-STANDARD-IO-SYNTAX, and then creates a symbol named
   20.56+by that string.
   20.57+
   20.58+If PACKAGE is NIL, returns an uninterned symbol, if package is T, returns a
   20.59+symbol interned in the current package, and otherwise returns a symbol
   20.60+interned in the package designated by PACKAGE."
   20.61+  (maybe-intern (with-standard-io-syntax
   20.62+                  (apply #'format nil (string control) arguments))
   20.63+                package))
   20.64+
   20.65+(defun make-keyword (name)
   20.66+  "Interns the string designated by NAME in the KEYWORD package."
   20.67+  (intern (string name) :keyword))
   20.68+
   20.69+(defmacro make-slot-name (name)
   20.70+  "make slot-name"
   20.71+  `(intern ,(string-upcase name) :keyword))
   20.72+
   20.73+(defun make-gensym (name)
   20.74+  "If NAME is a non-negative integer, calls GENSYM using it. Otherwise NAME
   20.75+must be a string designator, in which case calls GENSYM using the designated
   20.76+string as the argument."
   20.77+  (gensym (if (typep name '(integer 0))
   20.78+              name
   20.79+              (string name))))
   20.80+
   20.81+(sb-ext:with-unlocked-packages (:sb-int)
   20.82+  (handler-bind
   20.83+      ((sb-kernel:redefinition-warning #'muffle-warning))
   20.84+    (defun make-gensym-list (length &optional (x "G"))
   20.85+      "Returns a list of LENGTH gensyms, each generated as if with a call to
   20.86+MAKE-GENSYM, using the second (optional, defaulting to \"G\")
   20.87+argument. This function is implemented in SBCL
   20.88+src/code/primordial-extensions.lisp but re-implemented here. The only
   20.89+difference is that we also handle non-zero integers, which can be
   20.90+passed as the first argument to `gensym'."
   20.91+      (let ((g (if (typep x '(integer 0)) x (string x))))
   20.92+	(loop repeat length
   20.93+              collect (gensym g))))))
    21.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2+++ b/lisp/std/tests.lisp	Mon Oct 16 19:33:42 2023 -0400
    21.3@@ -0,0 +1,131 @@
    21.4+;;; tests.lisp --- macs system tests
    21.5+
    21.6+;;; Commentary:
    21.7+
    21.8+;; TODO: fix false positives when using (eval-test)
    21.9+
   21.10+;;; Code:
   21.11+(defpackage :std/tests
   21.12+  (:use
   21.13+   :cl
   21.14+   :readtables
   21.15+   :str
   21.16+   :fmt
   21.17+   :sym
   21.18+   :list
   21.19+   :cond
   21.20+   :log
   21.21+   :fu
   21.22+   :ana
   21.23+   :pan
   21.24+   :fs
   21.25+   :alien
   21.26+   :thread
   21.27+   :rt))
   21.28+
   21.29+(in-package :std/tests)
   21.30+
   21.31+(in-readtable :std)
   21.32+
   21.33+;;; READTABLES
   21.34+(defsuite :named-readtables)
   21.35+(in-suite :named-readtables)
   21.36+(deftest readtables ()
   21.37+  "Test :std readtable without cl-ppcre"
   21.38+  (is (typep #`(,a1 ,a1 ',a1 ,@a1) 'function))
   21.39+  (is (string= #"test "foo" "# "test \"foo\" "))
   21.40+  (is (string= #$test "1 2 3"$# "test \"1 2 3\"")))
   21.41+
   21.42+#+cl-ppcre
   21.43+(deftest ppcre-readtables (:persist t)
   21.44+  "Test *macs-readtable* with cl-ppcre"
   21.45+  (is (= 1 1)))
   21.46+
   21.47+;;; STD
   21.48+(defsuite :std)
   21.49+(in-suite :std)
   21.50+
   21.51+(deftest sym ()
   21.52+  "Test STD.SYM"
   21.53+  ;; gensyms
   21.54+  (is (not (equalp (make-gensym 'a) (make-gensym 'a))))
   21.55+  (is (eq (ensure-symbol 'tests :macs.tests) 'tests))
   21.56+  (is (eq 'macs.tests::foo (format-symbol :macs.tests "~A" 'foo)))
   21.57+  (is (eq (make-keyword 'fizz) :fizz)))
   21.58+
   21.59+;;;; TODO
   21.60+(deftest str ()
   21.61+  "Test STD.STR"
   21.62+  (is (typep "test" 'string-designator))
   21.63+  (is (typep 'test 'string-designator))
   21.64+  (is (typep #\C 'string-designator))
   21.65+  (is (not (typep 0 'string-designator))))
   21.66+
   21.67+(deftest list ()
   21.68+  "Test STD.LIST"
   21.69+  ;; same object - a literal
   21.70+  (is (eq (ensure-car '(0)) (ensure-car 0)))
   21.71+  (is (eq (ensure-car '(nil)) (ensure-car nil)))
   21.72+  ;; different objects
   21.73+  (is (not (eq (ensure-cons 0) (ensure-cons 0))))
   21.74+  (is (equal (ensure-cons 0) (ensure-cons 0))))
   21.75+
   21.76+(deftest log ()
   21.77+  "Test STD.LONG"
   21.78+  (is (debug! "test" *log-level*)))
   21.79+
   21.80+(deftest cond ()
   21.81+  "Test STD.COND")
   21.82+
   21.83+(deftest thread ()
   21.84+  "Test STD.THREAD"
   21.85+  (is (stringp (print-thread-info nil))))
   21.86+
   21.87+(deftest alien ()
   21.88+  "Test STD.ALIEN"
   21.89+  (is (= 0 (foreign-int-to-integer 0 4)))
   21.90+  (is (= 1 (bool-to-foreign-int t))))
   21.91+
   21.92+(deftest fmt ()
   21.93+  "Test STD.FMT"
   21.94+  (is (string= (format nil "| 1 | 2 | 3 |~%") (fmt-row '(1 2 3))))
   21.95+  (is (string= (fmt-sxhash (sxhash t)) (fmt-sxhash (sxhash t))))
   21.96+  (is (string= 
   21.97+       ;; note the read-time-eval..
   21.98+       #.(fmt-tree nil '(foobar (:a) (:b) (c) (d)) :layout :down)
   21.99+       #"FOOBAR
  21.100+ ├─ :A
  21.101+ ├─ :B
  21.102+ ├─  C
  21.103+ ╰─  D
  21.104+"#))
  21.105+;; with plist option
  21.106+  (is (string= 
  21.107+       #.(fmt:fmt-tree nil '(sk-project :name "foobar" :path "/a/b/c.asd" :vc :hg) :layout :down :plist t)
  21.108+       #"SK-PROJECT
  21.109+ ├─ :NAME
  21.110+ │   ╰─ "foobar"
  21.111+ ├─ :PATH
  21.112+ │   ╰─ "/a/b/c.asd"
  21.113+ ╰─ :VC
  21.114+     ╰─ :HG
  21.115+"#)))
  21.116+
  21.117+(deftest ana ()
  21.118+  "Test STD.ANA"
  21.119+  (is (= 8 
  21.120+	 (aif (+ 2 2)
  21.121+	      (+ it it)))))
  21.122+
  21.123+(deftest pan ()
  21.124+  "Test STD.PAN"
  21.125+  (let ((p
  21.126+	  (plambda (a) (b c)
  21.127+		   (if (not a)
  21.128+		       (setq b 0
  21.129+			     c 0)
  21.130+		       (progn (incf b a) (incf c a))))))
  21.131+    (with-pandoric (b c) p
  21.132+      (is (= 0 (funcall p nil)))
  21.133+      (is (= 1 (funcall p 1)))
  21.134+      (is (= 1 b c)))))
    22.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2+++ b/lisp/std/thread.lisp	Mon Oct 16 19:33:42 2023 -0400
    22.3@@ -0,0 +1,56 @@
    22.4+;;; threads.lisp --- Multi-thread utilities
    22.5+
    22.6+;; Threading Macros
    22.7+
    22.8+;;; Commentary:
    22.9+
   22.10+;; mostly yoinked from sb-thread and friends
   22.11+
   22.12+;;; Code:
   22.13+(defpackage :std/thread
   22.14+  (:use :cl :macs.alien :sb-thread)
   22.15+  (:nicknames :thread)
   22.16+  (:export
   22.17+   :print-thread-info :print-thread-message-top-level :thread-support-p))
   22.18+
   22.19+(in-package :thread)
   22.20+
   22.21+;; (reexport-from :sb-thread
   22.22+;; 	       :include '(:main-thread
   22.23+;; 			  :*current-thread*
   22.24+;; 			  :list-all-threads
   22.25+;; 			  :thread
   22.26+;; 			  :thread-alive-p
   22.27+;; 			  :thread-name
   22.28+;; 			  :thread-error
   22.29+;; 			  :thread-yield
   22.30+;; 			  :make-thread
   22.31+;; 			  :join-thread
   22.32+;; 			  :destroy-thread
   22.33+;; 			  :interrupt-thread
   22.34+;; 			  :semaphore
   22.35+;; 			  :get-semaphore
   22.36+;; 			  :make-semaphore
   22.37+;; 			  :mutex
   22.38+;; 			  :get-mutex
   22.39+;; 			  :make-mutex
   22.40+;; 			  :spinlock
   22.41+;; 			  :get-spinlock
   22.42+;; 			  :make-spinlock))
   22.43+
   22.44+(defun thread-support-p () (member :thread-support *features*))
   22.45+
   22.46+(defun print-thread-info (&optional (stream *standard-output*))
   22.47+  (let* ((curr-thread sb-thread:*current-thread*)
   22.48+         (curr-thread-name (sb-thread:thread-name curr-thread))
   22.49+         (all-threads (sb-thread:list-all-threads)))
   22.50+	(format stream "Current thread: ~a~%~%" curr-thread)
   22.51+	(format stream "Current thread name: ~a~%~%" curr-thread-name)
   22.52+	(format stream "All threads:~% ~{~a~%~}~%" all-threads)))
   22.53+
   22.54+(eval-when (:compile-toplevel)
   22.55+  (defun print-thread-message-top-level (msg)
   22.56+    (sb-thread:make-thread
   22.57+     (lambda ()
   22.58+       (format #.*standard-output* msg)))
   22.59+    nil))