1 | kit = require "./kit"
|
2 | config = require "./config"
|
3 | root = module.exports = {}
|
4 | assert = require "assert"
|
5 |
|
6 | CAPTURE_SET = true
|
7 |
|
8 |
|
9 | class VarUsage
|
10 | constructor: ->
|
11 | @sets = {}
|
12 | @upds = {}
|
13 | @mods = {}
|
14 | @uses = {}
|
15 | @after = {}
|
16 | @before = {}
|
17 | @vars = {}
|
18 |
|
19 | init: ->
|
20 | deps = {}
|
21 | for i, v of @vars when v and not @refs[i]
|
22 | w = deps[i] = {}
|
23 | w.fin = true unless @after[i]
|
24 | w.use = true if @uses[i]
|
25 | if @sets[i]
|
26 | if @before[i]
|
27 | w.mod = true
|
28 | else
|
29 | w.set = true
|
30 | if @upds[i]
|
31 | w.use = w.mod = true
|
32 | deps
|
33 |
|
34 | setBefore: (known) ->
|
35 | for i of known
|
36 | @before[i] = true
|
37 | if @sets[i]
|
38 | @sets[i] = false
|
39 | @upds[i] = true
|
40 |
|
41 | addAssign: (name) ->
|
42 | if @uses[name]
|
43 | @upds[name] = true
|
44 | else
|
45 | @sets[name] = true
|
46 | @mods[name] = true
|
47 | @mod = true
|
48 | @
|
49 |
|
50 | addUpd: (name) ->
|
51 | @upds[name] = true
|
52 | @uses[name] = true
|
53 | @mods[name] = true
|
54 | @mod = true
|
55 | @upd = true
|
56 | @
|
57 | skipMods: -> @_skipMods = true; @
|
58 |
|
59 |
|
60 | addInner: (d) ->
|
61 | @mod = true if d.mod
|
62 | @upd = true if d.upd
|
63 | unless @_skipMods
|
64 | @mods[i] = true for i,v of d.mods when v
|
65 | @uses[i] = true for i,v of d.uses when v
|
66 | return
|
67 |
|
68 |
|
69 | addInnerDeep: (d) ->
|
70 | @addInner(d)
|
71 | @mod = true if d.mod
|
72 | @upd = true if d.upd
|
73 | @upds[i] = true for i,v of d.upds when v
|
74 | @uses[i] = true for i,v of d.uses when v
|
75 | for i,v of d.sets when v
|
76 | if @uses[i]
|
77 | @upds[i] = true
|
78 | @upd = true
|
79 | else
|
80 | @sets[i] = true
|
81 | return
|
82 | threadOutMap: (res) ->
|
83 | res ?= {}
|
84 | for i, v of @mods when v
|
85 | continue unless @after[i]
|
86 | continue unless @vars[i] and not @refs[i]
|
87 | res[i] = true
|
88 | res
|
89 |
|
90 | envPush = (dst, src) ->
|
91 | for i, v of src
|
92 | w = dst[i] ?= {}
|
93 | w.mod = true if v.mod or v.set and w.use
|
94 | w.use = true if v.use
|
95 | w.set = true if v.set and not w.mod
|
96 | w.fin = true if v.fin
|
97 | w.thread = true if v.thread and not v.fin
|
98 | dst
|
99 |
|
100 | envMerge = (dst, src) ->
|
101 | for i, v of src
|
102 | w = dst[i] ?= {}
|
103 | w.use = true if v.use
|
104 | w.mod = true if v.mod
|
105 | w.fin = true if v.fin
|
106 | w.thread = true if v.thread and not v.fin
|
107 | dst
|
108 |
|
109 | envThread = (env) ->
|
110 | (i for i, v of env when v.thread and not v.fin).sort().map(kit.id)
|
111 | envBefore = (f, s) ->
|
112 | for i, v of s when (w = f[i]) and w.use
|
113 | v.use = true
|
114 | return
|
115 |
|
116 | root.VarUsage = VarUsage
|
117 |
|
118 | $debId = 0
|
119 |
|
120 |
|
121 |
|
122 | class Builder
|
123 | constructor: ->
|
124 | @env = {}
|
125 | @opts = {}
|
126 | @$debId = $debId++
|
127 | setOpts: (opts) ->
|
128 | o = @opts
|
129 | if o?
|
130 | o[i] = v for i,v of opts when not o[i]?
|
131 | else
|
132 | @opts = opts
|
133 | @
|
134 |
|
135 | needCoerce: (v) ->
|
136 | v ?= true
|
137 | @_needCoerce = v
|
138 | @
|
139 |
|
140 | coerceObj: ->
|
141 | return @ unless @_needCoerce
|
142 | @toExprBuilder()._coerceObj()
|
143 | _coerceObj: -> @
|
144 |
|
145 | noCoerceTry: -> @_noCoerceTry = true; @
|
146 |
|
147 | coerceTry: ->
|
148 | return @ if @_noCoerceTry and @opts.coerce is "all"
|
149 | b = @capture().toBlockBuilder()
|
150 | root.expr(kit.coerceThunk(b.block), b.eff).morph(b).noCoerceTry()
|
151 |
|
152 | setBindVar: (bv) ->
|
153 | if bv is false
|
154 | delete @_bindvar
|
155 | return @
|
156 | @_bindVar = bv if bv?
|
157 | @
|
158 | getBindVar: -> @_bindVar
|
159 |
|
160 | noCapture: -> @_captured = true; @
|
161 |
|
162 | capture: ->
|
163 | return @ if @_captured
|
164 | @_captured = true
|
165 | return @_capture()
|
166 | _capture: -> @
|
167 |
|
168 | morph: (other,env) ->
|
169 | @setBindVar(other.getBindVar())
|
170 | assert.ok(other.opts)
|
171 | @opts = other.opts
|
172 | @_captured ?= other._captured
|
173 | env = other.env unless env?
|
174 | envPush(@env,env) if env
|
175 | brk = other.isBrk()
|
176 | @setBrk(brk) if brk?
|
177 | @_needCoerce = other._needCoerce if not @_needCoerce?
|
178 | @
|
179 |
|
180 |
|
181 | toBlockBuilder: (eff) ->
|
182 | n = @toExprBuilder(eff)
|
183 | block = [kit.ret(n.expr)]
|
184 | (if n.eff then root.blockEff(block) else root.pureBlock(block)).morph(n)
|
185 |
|
186 |
|
187 | toExprBuilder: (eff) ->
|
188 | assert.ok(@toBlockBuilder isnt Builder::toBlockBuilder)
|
189 | n = @toBlockBuilder(eff)
|
190 | expr = kit.call(kit.fun([], n.block),[])
|
191 | (if n.eff then root.exprEff(expr) else root.exprPure(expr)).morph(n)
|
192 |
|
193 | toBlock: (eff) -> @capture().toBlockBuilder(eff).block
|
194 |
|
195 | toExpr: (eff) -> @capture().toExprBuilder(eff).expr
|
196 |
|
197 | pushEnv: (env) -> envPush(@env, env); @
|
198 |
|
199 | mergeEnv: (env) ->
|
200 | envPush(@env, env.init())
|
201 | @
|
202 |
|
203 |
|
204 | bindTo: ->
|
205 | vars = []
|
206 | exprs = []
|
207 | eff = false
|
208 | assert.ok(arguments.length)
|
209 | eff or= i.eff for i in arguments
|
210 | env = {}
|
211 | threadVars = []
|
212 | captVars = {}
|
213 | threadIn = {}
|
214 | lhs = {}
|
215 | for i,x in arguments
|
216 | capt = i
|
217 | capt = capt.coerceObj() if arguments.length is 1
|
218 | capt = capt.capture().toExprBuilder(eff)
|
219 | tvars = []
|
220 | for j, v of capt.env
|
221 | w = env[j] ?= {}
|
222 | w.use = true if v.use
|
223 | w.mod = true if v.mod or w.use and v.set
|
224 | w.set = true if v.set and not w.mod
|
225 | w.fin = true if v.fin
|
226 | if v.thread and not v.fin
|
227 | w.thread = true
|
228 | threadIn[j] = true
|
229 | tvars.push j
|
230 | if tvars.length isnt 0
|
231 | assert.ok(arguments.length is 1)
|
232 | threadVars.push(tvars...)
|
233 | exprs.push(capt.expr)
|
234 | bv = capt.getBindVar()
|
235 | vars.push(bv) if bv?
|
236 | vars.push(tvars.sort().map(kit.id)...)
|
237 | threadOut = {}
|
238 | for i, v of env
|
239 | w = @env[i] ? {}
|
240 | threadOut[i] = true if not v.fin and (v.thread or v.mod or w.mod)
|
241 | thisCapt = @addThread(threadOut).capture()
|
242 | block = thisCapt.toBlock()
|
243 | thisCapt.setBrk(true) if @isBrk()
|
244 | closVars = []
|
245 | for i, v of thisCapt.env when v.mod
|
246 | v.mod = false
|
247 | closVars.push i unless threadIn[i]
|
248 | closVars = closVars.sort().map(kit.id)
|
249 | if closVars.length
|
250 | block = [kit.ret(kit.call(kit.fun(closVars,block),closVars))]
|
251 | for i,v of env when v.mod
|
252 | w = thisCapt.env[i] ?= {fin:true}
|
253 | w.use = w.mod = true
|
254 | cont = kit.fun(vars, block)
|
255 | return thisCapt if exprs.length is 0
|
256 | root.exprEffCaptured(thisCapt.mkMap(exprs,cont))
|
257 | .needCoerce(false).morph(thisCapt)
|
258 |
|
259 |
|
260 | setBrk: (@brk) -> @
|
261 |
|
262 | isBrk: -> @brk
|
263 |
|
264 | addThread: (thread) ->
|
265 | if @isBrk()
|
266 | for i, v of thread
|
267 | (w = @env[i] ?= {fin:true}).thread = true
|
268 | @
|
269 | unless @_captured
|
270 | for i, v of thread when v
|
271 | (w = @env[i] ?= {fin:true}).thread = true
|
272 | return @
|
273 | env = {}
|
274 | if thread?
|
275 | for i,v of thread when v
|
276 | w = @env[i]
|
277 | continue if w? and w.fin
|
278 | (env[i] = {}).thread = true
|
279 | any = true if w? and not w.thread
|
280 | for i, v of @env when not v.fin
|
281 | w = env[i] ?= {}
|
282 | if v.thread and not thread[i]
|
283 | some = true
|
284 | w.thread = true
|
285 | return @ unless any
|
286 | root.purePostfix(@,root.pure([]).pushEnv(env)).morph(@)
|
287 |
|
288 |
|
289 | class BlockPure extends Builder
|
290 | constructor: (@block) -> super()
|
291 | mkMap: (args, fun) ->
|
292 | kit.mapply(args, fun)
|
293 | pureBlock: -> @block
|
294 | toPrefix: -> @
|
295 | toBlockBuilder: (eff) ->
|
296 | {block} = @
|
297 | expr = @ret
|
298 | nocoerce = @opts.coerce is "none"
|
299 | if expr?
|
300 | expr = kit.pure(expr) if eff and nocoerce
|
301 | block.push(kit.ret(expr))
|
302 | return @needCoerce(not nocoerce) unless eff
|
303 | unless expr
|
304 | block.push(kit.ret(kit.pure())) if nocoerce
|
305 | root.blockEff(block).morph(@).needCoerce(not nocoerce)
|
306 | prepend: (other) ->
|
307 | (if @opts.bindAssoc is "left"
|
308 | root.postfixLeft(other,@)
|
309 | else root.purePostfix(other,@)).setOpts(@opts)
|
310 | _append: (other) ->
|
311 | if other.pureBlock?
|
312 | @block.push(other.pureBlock()...)
|
313 | envPush(@env,other.env)
|
314 | @setBindVar(other._bindVar)
|
315 | return @
|
316 | root.purePrefix(@,other).setOpts(@opts)
|
317 | append: (other) -> @_append(other)
|
318 | coerceTry: ->
|
319 | w = @opts.coerce
|
320 | if w isnt "all"
|
321 | return @
|
322 | return root.exprEff(kit.coerceThunk(@block)).morph(@)
|
323 | _capture: ->
|
324 | ov = envThread(@env)
|
325 | if ov.length
|
326 | assert.ok(not @ret)
|
327 | @ret = if ov.length is 1 then ov[0] else kit.arr(ov)
|
328 | return @
|
329 |
|
330 | root.pure = (block) -> new BlockPure(block)
|
331 |
|
332 |
|
333 | class Empty extends BlockPure
|
334 | constructor: ->
|
335 | super([])
|
336 | append: (other) -> other
|
337 | prepend: (other) -> other
|
338 |
|
339 | root.empty = -> new Empty()
|
340 |
|
341 |
|
342 | class Eff extends Builder
|
343 | constructor: -> super()
|
344 | eff: true
|
345 | mkMap: (args, fun) ->
|
346 | kit.mbind args, fun
|
347 | append: (other) ->
|
348 | envBefore(@env,other.env)
|
349 | if other.prepend?
|
350 | res = other.prepend(@)
|
351 | return res if res?
|
352 | @_append other
|
353 | _append: (other) ->
|
354 | if not @_assocDone && @opts.bindAssoc is "left" and other.eff
|
355 | @_assocDone = true
|
356 | return root.postfixLeft(@,other).setOpts(other.opts)
|
357 | other.bindTo(@)
|
358 | _capture: ->
|
359 | ov = envThread(@env)
|
360 | if ov.length
|
361 | if @getBindVar()?
|
362 | fn = "munshiftTo"
|
363 | val = kit.arr(ov)
|
364 | else
|
365 | fn = "mconst"
|
366 | val = if ov.length is 1 then ov[0] else kit.arr(ov)
|
367 | return root.exprEff(kit.call(
|
368 | kit.mem(@coerceObj().toExpr(),kit.id(fn)),[val])).morph(@).noCapture()
|
369 | return @
|
370 |
|
371 |
|
372 |
|
373 | class Cont extends Eff
|
374 | constructor: (@inner, @cont, isFinal) ->
|
375 | super()
|
376 | @isFinal ?= isFinal
|
377 | addThread: (thread) ->
|
378 | @cont = @cont.addThread(thread)
|
379 | @
|
380 | setBindVar: (bv) -> @cont.setBindVar(bv); @
|
381 | setBrk: (bv) -> @cont.setBrk(bv); @
|
382 | isBrk: -> @cont.isBrk()
|
383 | result: ->
|
384 | @inner._append(@cont).morph(@)
|
385 | append: (other) ->
|
386 | @cont = @cont.append(other)
|
387 | if @isFinal() then @result() else @
|
388 | toExprBuilder: (eff) ->
|
389 | @result().toExprBuilder(eff)
|
390 | toBlockBuilder: (eff) ->
|
391 | @result().toBlockBuilder(eff)
|
392 | capture: ->
|
393 | return @result()
|
394 |
|
395 | root.cont = (inner, cont, isFinal) -> new Cont(inner, cont, isFinal)
|
396 |
|
397 |
|
398 |
|
399 | class Postfix extends Cont
|
400 | constructor: (inner, cont, @left) ->
|
401 | super(inner,cont)
|
402 | isFinal: -> not @left and @cont.eff
|
403 |
|
404 | root.purePostfix = (inner, cont) -> new Postfix(inner, cont)
|
405 | root.postfixLeft = (inner, cont) -> new Postfix(inner, cont, true)
|
406 |
|
407 |
|
408 | class Par extends Eff
|
409 | constructor: (@args, @cont) ->
|
410 | super()
|
411 | setBindVar: (bv) -> @cont.setBindVar(bv); @
|
412 | addThread: (thread) ->
|
413 | return @ if @isBrk()
|
414 | @cont = @cont.addThread(thread)
|
415 | @
|
416 | setBrk: (bv) -> @cont.setBrk(bv); @
|
417 | isBrk: -> @cont.isBrk()
|
418 | append: (other) ->
|
419 | if @cont.eff
|
420 | return super(other)
|
421 | @cont = @cont.append(other)
|
422 | @
|
423 | getDepVars: ->
|
424 | @vars
|
425 | toExprBuilder: ->
|
426 | @cont.bindTo(@args...).morph(@).toExprBuilder()
|
427 | capture: -> @toExprBuilder()
|
428 |
|
429 | root.par = (vars, args, cont) -> new Par(vars, args, cont)
|
430 |
|
431 |
|
432 | class PurePrefix extends Eff
|
433 | constructor: (@prefix, @cont) ->
|
434 | super()
|
435 | assert.ok(@cont.eff)
|
436 | assert.ok(not @prefix.eff)
|
437 | return
|
438 | addThread: (thread) ->
|
439 | return @ if @isBrk()
|
440 | if @last?
|
441 | @last = @last.addThread(thread)
|
442 | else
|
443 | @cont = @cont.addThread(thread)
|
444 | @
|
445 | setBindVar: (bv) -> (@last ? @cont).setBindVar(bv); @
|
446 | setBrk: (bv) -> (@last ? @cont).setBrk(bv); @
|
447 | isBrk: -> (@last ? @cont).isBrk()
|
448 | prepend: (other) ->
|
449 | unless other.eff
|
450 | @prefix = other.append(@prefix)
|
451 | return @
|
452 | @.bindTo(other)
|
453 | append: (other) ->
|
454 | if @last
|
455 | @cont = @cont.append(@last)
|
456 | @last = other
|
457 | @
|
458 | toBlockBuilder: -> @capture()
|
459 | capture: ->
|
460 | if @last
|
461 | last = @last
|
462 | else
|
463 | last = @cont
|
464 | threadOut = {}
|
465 | for i, v of @prefix.env when v.mod and not v.fin
|
466 | threadOut[i] = true
|
467 | last = last.addThread(threadOut)
|
468 | cont = last.capture()
|
469 | if @last
|
470 | bv = cont._bindVar
|
471 | cont = @cont.append(cont).capture()
|
472 | env = {}
|
473 | envMerge(env = {},@prefix.env)
|
474 | envMerge(env,cont.env)
|
475 | pblock = @prefix.toPrefix()
|
476 | cblock = cont.toBlockBuilder()
|
477 | for i,v of cblock.env when v.thread
|
478 | w = env[i] ?= {fin:true}
|
479 | w.thread = true
|
480 | root.blockEff(pblock.block.concat(cblock.block)).morph(@,env)
|
481 | .needCoerce(cblock._needCoerce)
|
482 | .setBindVar(cont.getBindVar()).noCapture().setBrk(@isBrk())
|
483 |
|
484 | root.purePrefix = (prefix, cont) -> new PurePrefix(prefix, cont)
|
485 |
|
486 |
|
487 | class BlockEff extends Eff
|
488 | constructor: (@block) -> super()
|
489 | toBlockBuilder: -> @
|
490 |
|
491 | root.blockEff = (block) ->
|
492 | new BlockEff(block)
|
493 |
|
494 | root.block = (eff, block) ->
|
495 | if eff then root.blockEff(block)
|
496 | else root.pure(block)
|
497 |
|
498 |
|
499 | class ExprEff extends Eff
|
500 | constructor: (@expr) ->
|
501 | super()
|
502 | _coerceObj: ->
|
503 | w = @opts.coerce
|
504 | return @ if w is "none"
|
505 | root.exprEff(kit.coerceVal(@expr)).morph(@)
|
506 | toExprBuilder: -> @
|
507 |
|
508 | root.exprEffCoerce = (expr) -> (new ExprEff(expr)).needCoerce()
|
509 | root.exprEff = (expr) -> (new ExprEff(expr)).needCoerce(false)
|
510 | root.exprEffCaptured = (expr) -> (new ExprEff(expr)).noCapture()
|
511 |
|
512 |
|
513 |
|
514 | class ExprPure extends BlockPure
|
515 | constructor: (@expr) ->
|
516 | super([kit.ret(@expr)])
|
517 | toExprBuilder: (eff) ->
|
518 | return super(eff) unless @expr
|
519 | return @ unless eff
|
520 | expr = if @opts.coerce is "none" then kit.pure(@expr) else @expr
|
521 | return root.exprEff(expr).morph(@)
|
522 | toBlockBuilder: (eff) ->
|
523 | res = root.pure([])
|
524 | res.ret = @expr
|
525 | res.morph(@).toBlockBuilder(eff)
|
526 | toPrefix: ->
|
527 | root.pure(
|
528 | [if @_bindVar?
|
529 | kit.varDecl(@_bindVar,@expr)
|
530 | else kit.exprStmt(@expr)]).morph(@).setBindVar(false)
|
531 | _capture: ->
|
532 | return super() unless @expr
|
533 | ov = envThread(@env)
|
534 | if ov.length
|
535 | @expr = kit.arr([@expr,ov...])
|
536 | @block = [kit.ret(@expr)]
|
537 | return @
|
538 |
|
539 | root.exprPure = (expr) -> new ExprPure(expr)
|
540 |
|
541 | root.expr = (expr, eff) ->
|
542 | if eff then root.exprEffCoerce(expr) else root.exprPure(expr)
|
543 |
|