1 | config = require "./config"
|
2 | {Scope} = require "./scope"
|
3 | {Visitor, ActiveVisitor, DisabledVisitor} = require "./visitor"
|
4 | kit = require "./kit"
|
5 | policy = require "./policy"
|
6 | directive = require "./directives"
|
7 | {VarUsage} = require "./builder"
|
8 | require "./graph"
|
9 | require "./expr"
|
10 |
|
11 | class ExprBuilder
|
12 | constructor: (@expr, @ctx) ->
|
13 | @init()
|
14 | @cur = if @ctx.policy.opts.bind
|
15 | @ctx.effValNode(@expr)
|
16 | else @ctx.exprNode(@expr)
|
17 | init: ->
|
18 | @deps = []
|
19 | add: (s,par,x,opts) ->
|
20 | r = @ctx.expr s, opts
|
21 | if r.pureExpr? and not r.vdeps.mod
|
22 | par[x] = r.pureExpr
|
23 | @cur.vdeps.addInnerDeep(r.vdeps)
|
24 | return
|
25 | r.setPosition([par,x])
|
26 | @deps.push(r)
|
27 | return
|
28 | result: ->
|
29 | res = @cur
|
30 | {vdeps} = res
|
31 | if @deps.length
|
32 | res = @ctx.bindNode(res, @deps)
|
33 | res.origExpr = false if @orig is false
|
34 | @init()
|
35 | @cur = res
|
36 |
|
37 | class DummyBuilder
|
38 | constructor: (@expr,@ctx) ->
|
39 | add: (s,par,x,opts) ->
|
40 | r = @ctx.expr s, opts
|
41 | if r.pureExpr?
|
42 | par[x] = r.pureExpr
|
43 | return
|
44 | throw new Error("INTERNAL: not expected effectful expression")
|
45 | result: -> @ctx.pureExprNode(@expr)
|
46 |
|
47 | Visitor::CallExpression = (e) ->
|
48 | opts = @policy.opts
|
49 | {callee,arguments:args} = e
|
50 | if callee.type is "Identifier"
|
51 | n = callee.name
|
52 | if n is "require" and args.length is 1
|
53 | a = args[0]
|
54 | if a.type is "Literal" and a.value is config.packageName
|
55 | @ctx.policy._libRequire()
|
56 | return @ctx.pureExprNode(e)
|
57 | dir = do ->
|
58 | return directive.$packVar if n is config.packageVar
|
59 | return unless callee.type is "MemberExpression"
|
60 | obj = callee.object
|
61 | return unless obj.type is "Identifier" and obj.name is config.packageVar
|
62 | prop = callee.property
|
63 | if prop.type is "Identifier"
|
64 | dn = prop.name
|
65 | else if callee.computed and prop.type is "Literal" and prop.value.substr
|
66 | dn = prop.value
|
67 | else
|
68 | return
|
69 | directive[dn]
|
70 | if dir?
|
71 | res = kit.errorPos(e, => dir.call(@ctx,e,e.arguments...))
|
72 | return @ctx.emptyNode() unless res?
|
73 | return res
|
74 | idn = opts.identity
|
75 | if idn? and idn isnt false
|
76 | idn = 0 if idn is true
|
77 | return @ctx.expr(e.arguments[idn])
|
78 | builder = @getExprBuilder(e)
|
79 | {callee,arguments:args} = e
|
80 | builder.add(callee, e, "callee")
|
81 | if opts.subScope isnt false and callee.$dm$eff is true
|
82 | opts.bind = true
|
83 | builder.add(i, args, x) for i, x in args
|
84 | builder.result()
|
85 |
|
86 | directive.$packVar = (e,arg) ->
|
87 | bnd = @policy.opts.bind
|
88 | e = @expr arg
|
89 | return @effValNode(e.pureExpr).morphInit(e) if bnd and e.pureExpr
|
90 | return e
|
91 |
|
92 | directive.reflect = (e,arg) ->
|
93 | bnd = @policy.opts.bind
|
94 | expr = kit.call(kit.packId("reflect"), [arg])
|
95 | @bindNode(@effValNode(expr),
|
96 | [@expr(arg).setPosition([expr.arguments,0])])
|
97 |
|
98 | directive.reify = (e, arg) ->
|
99 | @reifyNode().setBody(@expr(arg))
|
100 | .setIsThunk(arg.type is "FunctionExpression")
|
101 |
|
102 | directive.p = directive.p = (e, arg) -> @pureExprNode(arg)
|
103 |
|
104 | Visitor::FunctionExpression = (e) ->
|
105 | ctx = @ctx.subScope(e)
|
106 | if e.params?
|
107 | ctx.vars[i.name] = true for i in e.params when i.name
|
108 | return @ctx.pureExprNode(e) unless ctx?
|
109 | e.body = ctx.prog e.body
|
110 | e.$dm$eff = true if ctx.eff
|
111 | res = @ctx.exprNode(e)
|
112 | vars = ctx.vars
|
113 | @ctx.refs[i] = true for i, v of ctx.upds when v and not ctx.vars[i]
|
114 | if e.generator
|
115 | e.generator = false
|
116 | return res
|
117 |
|
118 | ActiveVisitor::ThisExpression = (e) ->
|
119 | @ctx.thisVar ?= @ctx.uniqId("_this")
|
120 | return @ctx.exprNode(@ctx.thisVar)
|
121 |
|
122 | Visitor::AssignmentExpression = (e) ->
|
123 | {left,right} = e
|
124 | deps = []
|
125 | res = @ctx.exprNode(e)
|
126 | @policy.nameDetails(left,e.operator)
|
127 | if left.type is "Identifier"
|
128 | if e.operator is "="
|
129 | res.vdeps.addAssign(left.name)
|
130 | else
|
131 | res.vdeps.addUpd(left.name)
|
132 | unless @ctx.vars[left.name]
|
133 | @ctx.upds[left.name] = true
|
134 | else
|
135 | lv = @ctx.expr left
|
136 | if lv.pureExpr?
|
137 | res.vdeps.addInnerDeep(lv.vdeps)
|
138 | e.left = lv.pureExpr
|
139 | else
|
140 | deps.push(lv.setPosition([e,"left"]))
|
141 | rv = @ctx.expr right
|
142 | if rv.pureExpr?
|
143 | res.vdeps.addInnerDeep(rv.vdeps)
|
144 | e.right = rv.pureExpr
|
145 | else
|
146 | deps.push(rv.setPosition([e,"right"]))
|
147 | if deps.length
|
148 | res = @ctx.bindNode(res,deps)
|
149 | res
|
150 |
|
151 | ActiveVisitor::UpdateExpression = (e) ->
|
152 | res = @ctx.exprNode(e)
|
153 | a = e.argument
|
154 | if a.type is "Identifier"
|
155 | unless @ctx.vars[a.name]
|
156 | @ctx.upds[a.name] = true
|
157 | res.vdeps.addUpd(a.name)
|
158 | else
|
159 | av = @ctx.expr a
|
160 | if av.pureExpr
|
161 | e.argument = av.pureExpr
|
162 | else
|
163 | res = @ctx.bindNode(res, [av.setPosition([e,"argument"])])
|
164 | res
|
165 |
|
166 | directive.$ = (e, arg) ->
|
167 | @placeholderNode(arg)
|
168 |
|
169 | ActiveVisitor::MemberExpression = (e) ->
|
170 | {object, property} = e
|
171 | if object.type is "Identifier" and object.name is config.packageVar and
|
172 | property.type is "Identifier" and property.name is "$"
|
173 | return @ctx.placeholderNode()
|
174 | builder = @getExprBuilder(e)
|
175 | builder.add(object, e, "object")
|
176 | if e.computed
|
177 | builder.add(property, e,"property")
|
178 | builder.result()
|
179 |
|
180 | ActiveVisitor::Identifier = (e) ->
|
181 | res = @ctx.exprNode(e)
|
182 | res.vdeps.uses[e.name] = true
|
183 | @ctx.ids[e.name] = true unless e.$dm$orig
|
184 | res
|
185 |
|
186 | Visitor::getExprBuilder = (e) -> new DummyBuilder(e,@ctx)
|
187 | ActiveVisitor::getExprBuilder = (e) -> new ExprBuilder(e,@ctx)
|
188 |
|
189 | ActiveVisitor::LogicalExpression = (e) ->
|
190 | if @policy.opts.expr is "par"
|
191 | builder = @getExprBuilder(e)
|
192 | builder.add(e.left, e, "left")
|
193 | builder.add(e.right, e, "right")
|
194 | return builder.result()
|
195 | cexpr = {type:"ConditionalExpression"}
|
196 | l = @ctx.expr(e.left)
|
197 | r = @ctx.expr(e.right)
|
198 | return @ctx.exprNode(e) unless l.eff or r.eff
|
199 | rexpr = r.getBuilder().setOpts(@policy.opts).toExpr()
|
200 | b = if r.eff then @ctx.effValNode(cexpr) else @ctx.pureExprNode(cexpr)
|
201 | res = @ctx.sharedNode(l,b)
|
202 | lexpr = cexpr.test = res._ref
|
203 | lexpr = kit.pure(lexpr) if @policy.opts.coerce is "none" and r.eff
|
204 | if e.operator is "||"
|
205 | cexpr.consequent = lexpr
|
206 | cexpr.alternate = rexpr
|
207 | else
|
208 | cexpr.consequent = rexpr
|
209 | cexpr.alternate = lexpr
|
210 | res
|
211 |
|
212 | ActiveVisitor::ConditionalExpression = (e) ->
|
213 | if @policy.opts.expr is "par"
|
214 | builder = @getExprBuilder(e)
|
215 | builder.add(e.test, e, "test")
|
216 | builder.add(e.consequent, e, "consequent")
|
217 | builder.add(e.alternate, e, "alternate")
|
218 | return builder.result()
|
219 | c = @ctx.expr(e.consequent)
|
220 | a = @ctx.expr(e.alternate)
|
221 | if c.eff or a.eff
|
222 | e.consequent = c.getFullExpr(true)
|
223 | e.alternate = a.getFullExpr(true)
|
224 | n = @ctx.effValNode(e)
|
225 | else
|
226 | n = @ctx.pureExprNode(e)
|
227 | @ctx.bindNode(n, [@ctx.expr(e.test).setPosition([e,"test"])])
|
228 |
|
229 | directive.answer = directive.yield = (s,arg) ->
|
230 | e = @expr(arg) if arg?
|
231 | @root.yieldNode(e)
|
232 |
|
233 | ActiveVisitor::YieldExpression = (e) ->
|
234 | if @ctx.policy.opts.mopt is false
|
235 | if e.delegate
|
236 | return directive.$packVar.call(@ctx,e,e.argument)
|
237 | return directive.reflect.call(@ctx,e,e.argument)
|
238 | @ctx.root.yieldNode(@ctx.expr(e.argument))
|
239 |
|
240 | ActiveVisitor::BinaryExpression = (e) ->
|
241 | builder = @getExprBuilder(e)
|
242 | builder.add(e.left, e, "left")
|
243 | builder.add(e.right, e, "right")
|
244 | builder.result()
|
245 |
|
246 | ActiveVisitor::UnaryExpression = (e) ->
|
247 | builder = @getExprBuilder(e)
|
248 | builder.add(e.argument, e, "argument")
|
249 | builder.result()
|
250 |
|
251 | ActiveVisitor::NewExpression = (e) ->
|
252 | builder = @getExprBuilder(e)
|
253 | args = e.arguments
|
254 | builder.add(i, args, x) for i, x in args
|
255 | builder.result()
|
256 |
|
257 | ActiveVisitor::SequenceExpression = (e) ->
|
258 | builder = @getExprBuilder(e)
|
259 | args = e.expressions
|
260 | builder.add(i, args, x) for i, x in args
|
261 | builder.result()
|
262 |
|
263 | ActiveVisitor::ArrayExpression = (e) ->
|
264 | builder = @getExprBuilder(e)
|
265 | args = e.elements
|
266 | builder.add(i, args, x) for i, x in args when i
|
267 | builder.result()
|
268 |
|
269 | ActiveVisitor::ObjectExpression = (e) ->
|
270 | builder = @getExprBuilder(e)
|
271 | builder.add(i.value, i, "value") for i in e.properties
|
272 | builder.result()
|
273 |
|
274 | module.exports = {}
|
275 |
|