1 | {Scope} = require "./scope"
|
2 | config = require "./config"
|
3 | kit = require "./kit"
|
4 | directive = require "./directives"
|
5 | esquery = require "esquery"
|
6 | escodegen = require("escodegen")
|
7 | resolve = require "resolve"
|
8 | path = require "path"
|
9 |
|
10 | root = module.exports = {}
|
11 |
|
12 | hier = {}
|
13 |
|
14 | hierIs = (f, t) ->
|
15 | (h = hier[f] ?= [f]).push t
|
16 | ht = hier[t]
|
17 | if ht?
|
18 | for i in ht
|
19 | h.push(i)
|
20 | return
|
21 |
|
22 | hierIs("WhileStatement",":loop")
|
23 | hierIs("DoWhileStatement",":loop")
|
24 | hierIs("ForStatement",":loop")
|
25 | hierIs("ForInStatement",":loop")
|
26 | hierIs("VariableDeclaration",":assignment")
|
27 | hierIs("FunctionExpression","Function")
|
28 | hierIs("FunctionDeclaration","Function")
|
29 | hierIs("AssignmentExpression",":assignment")
|
30 |
|
31 | root.selector =
|
32 | property: (name, next) ->
|
33 | p = name.split(".")
|
34 | next ?= (v) -> v
|
35 | (e) ->
|
36 | return next() unless e?
|
37 | for i in p
|
38 | e = e[i]
|
39 | break unless e?
|
40 | next(e)
|
41 | cases: (sel) ->
|
42 | (s) ->
|
43 | v = sel(s)
|
44 | return @cases[v] if v?
|
45 | prop: (n) ->
|
46 | (s) ->
|
47 | @cases[s.generator ? false]
|
48 | generatorCall: -> false
|
49 | matchDeclName: (s,x,tpref) ->
|
50 | m = @match
|
51 | n = kit.getId(s.id)
|
52 | r = do ->
|
53 | if s.id?
|
54 | if n?
|
55 | return true if m.name? and m.name[n]
|
56 | return true if m.qname? and m.qname[n]
|
57 | return true if m.postfix? and m.postfix[n[n.length - 1]]
|
58 | return true if m.prefix? and m.prefix[n[0]]
|
59 | if s.type is "FunctionExpression" and x.name?
|
60 | p = kit.getMembersPathIds(x.name)
|
61 | if p.length
|
62 | n = p[p.length - 1]
|
63 | if n?
|
64 | return true if m.name? and m.name[n]
|
65 | return true if m.postfix? and m.postfix[n[n.length - 1]]
|
66 | return true if m.prefix? and m.prefix[n[0]]
|
67 | return true if m.package? and p[0]? and m.package[p[0]]
|
68 | return true if m.qname? and m.qname[p.join(".")]
|
69 | return false
|
70 | if tpref?
|
71 | console.log("#{tpref}: match decl #{n}: #{r}")
|
72 | return unless r?
|
73 | return @cases[r]
|
74 | matchCallName: (s, p, tpref) ->
|
75 | p = kit.getMembersPathIds(s.callee)
|
76 | m = @match
|
77 | r = do ->
|
78 | n = p[0]
|
79 | if p.length is 1
|
80 | if n?
|
81 | return true if m.libVar and n is config.packageVar
|
82 | return false if m.libVar and p.length is 2 and n is config.packageName
|
83 | if p.length > 0
|
84 | n = p[p.length - 1]
|
85 | return false unless n?
|
86 | return true if m.package? and m.package[p[0]]
|
87 | return true if m.name? and m.name[n]
|
88 | return true if m.postfix? and m.postfix[n[n.length - 1]]
|
89 | return true if m.prefix? and m.prefix[n[0]]
|
90 | return true if m.qname? and m.qname[p.join(".")]
|
91 | return false
|
92 | if tpref?
|
93 | console.log("#{tpref}: match call #{p.join('.')}: #{r}")
|
94 | return unless r?
|
95 | return @cases[r]
|
96 |
|
97 | rebuildDT = (c) ->
|
98 | copy = (p, c) ->
|
99 | unless p? and p.$
|
100 | throw new Error("invalid transitions")
|
101 | r = {}
|
102 | for i of options
|
103 | unless p.$?
|
104 | throw new Error('invalid config')
|
105 | v = c[i] ? p.$[i]
|
106 | r[i] = v if v?
|
107 | c.$ = r
|
108 | copy({$:{}},c)
|
109 | for state, sv of c when state[0] isnt '$' and not options[state]
|
110 | copy(c,sv)
|
111 | for ty,tv of sv when ty[0] isnt '$' and not options[ty]
|
112 | copy(sv,tv)
|
113 | if tv.select? and tv.select.substr?
|
114 | tv.select = upStage(root.selector,tv.select)
|
115 | if tv.cases?
|
116 | for cn,cv of tv.cases
|
117 | copy(tv,cv)
|
118 | return
|
119 |
|
120 | options =
|
121 | bind: true
|
122 | compile: true
|
123 | coerce: true
|
124 | expr: true
|
125 | bindAssoc: true
|
126 | block: true
|
127 | loop: true
|
128 | subScope: true
|
129 | keepScope: true
|
130 | varCapt: true
|
131 | branch: true
|
132 | ref: true
|
133 | remove: true
|
134 | keepForOf: true
|
135 | mopt: true
|
136 |
|
137 | class Policy
|
138 | constructor: (@root,start) ->
|
139 | @root ?= config.states
|
140 | @state = start ? config.start
|
141 | @opts = @root[@state]
|
142 | @rebuild()
|
143 | transit: (n) ->
|
144 | if config.policyTrace
|
145 | console.log("policy: transit to #{n}")
|
146 | @state = n
|
147 | cur = @root[n]
|
148 | unless cur?
|
149 | throw new Error("no state #{n} defined")
|
150 | @opts = cur.$
|
151 | @
|
152 | nameDetails: (@name, @op) ->
|
153 | @
|
154 | scope: (f) ->
|
155 | oldRoot = @root
|
156 | @root = kit.merge({}, oldRoot)
|
157 | oldState = @state
|
158 | res = f()
|
159 | @state = oldState
|
160 | @root = oldRoot
|
161 | @opts = @root[@state].$
|
162 | res
|
163 | _libRequire: ->
|
164 | if @name? and @name.type is "Identifier"
|
165 | config.packageVar = @name.name
|
166 | @transit(config.start)
|
167 | rebuild: (changes) ->
|
168 | if changes?
|
169 | kit.merge(@root,changes)
|
170 | rebuildDT(@root)
|
171 | @opts = @root[@state].$
|
172 | item: (s, fun) ->
|
173 | trace = config.policyTrace
|
174 | if trace
|
175 | tpref = "polciy:#{kit.shortNodeDescr(s)}"
|
176 | console.log("#{tpref}: enter at #{@state}")
|
177 | if @name?
|
178 | console.log("#{tpref}: name #{@name}")
|
179 | if @op?
|
180 | console.log("#{tpref}: op #{@op}")
|
181 | cur = @root[@state]
|
182 | h = hier[s.type] ? [s.type]
|
183 | for i in h when n = cur[i]
|
184 | cur = n
|
185 | console.log("#{tpref}: by type #{i}: #{JSON.stringify(cur)}") if trace
|
186 | break
|
187 | sel = cur.select
|
188 | if sel?
|
189 | selv = sel.call(cur, s, @, tpref)
|
190 | console.log("#{tpref}: selector #{JSON.stringify(selv)}") if trace
|
191 | cur = selv if selv?
|
192 | if cur.move?
|
193 | console.log("#{n}: moving to #{cur.move}") if trace
|
194 | return @transit(cur.move).item(s,fun)
|
195 | jump = cur.sub ? cur.next ? cur.inner
|
196 | if trace
|
197 | console.log("#{tpref}: sub-state #{cur.sub}") if cur.sub
|
198 | console.log("#{tpref}: inner state #{cur.inner}") if cur.inner
|
199 | console.log("#{tpref}: next state #{cur.next}") if cur.next
|
200 | if jump?
|
201 | oldState = @state if jump is cur.sub
|
202 | @state = jump
|
203 | cur = @root[jump] unless jump is cur.inner
|
204 | else if s.type is "BlockStatement"
|
205 | oldState = @state
|
206 | oldOp = @op
|
207 | oldOpts = @opts
|
208 | @opts = cur.$
|
209 | console.log("#{tpref}: opts #{JSON.stringify(@opts)}") if trace
|
210 | oldName = @name
|
211 | @libVar = false
|
212 | index = @index
|
213 | @index = 0
|
214 | res = fun()
|
215 | @index = index+1
|
216 | @name = oldName
|
217 | @op = oldOp
|
218 | if oldState?
|
219 | console.log("#{tpref}: restoring state #{oldState}") if trace
|
220 | @state = oldState
|
221 | @opts = oldOpts
|
222 | console.log("#{tpref}: restoring opts #{JSON.stringify(oldOpts)}") if trace
|
223 | res
|
224 |
|
225 | Scope::option = (v) ->
|
226 | @policy.rebuild(v)
|
227 | @updateVisitor()
|
228 |
|
229 | Scope::profile = (v) ->
|
230 | if v.substr?
|
231 | @policy.transit(v)
|
232 | @updateVisitor()
|
233 | return
|
234 | throw new Error("cannot interpret profile #{v}")
|
235 |
|
236 | root.optionEnv = Object.create(root.selector)
|
237 |
|
238 | upStage = (env, str) ->
|
239 | args = []
|
240 | vals = []
|
241 | for i,v of env
|
242 | args.push i
|
243 | vals.push v
|
244 | args.push "return #{str};"
|
245 | fun = new Function(args...)
|
246 | fun.apply(env, vals)
|
247 |
|
248 | opts = directive.option = (e) ->
|
249 | for i in e.arguments
|
250 | if i.type is "Literal" and e.value? and e.value.substr?
|
251 | upStage(env, e.value)
|
252 | else if i.type is "ObjectExpression"
|
253 | @option(upStage(root.optionEnv, escodegen.generate(i)))
|
254 | else throw kit.exprError(
|
255 | "cannot interpret option #{escodegen.generate(i)}", i)
|
256 | @emptyNode()
|
257 |
|
258 | directive.profile = (e, arg) ->
|
259 | @profile(kit.toStr arg)
|
260 | @emptyNode()
|
261 |
|
262 | Scope::createPolicy = ->
|
263 | return new Policy()
|
264 |
|
265 | directive.require = (e,c) ->
|
266 | cs = kit.exprToStr c
|
267 | f = resolve.sync cs, basedir: path.dirname(config.filename)
|
268 | try
|
269 | cp = resolve.sync(config.packageName,
|
270 | basedir: path.dirname(config.filename))
|
271 | core = require cp
|
272 | core.compileTime = true
|
273 | catch e
|
274 | console.warn("couldn't load library ", config.packageName, e)
|
275 | m = require f
|
276 | if m? and m._compile? and m._compile.call?
|
277 | n = @policy.name
|
278 | if n.type is "Identifier"
|
279 | varname = n.name
|
280 | m._compile.call(@,[varname])
|
281 | return @pureExprNode(kit.call(kit.id("require"),[c]))
|
282 |
|
283 | directive.ref = (e) ->
|
284 | for a in e.arguments
|
285 | i = kit.getId(a)
|
286 | unless i?
|
287 | throw kit.exprError('expected identifiers',a)
|
288 | @refs[i] = true
|
289 | @emptyNode()
|
290 |
|