@barnum/barnum 0.2.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifacts/linux-arm64/barnum +0 -0
- package/artifacts/linux-x64/barnum +0 -0
- package/artifacts/macos-arm64/barnum +0 -0
- package/artifacts/macos-x64/barnum +0 -0
- package/artifacts/win-x64/barnum.exe +0 -0
- package/cli.cjs +33 -0
- package/dist/all.d.ts +43 -0
- package/dist/all.d.ts.map +1 -0
- package/dist/all.js +8 -0
- package/dist/ast.d.ts +476 -0
- package/dist/ast.d.ts.map +1 -0
- package/dist/ast.js +419 -0
- package/dist/bind.d.ts +59 -0
- package/dist/bind.d.ts.map +1 -0
- package/dist/bind.js +69 -0
- package/dist/builtins/array.d.ts +36 -0
- package/dist/builtins/array.d.ts.map +1 -0
- package/dist/builtins/array.js +93 -0
- package/dist/builtins/index.d.ts +6 -0
- package/dist/builtins/index.d.ts.map +1 -0
- package/dist/builtins/index.js +5 -0
- package/dist/builtins/scalar.d.ts +12 -0
- package/dist/builtins/scalar.d.ts.map +1 -0
- package/dist/builtins/scalar.js +41 -0
- package/dist/builtins/struct.d.ts +25 -0
- package/dist/builtins/struct.d.ts.map +1 -0
- package/dist/builtins/struct.js +67 -0
- package/dist/builtins/tagged-union.d.ts +54 -0
- package/dist/builtins/tagged-union.d.ts.map +1 -0
- package/dist/builtins/tagged-union.js +81 -0
- package/dist/builtins/with-resource.d.ts +23 -0
- package/dist/builtins/with-resource.d.ts.map +1 -0
- package/dist/builtins/with-resource.js +35 -0
- package/dist/chain.d.ts +3 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +8 -0
- package/dist/effect-id.d.ts +15 -0
- package/dist/effect-id.d.ts.map +1 -0
- package/dist/effect-id.js +16 -0
- package/dist/handler.d.ts +51 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +130 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/iterator.d.ts +32 -0
- package/dist/iterator.d.ts.map +1 -0
- package/dist/iterator.js +123 -0
- package/dist/option.d.ts +74 -0
- package/dist/option.d.ts.map +1 -0
- package/dist/option.js +141 -0
- package/dist/pipe.d.ts +12 -0
- package/dist/pipe.d.ts.map +1 -0
- package/dist/pipe.js +12 -0
- package/dist/race.d.ts +54 -0
- package/dist/race.d.ts.map +1 -0
- package/dist/race.js +116 -0
- package/dist/recursive.d.ts +40 -0
- package/dist/recursive.d.ts.map +1 -0
- package/dist/recursive.js +58 -0
- package/dist/result.d.ts +50 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +117 -0
- package/dist/run.d.ts +14 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +160 -0
- package/dist/runtime.d.ts +6 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +7 -0
- package/dist/schema.d.ts +9 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +95 -0
- package/dist/schemas.d.ts +5 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +13 -0
- package/dist/try-catch.d.ts +24 -0
- package/dist/try-catch.d.ts.map +1 -0
- package/dist/try-catch.js +37 -0
- package/dist/values.d.ts +6 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +12 -0
- package/dist/worker.d.ts +15 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +58 -0
- package/package.json +42 -16
- package/src/all.ts +133 -0
- package/src/ast.ts +1301 -0
- package/src/bind.ts +162 -0
- package/src/builtins/array.ts +121 -0
- package/src/builtins/index.ts +17 -0
- package/src/builtins/scalar.ts +49 -0
- package/src/builtins/struct.ts +111 -0
- package/src/builtins/tagged-union.ts +142 -0
- package/src/builtins/with-resource.ts +69 -0
- package/src/chain.ts +17 -0
- package/src/effect-id.ts +30 -0
- package/src/handler.ts +263 -0
- package/src/index.ts +37 -0
- package/src/iterator.ts +243 -0
- package/src/option.ts +199 -0
- package/src/pipe.ts +138 -0
- package/src/race.ts +173 -0
- package/src/recursive.ts +129 -0
- package/src/result.ts +168 -0
- package/src/run.ts +209 -0
- package/src/runtime.ts +16 -0
- package/src/schema.ts +118 -0
- package/src/schemas.ts +21 -0
- package/src/try-catch.ts +57 -0
- package/src/values.ts +21 -0
- package/src/worker.ts +71 -0
- package/README.md +0 -19
- package/barnum-config-schema.json +0 -408
- package/cli.js +0 -20
- package/index.js +0 -23
package/dist/ast.js
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { chain } from "./chain.js";
|
|
2
|
+
import { constant, drop, extractPrefix, flatten as flattenBuiltin, getField, getIndex, identity, panic, pick, splitFirst, splitLast, tag, wrapInField, asOption as asOptionStandalone, } from "./builtins/index.js";
|
|
3
|
+
import { Option } from "./option.js";
|
|
4
|
+
import { Result } from "./result.js";
|
|
5
|
+
// Lazy import — iterator.ts imports from ast.ts, but these are only called inside
|
|
6
|
+
// methods (after all modules load), so the circular reference is safe at runtime.
|
|
7
|
+
import { Iterator as IteratorNs } from "./iterator.js";
|
|
8
|
+
// Lazy import — bind.ts imports from ast.ts, but these are only called inside
|
|
9
|
+
// methods (after all modules load), so the circular reference is safe at runtime.
|
|
10
|
+
import { bind as bindStandalone, bindInput as bindInputStandalone, } from "./bind.js";
|
|
11
|
+
/**
|
|
12
|
+
* Strip phantom types from a Pipeable, returning a plain Action.
|
|
13
|
+
*
|
|
14
|
+
* Replaces `x as Action` casts throughout the codebase. The constraint
|
|
15
|
+
* ensures the argument is structurally a Pipeable — unlike a bare cast,
|
|
16
|
+
* `toAction(123)` is a type error.
|
|
17
|
+
*/
|
|
18
|
+
export function toAction(pipeable) {
|
|
19
|
+
return pipeable;
|
|
20
|
+
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// typedAction — attach .then() and .forEach() as non-enumerable methods
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Shared implementations (one closure, not per-instance)
|
|
25
|
+
function thenMethod(next) {
|
|
26
|
+
return chain(this, next);
|
|
27
|
+
}
|
|
28
|
+
function branchMethod(cases) {
|
|
29
|
+
return chain(toAction(this), toAction(branch(cases)));
|
|
30
|
+
}
|
|
31
|
+
function flattenMethod() {
|
|
32
|
+
return chain(toAction(this), toAction(flattenBuiltin()));
|
|
33
|
+
}
|
|
34
|
+
function dropMethod() {
|
|
35
|
+
return chain(toAction(this), toAction(drop));
|
|
36
|
+
}
|
|
37
|
+
function tagMethod(kind, enumName) {
|
|
38
|
+
return chain(toAction(this), toAction(tag(kind, enumName)));
|
|
39
|
+
}
|
|
40
|
+
function someMethod() {
|
|
41
|
+
return chain(toAction(this), toAction(Option.some()));
|
|
42
|
+
}
|
|
43
|
+
function okMethod() {
|
|
44
|
+
return chain(toAction(this), toAction(Result.ok()));
|
|
45
|
+
}
|
|
46
|
+
function errMethod() {
|
|
47
|
+
return chain(toAction(this), toAction(Result.err()));
|
|
48
|
+
}
|
|
49
|
+
function getFieldMethod(field) {
|
|
50
|
+
return chain(toAction(this), toAction(getField(field)));
|
|
51
|
+
}
|
|
52
|
+
function getIndexMethod(index) {
|
|
53
|
+
return chain(toAction(this), toAction(getIndex(index)));
|
|
54
|
+
}
|
|
55
|
+
function wrapInFieldMethod(field) {
|
|
56
|
+
return chain(toAction(this), toAction(wrapInField(field)));
|
|
57
|
+
}
|
|
58
|
+
function pickMethod(...keys) {
|
|
59
|
+
return chain(toAction(this), toAction(pick(...keys)));
|
|
60
|
+
}
|
|
61
|
+
function splitFirstMethod() {
|
|
62
|
+
return chain(toAction(this), toAction(branchFamily({
|
|
63
|
+
Iterator: IteratorNs.splitFirst(),
|
|
64
|
+
Array: splitFirst(),
|
|
65
|
+
})));
|
|
66
|
+
}
|
|
67
|
+
function splitLastMethod() {
|
|
68
|
+
return chain(toAction(this), toAction(branchFamily({
|
|
69
|
+
Iterator: IteratorNs.splitLast(),
|
|
70
|
+
Array: splitLast(),
|
|
71
|
+
})));
|
|
72
|
+
}
|
|
73
|
+
// --- Shared postfix methods (Option + Result) — dispatch via branchFamily ---
|
|
74
|
+
function mapMethod(action) {
|
|
75
|
+
return chain(toAction(this), toAction(branchFamily({
|
|
76
|
+
Result: branch({
|
|
77
|
+
Ok: chain(toAction(action), toAction(Result.ok())),
|
|
78
|
+
Err: Result.err(),
|
|
79
|
+
}),
|
|
80
|
+
Option: branch({
|
|
81
|
+
Some: chain(toAction(action), toAction(Option.some())),
|
|
82
|
+
None: Option.none(),
|
|
83
|
+
}),
|
|
84
|
+
Iterator: IteratorNs.map(action),
|
|
85
|
+
})));
|
|
86
|
+
}
|
|
87
|
+
function unwrapMethod() {
|
|
88
|
+
return chain(toAction(this), toAction(branchFamily({
|
|
89
|
+
Result: branch({ Ok: identity(), Err: panic("called unwrap on Err") }),
|
|
90
|
+
Option: branch({
|
|
91
|
+
Some: identity(),
|
|
92
|
+
None: panic("called unwrap on None"),
|
|
93
|
+
}),
|
|
94
|
+
})));
|
|
95
|
+
}
|
|
96
|
+
function unwrapOrMethod(defaultAction) {
|
|
97
|
+
return chain(toAction(this), toAction(branchFamily({
|
|
98
|
+
Result: branch({ Ok: identity(), Err: defaultAction }),
|
|
99
|
+
Option: branch({ Some: identity(), None: defaultAction }),
|
|
100
|
+
})));
|
|
101
|
+
}
|
|
102
|
+
function andThenMethod(action) {
|
|
103
|
+
return chain(toAction(this), toAction(branchFamily({
|
|
104
|
+
Result: branch({ Ok: action, Err: Result.err() }),
|
|
105
|
+
Option: branch({ Some: action, None: Option.none() }),
|
|
106
|
+
})));
|
|
107
|
+
}
|
|
108
|
+
function transposeMethod() {
|
|
109
|
+
return chain(toAction(this), toAction(branchFamily({
|
|
110
|
+
Option: branch({
|
|
111
|
+
Some: branch({
|
|
112
|
+
Ok: chain(toAction(Option.some()), toAction(Result.ok())),
|
|
113
|
+
Err: Result.err(),
|
|
114
|
+
}),
|
|
115
|
+
None: chain(toAction(chain(toAction(drop), toAction(Option.none()))), toAction(Result.ok())),
|
|
116
|
+
}),
|
|
117
|
+
Result: branch({
|
|
118
|
+
Ok: branch({
|
|
119
|
+
Some: chain(toAction(Result.ok()), toAction(Option.some())),
|
|
120
|
+
None: chain(toAction(drop), toAction(Option.none())),
|
|
121
|
+
}),
|
|
122
|
+
Err: chain(toAction(Result.err()), toAction(Option.some())),
|
|
123
|
+
}),
|
|
124
|
+
})));
|
|
125
|
+
}
|
|
126
|
+
// --- Result-only postfix methods ---
|
|
127
|
+
function mapErrMethod(action) {
|
|
128
|
+
return chain(toAction(this), toAction(branch({
|
|
129
|
+
Ok: Result.ok(),
|
|
130
|
+
Err: chain(toAction(action), toAction(Result.err())),
|
|
131
|
+
})));
|
|
132
|
+
}
|
|
133
|
+
function orMethod(fallback) {
|
|
134
|
+
return chain(toAction(this), toAction(branch({
|
|
135
|
+
Ok: Result.ok(),
|
|
136
|
+
Err: fallback,
|
|
137
|
+
})));
|
|
138
|
+
}
|
|
139
|
+
function asOkOptionMethod() {
|
|
140
|
+
return chain(toAction(this), toAction(branch({
|
|
141
|
+
Ok: Option.some(),
|
|
142
|
+
Err: chain(toAction(drop), toAction(Option.none())),
|
|
143
|
+
})));
|
|
144
|
+
}
|
|
145
|
+
function asErrOptionMethod() {
|
|
146
|
+
return chain(toAction(this), toAction(branch({
|
|
147
|
+
Ok: chain(toAction(drop), toAction(Option.none())),
|
|
148
|
+
Err: Option.some(),
|
|
149
|
+
})));
|
|
150
|
+
}
|
|
151
|
+
function isOkMethod() {
|
|
152
|
+
return chain(toAction(this), toAction(branch({
|
|
153
|
+
Ok: constant(true),
|
|
154
|
+
Err: constant(false),
|
|
155
|
+
})));
|
|
156
|
+
}
|
|
157
|
+
function isErrMethod() {
|
|
158
|
+
return chain(toAction(this), toAction(branch({
|
|
159
|
+
Ok: constant(false),
|
|
160
|
+
Err: constant(true),
|
|
161
|
+
})));
|
|
162
|
+
}
|
|
163
|
+
// --- Option-only postfix methods ---
|
|
164
|
+
function filterMethod(predicate) {
|
|
165
|
+
return chain(toAction(this), toAction(branchFamily({
|
|
166
|
+
Option: branch({
|
|
167
|
+
Some: predicate,
|
|
168
|
+
None: Option.none(),
|
|
169
|
+
}),
|
|
170
|
+
Iterator: IteratorNs.filter(predicate),
|
|
171
|
+
})));
|
|
172
|
+
}
|
|
173
|
+
function isSomeMethod() {
|
|
174
|
+
return chain(toAction(this), toAction(branch({
|
|
175
|
+
Some: constant(true),
|
|
176
|
+
None: constant(false),
|
|
177
|
+
})));
|
|
178
|
+
}
|
|
179
|
+
function isNoneMethod() {
|
|
180
|
+
return chain(toAction(this), toAction(branch({
|
|
181
|
+
Some: constant(false),
|
|
182
|
+
None: constant(true),
|
|
183
|
+
})));
|
|
184
|
+
}
|
|
185
|
+
function asOptionMethod() {
|
|
186
|
+
return chain(toAction(this), toAction(asOptionStandalone()));
|
|
187
|
+
}
|
|
188
|
+
// --- Iterator postfix methods ---
|
|
189
|
+
function iterateMethod() {
|
|
190
|
+
return chain(toAction(this), toAction(branchFamily({
|
|
191
|
+
Option: IteratorNs.fromOption(),
|
|
192
|
+
Result: IteratorNs.fromResult(),
|
|
193
|
+
Array: IteratorNs.fromArray(),
|
|
194
|
+
})));
|
|
195
|
+
}
|
|
196
|
+
function flatMapMethod(action) {
|
|
197
|
+
return chain(toAction(this), toAction(IteratorNs.flatMap(action)));
|
|
198
|
+
}
|
|
199
|
+
function collectMethod() {
|
|
200
|
+
return chain(toAction(this), toAction(branchFamily({
|
|
201
|
+
Array: Option.collect(),
|
|
202
|
+
Iterator: IteratorNs.collect(),
|
|
203
|
+
})));
|
|
204
|
+
}
|
|
205
|
+
function foldMethod(init, body) {
|
|
206
|
+
return chain(toAction(this), toAction(IteratorNs.fold(init, body)));
|
|
207
|
+
}
|
|
208
|
+
function isEmptyMethod() {
|
|
209
|
+
return chain(toAction(this), toAction(IteratorNs.isEmpty()));
|
|
210
|
+
}
|
|
211
|
+
function sliceMethod(start, end) {
|
|
212
|
+
return chain(toAction(this), toAction(IteratorNs.slice(start, end)));
|
|
213
|
+
}
|
|
214
|
+
function takeMethod(n) {
|
|
215
|
+
return chain(toAction(this), toAction(IteratorNs.take(n)));
|
|
216
|
+
}
|
|
217
|
+
function skipMethod(n) {
|
|
218
|
+
return chain(toAction(this), toAction(IteratorNs.skip(n)));
|
|
219
|
+
}
|
|
220
|
+
function bindMethod(bindings, body) {
|
|
221
|
+
return chain(toAction(this), toAction(bindStandalone(bindings, body)));
|
|
222
|
+
}
|
|
223
|
+
function bindInputMethod(body) {
|
|
224
|
+
return chain(toAction(this), toAction(bindInputStandalone(body)));
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Attach `.then()` and `.forEach()` methods to a plain Action object.
|
|
228
|
+
* Methods are non-enumerable: invisible to JSON.stringify and toEqual.
|
|
229
|
+
*/
|
|
230
|
+
export function typedAction(action) {
|
|
231
|
+
if (!("then" in action)) {
|
|
232
|
+
Object.defineProperties(action, {
|
|
233
|
+
then: { value: thenMethod, configurable: true },
|
|
234
|
+
branch: { value: branchMethod, configurable: true },
|
|
235
|
+
flatten: { value: flattenMethod, configurable: true },
|
|
236
|
+
drop: { value: dropMethod, configurable: true },
|
|
237
|
+
tag: { value: tagMethod, configurable: true },
|
|
238
|
+
some: { value: someMethod, configurable: true },
|
|
239
|
+
ok: { value: okMethod, configurable: true },
|
|
240
|
+
err: { value: errMethod, configurable: true },
|
|
241
|
+
getField: { value: getFieldMethod, configurable: true },
|
|
242
|
+
getIndex: { value: getIndexMethod, configurable: true },
|
|
243
|
+
wrapInField: { value: wrapInFieldMethod, configurable: true },
|
|
244
|
+
pick: { value: pickMethod, configurable: true },
|
|
245
|
+
splitFirst: { value: splitFirstMethod, configurable: true },
|
|
246
|
+
splitLast: { value: splitLastMethod, configurable: true },
|
|
247
|
+
map: { value: mapMethod, configurable: true },
|
|
248
|
+
mapErr: { value: mapErrMethod, configurable: true },
|
|
249
|
+
unwrap: { value: unwrapMethod, configurable: true },
|
|
250
|
+
unwrapOr: { value: unwrapOrMethod, configurable: true },
|
|
251
|
+
andThen: { value: andThenMethod, configurable: true },
|
|
252
|
+
filter: { value: filterMethod, configurable: true },
|
|
253
|
+
isSome: { value: isSomeMethod, configurable: true },
|
|
254
|
+
isNone: { value: isNoneMethod, configurable: true },
|
|
255
|
+
asOption: { value: asOptionMethod, configurable: true },
|
|
256
|
+
collect: { value: collectMethod, configurable: true },
|
|
257
|
+
fold: { value: foldMethod, configurable: true },
|
|
258
|
+
isEmpty: { value: isEmptyMethod, configurable: true },
|
|
259
|
+
slice: { value: sliceMethod, configurable: true },
|
|
260
|
+
take: { value: takeMethod, configurable: true },
|
|
261
|
+
skip: { value: skipMethod, configurable: true },
|
|
262
|
+
or: { value: orMethod, configurable: true },
|
|
263
|
+
iterate: { value: iterateMethod, configurable: true },
|
|
264
|
+
flatMap: { value: flatMapMethod, configurable: true },
|
|
265
|
+
asOkOption: { value: asOkOptionMethod, configurable: true },
|
|
266
|
+
asErrOption: { value: asErrOptionMethod, configurable: true },
|
|
267
|
+
isOk: { value: isOkMethod, configurable: true },
|
|
268
|
+
isErr: { value: isErrMethod, configurable: true },
|
|
269
|
+
transpose: { value: transposeMethod, configurable: true },
|
|
270
|
+
bind: { value: bindMethod, configurable: true },
|
|
271
|
+
bindInput: { value: bindInputMethod, configurable: true },
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return action;
|
|
275
|
+
}
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
// Combinators
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
export { pipe } from "./pipe.js";
|
|
280
|
+
export { chain } from "./chain.js";
|
|
281
|
+
export { all } from "./all.js";
|
|
282
|
+
export { bind, bindInput } from "./bind.js";
|
|
283
|
+
export { defineRecursiveFunctions } from "./recursive.js";
|
|
284
|
+
export { resetEffectIdCounter } from "./effect-id.js";
|
|
285
|
+
import { allocateRestartHandlerId, } from "./effect-id.js";
|
|
286
|
+
export { tryCatch } from "./try-catch.js";
|
|
287
|
+
export { race, sleep, withTimeout } from "./race.js";
|
|
288
|
+
export function forEach(action) {
|
|
289
|
+
return typedAction({ kind: "ForEach", action: toAction(action) });
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Insert GetField("value") before each case handler in a branch.
|
|
293
|
+
* This implements auto-unwrapping: the engine dispatches on `kind`, then
|
|
294
|
+
* extracts `value` before passing to the handler. Case handlers receive
|
|
295
|
+
* the payload directly, not the full `{ kind, value }` variant.
|
|
296
|
+
*/
|
|
297
|
+
function unwrapBranchCases(cases) {
|
|
298
|
+
const unwrapped = {};
|
|
299
|
+
for (const key of Object.keys(cases)) {
|
|
300
|
+
unwrapped[key] = toAction(chain(toAction(getField("value")), toAction(cases[key])));
|
|
301
|
+
}
|
|
302
|
+
return unwrapped;
|
|
303
|
+
}
|
|
304
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
305
|
+
export function branch(cases) {
|
|
306
|
+
return typedAction({ kind: "Branch", cases: unwrapBranchCases(cases) });
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Two-level dispatch: extract the enum prefix from a tagged value's `kind`,
|
|
310
|
+
* then branch on that prefix. Used by postfix methods (`.map()`, `.unwrapOr()`,
|
|
311
|
+
* etc.) to dispatch across union families (Option, Result) without runtime
|
|
312
|
+
* metadata.
|
|
313
|
+
*
|
|
314
|
+
* `branchFamily({ Result: ..., Option: ... })` ≡ `chain(extractPrefix(), branch(cases))`
|
|
315
|
+
*/
|
|
316
|
+
export function branchFamily(cases) {
|
|
317
|
+
return typedAction({
|
|
318
|
+
kind: "Chain",
|
|
319
|
+
first: toAction(extractPrefix()),
|
|
320
|
+
rest: toAction(branch(cases)),
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
// recur — restart body primitive
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
/**
|
|
327
|
+
* Restartable scope. The body callback receives `restart`, a TypedAction that
|
|
328
|
+
* re-executes the body from the beginning with a new input value.
|
|
329
|
+
*
|
|
330
|
+
* If the body completes normally → output is TOut.
|
|
331
|
+
* If restart fires → body re-executes with the restarted value.
|
|
332
|
+
*
|
|
333
|
+
* Compiled form: `RestartHandle(id, GetIndex(0), body)`
|
|
334
|
+
*/
|
|
335
|
+
export function recur(bodyFn) {
|
|
336
|
+
const restartHandlerId = allocateRestartHandlerId();
|
|
337
|
+
const restartAction = typedAction({
|
|
338
|
+
kind: "RestartPerform",
|
|
339
|
+
restart_handler_id: restartHandlerId,
|
|
340
|
+
});
|
|
341
|
+
const body = toAction(bodyFn(restartAction));
|
|
342
|
+
return typedAction({
|
|
343
|
+
kind: "RestartHandle",
|
|
344
|
+
restart_handler_id: restartHandlerId,
|
|
345
|
+
body,
|
|
346
|
+
handler: toAction(getIndex(0).unwrap()),
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
// ---------------------------------------------------------------------------
|
|
350
|
+
// earlyReturn — exit scope primitive (built on restart + Branch)
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
/**
|
|
353
|
+
* Early return scope. The body callback receives `earlyReturn`, a TypedAction
|
|
354
|
+
* that exits the scope immediately with the returned value.
|
|
355
|
+
*
|
|
356
|
+
* If the body completes normally → output is TOut.
|
|
357
|
+
* If earlyReturn fires → output is TEarlyReturn.
|
|
358
|
+
* Combined output: TEarlyReturn | TOut.
|
|
359
|
+
*
|
|
360
|
+
* Built on the restart mechanism: input is tagged Continue, body runs inside
|
|
361
|
+
* a Branch. earlyReturn tags with Break and performs — the handler restarts
|
|
362
|
+
* the body, Branch takes the Break path, and the value exits.
|
|
363
|
+
*/
|
|
364
|
+
export function earlyReturn(bodyFn) {
|
|
365
|
+
const restartHandlerId = allocateRestartHandlerId();
|
|
366
|
+
const earlyReturnAction = typedAction(toAction(chain(toAction(tag("Break", "LoopResult")), {
|
|
367
|
+
kind: "RestartPerform",
|
|
368
|
+
restart_handler_id: restartHandlerId,
|
|
369
|
+
})));
|
|
370
|
+
const body = toAction(bodyFn(earlyReturnAction));
|
|
371
|
+
return typedAction(buildRestartBranchAction(restartHandlerId, body, toAction(identity())));
|
|
372
|
+
}
|
|
373
|
+
// ---------------------------------------------------------------------------
|
|
374
|
+
// loop — iterative restart with break
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
/**
|
|
377
|
+
* Build the restart+branch compiled form:
|
|
378
|
+
* `Chain(Tag("Continue"), RestartHandle(id, GetIndex(0), Branch({ Continue: continueArm, Break: breakArm })))`
|
|
379
|
+
*
|
|
380
|
+
* Input is tagged Continue so the Branch enters the continueArm on first execution.
|
|
381
|
+
* Continue tag → restart → re-enters continueArm. Break tag → restart → runs breakArm, exits `RestartHandle`.
|
|
382
|
+
*
|
|
383
|
+
* Used by earlyReturn, loop, tryCatch, and race.
|
|
384
|
+
*/
|
|
385
|
+
export function buildRestartBranchAction(restartHandlerId, continueArm, breakArm) {
|
|
386
|
+
return toAction(chain(toAction(tag("Continue", "LoopResult")), {
|
|
387
|
+
kind: "RestartHandle",
|
|
388
|
+
restart_handler_id: restartHandlerId,
|
|
389
|
+
body: toAction(branch({ Continue: continueArm, Break: breakArm })),
|
|
390
|
+
handler: toAction(getIndex(0).unwrap()),
|
|
391
|
+
}));
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Iterative loop. The body callback receives `recur` and `done`:
|
|
395
|
+
* - `recur`: restart the loop with a new input
|
|
396
|
+
* - `done`: exit the loop with the break value
|
|
397
|
+
*
|
|
398
|
+
* Both are TypedAction values (not functions), consistent with throwError in tryCatch.
|
|
399
|
+
*
|
|
400
|
+
* Compiles to `RestartHandle`/`RestartPerform`/Branch — same effect substrate as tryCatch and earlyReturn.
|
|
401
|
+
*/
|
|
402
|
+
export function loop(bodyFn) {
|
|
403
|
+
const restartHandlerId = allocateRestartHandlerId();
|
|
404
|
+
const perform = {
|
|
405
|
+
kind: "RestartPerform",
|
|
406
|
+
restart_handler_id: restartHandlerId,
|
|
407
|
+
};
|
|
408
|
+
const recurAction = typedAction(toAction(chain(toAction(tag("Continue", "LoopResult")), toAction(perform))));
|
|
409
|
+
const doneAction = typedAction(toAction(chain(toAction(tag("Break", "LoopResult")), toAction(perform))));
|
|
410
|
+
const body = toAction(bodyFn(recurAction, doneAction));
|
|
411
|
+
return typedAction(buildRestartBranchAction(restartHandlerId, body, toAction(identity())));
|
|
412
|
+
}
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
// Config builders
|
|
415
|
+
// ---------------------------------------------------------------------------
|
|
416
|
+
/** Simple config factory. */
|
|
417
|
+
export function config(workflow) {
|
|
418
|
+
return { workflow };
|
|
419
|
+
}
|
package/dist/bind.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Action, type ExtractInput, type ExtractOutput, type TypedAction } from "./ast.js";
|
|
2
|
+
/**
|
|
3
|
+
* A typed reference to a bound value. Output is `TValue`.
|
|
4
|
+
*
|
|
5
|
+
* Use `.then()` (not `pipe()`) when chaining a VarRef into a generic
|
|
6
|
+
* action like `pick` or `getField` — pipe overloads can't infer
|
|
7
|
+
* the generic's type parameter from the VarRef's output.
|
|
8
|
+
*/
|
|
9
|
+
export type VarRef<TValue> = TypedAction<any, TValue>;
|
|
10
|
+
/**
|
|
11
|
+
* Maps each binding's output type to a VarRef. TypeScript resolves
|
|
12
|
+
* ExtractOutput from each binding expression.
|
|
13
|
+
*
|
|
14
|
+
* Constraint is `Action[]` (not `Pipeable<any, any>[]`) so that
|
|
15
|
+
* `ExtractOutput` extracts the correct output type from the phantom
|
|
16
|
+
* fields on the concrete types without fighting invariant `__in` checks.
|
|
17
|
+
*/
|
|
18
|
+
export type InferVarRefs<TBindings extends Action[]> = {
|
|
19
|
+
[K in keyof TBindings]: VarRef<ExtractOutput<TBindings[K]>>;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Bind concurrent values as VarRefs available throughout the body.
|
|
23
|
+
*
|
|
24
|
+
* All bindings are actions (Pipeable) evaluated concurrently with the
|
|
25
|
+
* pipeline input. The body callback receives an array of VarRefs,
|
|
26
|
+
* one per binding.
|
|
27
|
+
*
|
|
28
|
+
* Compiles to:
|
|
29
|
+
* Chain(
|
|
30
|
+
* All(...bindings, Identity),
|
|
31
|
+
* ResumeHandle(r0, readVar(0),
|
|
32
|
+
* ResumeHandle(r1, readVar(1),
|
|
33
|
+
* Chain(GetIndex(N), body)
|
|
34
|
+
* )
|
|
35
|
+
* )
|
|
36
|
+
* )
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Constraint for the body callback return type. Only requires the output
|
|
40
|
+
* phantom field — omits `__in` and `__in_co` so that body actions with
|
|
41
|
+
* any input type (e.g. pipelines starting from a VarRef) are assignable.
|
|
42
|
+
*/
|
|
43
|
+
type BodyResult<TOut> = Action & {
|
|
44
|
+
__out?: () => TOut;
|
|
45
|
+
};
|
|
46
|
+
export declare function bind<TBindings extends Action[], TOut>(bindings: [...TBindings], body: (vars: InferVarRefs<TBindings>) => BodyResult<TOut>): TypedAction<ExtractInput<TBindings[number]>, TOut>;
|
|
47
|
+
/**
|
|
48
|
+
* Convenience wrapper for the common pattern of capturing the pipeline
|
|
49
|
+
* input as a VarRef. The body's pipeline input is `never` — the input
|
|
50
|
+
* is dropped, so the body must access it through the VarRef.
|
|
51
|
+
*
|
|
52
|
+
* Sugar for: `bind([identity()], ([input]) => pipe(drop, body(input)))`
|
|
53
|
+
*
|
|
54
|
+
* TOut defaults to `any` so callers can specify just TIn:
|
|
55
|
+
* bindInput<FileEntry>((entry) => ...)
|
|
56
|
+
*/
|
|
57
|
+
export declare function bindInput<TIn, TOut = any>(body: (input: VarRef<TIn>) => BodyResult<TOut>): TypedAction<TIn, TOut>;
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=bind.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bind.d.ts","sourceRoot":"","sources":["../src/bind.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,WAAW,EAGjB,MAAM,UAAU,CAAC;AAWlB;;;;;;GAMG;AACH,MAAM,MAAM,MAAM,CAAC,MAAM,IAAI,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAetD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,CAAC,SAAS,SAAS,MAAM,EAAE,IAAI;KACpD,CAAC,IAAI,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5D,CAAC;AA4BF;;;;;;;;;;;;;;;;GAgBG;AACH;;;;GAIG;AACH,KAAK,UAAU,CAAC,IAAI,IAAI,MAAM,GAAG;IAC/B,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF,wBAAgB,IAAI,CAAC,SAAS,SAAS,MAAM,EAAE,EAAE,IAAI,EACnD,QAAQ,EAAE,CAAC,GAAG,SAAS,CAAC,EACxB,IAAI,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC,KAAK,UAAU,CAAC,IAAI,CAAC,GACxD,WAAW,CAAC,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAkCpD;AAMD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,GAAG,GAAG,EACvC,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,IAAI,CAAC,GAC7C,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAExB"}
|
package/dist/bind.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { toAction, typedAction, } from "./ast.js";
|
|
2
|
+
import { chain } from "./chain.js";
|
|
3
|
+
import { all } from "./all.js";
|
|
4
|
+
import { identity, drop, getIndex } from "./builtins/index.js";
|
|
5
|
+
import { allocateResumeHandlerId } from "./effect-id.js";
|
|
6
|
+
import { pipe } from "./pipe.js";
|
|
7
|
+
function createVarRef(resumeHandlerId) {
|
|
8
|
+
return typedAction({
|
|
9
|
+
kind: "ResumePerform",
|
|
10
|
+
resume_handler_id: resumeHandlerId,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// readVar — handler DAG for the nth binding
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
/**
|
|
17
|
+
* Returns an action that extracts the nth value from the ResumeHandle's
|
|
18
|
+
* state tuple and passes state through unchanged. When a ResumePerform
|
|
19
|
+
* fires, the engine calls the handler with `[payload, state]`. For bind,
|
|
20
|
+
* `state` (index 1) is the full All output tuple. The handler produces
|
|
21
|
+
* `[state[n], state]` — value is state[n], new_state is state (unchanged).
|
|
22
|
+
*
|
|
23
|
+
* Expanded AST: All(Chain(GetIndex(1).unwrap(), GetIndex(n).unwrap()), GetIndex(1).unwrap())
|
|
24
|
+
*/
|
|
25
|
+
function readVar(n) {
|
|
26
|
+
return toAction(all(chain(toAction(getIndex(1).unwrap()), toAction(getIndex(n).unwrap())), toAction(getIndex(1).unwrap())));
|
|
27
|
+
}
|
|
28
|
+
export function bind(bindings, body) {
|
|
29
|
+
// 1. Gensym one resumeHandlerId per binding.
|
|
30
|
+
const resumeHandlerIds = bindings.map(() => allocateResumeHandlerId());
|
|
31
|
+
// 2. Create VarRefs (ResumePerform nodes) for each binding.
|
|
32
|
+
const varRefs = resumeHandlerIds.map((id) => createVarRef(id));
|
|
33
|
+
// 3. Invoke the body callback with the VarRefs.
|
|
34
|
+
const bodyAction = toAction(body(varRefs));
|
|
35
|
+
// 4. Build nested Handles from inside out.
|
|
36
|
+
// Innermost: extract pipeline_input (last All element) → user body
|
|
37
|
+
const pipelineInputIndex = bindings.length;
|
|
38
|
+
let inner = toAction(chain(toAction(getIndex(pipelineInputIndex).unwrap()), toAction(bodyAction)));
|
|
39
|
+
for (let i = resumeHandlerIds.length - 1; i >= 0; i--) {
|
|
40
|
+
inner = {
|
|
41
|
+
kind: "ResumeHandle",
|
|
42
|
+
resume_handler_id: resumeHandlerIds[i],
|
|
43
|
+
handler: readVar(i),
|
|
44
|
+
body: inner,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// 5. All(...bindings, identity()) → nested Handles
|
|
48
|
+
const allAction = {
|
|
49
|
+
kind: "All",
|
|
50
|
+
actions: [...bindings.map((b) => toAction(b)), toAction(identity())],
|
|
51
|
+
};
|
|
52
|
+
return typedAction(toAction(chain(toAction(allAction), toAction(inner))));
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// bindInput — bind the pipeline input
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
/**
|
|
58
|
+
* Convenience wrapper for the common pattern of capturing the pipeline
|
|
59
|
+
* input as a VarRef. The body's pipeline input is `never` — the input
|
|
60
|
+
* is dropped, so the body must access it through the VarRef.
|
|
61
|
+
*
|
|
62
|
+
* Sugar for: `bind([identity()], ([input]) => pipe(drop, body(input)))`
|
|
63
|
+
*
|
|
64
|
+
* TOut defaults to `any` so callers can specify just TIn:
|
|
65
|
+
* bindInput<FileEntry>((entry) => ...)
|
|
66
|
+
*/
|
|
67
|
+
export function bindInput(body) {
|
|
68
|
+
return bind([identity()], ([input]) => pipe(drop, body(input)));
|
|
69
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Option as OptionT, type TypedAction } from "../ast.js";
|
|
2
|
+
export declare function getIndex<TTuple extends unknown[], TIndex extends number>(index: TIndex): TypedAction<TTuple, OptionT<TTuple[TIndex]>>;
|
|
3
|
+
export declare function flatten<TElement>(): TypedAction<TElement[][], TElement[]>;
|
|
4
|
+
/**
|
|
5
|
+
* Deconstruct an array into its first element and the remaining elements.
|
|
6
|
+
* `TElement[] → Option<[TElement, TElement[]]>`
|
|
7
|
+
*
|
|
8
|
+
* Returns `Some([first, rest])` for non-empty arrays, `None` for empty arrays.
|
|
9
|
+
* This is the array equivalent of cons/uncons — enables recursive iteration
|
|
10
|
+
* patterns via `loop` + `splitFirst` + `branch`.
|
|
11
|
+
*
|
|
12
|
+
* This is a builtin (SplitFirst) because it requires array-length branching
|
|
13
|
+
* that can't be composed from existing AST nodes.
|
|
14
|
+
*/
|
|
15
|
+
export declare function splitFirst<TElement>(): TypedAction<TElement[], OptionT<[TElement, TElement[]]>>;
|
|
16
|
+
/**
|
|
17
|
+
* Deconstruct an array into the leading elements and the last element.
|
|
18
|
+
* `TElement[] → Option<[TElement[], TElement]>`
|
|
19
|
+
*
|
|
20
|
+
* Returns `Some([init, last])` for non-empty arrays, `None` for empty arrays.
|
|
21
|
+
* Mirror of `splitFirst` — enables processing from the tail end.
|
|
22
|
+
*
|
|
23
|
+
* This is a builtin (SplitLast) because it requires array-length branching
|
|
24
|
+
* that can't be composed from existing AST nodes.
|
|
25
|
+
*/
|
|
26
|
+
export declare function splitLast<TElement>(): TypedAction<TElement[], OptionT<[TElement[], TElement]>>;
|
|
27
|
+
/**
|
|
28
|
+
* Slice an array from `start` (inclusive) to `end` (exclusive).
|
|
29
|
+
* `T[] → T[]`
|
|
30
|
+
*
|
|
31
|
+
* Both indices are clamped to array length. If `end` is omitted, slices
|
|
32
|
+
* to the end of the array. Returns empty array if `start >= end`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function slice<TElement>(start: number, end?: number): TypedAction<TElement[], TElement[]>;
|
|
35
|
+
export declare function range(start: number, end: number): TypedAction<any, number[]>;
|
|
36
|
+
//# sourceMappingURL=array.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../../src/builtins/array.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,WAAW,EAEjB,MAAM,WAAW,CAAC;AAMnB,wBAAgB,QAAQ,CAAC,MAAM,SAAS,OAAO,EAAE,EAAE,MAAM,SAAS,MAAM,EACtE,KAAK,EAAE,MAAM,GACZ,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAQ9C;AAMD,wBAAgB,OAAO,CAAC,QAAQ,KAAK,WAAW,CAAC,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAKzE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,QAAQ,KAAK,WAAW,CACjD,QAAQ,EAAE,EACV,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAChC,CAKA;AAMD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,QAAQ,KAAK,WAAW,CAChD,QAAQ,EAAE,EACV,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC,CAChC,CAKA;AAMD;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAC5B,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,MAAM,GACX,WAAW,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,CASrC;AAMD,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAS5E"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { typedAction, } from "../ast.js";
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// GetIndex — extract a single element from an array by index
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
export function getIndex(index) {
|
|
6
|
+
return typedAction({
|
|
7
|
+
kind: "Invoke",
|
|
8
|
+
handler: {
|
|
9
|
+
kind: "Builtin",
|
|
10
|
+
builtin: { kind: "GetIndex", index },
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Flatten — flatten a nested array one level
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
export function flatten() {
|
|
18
|
+
return typedAction({
|
|
19
|
+
kind: "Invoke",
|
|
20
|
+
handler: { kind: "Builtin", builtin: { kind: "Flatten" } },
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// SplitFirst — head/tail decomposition of an array
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Deconstruct an array into its first element and the remaining elements.
|
|
28
|
+
* `TElement[] → Option<[TElement, TElement[]]>`
|
|
29
|
+
*
|
|
30
|
+
* Returns `Some([first, rest])` for non-empty arrays, `None` for empty arrays.
|
|
31
|
+
* This is the array equivalent of cons/uncons — enables recursive iteration
|
|
32
|
+
* patterns via `loop` + `splitFirst` + `branch`.
|
|
33
|
+
*
|
|
34
|
+
* This is a builtin (SplitFirst) because it requires array-length branching
|
|
35
|
+
* that can't be composed from existing AST nodes.
|
|
36
|
+
*/
|
|
37
|
+
export function splitFirst() {
|
|
38
|
+
return typedAction({
|
|
39
|
+
kind: "Invoke",
|
|
40
|
+
handler: { kind: "Builtin", builtin: { kind: "SplitFirst" } },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// SplitLast — init/last decomposition of an array
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
/**
|
|
47
|
+
* Deconstruct an array into the leading elements and the last element.
|
|
48
|
+
* `TElement[] → Option<[TElement[], TElement]>`
|
|
49
|
+
*
|
|
50
|
+
* Returns `Some([init, last])` for non-empty arrays, `None` for empty arrays.
|
|
51
|
+
* Mirror of `splitFirst` — enables processing from the tail end.
|
|
52
|
+
*
|
|
53
|
+
* This is a builtin (SplitLast) because it requires array-length branching
|
|
54
|
+
* that can't be composed from existing AST nodes.
|
|
55
|
+
*/
|
|
56
|
+
export function splitLast() {
|
|
57
|
+
return typedAction({
|
|
58
|
+
kind: "Invoke",
|
|
59
|
+
handler: { kind: "Builtin", builtin: { kind: "SplitLast" } },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Slice — extract a sub-array from start to end
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
/**
|
|
66
|
+
* Slice an array from `start` (inclusive) to `end` (exclusive).
|
|
67
|
+
* `T[] → T[]`
|
|
68
|
+
*
|
|
69
|
+
* Both indices are clamped to array length. If `end` is omitted, slices
|
|
70
|
+
* to the end of the array. Returns empty array if `start >= end`.
|
|
71
|
+
*/
|
|
72
|
+
export function slice(start, end) {
|
|
73
|
+
const builtin = end === undefined
|
|
74
|
+
? { kind: "Slice", start }
|
|
75
|
+
: { kind: "Slice", start, end };
|
|
76
|
+
return typedAction({
|
|
77
|
+
kind: "Invoke",
|
|
78
|
+
handler: { kind: "Builtin", builtin },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Range — produce an integer array [start, start+1, ..., end-1]
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
export function range(start, end) {
|
|
85
|
+
const result = [];
|
|
86
|
+
for (let i = start; i < end; i++) {
|
|
87
|
+
result.push(i);
|
|
88
|
+
}
|
|
89
|
+
return typedAction({
|
|
90
|
+
kind: "Invoke",
|
|
91
|
+
handler: { kind: "Builtin", builtin: { kind: "Constant", value: result } },
|
|
92
|
+
});
|
|
93
|
+
}
|