-
Notifications
You must be signed in to change notification settings - Fork 23
/
symex-data.el
357 lines (276 loc) · 10.7 KB
/
symex-data.el
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
;;; symex-data.el --- An evil way to edit Lisp symbolic expressions as trees -*- lexical-binding: t -*-
;; URL: https://github.com/countvajhula/symex.el
;; This program is "part of the world," in the sense described at
;; https://drym.org. From your perspective, this is no different than
;; MIT or BSD or other such "liberal" licenses that you may be
;; familiar with, that is to say, you are free to do whatever you like
;; with this program. It is much more than BSD or MIT, however, in
;; that it isn't a license at all but an idea about the world and how
;; economic systems could be set up so that everyone wins. Learn more
;; at drym.org.
;;
;; This work transcends traditional legal and economic systems, but
;; for the purposes of any such systems within which you may need to
;; operate:
;;
;; This is free and unencumbered software released into the public domain.
;; The authors relinquish any copyright claims on this work.
;;
;;; Commentary:
;; Data abstractions for symex mode. Defines the linguistic primitives
;; of the symex DSL: moves, maneuvers, ventures, precautions, protocols,
;; decisions, circuits, and detours.
;;; Code:
(require 'cl-lib)
;;;;;;;;;;;;;;;;;;;;;;;;;
;;; DATA ABSTRACTIONS ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;
(defun symex-make-move (x y)
"Construct a tree \"move\".
A move represents the number of steps to be taken along the X or
forward-backward axis, and the Y or in-out axis."
(list 'move x y))
(defun symex--move-x (move)
"X (horizontal) component of MOVE."
(nth 1 move))
(defun symex--move-y (move)
"Y (vertical) component of MOVE."
(nth 2 move))
(defun symex-move-p (obj)
"Check if OBJ specifies a move."
(condition-case nil
(equal 'move
(nth 0 obj))
(error nil)))
(defconst symex--move-zero (symex-make-move 0 0))
(defconst symex--move-forward (symex-make-move 1 0))
(defconst symex--move-backward (symex-make-move -1 0))
(defconst symex--move-down (symex-make-move 0 -1))
(defconst symex--move-up (symex-make-move 0 1))
(defun symex--are-moves-equal-p (m1 m2)
"Check if two moves M1 and M2 are identical."
(equal m1 m2))
(defun symex--add-moves (moves)
"Add MOVES together as vectors.
This sum indicates height and distance along the branches of the tree."
(if moves
(let ((current (car moves))
(remaining (cdr moves)))
(let ((result (symex--add-moves remaining)))
(symex-make-move (+ (symex--move-x current)
(symex--move-x result))
(+ (symex--move-y current)
(symex--move-y result)))))
symex--move-zero))
(defun symex--move-length (move)
"Compute the length of the MOVE.
This is most naturally meaningful when the move is entirely along one axis,
but a result will be returned even if the move is across multiple axes,
as standard linear vector magnitude computation is used."
(let ((x (symex--move-x move))
(y (symex--move-y move)))
(if (not (= x 0))
x
y)))
(cl-defun symex-make-precaution (traversal &key pre-condition post-condition)
"A specification to check conditions before/after execution of a TRAVERSAL.
PRE-CONDITION is a boolean function executed before the traversal. The
traversal is not executed unless this returns true.
POST-CONDITION is a boolean function executed after the traversal. The
executed traversal is reversed if this returns false."
(let ((pre-condition (or pre-condition (lambda () t)))
(post-condition (or post-condition (lambda () t))))
(list 'precaution
traversal
pre-condition
post-condition)))
(defun symex--precaution-traversal (precaution)
"The traversal component of the PRECAUTION.
This is the traversal that is to be executed \"with precautions\"."
(nth 1 precaution))
(defun symex--precaution-pre-condition (precaution)
"Pre-condition of PRECAUTION."
(nth 2 precaution))
(defun symex--precaution-post-condition (precaution)
"Post-condition of PRECAUTION."
(nth 3 precaution))
(defun symex-precaution-p (obj)
"Check if OBJ specifies a precaution."
(condition-case nil
(equal 'precaution
(nth 0 obj))
(error nil)))
(defun symex-make-circuit (traversal &optional times)
"A specification to repeat a TRAVERSAL TIMES times.
If TIMES is nil, repeat indefinitely until the traversal fails."
(list 'circuit
traversal
times))
(defun symex-circuit-p (obj)
"Check if OBJ specifies a circuit."
(condition-case nil
(equal 'circuit
(nth 0 obj))
(error nil)))
(defun symex--circuit-traversal (circuit)
"Get the traversal component of the CIRCUIT.
This is the traversal that is intended to be looped."
(nth 1 circuit))
(defun symex--circuit-times (circuit)
"Get the times component of the CIRCUIT.
This is the number of times the traversal should be repeated."
(nth 2 circuit))
(defun symex--circuit-null-p (circuit)
"Check if CIRCUIT is empty or null."
(let ((times (symex--circuit-times circuit)))
(and times (zerop times))))
(defun symex--circuit-rest (circuit)
"A circuit defined from the remaining repetitions of the traversal.
This includes the remaining repetitions in CIRCUIT, not counting the
first. This is useful for structural recursion during circuit
execution."
(let ((traversal (symex--circuit-traversal circuit))
(times (symex--circuit-times circuit)))
(symex-make-circuit traversal (when times (1- times)))))
(defun symex-make-maneuver (&rest phases)
"Construct a maneuver from the given PHASES."
(list 'maneuver
phases))
(defun symex-maneuver-p (obj)
"Check if OBJ specifies a maneuver."
(condition-case nil
(equal 'maneuver
(nth 0 obj))
(error nil)))
(defun symex--maneuver-phases (maneuver)
"Get the phases of a MANEUVER.
Each phase could be any traversal."
(nth 1 maneuver))
(defun symex--maneuver-null-p (maneuver)
"Check if MANEUVER is empty or null."
(null (symex--maneuver-phases maneuver)))
(defun symex--maneuver-first (maneuver)
"Get the first phase of a MANEUVER.
This is useful for structural recursion during maneuver execution."
(car (symex--maneuver-phases maneuver)))
(defun symex--maneuver-rest (maneuver)
"A maneuver defined from the remaining phases in MANEUVER not counting the first.
This is useful for structural recursion during maneuver execution."
(apply #'symex-make-maneuver
(cdr (symex--maneuver-phases maneuver))))
(defun symex-make-venture (&rest phases)
"Construct a venture from the given PHASES."
(list 'venture
phases))
(defun symex-venture-p (obj)
"Check if OBJ specifies a venture."
(condition-case nil
(equal 'venture
(nth 0 obj))
(error nil)))
(defun symex--venture-phases (venture)
"Get the phases of a VENTURE.
Each phase could be any traversal."
(nth 1 venture))
(defun symex--venture-null-p (venture)
"Check if VENTURE is empty or null."
(null (symex--venture-phases venture)))
(defun symex--venture-first (venture)
"Get the first phase of a VENTURE.
This is useful for structural recursion during venture execution."
(car (symex--venture-phases venture)))
(defun symex--venture-rest (venture)
"A venture defined from the remaining phases in VENTURE not counting the first.
This is useful for structural recursion during venture execution."
(apply #'symex-make-venture
(cdr (symex--venture-phases venture))))
(defun symex-make-detour (reorientation traversal)
"Construct a detour.
A detour consists of two components -- a TRAVERSAL that we wish to execute, and
a REORIENTATION which is a transformation we want to apply prior to attempting
the traversal. Both the reorientation as well as the traversal could be any
type of traversal, for instance a detour or a maneuver.
The reorientation is applied repeatedly and the traversal is re-attempted each
time, until it succeeds. If the reorientation itself fails, then the detour
fails as well."
(list 'detour
reorientation
traversal))
(defun symex--detour-reorientation (detour)
"Get the reorientation component of the DETOUR."
(nth 1 detour))
(defun symex--detour-traversal (detour)
"Get the traversal component of the DETOUR."
(nth 2 detour))
(defun symex-detour-p (obj)
"Check if OBJ specifies a detour."
(condition-case nil
(equal 'detour
(nth 0 obj))
(error nil)))
(defun symex-make-protocol (&rest options)
"Construct a protocol abstraction for the given OPTIONS.
Each option could be any traversal."
(list 'protocol
options))
(defun symex-protocol-p (obj)
"Check if OBJ specifies a protocol."
(condition-case nil
(equal 'protocol
(nth 0 obj))
(error nil)))
(defun symex--protocol-options (protocol)
"Get the set of options that are part of the PROTOCOL."
(nth 1 protocol))
(defun symex--protocol-null-p (protocol)
"Check if PROTOCOL is null or empty."
(null (symex--protocol-options protocol)))
(defun symex--protocol-first (protocol)
"Get the first option in the PROTOCOL.
This is useful for structural recursion during protocol execution."
(car (symex--protocol-options protocol)))
(defun symex--protocol-rest (protocol)
"A protocol containing the remaining options in PROTOCOL, not counting the first.
This is useful for structural recursion during protocol execution."
(apply #'symex-make-protocol
(cdr (symex--protocol-options protocol))))
(defun symex-make-decision (condition consequent alternative)
"A specification to choose between two traversals.
If CONDITION is true, then the CONSEQUENT traversal is executed,
otherwise the ALTERNATIVE traversal is executed.
This is analogous to an `if` statement in common languages."
(list 'decision
condition
consequent
alternative))
(defun symex--decision-condition (decision)
"Get the condition component of the DECISION.
This is the condition upon which the decision
to choose one or the other traversal is based."
(nth 1 decision))
(defun symex--decision-consequent (decision)
"Get the consequent component of the DECISION.
This is the traversal that will be chosen if the condition is true."
(nth 2 decision))
(defun symex--decision-alternative (decision)
"Get the alternative component of the DECISION.
This is the traversal that will be chosen if the condition is false."
(nth 3 decision))
(defun symex-decision-p (obj)
"Check if OBJ specifies a decision."
(condition-case nil
(equal 'decision
(nth 0 obj))
(error nil)))
(defun symex-traversal-p (obj)
"Check if OBJ specifies a traversal."
(or (symex-move-p obj)
(symex-maneuver-p obj)
(symex-venture-p obj)
(symex-circuit-p obj)
(symex-detour-p obj)
(symex-precaution-p obj)
(symex-protocol-p obj)
(symex-decision-p obj)))
(provide 'symex-data)
;;; symex-data.el ends here