@effekt-lang/effekt 0.2.1 → 0.3.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/LICENSE +21 -0
- package/README.md +36 -0
- package/bin/effekt +0 -0
- package/bin/effekt.sh +2 -0
- package/libraries/chez/THIRD-PARTY.txt +11 -0
- package/libraries/chez/callcc/datatype.ss +188 -0
- package/libraries/chez/callcc/effekt.ss +31 -0
- package/libraries/chez/callcc/seq.ss +44 -0
- package/libraries/chez/callcc/seq0.ss +69 -0
- package/libraries/chez/callcc/tail.ss +76 -0
- package/libraries/chez/callcc/tail0.ss +89 -0
- package/libraries/chez/common/effekt_primitives.ss +105 -0
- package/libraries/chez/common/immutable/cslist.effekt +37 -0
- package/libraries/chez/common/mutable/dict.effekt +28 -0
- package/libraries/chez/common/text/pregexp.scm +772 -0
- package/libraries/chez/common/text/regex.effekt +31 -0
- package/libraries/chez/lift/effekt.ss +118 -0
- package/libraries/chez/monadic/effekt.ss +84 -0
- package/libraries/chez/monadic/seq0.ss +76 -0
- package/libraries/common/args.effekt +95 -0
- package/libraries/common/array.effekt +225 -0
- package/libraries/common/bench.effekt +60 -0
- package/libraries/common/buffer.effekt +123 -0
- package/libraries/common/bytes.effekt +125 -0
- package/libraries/common/dequeue.effekt +92 -0
- package/libraries/common/effekt.effekt +764 -0
- package/libraries/common/exception.effekt +122 -0
- package/libraries/common/io/console.effekt +66 -0
- package/libraries/common/io/error.effekt +365 -0
- package/libraries/common/io/files.effekt +282 -0
- package/libraries/common/io/network.effekt +105 -0
- package/libraries/common/io/time.effekt +15 -0
- package/libraries/common/io.effekt +158 -0
- package/libraries/common/list.effekt +687 -0
- package/libraries/common/option.effekt +72 -0
- package/libraries/common/process.effekt +10 -0
- package/libraries/common/queue.effekt +140 -0
- package/libraries/common/ref.effekt +63 -0
- package/libraries/common/result.effekt +45 -0
- package/libraries/common/seq.effekt +914 -0
- package/libraries/common/string.effekt +395 -0
- package/libraries/common/test.effekt +98 -0
- package/libraries/js/effekt_builtins.js +57 -0
- package/libraries/js/effekt_runtime.js +385 -0
- package/libraries/js/io.js +143 -0
- package/libraries/js/mutable/map.effekt +31 -0
- package/libraries/js/text/regex.effekt +33 -0
- package/libraries/js/unsafe/cont.effekt +11 -0
- package/libraries/js/web/dom.effekt +45 -0
- package/libraries/llvm/array.c +53 -0
- package/libraries/llvm/buffer.c +286 -0
- package/libraries/llvm/forward-declare-c.ll +43 -0
- package/libraries/llvm/hole.c +11 -0
- package/libraries/llvm/io.c +566 -0
- package/libraries/llvm/io.ll +19 -0
- package/libraries/llvm/main.c +41 -0
- package/libraries/llvm/ref.c +45 -0
- package/libraries/llvm/rts.ll +761 -0
- package/libraries/llvm/sanity.c +16 -0
- package/libraries/llvm/types.c +43 -0
- package/libraries/ml/effekt.sml +65 -0
- package/libraries/ml/internal/mllist.effekt +9 -0
- package/libraries/ml/internal/mloption.effekt +18 -0
- package/libraries/ml/random.effekt +85 -0
- package/libraries/ml/text/regex.effekt +102 -0
- package/licenses/THIRD-PARTY.txt +21 -0
- package/licenses/apache-2.0 - license-2.0.txt +202 -0
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +61 -0
- package/licenses/kiama-license.txt +373 -0
- package/licenses/kiama-readme.txt +186 -0
- package/licenses/mit license - mit-license.html +1239 -0
- package/licenses/the apache software license, version 2.0 - license-2.0.txt +202 -0
- package/licenses/the bsd license - bsd-license.html +1237 -0
- package/licenses/the mit license - mit.html +1239 -0
- package/package.json +26 -18
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
const $runtime = (function() {
|
|
2
|
+
|
|
3
|
+
// Common Runtime
|
|
4
|
+
// --------------
|
|
5
|
+
|
|
6
|
+
// Regions
|
|
7
|
+
// TODO maybe use weak refs (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef)
|
|
8
|
+
function Cell(init) {
|
|
9
|
+
var _value = init;
|
|
10
|
+
const cell = ({
|
|
11
|
+
backup: function() {
|
|
12
|
+
var _backup = _value
|
|
13
|
+
var cell = this;
|
|
14
|
+
return () => { _value = _backup; return cell }
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
// $getOp and $putOp are auto generated from the compiler
|
|
18
|
+
cell[$getOp] = function() {
|
|
19
|
+
return _value
|
|
20
|
+
};
|
|
21
|
+
cell[$putOp] = function(v) {
|
|
22
|
+
_value = v;
|
|
23
|
+
return $effekt.unit;
|
|
24
|
+
};
|
|
25
|
+
return cell
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function Arena() {
|
|
29
|
+
return {
|
|
30
|
+
fields: [], // Array[Cell],
|
|
31
|
+
fresh: function(init) {
|
|
32
|
+
const cell = Cell(init)
|
|
33
|
+
this.fields.push(cell)
|
|
34
|
+
return cell;
|
|
35
|
+
},
|
|
36
|
+
backup: function() {
|
|
37
|
+
return this.fields.map(c => c.backup())
|
|
38
|
+
},
|
|
39
|
+
restore: function(backup) {
|
|
40
|
+
this.fields = backup.map(c => c());
|
|
41
|
+
return this
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const global = {
|
|
47
|
+
fresh: function(init) { return Cell(init) },
|
|
48
|
+
backup: function() {},
|
|
49
|
+
restore: function(_) {}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Lists / Pairs
|
|
53
|
+
function Cons(head, tail) {
|
|
54
|
+
return { head: head, tail: tail }
|
|
55
|
+
}
|
|
56
|
+
const Nil = null
|
|
57
|
+
|
|
58
|
+
// reverseOnto[A, B, C](init: Frames[A, B], tail: Stack[B, C]): Stack[A, C]
|
|
59
|
+
function reverseOnto(init, tail) {
|
|
60
|
+
let rest = init;
|
|
61
|
+
let result = tail;
|
|
62
|
+
while (rest !== Nil) {
|
|
63
|
+
result = Cons(rest.head, result)
|
|
64
|
+
rest = rest.tail
|
|
65
|
+
}
|
|
66
|
+
return result
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let _prompt = 2;
|
|
70
|
+
|
|
71
|
+
// Monadic Style Runtime
|
|
72
|
+
// ---------------------
|
|
73
|
+
|
|
74
|
+
const toplevel = 1;
|
|
75
|
+
|
|
76
|
+
// Result -- Trampoline
|
|
77
|
+
function Step(c, k) {
|
|
78
|
+
return { isStep: true, c: c, k: k }
|
|
79
|
+
}
|
|
80
|
+
function trampoline(r) {
|
|
81
|
+
var res = r
|
|
82
|
+
while (res !== null && res !== undefined && res.isStep) {
|
|
83
|
+
res = res.c.apply(res.k)
|
|
84
|
+
}
|
|
85
|
+
return res
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Frame = A => Control[B]
|
|
89
|
+
|
|
90
|
+
// Metacontinuations / Stacks
|
|
91
|
+
// (frames: List<Frame>, fields: [Cell], prompt: Int, tail: Stack) -> Stack
|
|
92
|
+
function Stack(frames, arena, prompt, tail) {
|
|
93
|
+
return { frames: frames, arena: arena, prompt: prompt, tail: tail }
|
|
94
|
+
}
|
|
95
|
+
function SubStack(frames, arena, backup, prompt, tail) {
|
|
96
|
+
return { frames: frames, arena: arena, backup: backup, prompt: prompt, tail: tail }
|
|
97
|
+
}
|
|
98
|
+
const EmptyStack = null;
|
|
99
|
+
|
|
100
|
+
// (stack: Stack<A, B>, a: A) -> Step<B>
|
|
101
|
+
function apply(stack, a) {
|
|
102
|
+
var s = stack;
|
|
103
|
+
while (true) {
|
|
104
|
+
if (s === EmptyStack) return a;
|
|
105
|
+
const fs = s.frames;
|
|
106
|
+
if (fs === Nil) { s = s.tail; continue }
|
|
107
|
+
const result = fs.head(a);
|
|
108
|
+
s.frames = fs.tail;
|
|
109
|
+
return Step(result, s)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// (subcont: Stack, stack: Stack) -> Stack
|
|
114
|
+
function pushSubcont(subcont, stack) {
|
|
115
|
+
var sub = subcont;
|
|
116
|
+
var s = stack;
|
|
117
|
+
|
|
118
|
+
while (sub !== EmptyStack) {
|
|
119
|
+
s = Stack(sub.frames, sub.arena.restore(sub.backup), sub.prompt, s)
|
|
120
|
+
sub = sub.tail
|
|
121
|
+
}
|
|
122
|
+
return s;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function flatMap(stack, f) {
|
|
126
|
+
if (stack === EmptyStack) { return Stack(Cons(f, Nil), Arena(), null, stack) }
|
|
127
|
+
var fs = stack.frames
|
|
128
|
+
// it should be safe to mutate the frames field, since they are copied in the subcont
|
|
129
|
+
stack.frames = Cons(f, fs)
|
|
130
|
+
return stack
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function splitAt(stack, p) {
|
|
134
|
+
var sub = EmptyStack;
|
|
135
|
+
var s = stack;
|
|
136
|
+
|
|
137
|
+
while (s !== EmptyStack) {
|
|
138
|
+
const currentPrompt = s.prompt;
|
|
139
|
+
sub = SubStack(s.frames, s.arena, s.arena.backup(), currentPrompt, sub);
|
|
140
|
+
s = s.tail;
|
|
141
|
+
if (currentPrompt === p) { return Cons(sub, s) }
|
|
142
|
+
}
|
|
143
|
+
throw ("Prompt " + p + " not found")
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function allocateInto(stack, p, cell) {
|
|
147
|
+
var s = stack;
|
|
148
|
+
|
|
149
|
+
while (s !== EmptyStack) {
|
|
150
|
+
const currentPrompt = s.prompt
|
|
151
|
+
if (currentPrompt === p) {
|
|
152
|
+
return s.fields.push(cell);
|
|
153
|
+
} else {
|
|
154
|
+
s = s.tail
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
throw ("Prompt " + p + " not found")
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function withState(init, f) {
|
|
161
|
+
return Control(k => {
|
|
162
|
+
const cell = k.arena.fresh(init)
|
|
163
|
+
return Step(f(cell), k)
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function withRegion(prog) {
|
|
168
|
+
return Control(k => {
|
|
169
|
+
return Step(prog(k.arena), k)
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function withStateIn(prompt, init, f) {
|
|
174
|
+
const cell = Cell(init)
|
|
175
|
+
|
|
176
|
+
if (prompt === toplevel) {
|
|
177
|
+
return f(cell)
|
|
178
|
+
} else {
|
|
179
|
+
return Control(k => {
|
|
180
|
+
allocateInto(k, prompt, cell);
|
|
181
|
+
return Step(f(cell), k)
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Delimited Control
|
|
187
|
+
function Control(apply) {
|
|
188
|
+
const self = {
|
|
189
|
+
apply: apply,
|
|
190
|
+
run: () => trampoline(Step(self, Stack(Nil, global, toplevel, EmptyStack))),
|
|
191
|
+
then: f => Control(k => Step(self, flatMap(k, f))),
|
|
192
|
+
state: f => self.then(init => withState(init, f))
|
|
193
|
+
}
|
|
194
|
+
return self
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const pure = a => Control(k => apply(k, a))
|
|
198
|
+
|
|
199
|
+
const delayed = a => Control(k => apply(k, a()))
|
|
200
|
+
|
|
201
|
+
const shift = (p, f) => Control(k => {
|
|
202
|
+
const split = splitAt(k, p)
|
|
203
|
+
const localCont = a => Control(k =>
|
|
204
|
+
Step(pure(a), pushSubcont(split.head, k)))
|
|
205
|
+
return Step(f(localCont), split.tail)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
const callcc = f => Control(k => {
|
|
209
|
+
return f(a => trampoline(apply(k, a)))
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
const abort = Control(k => $effekt.unit)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
const capture = f => {
|
|
216
|
+
// [abort; f
|
|
217
|
+
const action = () => f($effekt.unit).then(() => abort)
|
|
218
|
+
return shift(toplevel)(k =>
|
|
219
|
+
k({
|
|
220
|
+
shouldRun: false,
|
|
221
|
+
cont : () => k({ shouldRun: true, cont: action })
|
|
222
|
+
})).then(a => a.shouldRun ? a.cont() : $effekt.pure(a.cont))
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
//const reset = (p, c => Control(k => Step(c, Stack(Nil, Arena(), p, k)))
|
|
226
|
+
|
|
227
|
+
function handleMonadic(body) {
|
|
228
|
+
const p = _prompt++;
|
|
229
|
+
return Control(k => Step(body(p), Stack(Nil, Arena(), p, k)))
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Direct Style Runtime
|
|
233
|
+
// --------------------
|
|
234
|
+
|
|
235
|
+
// sealed trait Resumption[A, R]
|
|
236
|
+
// case class Empty[A]() extends Resumption[A, A]
|
|
237
|
+
// case class Segment[A, B, C](head: Stack[B, C], prompt: Prompt, tail: Resumption[A, B]) extends Resumption[A, C]
|
|
238
|
+
class Segment {
|
|
239
|
+
constructor(frames, prompt, region, backup, tail) {
|
|
240
|
+
this.frames = frames;
|
|
241
|
+
this.prompt = prompt;
|
|
242
|
+
this.region = region;
|
|
243
|
+
this.backup = backup;
|
|
244
|
+
this.tail = tail;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const Empty = null;
|
|
248
|
+
|
|
249
|
+
// TODO maybe inline later to save native frames
|
|
250
|
+
function handleOrRethrow(prompt, s, rest) {
|
|
251
|
+
if (!(s instanceof Suspension)) throw s;
|
|
252
|
+
|
|
253
|
+
const region = currentRegion
|
|
254
|
+
const k = new Segment(reverseOnto(s.frames, rest), prompt, region, region.backup(), s.cont)
|
|
255
|
+
if (s.prompt === prompt) {
|
|
256
|
+
return s.body((value) => rewind(k, () => value))
|
|
257
|
+
} else {
|
|
258
|
+
throw new Suspension(s.prompt, s.body, Nil, k)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function rewind(k, thunk) {
|
|
263
|
+
if (k === Empty) {
|
|
264
|
+
return thunk()
|
|
265
|
+
} else {
|
|
266
|
+
const prompt = k.prompt;
|
|
267
|
+
const region = k.region;
|
|
268
|
+
let rest = k.frames // the pure frames
|
|
269
|
+
|
|
270
|
+
// The trampoline
|
|
271
|
+
try {
|
|
272
|
+
enterRegion(region);
|
|
273
|
+
region.restore(k.backup);
|
|
274
|
+
|
|
275
|
+
let curr = rewind(k.tail, thunk)
|
|
276
|
+
while (rest !== Nil) {
|
|
277
|
+
const f = rest.head
|
|
278
|
+
rest = rest.tail
|
|
279
|
+
curr = f(curr)
|
|
280
|
+
}
|
|
281
|
+
return curr
|
|
282
|
+
} catch (s) {
|
|
283
|
+
return handleOrRethrow(prompt, s, rest)
|
|
284
|
+
} finally {
|
|
285
|
+
leaveRegion()
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
// case class Suspend[A, X, Y, R](
|
|
292
|
+
// body: Continuation[A, R] => R,
|
|
293
|
+
// prompt: Prompt,
|
|
294
|
+
// pure: Frames[X, Y],
|
|
295
|
+
// cont: Resumption[A, X]
|
|
296
|
+
// )
|
|
297
|
+
class Suspension {
|
|
298
|
+
constructor(p, body, frames, cont) {
|
|
299
|
+
this.prompt = p;
|
|
300
|
+
this.body = body;
|
|
301
|
+
this.frames = frames;
|
|
302
|
+
this.cont = cont;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// initially the toplevel region.
|
|
307
|
+
let currentRegion = global
|
|
308
|
+
|
|
309
|
+
// a stack of regions
|
|
310
|
+
let regions = []
|
|
311
|
+
|
|
312
|
+
function enterRegion(r) {
|
|
313
|
+
regions.push(currentRegion)
|
|
314
|
+
currentRegion = r
|
|
315
|
+
return r
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function leaveRegion() {
|
|
319
|
+
const leftRegion = currentRegion
|
|
320
|
+
currentRegion = regions.pop()
|
|
321
|
+
return leftRegion
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
// Common API
|
|
326
|
+
// -----------
|
|
327
|
+
constructor: (_, tag) => function() {
|
|
328
|
+
return { __tag: tag, __data: Array.from(arguments) }
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
hole: function() { throw "Implementation missing" },
|
|
332
|
+
|
|
333
|
+
// Monadic API
|
|
334
|
+
// -----------
|
|
335
|
+
pure: pure,
|
|
336
|
+
callcc: callcc,
|
|
337
|
+
capture: capture,
|
|
338
|
+
delayed: delayed,
|
|
339
|
+
handleMonadic: handleMonadic,
|
|
340
|
+
ref: Cell,
|
|
341
|
+
state: withState,
|
|
342
|
+
shift: shift,
|
|
343
|
+
_if: (c, thn, els) => c ? thn() : els(),
|
|
344
|
+
withRegion: withRegion,
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
// Direct style API
|
|
348
|
+
// ----------------
|
|
349
|
+
fresh: function(init) {
|
|
350
|
+
return currentRegion.fresh(init)
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
freshPrompt: function() { return ++_prompt; },
|
|
354
|
+
|
|
355
|
+
suspend: function(prompt, body) {
|
|
356
|
+
_stacksize = 0;
|
|
357
|
+
throw new Suspension(prompt, body, Nil, Empty)
|
|
358
|
+
},
|
|
359
|
+
suspend_bidirectional: function(prompt, caps, body) {
|
|
360
|
+
throw new Suspension(prompt, body, Cons(thunk => thunk.apply(null, caps), Nil), Empty)
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
// suspension: the raised exception.
|
|
364
|
+
push: function(suspension, frame) {
|
|
365
|
+
if (!(suspension instanceof Suspension)) throw suspension;
|
|
366
|
+
// Assuming `suspension` is a value or variable you want to return
|
|
367
|
+
throw new Suspension(suspension.prompt, suspension.body,
|
|
368
|
+
Cons(frame, suspension.frames), suspension.cont);
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
handle: function(prompt, s) {
|
|
372
|
+
return handleOrRethrow(prompt, s, Nil)
|
|
373
|
+
},
|
|
374
|
+
|
|
375
|
+
freshRegion: function() {
|
|
376
|
+
return enterRegion(new Arena)
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
leaveRegion: leaveRegion,
|
|
380
|
+
|
|
381
|
+
global: global
|
|
382
|
+
}
|
|
383
|
+
})()
|
|
384
|
+
|
|
385
|
+
Object.assign($effekt, $runtime);
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Maps the error code to a Effekt-stable (platform independent) numeric value.
|
|
4
|
+
*
|
|
5
|
+
* Tries to use most common errno integer values, but introduces fresh values (> 200)
|
|
6
|
+
* for those without common errno values.
|
|
7
|
+
*/
|
|
8
|
+
function errnoToStableId(errorName) {
|
|
9
|
+
const errorMap = {
|
|
10
|
+
EPERM: 1,
|
|
11
|
+
ENOENT: 2,
|
|
12
|
+
ESRCH: 3,
|
|
13
|
+
EINTR: 4,
|
|
14
|
+
EIO: 5,
|
|
15
|
+
ENXIO: 6,
|
|
16
|
+
E2BIG: 7,
|
|
17
|
+
EBADF: 9,
|
|
18
|
+
EAGAIN: 11,
|
|
19
|
+
ENOMEM: 12,
|
|
20
|
+
EACCES: 13,
|
|
21
|
+
EFAULT: 14,
|
|
22
|
+
EBUSY: 16,
|
|
23
|
+
EEXIST: 17,
|
|
24
|
+
EXDEV: 18,
|
|
25
|
+
ENODEV: 19,
|
|
26
|
+
ENOTDIR: 20,
|
|
27
|
+
EISDIR: 21,
|
|
28
|
+
EINVAL: 22,
|
|
29
|
+
ENFILE: 23,
|
|
30
|
+
EMFILE: 24,
|
|
31
|
+
ENOTTY: 25,
|
|
32
|
+
ETXTBSY: 26,
|
|
33
|
+
EFBIG: 27,
|
|
34
|
+
ENOSPC: 28,
|
|
35
|
+
ESPIPE: 29,
|
|
36
|
+
EROFS: 30,
|
|
37
|
+
EMLINK: 31,
|
|
38
|
+
EPIPE: 32,
|
|
39
|
+
ERANGE: 34,
|
|
40
|
+
ENAMETOOLONG: 36,
|
|
41
|
+
ELOOP: 40,
|
|
42
|
+
EOVERFLOW: 75,
|
|
43
|
+
EFTYPE: 79,
|
|
44
|
+
EILSEQ: 84,
|
|
45
|
+
ENOTSOCK: 88,
|
|
46
|
+
EDESTADDRREQ: 89,
|
|
47
|
+
EMSGSIZE: 90,
|
|
48
|
+
EPROTOTYPE: 91,
|
|
49
|
+
ENOPROTOOPT: 92,
|
|
50
|
+
EPROTONOSUPPORT: 93,
|
|
51
|
+
ESOCKTNOSUPPORT: 94,
|
|
52
|
+
ENOTSUP: 95,
|
|
53
|
+
EAFNOSUPPORT: 97,
|
|
54
|
+
EADDRINUSE: 98,
|
|
55
|
+
EADDRNOTAVAIL: 99,
|
|
56
|
+
ENETDOWN: 100,
|
|
57
|
+
ENETUNREACH: 101,
|
|
58
|
+
ECONNABORTED: 103,
|
|
59
|
+
ECONNRESET: 104,
|
|
60
|
+
ENOBUFS: 105,
|
|
61
|
+
EISCONN: 106,
|
|
62
|
+
ENOTCONN: 107,
|
|
63
|
+
ETIMEDOUT: 110,
|
|
64
|
+
ECONNREFUSED: 111,
|
|
65
|
+
EHOSTUNREACH: 113,
|
|
66
|
+
EALREADY: 114,
|
|
67
|
+
ECANCELED: 125,
|
|
68
|
+
EAI_ADDRFAMILY: 200,
|
|
69
|
+
EAI_AGAIN: 201,
|
|
70
|
+
EAI_BADFLAGS: 202,
|
|
71
|
+
EAI_BADHINTS: 203,
|
|
72
|
+
EAI_CANCELED: 204,
|
|
73
|
+
EAI_FAIL: 205,
|
|
74
|
+
EAI_FAMILY: 206,
|
|
75
|
+
EAI_MEMORY: 207,
|
|
76
|
+
EAI_NODATA: 208,
|
|
77
|
+
EAI_NONAME: 209,
|
|
78
|
+
EAI_OVERFLOW: 210,
|
|
79
|
+
EAI_PROTOCOL: 211,
|
|
80
|
+
EAI_SERVICE: 212,
|
|
81
|
+
EAI_SOCKTYPE: 213,
|
|
82
|
+
ECHARSET: 215,
|
|
83
|
+
ENONET: 216,
|
|
84
|
+
UNKNOWN: 217,
|
|
85
|
+
EOF: 218,
|
|
86
|
+
EUNATCH: 219,
|
|
87
|
+
ESHUTDOWN: 220
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return errorMap[errorName] || -1; // Default to -1 for unknown error names
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function openFile(path, mode, onSuccess, onFailure) {
|
|
94
|
+
fs.open(path, mode,
|
|
95
|
+
(err, fd) => {
|
|
96
|
+
if (!!err) {
|
|
97
|
+
console.error(err)
|
|
98
|
+
return onFailure(errnoToStableId(err.code)).run();
|
|
99
|
+
} else {
|
|
100
|
+
return onSuccess(fd).run();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return $effekt.unit;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function readFile(fd, buffer, offset, onSuccess, onFailure) {
|
|
107
|
+
let position = offset === -1 ? null : offset;
|
|
108
|
+
fs.read(fd, toBuffer(buffer), 0, buffer.length, position,
|
|
109
|
+
(err, bytesRead) => {
|
|
110
|
+
if (!!err) {
|
|
111
|
+
return onFailure(errnoToStableId(err.code)).run()
|
|
112
|
+
} else {
|
|
113
|
+
return onSuccess(bytesRead).run()
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
return $effekt.unit;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @see {https://nodejs.org/api/fs.html#fswritefd-buffer-offset-length-position-callback
|
|
121
|
+
*/
|
|
122
|
+
function writeFile(fd, buffer, offset, onSuccess, onFailure) {
|
|
123
|
+
let position = offset === -1 ? null : offset;
|
|
124
|
+
fs.write(fd, toBuffer(buffer), 0, buffer.length, position,
|
|
125
|
+
(err, bytesWritten) => {
|
|
126
|
+
if (!!err) {
|
|
127
|
+
return onFailure(errnoToStableId(err.code)).run()
|
|
128
|
+
} else {
|
|
129
|
+
return onSuccess(bytesWritten).run()
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
return $effekt.unit;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Nodejs file operations expect buffers, but we represent buffers as typed arrays.
|
|
139
|
+
* This function converts between the two without copying.
|
|
140
|
+
*/
|
|
141
|
+
function toBuffer(buffer) {
|
|
142
|
+
return Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength)
|
|
143
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module mutable/map
|
|
2
|
+
|
|
3
|
+
import array
|
|
4
|
+
|
|
5
|
+
// A mutable map, backed by a JavaScript Map.
|
|
6
|
+
extern type Map[K, V]
|
|
7
|
+
|
|
8
|
+
extern pure def emptyMap[K, V](): Map[K, V] =
|
|
9
|
+
js "new Map()"
|
|
10
|
+
|
|
11
|
+
def get[K, V](m: Map[K, V], key: K): Option[V] =
|
|
12
|
+
undefinedToOption(m.unsafeGet(key))
|
|
13
|
+
|
|
14
|
+
extern pure def unsafeGet[K, V](m: Map[K, V], key: K): V =
|
|
15
|
+
js "${m}.get(${key})"
|
|
16
|
+
|
|
17
|
+
extern js """
|
|
18
|
+
function map$update(m, key, value) {
|
|
19
|
+
m.set(key, value);
|
|
20
|
+
return $effekt.unit
|
|
21
|
+
}
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
extern io def update[K, V](m: Map[K, V], key: K, value: V): Unit =
|
|
25
|
+
js "map$update(${m}, ${key}, ${value})"
|
|
26
|
+
|
|
27
|
+
extern pure def values[K, V](map: Map[K, V]): Array[V] =
|
|
28
|
+
js "Array.from(${map}.values())"
|
|
29
|
+
|
|
30
|
+
extern pure def keys[K, V](map: Map[K, V]): Array[K] =
|
|
31
|
+
js "Array.from(${map}.keys())"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module text/regex
|
|
2
|
+
|
|
3
|
+
import string
|
|
4
|
+
|
|
5
|
+
extern type Regex
|
|
6
|
+
|
|
7
|
+
record Match(matched: String, index: Int)
|
|
8
|
+
|
|
9
|
+
extern pure def regex(str: String): Regex =
|
|
10
|
+
js "new RegExp(${str})"
|
|
11
|
+
|
|
12
|
+
def exec(reg: Regex, str: String): Option[Match] =
|
|
13
|
+
undefinedToOption(reg.unsafeExec(str)) match {
|
|
14
|
+
case None() => None()
|
|
15
|
+
case Some(v) => Some(Match(v.matched, v.index))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// internal representation { matched: String, index: Int }
|
|
19
|
+
extern type RegexMatch
|
|
20
|
+
extern pure def matched(r: RegexMatch): String = js "${r}.matched"
|
|
21
|
+
extern pure def index(r: RegexMatch): Int = js "${r}.index"
|
|
22
|
+
|
|
23
|
+
extern js """
|
|
24
|
+
function regex$exec(reg, str) {
|
|
25
|
+
var res = reg.exec(str);
|
|
26
|
+
if (res === null) { return undefined }
|
|
27
|
+
else { return { matched: res[0], index: res.index } }
|
|
28
|
+
}
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
// internals
|
|
32
|
+
extern io def unsafeExec(reg: Regex, str: String): RegexMatch =
|
|
33
|
+
js "regex$exec(${reg}, ${str})"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module unsafe/cont
|
|
2
|
+
|
|
3
|
+
// The type of a captured continuation
|
|
4
|
+
extern type Cont[A, B]
|
|
5
|
+
|
|
6
|
+
// we mark this as {io} since pure functions should not take block parameters.
|
|
7
|
+
extern io def cont[A, B] { r: A => B / {} }: Cont[A, B] = js "${box r}"
|
|
8
|
+
|
|
9
|
+
// This is unsafe if the continuation left the scope of the
|
|
10
|
+
// capabilities it closed over
|
|
11
|
+
extern control def apply[A, B](k: Cont[A, B], a: A): B / {} = js "${k}(${a})"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module web/dom
|
|
2
|
+
|
|
3
|
+
extern type Node
|
|
4
|
+
|
|
5
|
+
// can be null
|
|
6
|
+
extern pure def getElementById(id: String): Node =
|
|
7
|
+
js "document.getElementById(${id})"
|
|
8
|
+
|
|
9
|
+
extern pure def createElement(tag: String): Node =
|
|
10
|
+
js "document.createElement(${tag})"
|
|
11
|
+
|
|
12
|
+
extern pure def createTextNode(text: String): Node =
|
|
13
|
+
js "document.createTextNode(${text})"
|
|
14
|
+
|
|
15
|
+
extern pure def setAttribute(node: Node, key: String, value: String): Node =
|
|
16
|
+
js "${node}.setAttribute(${key}, ${value})"
|
|
17
|
+
|
|
18
|
+
// the string return type is wrong here...
|
|
19
|
+
extern pure def getAttribute(node: Node, key: String): String =
|
|
20
|
+
js "${node}.getAttribute(${key})"
|
|
21
|
+
|
|
22
|
+
extern pure def appendChild(node: Node, child: Node): Node =
|
|
23
|
+
js "${node}.appendChild(${child})"
|
|
24
|
+
|
|
25
|
+
extern pure def innerHTML(node: Node, contents: String): Node =
|
|
26
|
+
js "(function() { ${node}.innerHTML = ${contents}; return ${node} })()"
|
|
27
|
+
|
|
28
|
+
extern type IdleDeadline
|
|
29
|
+
|
|
30
|
+
extern pure def requestIdleCallback(callback: (IdleDeadline) => Unit at {io}): Unit =
|
|
31
|
+
js "window.requestIdleCallback(deadline => ${callback}(deadline).run())"
|
|
32
|
+
|
|
33
|
+
extern pure def didDimeout(deadline: IdleDeadline): Bool =
|
|
34
|
+
js "${deadline}.didTimeout"
|
|
35
|
+
|
|
36
|
+
extern pure def timeRemaining(deadline: IdleDeadline): Double =
|
|
37
|
+
js "${deadline}.timeRemaining()"
|
|
38
|
+
|
|
39
|
+
extern pure def onClick(node: Node, handler: () => Unit at {io}): Node =
|
|
40
|
+
js "(function() { ${node}.onclick = () => ${handler}().run(); return ${node} })()"
|
|
41
|
+
|
|
42
|
+
extern def sleep(n: Int): Unit =
|
|
43
|
+
js "$effekt.callcc(k => window.setTimeout(() => k(null), ${n}))"
|
|
44
|
+
|
|
45
|
+
def clear(node: Node) = node.innerHTML("")
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#ifndef EFFEKT_ARRAY_C
|
|
2
|
+
#define EFFEKT_ARRAY_C
|
|
3
|
+
|
|
4
|
+
/** We represent arrays like positive types.
|
|
5
|
+
* The tag is 0 and the obj points to memory with the following layout:
|
|
6
|
+
*
|
|
7
|
+
* +--[ Header ]--+------+------------+
|
|
8
|
+
* | Rc | Eraser | Size | Fields ... |
|
|
9
|
+
* +--------------+------+------------+
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
void c_array_erase_fields(void *envPtr) {
|
|
13
|
+
uint64_t *sizePtr = envPtr;
|
|
14
|
+
struct Pos *dataPtr = envPtr + sizeof(uint64_t);
|
|
15
|
+
uint64_t size = *sizePtr;
|
|
16
|
+
for (uint64_t i = 0; i < size; i++) {
|
|
17
|
+
erasePositive(dataPtr[i]);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
struct Pos c_array_new(const Int size) {
|
|
22
|
+
void *objPtr = calloc(sizeof(struct Header) + sizeof(uint64_t) + size * sizeof(struct Pos), 1);
|
|
23
|
+
struct Header *headerPtr = objPtr;
|
|
24
|
+
uint64_t *sizePtr = objPtr + sizeof(struct Header);
|
|
25
|
+
*headerPtr = (struct Header) { .rc = 0, .eraser = c_array_erase_fields, };
|
|
26
|
+
*sizePtr = size;
|
|
27
|
+
return (struct Pos) {
|
|
28
|
+
.tag = 0,
|
|
29
|
+
.obj = objPtr,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Int c_array_size(const struct Pos arr) {
|
|
34
|
+
uint64_t *sizePtr = arr.obj + sizeof(struct Header);
|
|
35
|
+
return *sizePtr;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
struct Pos c_array_get(const struct Pos arr, const Int index) {
|
|
39
|
+
struct Pos *dataPtr = arr.obj + sizeof(struct Header) + sizeof(uint64_t);
|
|
40
|
+
struct Pos element = dataPtr[index];
|
|
41
|
+
sharePositive(element);
|
|
42
|
+
return element;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
struct Pos c_array_set(const struct Pos arr, const Int index, const struct Pos value) {
|
|
46
|
+
struct Pos *dataPtr = arr.obj + sizeof(struct Header) + sizeof(uint64_t);
|
|
47
|
+
struct Pos element = dataPtr[index];
|
|
48
|
+
erasePositive(element);
|
|
49
|
+
dataPtr[index] = value;
|
|
50
|
+
return Unit;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#endif
|