|
@@ -0,0 +1,462 @@
|
|
1
|
+# from typing import Callable, Iterable
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+# class Error(Exception):
|
|
5
|
+# """Base class for errors."""
|
|
6
|
+
|
|
7
|
+# pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+# class DefinitionError(Error):
|
|
11
|
+# """Error during program definition."""
|
|
12
|
+
|
|
13
|
+# pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+# class ExecutionError(Error):
|
|
17
|
+# """Error during program execution."""
|
|
18
|
+
|
|
19
|
+# pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+# class Program(object):
|
|
23
|
+# """Program as a sequence of steps."""
|
|
24
|
+
|
|
25
|
+# def __init__(self, *steps):
|
|
26
|
+# """Initialize the program steps."""
|
|
27
|
+# self.steps = steps # trigger setter
|
|
28
|
+
|
|
29
|
+# # PROPERTY
|
|
30
|
+
|
|
31
|
+# @property
|
|
32
|
+# def steps(self):
|
|
33
|
+# """Return the program steps."""
|
|
34
|
+# return self._steps
|
|
35
|
+
|
|
36
|
+# @steps.setter
|
|
37
|
+# def steps(self, steps):
|
|
38
|
+# """Append steps to the program."""
|
|
39
|
+# self._steps = []
|
|
40
|
+
|
|
41
|
+# for s in steps:
|
|
42
|
+# # fill blanks
|
|
43
|
+# if len(s) == 1:
|
|
44
|
+# step = (s[0], list(), dict())
|
|
45
|
+# elif len(s) == 2:
|
|
46
|
+# step = (s[0], s[1], dict())
|
|
47
|
+# elif len(s) == 3:
|
|
48
|
+# step = (s[0], s[1], s[2])
|
|
49
|
+# else:
|
|
50
|
+# raise DefinitionError(
|
|
51
|
+# f"The number of step arguments should be between 1 and 3. Got: {len(steps)}"
|
|
52
|
+# )
|
|
53
|
+
|
|
54
|
+# # validate steps
|
|
55
|
+# if not isinstance(step[0], Callable):
|
|
56
|
+# raise DefinitionError(
|
|
57
|
+# f"The first step argument should be Callable. Got: {type(step[0]).__name__}"
|
|
58
|
+# )
|
|
59
|
+# elif not isinstance(step[1], Iterable):
|
|
60
|
+# raise DefinitionError(
|
|
61
|
+# f"The second step argument should be Iterable. Got: {type(step[1]).__name__}"
|
|
62
|
+# )
|
|
63
|
+# elif not isinstance(step[2], Iterable):
|
|
64
|
+# raise DefinitionError(
|
|
65
|
+# f"The third step argument should be Iterable. Got: {type(step[2]).__name__}"
|
|
66
|
+# )
|
|
67
|
+
|
|
68
|
+# self._steps.append(step)
|
|
69
|
+
|
|
70
|
+# # FORMAT
|
|
71
|
+
|
|
72
|
+# def __str__(self):
|
|
73
|
+# """Return a string from the program steps."""
|
|
74
|
+
|
|
75
|
+# def fmt(step):
|
|
76
|
+# f, args, kwargs = step
|
|
77
|
+
|
|
78
|
+# strf = f.__name__
|
|
79
|
+# strargs = [str(x) for x in args]
|
|
80
|
+# strkwargs = [f"{k}={v}" for k, v in kwargs.items()]
|
|
81
|
+
|
|
82
|
+# return f"{strf}({', '.join(strargs + strkwargs) })"
|
|
83
|
+
|
|
84
|
+# return "\n".join(map(fmt, self.steps))
|
|
85
|
+
|
|
86
|
+# def __repr__(self):
|
|
87
|
+# """Return a representation of the program steps."""
|
|
88
|
+# return str(self.steps)
|
|
89
|
+
|
|
90
|
+# # ALGEBRA
|
|
91
|
+
|
|
92
|
+# def __add__(self, other):
|
|
93
|
+# """Combine program steps."""
|
|
94
|
+# return self.__class__(self.steps + other.steps)
|
|
95
|
+
|
|
96
|
+# def __sub__(self, other):
|
|
97
|
+# """Filter program steps."""
|
|
98
|
+# steps = []
|
|
99
|
+
|
|
100
|
+# for s in self.steps:
|
|
101
|
+# if s not in other:
|
|
102
|
+# steps.append(s)
|
|
103
|
+
|
|
104
|
+# return self.__class__(steps)
|
|
105
|
+
|
|
106
|
+# def __mul__(self, n):
|
|
107
|
+# """Duplicate program steps."""
|
|
108
|
+# return self.__class__(self._steps * 2)
|
|
109
|
+
|
|
110
|
+# def __matmul__(self, g):
|
|
111
|
+# """Compose program functions."""
|
|
112
|
+# return self.__class__((g(f), args, kwargs) for f, args, kwargs in self.steps)
|
|
113
|
+
|
|
114
|
+# def __truediv__(self, n):
|
|
115
|
+# """Chunk steps in smaller programs."""
|
|
116
|
+# chunk = []
|
|
117
|
+
|
|
118
|
+# for i, s in enumerate(self.steps, 1):
|
|
119
|
+# chunk.append(s)
|
|
120
|
+
|
|
121
|
+# if i % n == 0:
|
|
122
|
+# yield self.__class__(*chunk)
|
|
123
|
+# chunk.clear()
|
|
124
|
+
|
|
125
|
+# def __floordiv__(self, n):
|
|
126
|
+# """Chunk all steps in smaller programs."""
|
|
127
|
+# chunk = []
|
|
128
|
+
|
|
129
|
+# for i, x in enumerate(self.steps, 1):
|
|
130
|
+# chunk.append(x)
|
|
131
|
+
|
|
132
|
+# if i % n == 0:
|
|
133
|
+# yield self.__class__(*chunk)
|
|
134
|
+# chunk.clear()
|
|
135
|
+
|
|
136
|
+# if chunk:
|
|
137
|
+# yield self.__class__(*chunk)
|
|
138
|
+
|
|
139
|
+# def __mod__(self, other):
|
|
140
|
+# """Alternate program steps."""
|
|
141
|
+# gen = it.chain.from_iterable(it.zip_longest(self.steps, other.steps))
|
|
142
|
+
|
|
143
|
+# steps = [s for s in gen if s is not None]
|
|
144
|
+
|
|
145
|
+# return self.__class__(steps)
|
|
146
|
+
|
|
147
|
+# def __pow__(self, other):
|
|
148
|
+# pass
|
|
149
|
+
|
|
150
|
+# def __lshift__(self, n):
|
|
151
|
+# """Shift program steps to the left."""
|
|
152
|
+# return self.__class__(self.steps[n:] + self.steps[:n])
|
|
153
|
+
|
|
154
|
+# def __rshift__(self, n):
|
|
155
|
+# """Shift program steps to the right."""
|
|
156
|
+# return self.__class__(self.steps[-n:] + self.steps[:-n])
|
|
157
|
+
|
|
158
|
+# def __and__(self, other):
|
|
159
|
+# """Intersect program steps."""
|
|
160
|
+# steps = []
|
|
161
|
+
|
|
162
|
+# for s in self.steps:
|
|
163
|
+# if s in other:
|
|
164
|
+# steps.append(s)
|
|
165
|
+
|
|
166
|
+# return self.__class__(steps)
|
|
167
|
+
|
|
168
|
+# def __xor__(self, other):
|
|
169
|
+# """Symmetric program steps."""
|
|
170
|
+# return (self + other) - (self & other)
|
|
171
|
+
|
|
172
|
+# def __or__(self, other):
|
|
173
|
+# pass
|
|
174
|
+
|
|
175
|
+# # INPLACE
|
|
176
|
+
|
|
177
|
+# def __iadd__(self, other):
|
|
178
|
+# """Combine program steps."""
|
|
179
|
+# self._steps += other.steps
|
|
180
|
+
|
|
181
|
+# def __isub__(self, other):
|
|
182
|
+# """Filter program steps."""
|
|
183
|
+# steps = []
|
|
184
|
+
|
|
185
|
+# for s in self.steps:
|
|
186
|
+# if s not in other:
|
|
187
|
+# steps.append(s)
|
|
188
|
+
|
|
189
|
+# self._steps = steps
|
|
190
|
+
|
|
191
|
+# def __imul__(self, n):
|
|
192
|
+# """Duplicate program steps"""
|
|
193
|
+# self._steps = self.steps * n
|
|
194
|
+
|
|
195
|
+# def __imatmul__(self, g):
|
|
196
|
+# """Combine program functions."""
|
|
197
|
+# self._steps = [(g(f), args, kwargs) for f, args, kwargs in self.steps]
|
|
198
|
+
|
|
199
|
+# def __imod__(self, other):
|
|
200
|
+# """Alternate program steps."""
|
|
201
|
+# gen = it.chain.from_iterable(it.zip_longest(self.steps, other.steps))
|
|
202
|
+
|
|
203
|
+# self._steps = [s for s in gen if s is not None]
|
|
204
|
+
|
|
205
|
+# def __ipow__(self, other):
|
|
206
|
+# pass
|
|
207
|
+
|
|
208
|
+# def __ilshift__(self, n):
|
|
209
|
+# """Shift program steps to the left."""
|
|
210
|
+# self._steps = self.steps[n:] + self.steps[:n]
|
|
211
|
+
|
|
212
|
+# def __irshift__(self, n):
|
|
213
|
+# """Shift program steps to the right."""
|
|
214
|
+# self.steps = self.steps[-n:] + self.steps[:-n]
|
|
215
|
+
|
|
216
|
+# def __iand__(self, other):
|
|
217
|
+# """Intersect program steps."""
|
|
218
|
+# steps = []
|
|
219
|
+
|
|
220
|
+# for s in self.steps:
|
|
221
|
+# if s in other:
|
|
222
|
+# steps.append(s)
|
|
223
|
+
|
|
224
|
+# self._steps = steps
|
|
225
|
+
|
|
226
|
+# def __ixor__(self, other):
|
|
227
|
+# """Symmetric program steps."""
|
|
228
|
+# self._steps = (self ^ other).steps
|
|
229
|
+
|
|
230
|
+# def __ior__(self, other):
|
|
231
|
+# pass
|
|
232
|
+
|
|
233
|
+# # CONTEXT
|
|
234
|
+
|
|
235
|
+# def __enter__(self):
|
|
236
|
+# """Return the program steps."""
|
|
237
|
+# return self.steps
|
|
238
|
+
|
|
239
|
+# def __exit__(self, exc_type, exc_value, traceback):
|
|
240
|
+# """Update the program steps."""
|
|
241
|
+# self.steps = self._steps # trigger setter
|
|
242
|
+
|
|
243
|
+# # CALLABLE
|
|
244
|
+# def __call__(self, state, control=None):
|
|
245
|
+# """Execute program steps."""
|
|
246
|
+# try:
|
|
247
|
+# for step in self.steps:
|
|
248
|
+# if control is not None:
|
|
249
|
+# step, state = control(step, state)
|
|
250
|
+
|
|
251
|
+# f, args, kwargs = step
|
|
252
|
+# state = f(*args, **kwargs)
|
|
253
|
+
|
|
254
|
+# return state
|
|
255
|
+# except Exception as err:
|
|
256
|
+# raise ExecutionError() from err
|
|
257
|
+
|
|
258
|
+# # CONVERTER
|
|
259
|
+
|
|
260
|
+# def __bool__(self):
|
|
261
|
+# """Return True if program is not empty."""
|
|
262
|
+# return len(self.steps) > 0
|
|
263
|
+
|
|
264
|
+# # COLLECTION
|
|
265
|
+
|
|
266
|
+# def __len__(self):
|
|
267
|
+# """Return the number of steps."""
|
|
268
|
+# return len(self.steps)
|
|
269
|
+
|
|
270
|
+# def __iter__(self):
|
|
271
|
+# """Iterate over the program steps."""
|
|
272
|
+# return iter(self.steps)
|
|
273
|
+
|
|
274
|
+# def __reversed__(self):
|
|
275
|
+# """Reverse the steps of the program."""
|
|
276
|
+# return self.__init__(*reversed(self.steps))
|
|
277
|
+
|
|
278
|
+# def __getitem__(self, n):
|
|
279
|
+# """Return the n step of the program."""
|
|
280
|
+# return self.steps[n]
|
|
281
|
+
|
|
282
|
+# def __delitem__(self, n):
|
|
283
|
+# """Delete the n step of the program."""
|
|
284
|
+# del self.steps[n]
|
|
285
|
+
|
|
286
|
+# def __setitem__(self, n, step):
|
|
287
|
+# """Change the n step of the program."""
|
|
288
|
+# self.steps[n] = step
|
|
289
|
+
|
|
290
|
+# def __contains__(self, step):
|
|
291
|
+# """Return True if step exists in the program."""
|
|
292
|
+# return step in self.steps
|
|
293
|
+
|
|
294
|
+# # COMPARABLE
|
|
295
|
+
|
|
296
|
+# def __lt__(self, other):
|
|
297
|
+# """Compare the program lengths with <."""
|
|
298
|
+# return len(self) < len(other)
|
|
299
|
+
|
|
300
|
+# def __le__(self, other):
|
|
301
|
+# """Compare the program lengths with <=."""
|
|
302
|
+# return len(self) <= len(other)
|
|
303
|
+
|
|
304
|
+# def __eq__(self, other):
|
|
305
|
+# """Compare the program lengths with ==."""
|
|
306
|
+# return len(self) == len(other)
|
|
307
|
+
|
|
308
|
+# def __ne__(self, other):
|
|
309
|
+# """Compare the program lengths with !=."""
|
|
310
|
+# return len(self) != len(other)
|
|
311
|
+
|
|
312
|
+# def __ge__(self, other):
|
|
313
|
+# """Compare the program lengths with >=."""
|
|
314
|
+# return len(self) >= len(other)
|
|
315
|
+
|
|
316
|
+# def __gt__(self, other):
|
|
317
|
+# """Compare the program lengths with >."""
|
|
318
|
+# return len(self) > len(other)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+# # promise to store state
|
|
322
|
+# # append f as args
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+# # def optional(default):
|
|
326
|
+# # def decorator(f):
|
|
327
|
+# # @wraps(f)
|
|
328
|
+# # def decorated(*args, **kwargs):
|
|
329
|
+# # state = f(*args, **kwargs)
|
|
330
|
+
|
|
331
|
+# # if state is None:
|
|
332
|
+# # return default
|
|
333
|
+
|
|
334
|
+# # return decorated
|
|
335
|
+
|
|
336
|
+# # return decorator
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+# # def Except(on=Exception):
|
|
340
|
+# # def except_(f, args, kwargs, state):
|
|
341
|
+# # if issubclass(state, on):
|
|
342
|
+# # return state
|
|
343
|
+
|
|
344
|
+# # try:
|
|
345
|
+# # state = f(*args, **kwargs)
|
|
346
|
+# # except on as ex:
|
|
347
|
+# # return ex
|
|
348
|
+
|
|
349
|
+# # return except_
|
|
350
|
+
|
|
351
|
+# # class Pipe(Program):
|
|
352
|
+# # FIRST = 0
|
|
353
|
+# # LAST = -1
|
|
354
|
+
|
|
355
|
+# # def __init__(self, *steps, n=FIRST):
|
|
356
|
+# # super().__init__(*steps, {"n": n})
|
|
357
|
+
|
|
358
|
+# # def __call__(self, state=None):
|
|
359
|
+# # """Call the program and inject state"""
|
|
360
|
+# # for f, args, kwargs in self.steps:
|
|
361
|
+# # args_ = args.copy()
|
|
362
|
+
|
|
363
|
+# # if self.n < 0:
|
|
364
|
+# # args_.append(state)
|
|
365
|
+# # else:
|
|
366
|
+# # args_.insert(self.n, state)
|
|
367
|
+
|
|
368
|
+# # return f(*args_, **kwargs)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+# # class Block(Program):
|
|
372
|
+# # def __call__(self, state=None):
|
|
373
|
+# # """Call the program and ignore state"""
|
|
374
|
+# # for f, args, kwargs in self.steps:
|
|
375
|
+# # return f(*args, **kwargs)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+# # def Context():
|
|
379
|
+# # pass
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+# # def Capture():
|
|
383
|
+# # # capture stdout/stderr
|
|
384
|
+# # pass
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+# # def Memoize():
|
|
388
|
+# # pass
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+# # def Log():
|
|
392
|
+# # pass
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+# # def Pre(do):
|
|
396
|
+# # def pre(f, args, kwargs, state):
|
|
397
|
+# # pre(f, args, kwargs, state)
|
|
398
|
+
|
|
399
|
+# # return f(args, kwargs, state)
|
|
400
|
+
|
|
401
|
+# # return pre
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+# # def Post(do):
|
|
405
|
+# # def post(f, args, kwargs, state):
|
|
406
|
+# # state = f(args, kwargs, state)
|
|
407
|
+# # do(f, args, kwargs, state)
|
|
408
|
+
|
|
409
|
+# # return state
|
|
410
|
+
|
|
411
|
+# # return post
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+# # # Cache: cache f/args
|
|
415
|
+
|
|
416
|
+# # # Cat: (list monad)
|
|
417
|
+
|
|
418
|
+# # # Lazy: convert to gen
|
|
419
|
+
|
|
420
|
+# # # Watch: pre/post fn
|
|
421
|
+
|
|
422
|
+# # # Strict: convert to list
|
|
423
|
+
|
|
424
|
+# # # Ident: imperative style
|
|
425
|
+
|
|
426
|
+# # # Do: independent actions
|
|
427
|
+
|
|
428
|
+# # # On: perform on object
|
|
429
|
+
|
|
430
|
+# # # Pipe: (->, ->>, as->)
|
|
431
|
+
|
|
432
|
+# # # Resource: use context lib
|
|
433
|
+
|
|
434
|
+# # # Context: pass dict
|
|
435
|
+
|
|
436
|
+# # # Delay / Future
|
|
437
|
+
|
|
438
|
+# # # Parallel
|
|
439
|
+
|
|
440
|
+# # # Partial / Compose
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+# # def run(prog, state=None):
|
|
444
|
+# # for f, args, kwargs in prog.steps:
|
|
445
|
+# # state = f(*args, **kwargs)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+# # def opt(prog, state=None, default=0):
|
|
449
|
+# # for f, args, kwargs in prog.steps:
|
|
450
|
+# # state = f(*args, **kwargs)
|
|
451
|
+
|
|
452
|
+# # if state is None:
|
|
453
|
+# # state = default
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+# # prog = Program((print, [0]))
|
|
457
|
+
|
|
458
|
+# # with prog as steps:
|
|
459
|
+# # steps.append((print, [1, 2]))
|
|
460
|
+# # steps.append((print, [3, 4]))
|
|
461
|
+
|
|
462
|
+# # run(prog)
|