changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/lib/dat/sxp.lisp

changeset 698: 96958d3eb5b0
parent: 5bd0eb9fa1fa
author: Richard Westhaver <ellis@rwest.io>
date: Fri, 04 Oct 2024 22:04:59 -0400
permissions: -rw-r--r--
description: fixes
1 ;;; lib/dat/sxp.lisp --- S-eXPressions
2 
3 ;; sxp is a unified S-Expression data format
4 
5 ;;; Code:
6 (in-package :dat/sxp)
7 
8 (defun formp (form)
9  (or (consp form) (atom form)))
10 
11 (deftype form ()
12  '(satisfies formp))
13 
14 ;;; Conditions
15 (define-condition sxp-error (error) ())
16 
17 (define-condition sxp-syntax-error (sxp-error) ())
18 
19  ;;; Protocol
20 (defgeneric wrap (self form))
21 (defgeneric unwrap (self))
22 (defgeneric unwrap-or (self lambda))
23 (defgeneric sxpp (self form))
24 
25 (defgeneric write-sxp-stream (self stream &key pretty case))
26 (defgeneric read-sxp-stream (self stream))
27 
28 (defgeneric build-ast (self &key &allow-other-keys)
29  (:documentation "build the sxp representation of SELF and store it in the :ast
30 slot. The :ast slot is always ignored."))
31 
32 (defgeneric load-ast (self)
33  (:documentation "load the object SELF from the :ast slot."))
34 
35 (defgeneric load-ast* (self context)
36  (:documentation "load the object SELF from the :ast slot with additional CONTEXT."))
37 
38 ;;; Objects
39 (defclass sxp ()
40  ((ast :initarg :ast :type form :accessor ast))
41  (:documentation "Dynamic class representing a SXP form."))
42 
43 (defmethod wrap ((self sxp) form) (setf (slot-value self 'ast) form))
44 
45 (defmethod unwrap ((self sxp)) (slot-value self 'ast))
46 
47 (defmethod unwrap-or ((self sxp) (else-fn function))
48  (if (slot-unbound 'sxp self 'ast)
49  (slot-value self 'ast)
50  (if (null (slot-value self 'ast))
51  (funcall else-fn))))
52 
53 (defmethod write-sxp-stream ((self sxp) stream &key (pretty *print-pretty*) (case :downcase))
54  (write (ast self)
55  :stream stream
56  :pretty pretty
57  :case case))
58 
59 (defmethod read-sxp-stream ((self sxp) stream)
60  (setf (ast self) (slurp-stream-forms stream :count nil)))
61 
62 ;; (defsetf unwrap ) (defsetf wrap )
63 
64 ;;; Functions
65 (defun read-sxp-file (file)
66  (make-instance 'sxp :ast (read-file-forms file)))
67 
68 (defun write-sxp-file (sxp file &optional &key if-exists)
69  (with-output-file (out file) :if-exists if-exists
70  (write-sxp-stream sxp out)))
71 
72 (defun read-sxp-string (self str) (with-input-from-string (s str) (read-sxp-stream self s)))
73 
74 (defun write-sxp-string (sxp)
75  (let ((ast (ast sxp)))
76  (declare (list ast))
77  (if (> (length ast) 1)
78  (write-to-string ast)
79  (write-to-string (car ast)))))
80 
81 (defun make-sxp (&rest form) (make-instance 'sxp :ast form))
82 
83 (deftype sxp-fmt-designator () '(member :canonical :collapsed :pretty))
84 
85 (declaim (inline unwrap-object)) ;; inline -200
86 (defun unwrap-object (obj &key (slots t) (methods nil)
87  (indirect nil) (tag nil)
88  (unboundp nil) (nullp nil)
89  (exclude nil))
90  "Build and return a new `form' from OBJ by traversing the class
91 definition. This differs from the generic function `unwrap' which
92 always uses the ast slot as an internal buffer. We can also call this
93 on any class instance (doesn't need to subclass `sxp').
94 
95 SLOTS specifies the slots to be included in the output. If the value
96 is t, all slots are included. The ast slot is not included by default,
97 but this behavior may change in future revisions.
98 
99 When INDIRECT is non-nil, also include methods which indirectly
100 specialize on OBJ.
101 
102 When TAG is non-nil, return a cons where car is TAG and cdr is the
103 output. If TAG is t, use the class-name symbol."
104  (declare (type standard-object obj)
105  (type (or list boolean) slots)
106  (type (or list boolean) methods)
107  (type boolean indirect)
108  (type list exclude))
109  (unless (or slots methods)
110  (error "Required one missing key arg: SLOTS or METHODS"))
111  (let* ((class (class-of obj))
112  (res (when tag (list (if (eq t tag) (class-name class) tag)))))
113  (block unwrap
114  (when-let ((slots (when slots
115  (list-class-slots class slots exclude))))
116  (let ((slot-vals (list-slot-values-using-class class obj (remove-if #'null slots) nullp unboundp)))
117  (if methods
118  (push slot-vals res)
119  (return-from unwrap (push slot-vals res)))))
120  (when-let ((methods (when methods (list-class-methods class methods indirect))))
121  (push methods res)))
122  (flatten res)))
123 
124 ;; TODO 2024-03-22:
125 (defun wrap-object (class form)
126  "Given a CLASS prototype and an input FORM, return a new instance of
127 CLASS. FORM is assumed to be the finalized lisp object which has
128 already passed through `read' -- not a string or file-stream for
129 example."
130  (declare (type class class)
131  (type form form)))
132 
133 (defun file-read-forms (file)
134  (declare (sb-kernel:pathname-designator file))
135  (awhen (the list (read-file-forms file))
136  (if (> (length it) 1)
137  it
138  (car it))))
139 
140 ;; (defmacro define-fmt ())
141 ;; (defmacro define-macro ())