1 | kit = require "./kit"
|
2 | {Scope} = require "./scope"
|
3 | builder = require "./builder"
|
4 | assert = require "assert"
|
5 | config = require "./config"
|
6 | {VarUsage} = builder
|
7 |
|
8 | module.exports = root = {}
|
9 |
|
10 |
|
11 | root.regNodeType = regNodeType = (n, ctr) ->
|
12 | root["#{n[0].toUpperCase()}#{n.substr(1)}"] = ctr
|
13 | Scope.prototype[n] = (args...) ->
|
14 | args.unshift null
|
15 | nctr = Function::bind.apply(ctr,args)
|
16 | nctr.name = ctr.name
|
17 | res = (new nctr()).init(@)
|
18 | res
|
19 |
|
20 | id = 0
|
21 |
|
22 |
|
23 | class Node
|
24 | constructor: ->
|
25 | @unwindBy = {}
|
26 | @id = id++
|
27 | @vdeps = new VarUsage()
|
28 |
|
29 | morphInit: (other) ->
|
30 | @setOrig(other.orig)
|
31 | @vdeps = other.vdeps
|
32 | @
|
33 |
|
34 | init: (ctx) ->
|
35 | @opts = kit.merge({},ctx.policy.opts)
|
36 | @ctx = ctx
|
37 | @vdeps.vars = ctx.vars
|
38 | @vdeps.refs = ctx.refs
|
39 | @
|
40 |
|
41 |
|
42 |
|
43 | setOrig: (orig) ->
|
44 | @orig = orig unless @orig?
|
45 | @
|
46 |
|
47 | setParent: (@parent) -> @
|
48 |
|
49 | getFullBlock: (eff) ->
|
50 | eff = eff and @opts.coerce is "none"
|
51 | @getBuilder().toBlock(eff)
|
52 |
|
53 | getFullExpr: (eff) ->
|
54 | eff = eff and @opts.coerce is "none"
|
55 | @getBuilder().toExpr(eff)
|
56 |
|
57 | fwdPass: (use) ->
|
58 | unless @_fwdPassDone
|
59 | @_fwdPassDone = true
|
60 | @vdeps.setBefore(use)
|
61 | @_fwdPass(use)
|
62 | kit.extend(use,@vdeps.uses)
|
63 | @parent.vdeps.addInner(@vdeps) if @parent?
|
64 | @
|
65 | _fwdPass: (use) ->
|
66 |
|
67 | backPass: (use) ->
|
68 | if @eff
|
69 | @propagateEff()
|
70 | kit.extend(@vdeps.after,use)
|
71 | @_backPass(use)
|
72 | kit.extend(use,@vdeps.uses)
|
73 | @
|
74 | _backPass: ->
|
75 |
|
76 |
|
77 | propagateEff: ->
|
78 | return @ if @_propagateEffDone
|
79 | @_propagateEff()
|
80 | _propagateEff: ->
|
81 | @_propagateEffDone = true
|
82 | @eff = true
|
83 | @ctx.eff = true
|
84 | @
|
85 |
|
86 | getBuilder: ->
|
87 | b = (do =>
|
88 | return builder.empty() if @isEmpty()
|
89 | unless @eff
|
90 | return builder.pure(kit.blockStmts(@orig)) if @orig
|
91 | @_getBuilder())
|
92 | b.setOpts(@opts).mergeEnv(@vdeps)
|
93 |
|
94 |
|
95 | ignoreResult: ->
|
96 | @ignoreResult = true
|
97 | @
|
98 |
|
99 | root.Node = Node
|
100 |
|
101 |
|
102 | class EffectfulNode extends Node
|
103 | constructor: -> super()
|
104 | eff: true
|
105 |
|
106 |
|
107 |
|
108 |
|
109 | class PureNode extends Node
|
110 | constructor: (@block) ->
|
111 | super()
|
112 | @block ?= []
|
113 | eff: false
|
114 | _getBuilder: -> builder.pure(@block)
|
115 | pureBlock: -> @block
|
116 |
|
117 |
|
118 | class PureExprNode extends PureNode
|
119 | constructor: (@pureExpr) ->
|
120 | super [kit.ret(@pureExpr)]
|
121 | setExpr: (@pureExpr) ->
|
122 | @block = [kit.ret(@pureExpr)]
|
123 | @
|
124 | _getBuilder: -> builder.exprPure(@pureExpr)
|
125 | ignoreResult: ->
|
126 | @ctx.pureNode([kit.exprStmt(@pureExpr)]).morphInit(@)
|
127 |
|
128 | regNodeType "pureNode", PureNode
|
129 | regNodeType "pureExprNode", PureExprNode
|
130 |
|
131 | Scope::exprNode = (e) ->
|
132 | (if @policy.opts.bind
|
133 | new EffValNode(e)
|
134 | else
|
135 | new PureExprNode(e)).init(@)
|
136 |
|
137 | Scope::pureExprNode = (e) ->
|
138 | new PureExprNode(e).init(@)
|
139 |
|
140 |
|
141 | class EmptyNode extends PureNode
|
142 | constructor: ->
|
143 | super([])
|
144 | @orig = {type:"EmptyStatement"}
|
145 | setOrig: -> @
|
146 | getBuilder: -> builder.empty().setOpts(@opts).mergeEnv(@vdeps)
|
147 |
|
148 | regNodeType "emptyNode", EmptyNode
|
149 |
|
150 |
|
151 | class DynNode extends Node
|
152 | constructor: ->
|
153 | super()
|
154 | _propagateEff: ->
|
155 | super()
|
156 | if @parent?
|
157 | @parent.propagateEff()
|
158 | @
|
159 |
|
160 |
|
161 | class ReifyNode extends Node
|
162 | constructor: ->
|
163 | super()
|
164 | eff: false
|
165 | setBody: (@body) ->
|
166 | @body.setParent(@)
|
167 | @
|
168 | setIsThunk: (@isThunk) -> @
|
169 | setVar: (@var) -> @
|
170 | _backPass: (use) ->
|
171 | @body.backPass(use)
|
172 | _fwdPass: (use) ->
|
173 | @body.fwdPass(use)
|
174 | _propagateEff: -> @
|
175 | getBuilder: ->
|
176 | e = if @isThunk
|
177 | @body.getBuilder().toExpr(true)
|
178 | else
|
179 | kit.fun([], @body.getBuilder().toBlock(true))
|
180 | expr = kit.call(kit.packId("reify"),[e])
|
181 | if @var
|
182 | return builder.pure([kit.varDecl(@var, expr)]).mergeEnv(@vdeps)
|
183 | return builder.exprPure(expr).mergeEnv(@vdeps)
|
184 |
|
185 | regNodeType "reifyNode", ReifyNode
|
186 |
|
187 |
|
188 | class BlockNode extends DynNode
|
189 | constructor: ->
|
190 | super()
|
191 | @block = []
|
192 | @placeholders = []
|
193 |
|
194 | append: (c...) ->
|
195 | for i,x in c when i? and not i.isEmpty()
|
196 | i.setParent(@)
|
197 | if i.eff
|
198 | @eff = true
|
199 | @block.push i
|
200 | @
|
201 | _getBuilder: ->
|
202 | cur = builder.empty()
|
203 | {block} = @
|
204 | for i,x in block
|
205 | cur = cur.append(i.getBuilder())
|
206 | cur
|
207 | addPlaceholder: (e, p) ->
|
208 | x = @block.indexOf e
|
209 | assert.ok(x >= 0)
|
210 | (@placeholders[x] ?= []).push p
|
211 | _fwdPass: (use) ->
|
212 | @placeholders = []
|
213 | ouse = kit.extend({},use)
|
214 | i.fwdPass(use) for i,x in @block
|
215 | if @placeholders.length
|
216 | c = @
|
217 | for i, x in @placeholders when i by -1
|
218 | b = c.block
|
219 | n = b.splice(0,x)
|
220 | nb = switch n.length
|
221 | when 0 then @ctx.emptyNode()
|
222 | when 1 then n[0]
|
223 | else c = @ctx.blockNode().append(n...).fwdPass(ouse)
|
224 | s = {}
|
225 | j.setBlock(nb,b,s) for j in i
|
226 | delete @placeholders
|
227 | _backPass: (use) ->
|
228 | eff = @eff
|
229 | unless eff
|
230 | for f in @block
|
231 | if f.eff
|
232 | eff = true
|
233 | break
|
234 | for i in @block by -1
|
235 | i.backPass(use)
|
236 | if eff
|
237 | @_propagateEff()
|
238 | @
|
239 | last: -> @block[@block.length - 1]
|
240 |
|
241 | regNodeType "blockNode", BlockNode
|
242 |
|
243 | Node::getTail = -> @
|
244 |
|
245 | Scope::nodes = (arr...) ->
|
246 | if arr.length is 1
|
247 | return arr[0]
|
248 | res = []
|
249 | walk = (n) ->
|
250 | if n instanceof BlockNode
|
251 | for i in n.block
|
252 | walk(i)
|
253 | else
|
254 | res.push n
|
255 | walk(i) for i in arr
|
256 | return @emptyNode() unless res.length
|
257 | return res[0] if res.length is 1
|
258 | return @blockNode().append(arr...)
|
259 |
|
260 | Node::append = (args...) ->
|
261 | @ctx.nodes(@,args...)
|
262 | EmptyNode::append = (f,nxt...) ->
|
263 | return f.append(nxt...) if nxt.length
|
264 | return f
|
265 | PureNode::append = (f,nxt...) ->
|
266 | if f.pureBlock?
|
267 | @block.push(f.pureBlock()...)
|
268 | return @append(nxt...) if nxt.length
|
269 | return @
|
270 | super(f,nxt...)
|
271 |
|
272 | Node::fullDeps = ->
|
273 | Node::setPosition = (@exprPos) -> @
|
274 |
|
275 | Node::fullDeps = (pos, deps) ->
|
276 | if @exprPos
|
277 | pos.push @exprPos
|
278 | deps.push @
|
279 |
|
280 | PureNode::fullDeps = ->
|
281 | if @exprPos
|
282 | [p,px] = @exprPos
|
283 | p[px] = kit.lit(null)
|
284 |
|
285 | PureExprNode::fullDeps = (pos,deps) ->
|
286 | if @exprPos
|
287 | if @vdeps.mod
|
288 | pos.push @exprPos
|
289 | deps.push(@)
|
290 | else
|
291 | [p,px] = @exprPos
|
292 | p[px] = @pureExpr
|
293 |
|
294 | class EffValNode extends EffectfulNode
|
295 | constructor: (@val) -> super()
|
296 | ignoreResult: -> @noRes = true; @
|
297 | _getBuilder: -> builder.exprEffCoerce(@val)
|
298 |
|
299 | regNodeType "effValNode", EffValNode
|
300 |
|
301 | lookupPlaceholderTarget = ->
|
302 | i = @
|
303 | loop
|
304 | p = i.parent
|
305 | break unless p?
|
306 | if p.addPlaceholder?
|
307 | p.addPlaceholder(i, @)
|
308 | break
|
309 | i = p
|
310 | @
|
311 |
|
312 |
|
313 | class PlaceholderNode extends PureExprNode
|
314 | constructor: (@arg) ->
|
315 | super()
|
316 | setExpr: (expr) ->
|
317 | return super(expr) unless @arg
|
318 | super(kit.mapply([expr],kit.fun([], [kit.ret @arg])))
|
319 | setBlock: (n, b, s) ->
|
320 | vn = s.vn
|
321 | @depBlock = n
|
322 | unless s.vn?
|
323 | @prim = true
|
324 | s.vn = vn = @ctx.uniqId("n")
|
325 | b.unshift(@ctx.reifyNode().setBody(n).setVar(vn).setParent(@))
|
326 | @ctx.refs[i] = true for i of n.vdeps.mods
|
327 | @setExpr(vn)
|
328 | _fwdPass: (use) ->
|
329 | lookupPlaceholderTarget.call(@)
|
330 |
|
331 | Node::fillPlaceholder = (p) ->
|
332 | @parent.fillPlaceholder(p, @)
|
333 |
|
334 | BlockNode::fillPlaceholder = (p) ->
|
335 | {block} = @
|
336 | x = block.indexOf(n)
|
337 | assert.ok(x >= 0)
|
338 |
|
339 | regNodeType "placeholderNode", PlaceholderNode
|
340 |
|
341 | Node::setExprPos = (@exprPos) -> @
|
342 |
|
343 |
|
344 | class BindNode extends DynNode
|
345 | constructor: (body,deps) ->
|
346 | super()
|
347 | @setDeps(deps)
|
348 | @setBody(body)
|
349 | bindNode: true
|
350 | setBody: (body) ->
|
351 | body.setParent(@) if body?
|
352 | @body = body
|
353 | @
|
354 |
|
355 |
|
356 | setDeps: (deps) ->
|
357 | for i in deps
|
358 | i.setParent(@)
|
359 | @deps = deps
|
360 | @
|
361 | _fullDeps: (pos, deps) ->
|
362 | @body.fullDeps(pos,deps)
|
363 | for i,x in @deps
|
364 | i.fullDeps(pos,deps)
|
365 | return
|
366 |
|
367 | fullDeps: (pos,deps) ->
|
368 | if @body.eff
|
369 | super(pos,deps)
|
370 | else @_fullDeps(pos,deps)
|
371 |
|
372 |
|
373 | assemblePar: (deps, cur) -> builder.par(deps,cur)
|
374 | _getBuilder: ->
|
375 | pos = []
|
376 | eargs = []
|
377 | @_fullDeps(pos,eargs)
|
378 | deps = []
|
379 | cur = @body.getBuilder()
|
380 | shallow = true
|
381 | commit = =>
|
382 | switch deps.length
|
383 | when 0 then return cur
|
384 | when 1 then cur = deps[0].append(cur)
|
385 | else cur = @assemblePar(deps, cur)
|
386 | deps = []
|
387 | cur
|
388 | for [p,px],x in pos by -1
|
389 | e = eargs[x]
|
390 | unless e.eff
|
391 | if not e.vdeps.mod
|
392 | eb = e.getBuilder().toExprBuilder()
|
393 | cur.pushEnv(eb.env)
|
394 | p[px] = eb.expr
|
395 | continue
|
396 | if shallow
|
397 | eb = e.getBuilder().toExprBuilder()
|
398 | p[px] = eb.expr
|
399 | nxt = commit()
|
400 | cur = builder.pure([]).pushEnv(eb.env).setOpts(@opts).append(nxt)
|
401 | continue
|
402 | b = e.getBuilder().setBindVar(p[px] = @ctx.uniqId("b"))
|
403 | if e.vdeps.mod
|
404 | cur = b.append(commit())
|
405 | else
|
406 | deps.unshift(b)
|
407 | shallow = false
|
408 | commit()
|
409 | ignoreResult: ->
|
410 | super()
|
411 | @setBody(@body.ignoreResult())
|
412 | @
|
413 | _backPass: (use) ->
|
414 | @body.backPass(use)
|
415 | for i in @deps by -1
|
416 | i.backPass(use)
|
417 | for i in @deps
|
418 | eff = eff or i.eff
|
419 | eff or= @eff or @body.eff
|
420 | if eff
|
421 | @propagateEff()
|
422 | @
|
423 | _fwdPass: (use) ->
|
424 | {block} = @
|
425 | for i in @deps
|
426 | i.fwdPass(use)
|
427 | @body.fwdPass(use)
|
428 |
|
429 | regNodeType "bindNode", BindNode
|
430 |
|
431 | simpleExpressions =
|
432 | Literal: true
|
433 | Identifier: true
|
434 |
|
435 | isSimpleExpr = (e) ->
|
436 | simpleExpressions[e.type]
|
437 |
|
438 |
|
439 | class SharedNode extends DynNode
|
440 | constructor: (def,body) ->
|
441 | super()
|
442 | @setDef(def)
|
443 | @setBody(body)
|
444 | @refs = []
|
445 | setDef: (@def) ->
|
446 | if @def
|
447 | @def.setParent(@)
|
448 | @eff = true if @def.eff
|
449 | @
|
450 | setBody: (@body) ->
|
451 | if @body
|
452 | @body.setParent(@)
|
453 | @eff = true if @body.eff
|
454 | @
|
455 | init: (ctx) ->
|
456 | if @def.pureExpr and isSimpleExpr(@def.pureExpr)
|
457 | @_ref = @def.pureExpr
|
458 | @_simplExpr = true
|
459 | else
|
460 | @_ref = @name = ctx.uniqId "s"
|
461 | super(ctx)
|
462 | _getBuilder: ->
|
463 | return @body.getBuilder() if @_simplExpr
|
464 | if @def.pureExpr?
|
465 | builder.pure([kit.varDecl(@name,@def.pureExpr)])
|
466 | .mergeEnv(@def.vdeps)
|
467 | .append(@body.getBuilder())
|
468 | else
|
469 | builder.par([@def.getBuilder().setBindVar(@name)],@body.getBuilder())
|
470 | ignoreResult: ->
|
471 | super()
|
472 | @setBody(@body.ignoreResult())
|
473 | @
|
474 | _backPass: (use) ->
|
475 | @body.backPass(use)
|
476 | @def.backPass(use)
|
477 | eff = @eff or @body.eff or @def.eff
|
478 | if eff
|
479 | @propagateEff()
|
480 | @
|
481 | _fwdPass: (use) ->
|
482 | {block} = @
|
483 | @def.fwdPass(use)
|
484 | @body.fwdPass(use)
|
485 |
|
486 | regNodeType "sharedNode", SharedNode
|
487 |
|
488 |
|
489 |
|
490 | class JumpNode extends DynNode
|
491 | constructor: (@dst,@kind) ->
|
492 | super()
|
493 | @vdeps.skipMods()
|
494 | setDst: (@dst) -> @
|
495 | _fwdPass: (use) ->
|
496 | @val.fwdPass(use) if @val
|
497 | i = @
|
498 | loop
|
499 | i = i.parent
|
500 | break if not i? or i is @dst
|
501 | i.unwindBy[@id] = @
|
502 | return
|
503 |
|
504 |
|
505 | class BreakNode extends JumpNode
|
506 | constructor: (dst, val, name) ->
|
507 | super(dst, name ? "brk")
|
508 | @setVal(val)
|
509 | setVal: (@val) ->
|
510 | @val.setParent(@) if @val
|
511 | @
|
512 | _getBuilder: ->
|
513 | if @ret
|
514 | return @val.getBuilder() if @val
|
515 | return builder.empty()
|
516 | lab = @dst.getLabel()
|
517 | if @val
|
518 | vexpr = @val.getBuilder().coerceObj().toExpr() if @val?
|
519 | expr = if @val.eff
|
520 | kit.mbind([vexpr],lab)
|
521 | else
|
522 | kit.call(lab,[vexpr])
|
523 | else
|
524 | thread = @dst.thread
|
525 | if thread.length > 1
|
526 | thread = [kit.arr(thread)]
|
527 | expr = kit.call(lab,thread)
|
528 | return builder.exprEff(expr).noCapture().setBrk(true)
|
529 | _backPass: (use) ->
|
530 | eff = @eff
|
531 | if @val
|
532 | @val.backPass(use)
|
533 | eff or= @val.eff
|
534 | if eff
|
535 | @propagateEff()
|
536 | @
|
537 |
|
538 |
|
539 | class YieldNode extends BreakNode
|
540 | constructor: (dst,val) ->
|
541 | super(dst,val)
|
542 | @eff = true
|
543 | ignoreResult: -> @
|
544 | setBlock: (@block) -> @
|
545 | _getBuilder: ->
|
546 | b = @block.getBuilder().append(super())
|
547 | .setOpts(@opts).mergeEnv(@vdeps).toExprBuilder()
|
548 | return builder.exprEff(kit.call(kit.mem(b.expr,kit.id("mopt")),[])).morph(b)
|
549 | _fwdPass: (use) ->
|
550 | @dst.opts.keepScope = true
|
551 | lookupPlaceholderTarget.call(@)
|
552 | _backPass: (use) ->
|
553 | @propagateEff()
|
554 | @block.backPass(use)
|
555 |
|
556 | regNodeType "breakNode", BreakNode
|
557 | regNodeType "yieldNode", YieldNode
|
558 |
|
559 |
|
560 | class BranchedNode extends DynNode
|
561 | constructor: (@fun, @template) ->
|
562 | super()
|
563 | @deps = []
|
564 | setDeps: (deps...) ->
|
565 | cur = @deps
|
566 | cur.length = 0
|
567 | if deps?
|
568 | for i in deps
|
569 | i.setParent(@)
|
570 | cur.push i
|
571 | @
|
572 | _getItemBuilder: (i) ->
|
573 | i.getBuilder()
|
574 | _getBuilder: ->
|
575 | {eff} = @
|
576 | threadOut = {}
|
577 | nenv = {}
|
578 | for i in @deps
|
579 | i.vdeps.threadOutMap(threadOut)
|
580 | blocks = []
|
581 | needCoerce = false
|
582 | for i in @deps
|
583 | cb = @_getItemBuilder(i)
|
584 | cb = cb.addThread(threadOut)
|
585 | bn = cb.capture().toBlockBuilder(eff)
|
586 | needCoerce or= bn._needCoerce is true
|
587 | for j, v of bn.env
|
588 | w = nenv[j] ?= {}
|
589 | w.use = true if v.use
|
590 | w.set = true if v.set
|
591 | w.mod = true if v.mod
|
592 | w.thread = true if threadOut[j]
|
593 | blocks.push(kit.flatBlock(bn.block))
|
594 | block = builder.block(eff, [@fun(blocks...)])
|
595 | .pushEnv(nenv)
|
596 | .noCapture()
|
597 | .needCoerce(needCoerce ? false)
|
598 | return block
|
599 | _propagateEff: ->
|
600 | super()
|
601 | {deps} = @
|
602 | for i,x in deps
|
603 | deps[x] = i.propagateEff()
|
604 | @
|
605 | _backPass: (use) ->
|
606 | unless @eff
|
607 | for i in @deps when i.eff
|
608 | @eff = true
|
609 | break
|
610 | if @eff
|
611 | @_propagateEff()
|
612 | for i in @deps
|
613 | i.backPass(kit.extend({},use))
|
614 | for i in @deps
|
615 | kit.extend(use,i.vdeps.uses)
|
616 | @
|
617 | _fwdPass: (use) ->
|
618 | for i in @deps
|
619 | i.fwdPass(kit.extend({},use))
|
620 | return
|
621 |
|
622 | regNodeType "branchedNode", BranchedNode
|
623 |
|
624 |
|
625 | class WrapNode extends DynNode
|
626 | constructor: (body) ->
|
627 | super()
|
628 | @setBody(body)
|
629 | setBody: (@body) ->
|
630 | @body.setParent(@) if @body?
|
631 | @
|
632 | _backPass: (use) ->
|
633 | if @eff or @body.eff
|
634 | @_propagateEff()
|
635 | @body.backPass(use)
|
636 | @
|
637 | _fwdPass: (use) ->
|
638 | @body.fwdPass(use)
|
639 |
|
640 |
|
641 | getBuilderFun = (node,ctx) ->
|
642 | thread = node.vdeps.threadOutMap()
|
643 | b = node.getBuilder()
|
644 | b.addThread(thread) if thread?
|
645 | b = b.capture().toBlockBuilder()
|
646 | thread[i] = true for i of b.env when i.thread
|
647 | vars = (i for i of thread).sort().map(kit.id)
|
648 | fun = kit.spreadFun(vars,kit.fun(vars, b.block))
|
649 | return [fun,vars,b,thread]
|
650 |
|
651 |
|
652 | class RepeatNode extends WrapNode
|
653 | constructor: (body) -> super(body)
|
654 | _getBuilder: ->
|
655 | [fun,vars,b] = getBuilderFun(@body,@ctx)
|
656 | if vars.length > 1
|
657 | vars = [kit.arr(vars)]
|
658 | expr = kit.call(kit.packId("repeat"),[fun,vars...])
|
659 | builder.exprEff(expr).morph(b).mergeEnv(@vdeps).noCapture()
|
660 | _fwdPass: (use) ->
|
661 | @body.fwdPass({})
|
662 | _backPass: (use) ->
|
663 | use[i] = true for i of @body.vdeps.uses
|
664 | super(use)
|
665 |
|
666 | regNodeType "repeatNode", RepeatNode
|
667 |
|
668 |
|
669 | class ControlNode extends WrapNode
|
670 | constructor: (@kind) ->
|
671 | @exits = []
|
672 | @kind ?= "block"
|
673 | @labCount = 0
|
674 | @thread = []
|
675 | super()
|
676 | setLabel: (lab) ->
|
677 | lab ?= "l"
|
678 | @label = @ctx.uniqId(lab)
|
679 | @
|
680 | getLabel: ->
|
681 | return @label if @label?
|
682 | @label = @ctx.uniqId("l")
|
683 | breakNode: (v) ->
|
684 | res = @ctx.breakNode(@,v)
|
685 | @exits.push res
|
686 | res
|
687 | yieldNode: (v) ->
|
688 | res = @ctx.yieldNode(@,v)
|
689 | @exits.push res
|
690 | res
|
691 | trimExits: (loopBody) ->
|
692 | if not @opts.keepScope
|
693 | if @exits.length is 1
|
694 | i = j = @exits[0]
|
695 | loop
|
696 | k = i
|
697 | i = i.parent
|
698 | if i is @ or i is loopBody
|
699 | j.ret = true
|
700 | @exits.length = 0
|
701 | break
|
702 | if i instanceof BlockNode
|
703 | x = i.block.indexOf(k)
|
704 | return @ if x < 0
|
705 | i.block.splice(x+1,i.block.length)
|
706 | continue
|
707 | return unless i instanceof BindNode
|
708 | return
|
709 | _getBuilder: ->
|
710 | @trimExits()
|
711 | return @body.getBuilder() unless @exits.length and @eff
|
712 | unless @kind is "scope"
|
713 | thread = @body.vdeps.threadOutMap()
|
714 | @thread = (i for i of thread).sort().map(kit.id)
|
715 | else
|
716 | @thread = []
|
717 | b = @body.getBuilder()
|
718 | b = b.capture().toBlockBuilder()
|
719 | bodyBlock = b.block
|
720 | builder.exprEff(
|
721 | kit.call(kit.packId(@kind),[kit.fun([@getLabel()], bodyBlock)])
|
722 | ).morph(b).mergeEnv(@vdeps).noCapture()
|
723 | _propagateEff: ->
|
724 | super()
|
725 | for i in @exits
|
726 | i.propagateEff()
|
727 | @
|
728 | _backPass: (use) ->
|
729 | @body.backPass(use)
|
730 | unless @kind is "scope"
|
731 |
|
732 | for i in @exits
|
733 | i.vdeps.after[j] = true for j,v of @vdeps.after when v
|
734 | return
|
735 |
|
736 | regNodeType "controlNode", ControlNode
|
737 |
|
738 |
|
739 | class LoopNode extends ControlNode
|
740 | constructor: -> super()
|
741 | simpleLoop: ->
|
742 | return unless @pre
|
743 | return if @opts.loop is "seq"
|
744 | @cont.trimExits()
|
745 | return if @cont.exits.length
|
746 | return if @update? and @update.eff
|
747 | return if @test.eff or @test.vdeps.upd
|
748 | return if @inner.vdeps.upd
|
749 | return for i,v of @inner.unwindBy when v.dst isnt @ and v.dst isnt @cont
|
750 | @trimExits(@inner.parent)
|
751 | return if @exits.length
|
752 | [update, updVars, b, thread] = getBuilderFun(@update, @ctx)
|
753 | tb = @test.getBuilder().toBlockBuilder().capture()
|
754 | test = kit.spreadFun(updVars, kit.fun(updVars, tb.block))
|
755 | bb = @inner.getBuilder()
|
756 | bb = bb.capture().toBlockBuilder()
|
757 | body = kit.spreadFun(updVars, kit.fun(updVars, bb.block))
|
758 | v.mod = false for i, v of b.env when v.mod
|
759 | updVars = [kit.arr(updVars)] if updVars.length > 1
|
760 | return builder.exprEff(
|
761 | kit.call(kit.packId("forPar"),[test,body,update,updVars...])
|
762 | ).morph(b).mergeEnv(@vdeps).noCapture()
|
763 | _getBuilder: ->
|
764 | res = @simpleLoop()
|
765 | return res if res?
|
766 | super()
|
767 |
|
768 | regNodeType "loopNode", LoopNode
|
769 |
|
770 |
|
771 | class TryCatchNode extends DynNode
|
772 | constructor: ->
|
773 | super()
|
774 | @throws = []
|
775 | setBody: (b) ->
|
776 | @body = b
|
777 | b.setParent(@) if b?
|
778 | @
|
779 | setHandler: (@hvar, h) ->
|
780 | @handler = h
|
781 | h.setParent(@) if h?
|
782 | @
|
783 | setFin: (f) ->
|
784 | @fin = f
|
785 | f.setParent(@) if f?
|
786 | @
|
787 | _getBuilder: ->
|
788 | return @body.getBuilder() unless @handler? or @fin?
|
789 | bb = @body.getBuilder().coerceTry().toExprBuilder(true)
|
790 | e = bb.expr
|
791 | if @handler?
|
792 | hb = @handler.getBuilder().toBlockBuilder(true)
|
793 | e = kit.catch(bb.expr,@hvar,hb.block)
|
794 | if @fin?
|
795 | fb = @fin.getBuilder().toBlockBuilder(true)
|
796 | e = kit.finally(e,fb.block)
|
797 | res = builder.exprEff(e).pushEnv(bb.env)
|
798 | res.pushEnv(hb.env) if hb?
|
799 | res.pushEnv(fb.env) if fb?
|
800 | res
|
801 | _fwdPass: (use) ->
|
802 | @body.fwdPass(use)
|
803 | if @handler
|
804 | @handler.fwdPass(use)
|
805 | hdeps = @handler.vdeps.uses
|
806 | else
|
807 | hdeps = {}
|
808 | if @fin
|
809 | @fin.fwdPass(use)
|
810 | fdeps = @fin.vdeps.uses
|
811 | else
|
812 | fdeps = {}
|
813 | for i of @body.vdeps.mods when fdeps[i] or hdeps[i]
|
814 | @ctx.refs[i] = true
|
815 | return
|
816 | _propagateEff: ->
|
817 | super()
|
818 | i.propagateEff() for i in @throws
|
819 | @
|
820 | _backPass: (use) ->
|
821 |
|
822 | unless @eff
|
823 | @eff = @body.eff or @handler? and @handler.eff or @fin and @fin.eff
|
824 | if @eff
|
825 | @_propagateEff()
|
826 | @fin.backPass(use) if @fin?
|
827 | @handler.backPass(use) if @handler?
|
828 | @body.backPass(use)
|
829 |
|
830 | regNodeType "tryCatchNode", TryCatchNode
|
831 |
|
832 |
|
833 | class ThrowNode extends DynNode
|
834 | constructor: (val) ->
|
835 | super()
|
836 | @setVal(val)
|
837 | setVal: (val) ->
|
838 | @val = val.setParent(@)
|
839 | @
|
840 | _backPass: (use) ->
|
841 | super()
|
842 | unless @eff
|
843 | @eff = @val.eff
|
844 | if @eff
|
845 | @_propagateEff()
|
846 | @val.backPass(use)
|
847 | @
|
848 | _fwdPass: (use) ->
|
849 | p = @parent
|
850 | l = @
|
851 | loop
|
852 | if p.throws? and p.body is l
|
853 | @dst = p
|
854 | break
|
855 | l = p
|
856 | p = p.parent
|
857 | @dst.throws.push @
|
858 | @val.fwdPass(use)
|
859 | @
|
860 | _getBuilder: ->
|
861 | assert.ok(@eff)
|
862 | v = @ctx.uniqId("e")
|
863 | if @val.pureExpr
|
864 | return builder.exprEff(kit.call(kit.packId("raise"), [@val.pureExpr]))
|
865 | @val.getBuilder().setBindVar(v).setOpts(@opts)
|
866 | .append(builder.exprEff(kit.call(kit.packId("raise"), [v]))
|
867 | .setOpts(@opts))
|
868 |
|
869 | regNodeType "throwNode", ThrowNode
|
870 |
|
871 | Node::tryCatch = (hv,h,f) ->
|
872 | @ctx.tryCatchNode().setBody(@)
|
873 | .setHandler(hv,h)
|
874 | .setFin(f)
|
875 |
|
876 | Scope::stmtNode = (s) ->
|
877 | return EmptyNode() unless s?
|
878 | sv = @stmt s
|
879 | return sv if sv?
|
880 | return PureNode([s])
|
881 |
|
882 | Node::isEmpty = -> false
|
883 | BlockNode::isEmpty = -> not @block.length
|
884 | EmptyNode::isEmpty = -> true
|