-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjezebel-tree.el
More file actions
234 lines (195 loc) · 9.13 KB
/
Copy pathjezebel-tree.el
File metadata and controls
234 lines (195 loc) · 9.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
(require 'jezebel-util)
(declare (optimize (speed 3) (safety 0)))
;;
;; Purely functional AST built incrementally by parsing.
;;
(define-functional-struct
(jez-tree-node
(:constructor jez--make-tree-node)
(:copier nil)
(:conc-name jez-tree-node--))
"Node of an N-ary purely-functional zippered tree."
;; List of children of this node; updated lazily. jez-tree-node's
;; zippered list for this node is authoritative when it exists.
children
;; plist of properties of this node
properties)
(define-functional-struct
(jez-tree
(:constructor jez--make-tree)
(:copier nil)
(:conc-name jez-tree--))
"View into a jez-tree. Pure functional data structure."
;; Current jez-tree-node
current
;; Is the current node dirty?
dirty
;; Children of parent to the left of current; stored in reverse
;; order
left
;; Children of parent to the right of current; stored in forward
;; order
right
;; Parent jez-tree (not jez-tree-node!) or nil if we're at top
parent
;; Properties of this tree node.
properties
)
(cl-defun jez-make-empty-tree ()
"Create a brand-new empty tree."
(jez--make-tree
:current (jez--make-tree-node)))
(cl-defun jez-tree-prepend-child (tree &optional properties)
"Add a child to the beginning of TREE's child list. Return a
new cursor pointing at the new child. Constant time."
(copy-and-modify-jez-tree tree
:current (jez--make-tree-node)
:properties properties
:dirty t
:left nil
:right (jez-tree-node--children (jez-tree--current tree))
:parent tree))
(cl-defun jez-tree-append-child (tree &optional properties)
"Add a child to the end of TREE's child list. Return a new
cursor pointing at the new child. Takes time proportional to the
number of children in TREE's current node."
(copy-and-modify-jez-tree tree
:current (jez--make-tree-node)
:properties properties
:dirty t
:left (reverse
(jez-tree-node--children (jez-tree--current tree)))
:right nil
:parent tree))
(cl-defun jez-tree-up (tree)
"Move cursor to parent of current node. Return a new cursor
pointing at the parent. Raise error if already at top of tree.
Constant time if tree has not been modified; otherwise, takes
time proportional to the number of children in the parent."
;; If the current node isn't dirty, all we have to do is return the
;; cursor we saved when we went down into the current node.
;; Otherwise, life becomes trickier. We return a new cursor that
;; points at a new tree node. This new node is just like our parent,
;; except that its child list is reconstructed from TREE's zippered
;; child list, which takes into account any modifications we made.
(let ((old-parent (jez-tree--parent tree)))
(unless old-parent
(error "already at top of tree"))
(if (jez-tree--dirty tree)
(copy-and-modify-jez-tree old-parent
;; Make new child to stand in for (jez-tree--current
;; old-parent). The new child incorporates any changes we've
;; made since we branched from parent.
:current (jez--make-tree-node
:children (append (reverse (jez-tree--left tree))
(list (jez-tree--current tree))
(jez-tree--right tree))
:properties (jez-tree-node--properties
(jez-tree--current old-parent)))
;; The new cursor is dirty because we need to propagate changes
;; all the way up to the top of the tree.
:dirty t)
;; Not dirty. Return original parent cursor unchanged.
old-parent)))
(cl-defun jez-tree-first-p (tree)
"Return non-nil if current node of TREE has a previous sibling.
Constant time."
(jez-tree--left tree))
(cl-defun jez-tree-last-p (tree)
"Return non-nil if current node of TREE has a next sibling.
Constant time."
(jez-tree--right tree))
(cl-defun jez-tree-children-p (tree)
"Return non-nil if current node of TREE has children. Constant
time."
(jez-tree-node--children
(jez-tree--current tree)))
(cl-defun jez-tree-root-p (tree)
"Return non-nil if current node of TREE is the root. Constant
time."
(not (jez-tree--parent tree)))
(cl-defun jez-tree-prev-sibling (tree)
"Return a new cursor pointing to the previous sibling of the
current node. Raise error if there is no previous sibling.
Constant time."
(let* ((old-left (jez-tree--left tree)))
(copy-and-modify-jez-tree tree
:current (or (first old-left)
(error "already at leftmost child"))
:left (rest old-left)
:right (list* (jez-tree--current tree)
(jez-tree--right tree)))))
(cl-defun jez-tree-next-sibling (tree)
(let* ((old-right (jez-tree--right tree)))
(copy-and-modify-jez-tree tree
:current (or (first old-right)
(error "already at rightmost child"))
:left (list* (jez-tree--current tree)
(jez-tree--left tree))
:right (rest old-right))))
(cl-defun jez-tree-first-child (tree)
"Return a cursor pointing to the first child of the current
node. Raise error if the current node has no children. Constant
time."
(let ((children (jez-tree-node--children
(jez-tree--current tree))))
(copy-and-modify-jez-tree tree
:current (or (first children)
(error "current node has no children"))
:dirty nil
:left nil
:right (rest children)
:parent tree)))
(cl-defun jez-tree-last-child (tree)
"Return a cursor pointing to the last child of the current
node. Raise error if the current node has no children. Time
proportional to number of children in current node."
(let ((rchildren (reverse (jez-tree-node--children
(jez-tree--current tree)))))
(copy-and-modify-jez-tree tree
:current (or (first rchildren)
(error "current node has no children"))
:dirty nil
:left (rest rchildren)
:right nil
:parent tree)))
(cl-defun jez-tree-insert-sibling-before (tree)
"Insert a sibling before current node. Return a cursor
pointing to the new node. Raise error if current node is the
root node. Constant time."
(copy-and-modify-jez-tree tree
:current (jez--make-tree-node)
:dirty t
:left (jez-tree--left tree)
:right (list* (jez-tree--current tree)
(jez-tree--right tree))
:parent (or (jez-tree--parent tree)
(error "root node cannot have siblings"))))
(cl-defun jez-tree-insert-sibling-after (tree)
"Insert a sibling after current node. Return a cursor pointing
to new node. Raise error if current node is the root node.
Constant time."
(copy-and-modify-jez-tree tree
:current (jez--make-tree-node)
:dirty t
:left (list* (jez-tree--current tree)
(jez-tree--left tree))
:right (jez-tree--right tree)
:parent (or (jez-tree--parent tree)
(error "root node cannot have siblings"))))
(cl-defun jez-tree-get (tree prop)
"Return the value of PROP in the current node of TREE. Time
proportional to number of existing properties."
(plist-get
(jez-tree-node--properties (jez-tree--current tree))
prop))
(cl-defun jez-tree-put (tree prop val)
"Set PROP to VAL in the current node of TREE. Return a new
cursor pointing to the modified node. Time proportional to
number of existing properties."
(copy-and-modify-jez-tree tree
:current (copy-and-modify-jez-tree-node orig
:properties (plist-put (copy-sequence orig)
prop val))
:dirty t))
(provide 'jezebel-tree)