|
1 | 1 | local create = coroutine.create
|
2 |
| -local resume = coroutine.resume |
3 | 2 | local yield = coroutine.yield
|
4 |
| -local unpack0 = table.unpack or unpack |
5 | 3 |
|
6 |
| -local unpack = function(t) |
7 |
| - if t and #t > 0 then |
8 |
| - return unpack0(t) |
| 4 | +local resume = function(co, a) |
| 5 | + local st, r = coroutine.resume(co, a) |
| 6 | + |
| 7 | + if not st then |
| 8 | + return error(r) |
| 9 | + else |
| 10 | + return r |
9 | 11 | end
|
10 | 12 | end
|
11 | 13 |
|
12 |
| -local inst do |
13 |
| - local cls = ("Eff: %s"):format(tostring(v):match('0x[0-f]+')) |
14 |
| - |
15 |
| - inst = setmetatable({ cls = cls }, { |
16 |
| - __call = function(self) |
17 |
| - local eff = ("instance: %s"):format(tostring{}:match('0x[0-f]+')) |
18 |
| - return { eff = eff, cls = self.cls} |
19 |
| - end |
20 |
| - }) |
| 14 | +local inst = function() |
| 15 | + return {} |
21 | 16 | end
|
22 | 17 |
|
23 |
| -local perform = function(eff, arg) |
24 |
| - return yield { cls = eff.cls, eff = eff.eff, arg = arg } |
| 18 | +-- Call : ('arg, 'res) operation * 'arg * ('res -> 'a computation) -> 'a computation |
| 19 | +local callT = "call" |
| 20 | +local call = function(op, x, k) |
| 21 | + return { type = callT, op = op, x = x, k = k } |
25 | 22 | end
|
26 | 23 |
|
27 |
| -local show_error = function(eff) |
28 |
| - return function() |
29 |
| - return ("uncaught effect `%s'"):format(eff) |
30 |
| - end |
31 |
| -end |
| 24 | +local throwT = { |
| 25 | + perform = false, |
| 26 | + resend = true |
| 27 | +} |
32 | 28 |
|
33 |
| -local Resend do |
34 |
| - local cls = ("Resend: %s"):format(tostring(v):match('0x[0-f]+')) |
| 29 | +local perform = function(op, arg) |
| 30 | + local current = coroutine.running() |
35 | 31 |
|
36 |
| - Resend = setmetatable({ cls = cls }, { |
37 |
| - __call = function(self, effobj, continue) |
38 |
| - return yield { eff = effobj.eff, arg = effobj.arg, continue = continue, cls = self.cls } |
39 |
| - end |
40 |
| - }) |
41 |
| -end |
| 32 | + local k = function(a) |
| 33 | + return resume(current, a) |
| 34 | + end |
42 | 35 |
|
43 |
| -local is_eff_obj = function(obj) |
44 |
| - return type(obj) == "table" and (obj.cls == inst.cls or obj.cls == Resend.cls) |
| 36 | + return yield(call(op, {arg, type = throwT.perform}, k) ) |
45 | 37 | end
|
46 | 38 |
|
47 |
| -local function handle_error_message(r) |
48 |
| - if type(r) == "string" and |
49 |
| - (r:match("attempt to yield from outside a coroutine") |
50 |
| - or r:match("cannot resume dead coroutine")) |
51 |
| - then |
52 |
| - return error("continuation cannot be performed twice") |
53 |
| - else |
54 |
| - return error(r) |
55 |
| - end |
| 39 | + |
| 40 | +local resend = function(op, arg, k) |
| 41 | + return yield(call(op, {arg, type = throwT.resend}, k) ) |
56 | 42 | end
|
57 | 43 |
|
58 |
| -local gen_continue = function(co, handle) |
59 |
| - return function(arg) |
60 |
| - local st, r = resume(co, arg) |
61 |
| - if not st then |
62 |
| - return handle_error_message(r) |
63 |
| - else |
64 |
| - return handle(r) |
65 |
| - end |
66 |
| - end |
| 44 | +local is_eff_obj = function(obj) |
| 45 | + return type(obj) == "table" and (obj.type == callT) |
67 | 46 | end
|
68 | 47 |
|
69 | 48 | local handler
|
70 |
| -handler = function(eff, vh, effh) |
71 |
| - local eff_type = eff.eff |
72 |
| - |
| 49 | +handler = function(op, vh, effh) |
73 | 50 | return function(th)
|
74 | 51 | local co = create(th)
|
75 | 52 |
|
76 | 53 | local handle
|
77 |
| - local continue |
78 |
| - |
79 |
| - local rehandle = function(k) |
80 |
| - return function(arg) |
81 |
| - return handler(eff, continue, effh)(function() |
82 |
| - return k(arg) |
83 |
| - end) |
| 54 | + local handler_ do |
| 55 | + local vh_ = function(arg) |
| 56 | + return handle(resume(co, arg)) |
84 | 57 | end
|
| 58 | + |
| 59 | + handler_ = handler(op, vh_, effh) |
85 | 60 | end
|
86 | 61 |
|
87 | 62 | handle = function(r)
|
88 | 63 | if not is_eff_obj(r) then
|
89 | 64 | return vh(r)
|
90 | 65 | end
|
91 | 66 |
|
92 |
| - if r.cls == inst.cls then |
93 |
| - if r.eff == eff_type then |
94 |
| - return effh(r.arg, continue) |
| 67 | + if r.type == callT then |
| 68 | + local resended = r.x.type |
| 69 | + local k |
| 70 | + |
| 71 | + if resended then |
| 72 | + k = function(arg) |
| 73 | + return handler_(function() |
| 74 | + return r.k(arg) |
| 75 | + end) |
| 76 | + end |
95 | 77 | else
|
96 |
| - return Resend(r, function(arg) |
97 |
| - return continue(arg) |
98 |
| - end) |
| 78 | + k = function(arg) |
| 79 | + return handle(r.k(arg)) |
| 80 | + end |
99 | 81 | end
|
100 |
| - elseif r.cls == Resend.cls then |
101 |
| - if r.eff == eff_type then |
102 |
| - return effh(r.arg, rehandle(r.continue)) |
| 82 | + |
| 83 | + local arg = r.x[1] |
| 84 | + if r.op == op then |
| 85 | + return effh(arg, k) |
103 | 86 | else
|
104 |
| - return Resend(r, rehandle(r.continue)) |
| 87 | + return resend(r.op, arg, k) |
105 | 88 | end
|
106 | 89 | end
|
107 | 90 | end
|
108 | 91 |
|
109 |
| - continue = function(arg) |
110 |
| - local st, r = resume(co, arg) |
111 |
| - if not st then |
112 |
| - return handle_error_message(r) |
113 |
| - else |
114 |
| - return handle(r) |
115 |
| - end |
116 |
| - end |
117 |
| - |
118 |
| - return continue(nil) |
| 92 | + return handle(resume(co, nil)) |
119 | 93 | end
|
120 | 94 | end
|
121 | 95 |
|
|
0 commit comments