-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathapplication-model.coffee
257 lines (198 loc) · 6.08 KB
/
application-model.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
inventingOnPrinciple.Models.ApplicationModel = Backbone.Model.extend
defaults:
parsingOptions:
# Range is required
range: true
# comment: true,
loc: true
raw: true
tokens: true
initialize: (attributes, options) ->
if attributes? and attributes.text?
@setSource(attributes.text, options)
vars = new inventingOnPrinciple.Collections.VariableCollection()
funs = new inventingOnPrinciple.Collections.FunctionCollection()
@set({
vars: vars
funs: funs
}, {
silent: true
})
vars
.on 'change:var', =>
inventingOnPrinciple.updating = true
inventingOnPrinciple.codeEditor.setValue @source()
inventingOnPrinciple.updating = false
@instrumentFunctions()
.on 'endChange', =>
@trigger 'reparse'
@on 'change:ast', @onASTChange, this
setSource: (text, options) ->
return unless typeof text is 'string'
parsedResult = esprima.parse text, @get 'parsingOptions'
tokens = parsedResult.tokens
ast = _.omit parsedResult, 'tokens'
chunks = text.split ''
@set
ast: ast
astText: util.printJSON ast
chunks: chunks
tokens: tokens
,
_.extend options, silent: true
@posttraverse util.insertHelpers
this
traverse: (prefunc, postfunc, ast) ->
ast ?= @get 'ast'
chunks = @get 'chunks'
if ast? and chunks?
util.traverse.call this, ast, chunks, prefunc, postfunc
pretraverse: (f, ast) ->
@traverse f, null, ast
posttraverse: (f, ast) ->
@traverse null, f, ast
extractFunction: (node, functionList) ->
parent = node.parent
name = ''
if node.type is Syntax.FunctionDeclaration
name = node.id.name
else if node.type is Syntax.FunctionExpression
if parent.type is Syntax.AssignmentExpression and parent.left.range?
name = code.slice parent.left.range[0], parent.left.range[1] + 1
else if parent.type is Syntax.VariableDeclarator
name = parent.id.name
else if parent.type is Syntax.CallExpression
name = if parent.id then parent.id.name else '[Anonymous]'
else if typeof parent.length is 'number'
name = if parent.id then parent.id.name else '[Anonymous]'
else if parent.key?
if parent.key.type is Syntax.Identifier and parent.value is node and parent.key.name
name = parent.key.name
if name? and name.length
functionList.push _.extend node, name: name
instrumentFunctions: ->
trackList = []
@pretraverse (node) =>
@extractFunction node, trackList
if node.type in [
Syntax.ForStatement,
Syntax.ForInStatement,
Syntax.ExpressionStatement,
Syntax.VariableDeclaration,
Syntax.ReturnStatement
]
trackList.push node
chunks = @get 'chunks'
chunksCopy = _.clone chunks
window.tracer.genTraces trackList, @scopes
# Store updated source with function traces
source = @source()
# Reset chunks
for chunk, i in chunksCopy
chunks[i] = chunksCopy[i]
window.tracer.active = true
inventingOnPrinciple.view.clearConsole()
if source.length
eval.call
_: _
, source
hist = window.tracer.funcHistogram()
funs = _.filter trackList, (exp) -> exp.type in [Syntax.FunctionExpression, Syntax.FunctionDeclaration]
@trigger 'tracedFunctions', hist, funs
list = window.tracer.getStatementList()
@trigger 'tracedStatements', list
# vars = window.tracer.getVars()
# @trigger 'tracedVars', vars
window.tracer.active = false
this
extractDeclarations: ->
ast = @get 'ast'
scope = {}
vars = []
funs = []
@pretraverse (node) ->
if node.type is Syntax.VariableDeclarator
model = new inventingOnPrinciple.Models.VariableModel(node)
vars.push model
else if node.type is Syntax.FunctionDeclaration
model = new inventingOnPrinciple.Models.FunctionModel(node)
funs.push(node)
@get('vars').reset vars
@get('funs').reset funs
@trigger 'change:decs', vars, funs
this
buildScope: ->
ast = @get 'ast'
@scopes =
global:
name: 'global'
node: ast
vars: []
funcs: []
@posttraverse (node) ->
scope = util.scopeLookup(node, @scopes)
switch (node.type)
when Syntax.VariableDeclarator
scope.vars.push node.id.name
when Syntax.FunctionDeclaration
scopedName = util.scopeName node.id.name, scope
scope.funcs.push scopedName
@scopes[scopedName] =
name: scopedName
node: node
parent: scope
vars: ['arguments']
funcs: []
else
this
onASTChange: ->
try
generated = window.escodegen.generate @get 'ast'
@set generatedCode: generated
catch e
console.log 'gen Error', e
trackCursor: (cursorLoc, cursorIndex) ->
markers =
indentifier: []
highlight: []
id = null
@pretraverse (node) =>
if node? and node.type is Syntax.Identifier and util.withinRange(cursorIndex, node.range)
markers.indentifier.push node.loc
id = node
if id?
@pretraverse (node) =>
if node? and node.type is Syntax.Identifier and node isnt id and node.name is id.name
markers.highlight.push node.loc
markers
updateHints: (editor) ->
hasError = false
editor.operation =>
JSHINT editor.getValue()
for err in JSHINT.errors
continue unless err?
@trigger 'error', err
hasError = true
hasError
parse: (text, editor) ->
# if (text == @source()) return
return if inventingOnPrinciple.updating
if not @updateHints editor
try
@setSource text
@extractDeclarations?()
@buildScope?()
@instrumentFunctions?()
catch e
# console.log e.name + ': ' + e.message
# console.log @source()
# console.trace e
@trigger 'error', e
tokens: ->
@get 'tokens'
generatedCode: ->
@get 'generatedCode'
astString: ->
@get 'astText'
source: ->
@get('ast').source?()