parsejs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,183 @@
1
+ require "parsejs/visitor"
2
+
3
+ module ParseJS
4
+ module AST
5
+ module Scope
6
+ attr_accessor :scope_variables, :parent_variables, :variable_access
7
+ attr_accessor :parent_scope, :child_scopes
8
+
9
+ def variable_in_scope?(name)
10
+ return true if variable?(name)
11
+ parent_scope && parent_scope.variable_in_scope?(name)
12
+ end
13
+
14
+ def variable?(name)
15
+ scope_variable?(name) || parent_variable?(name) || variable_access?(name)
16
+ end
17
+
18
+ def scope_variable?(name)
19
+ scope_variables && scope_variables.include?(name)
20
+ end
21
+
22
+ def parent_variable?(name)
23
+ parent_variables && parent_variables.include?(name)
24
+ end
25
+
26
+ def variable_access?(name)
27
+ variable_access && variable_access.include?(name)
28
+ end
29
+
30
+ # determine whether there is a reference for a particular variable
31
+ # to a parent scope.
32
+ def parent_variable_access?(name)
33
+ # if this scope has a "var x = 1" type of declaration for this name,
34
+ # it is not referencing a parent scope.
35
+ return false if scope_variable?(name)
36
+
37
+ # otherwise, if there is a variable access for this name, it's
38
+ # referencing a parent scope.
39
+ variable_access?(name)
40
+ end
41
+
42
+ # determine whether a variable can be added without causing damage
43
+ # to child scopes.
44
+ def available_variable?(name)
45
+ # if the current scope is already using the variable, it's
46
+ # unavailable.
47
+ return false if variable?(name)
48
+
49
+ # if any of the child scopes reference the variable as a
50
+ # parent variable, it's not available.
51
+ !any_child_references_parent_variable?(name)
52
+ end
53
+
54
+ def any_child_references_parent_variable?(name)
55
+ unless child_scopes.nil?
56
+ # this should really check if all descendent scopes see a
57
+ # scope variable before they see a parent refernce
58
+
59
+ return false if child_scopes.all? { |s| s.scope_variable?(name) }
60
+
61
+ child_scopes.any? do |s|
62
+ s.parent_variable_access?(name) ||
63
+ s.parent_variable?(name) ||
64
+ s.any_child_references_parent_variable?(name)
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ class ProcessVariables < Visitor
71
+ include ParseJS::Visitor::ScopeManager
72
+
73
+ def self.process(ast)
74
+ new.visit(ast)
75
+ end
76
+
77
+ def push_scope_variable(name)
78
+ vars = current_scope.scope_variables ||= Set.new
79
+ vars << name
80
+ end
81
+
82
+ def push_parent_variable(name)
83
+ vars = current_scope.parent_variables ||= Set.new
84
+ vars << name
85
+ end
86
+
87
+ def push_variable_access(name)
88
+ vars = current_scope.variable_access ||= Set.new
89
+ vars << name
90
+ end
91
+
92
+ def possible_variable_access(var)
93
+ push_variable_access var.val if var && var.type?("Identifier")
94
+ end
95
+
96
+ def possible_variable_access_array(arr)
97
+ arr.each { |arg| possible_variable_access arg }
98
+ end
99
+
100
+ def visit_VariableDeclarator(decl)
101
+ push_scope_variable decl.id.val
102
+ possible_variable_access decl.init
103
+
104
+ super
105
+ end
106
+
107
+ def visit_MemberExpression(expr)
108
+ possible_variable_access expr.object
109
+
110
+ super
111
+ end
112
+
113
+ def visit_CallExpression(expr)
114
+ possible_variable_access expr.callee
115
+ possible_variable_access_array expr.args
116
+
117
+ super
118
+ end
119
+
120
+ def visit_NewExpression(expr)
121
+ possible_variable_access expr.callee
122
+ possible_variable_access_array expr.args if expr.args
123
+
124
+ super
125
+ end
126
+
127
+ def visit_ArrayExpression(expr)
128
+ possible_variable_access_array expr.elements
129
+
130
+ super
131
+ end
132
+
133
+ def visit_AssignmentExpression(expr)
134
+ if expr.left.type?("Identifier")
135
+ push_parent_variable expr.left.val
136
+ end
137
+
138
+ possible_variable_access expr.right
139
+
140
+ super
141
+ end
142
+
143
+ def visit_Property(prop)
144
+ possible_variable_access prop.value
145
+
146
+ super
147
+ end
148
+
149
+ def visit_SequenceExpression(expr)
150
+ possible_variable_access_array expr.expressions
151
+
152
+ super
153
+ end
154
+
155
+ def visit_UpdateExpression(expr)
156
+ possible_variable_access expr.argument
157
+
158
+ super
159
+ end
160
+
161
+ def visit_UnaryExpression(expr)
162
+ possible_variable_access expr.argument
163
+
164
+ super
165
+ end
166
+
167
+ def visit_BinaryExpression(expr)
168
+ possible_variable_access expr.left
169
+ possible_variable_access expr.right
170
+
171
+ super
172
+ end
173
+
174
+ def visit_ConditionalExpression(expr)
175
+ possible_variable_access expr.alternate
176
+ possible_variable_access expr.consequent
177
+ possible_variable_access expr.test
178
+
179
+ super
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,450 @@
1
+ require "parsejs/visitor"
2
+ require "json"
3
+
4
+ module ParseJS
5
+ require "pp"
6
+
7
+ class Stringifier < Visitor
8
+ def self.to_string(ast)
9
+ stringifier = new(ast)
10
+ yield stringifier if block_given?
11
+ stringifier.to_string
12
+ end
13
+
14
+ attr_accessor :include_comments
15
+
16
+ def initialize(ast)
17
+ @ast = ast
18
+ @indent = 0
19
+ @include_comments = false
20
+ end
21
+
22
+ def accept(node)
23
+ return "" if node.nil?
24
+
25
+ nl = @newline
26
+
27
+ out = if node.cuddly?
28
+ " " << super
29
+ elsif @newline
30
+ @newline = false
31
+ current_indent << super
32
+ else
33
+ super
34
+ end
35
+
36
+ out << newline if node.needs_newline? && needs_newline?(out)
37
+
38
+ out
39
+ end
40
+
41
+ def indent
42
+ @indent += 1
43
+ end
44
+
45
+ def outdent
46
+ @indent -= 1
47
+ end
48
+
49
+ def current_indent
50
+ " " * @indent
51
+ end
52
+
53
+ def newline
54
+ @newline = true
55
+ "\n"
56
+ end
57
+
58
+ def cuddle(node, out, more=false)
59
+ if node.cuddly?
60
+ node.cuddle! if more
61
+ else
62
+ indent
63
+ out << newline
64
+ end
65
+
66
+ out << accept(node)
67
+
68
+ if more && node.cuddly?
69
+ out << " "
70
+ @newline = false
71
+ end
72
+
73
+ unless node.cuddly?
74
+ outdent
75
+ out << current_indent if more
76
+ end
77
+ end
78
+
79
+ def without_newline
80
+ old, @skip_newline = @skip_newline, true
81
+ ret = yield
82
+ @skip_newline = old
83
+ ret
84
+ end
85
+
86
+ def needs_newline?(out)
87
+ out !~ /\n$/ && !@skip_newline
88
+ end
89
+
90
+ def strip_newline(str)
91
+ @newline = false
92
+ str.sub(/\n$/, '')
93
+ end
94
+
95
+ def to_string
96
+ accept @ast
97
+ end
98
+
99
+ def params(node)
100
+ map(node).join(", ")
101
+ end
102
+
103
+ def each(node)
104
+ node.each { |element| accept(element) }
105
+ end
106
+
107
+ def visit_Program(program)
108
+ map(program.elements).join("")
109
+ end
110
+
111
+ def visit_ExpressionStatement(statement)
112
+ accept(statement.expression) + ";"
113
+ end
114
+
115
+ def visit_SequenceExpression(expression)
116
+ out = ""
117
+ out << "(" if expression.parens
118
+ exprs = params(expression.expressions)
119
+ exprs = strip_newline(exprs) if expression.parens
120
+ out << exprs
121
+ out << ")" if expression.parens
122
+ out
123
+ end
124
+
125
+ def visit_Literal(literal)
126
+ case val = literal.val
127
+ when nil
128
+ "null"
129
+ when ParseJS::AST::Node
130
+ accept val
131
+ else
132
+ val.inspect
133
+ end
134
+ end
135
+
136
+ def visit_String(string)
137
+ string.quote + super + string.quote
138
+ end
139
+
140
+ def visit_RegExp(regex)
141
+ "/#{super.join("/")}"
142
+ end
143
+
144
+ def visit_DebuggerStatement(expr)
145
+ "#{super};"
146
+ end
147
+
148
+ def visit_UnaryExpression(unary)
149
+ op, argument = super
150
+ space = op =~ /\w/ ? sp : ""
151
+ "#{op}#{space}#{argument}"
152
+ end
153
+
154
+ def visit_AssignmentExpression(expr)
155
+ left, op, right = super
156
+ "#{left} #{op} #{right}"
157
+ end
158
+
159
+ def visit_CallExpression(expr)
160
+ out = strip_newline(accept(expr.callee))
161
+ args = params(expr.args)
162
+ args = strip_newline(args)
163
+ out << "(" + args + ")"
164
+ end
165
+
166
+ def visit_ArrayExpression(expr)
167
+
168
+ "[" + begin
169
+
170
+ last, out = expr.elements.size - 1, ""
171
+ expr.elements.each_with_index do |element, i|
172
+ if element.nil?
173
+ out << ","
174
+ out << " " unless i == last
175
+ else
176
+ out << accept(element)
177
+ out << ", " unless i == last
178
+ end
179
+ end
180
+ out
181
+
182
+ end + "]"
183
+ end
184
+
185
+ def visit_ObjectExpression(expr)
186
+ if expr.properties.length > 2
187
+ out = "{" << newline
188
+ indent
189
+
190
+ last = expr.properties.size - 1
191
+ expr.properties.each_with_index do |prop, i|
192
+ out << strip_newline(accept(prop))
193
+ out << "," unless last == i
194
+ out << newline
195
+ end
196
+
197
+ outdent
198
+ out << current_indent << "}"
199
+ else
200
+ "{#{params(expr.properties)}}"
201
+ end
202
+ end
203
+
204
+ def visit_Property(property)
205
+ comments, key, value = super
206
+
207
+ "#{comments.join}#{key}: #{value}"
208
+ end
209
+
210
+ def visit_CommentedStatement(statement)
211
+ comments, statement = super
212
+ "#{comments.join}#{statement}"
213
+ end
214
+
215
+ def visit_MemberExpression(expr)
216
+ left = strip_newline(accept(expr.object))
217
+ right = accept(expr.property)
218
+
219
+ if expr.computed
220
+ "#{left}[#{right}]"
221
+ else
222
+ "#{left}.#{right}"
223
+ end
224
+ end
225
+
226
+ def visit_NewExpression(expr)
227
+ callee, args = super
228
+
229
+ left = "new #{callee}"
230
+ arg_string = "(#{args.join(", ")})" if args
231
+ return "#{left}#{arg_string}"
232
+ end
233
+
234
+ def visit_BinaryExpression(expr)
235
+ left = strip_newline(accept(expr.left))
236
+ right = accept(expr.right)
237
+
238
+ "#{left} #{expr.op} #{right}"
239
+ end
240
+
241
+ def visit_BlockStatement(statement)
242
+ out = "{" << newline
243
+ indent
244
+ out << super.join
245
+ outdent
246
+ out << current_indent << "}"
247
+ @newline = false unless statement.cuddly
248
+ out
249
+ end
250
+
251
+ def visit_IfStatement(statement)
252
+ consequent = statement.consequent
253
+ alternate = statement.alternate
254
+
255
+ out = "if (" + accept(statement.test) + ")"
256
+
257
+ cuddle(consequent, out, alternate)
258
+
259
+ if alternate
260
+ out << "else"
261
+ cuddle(alternate, out, false)
262
+ end
263
+
264
+ out
265
+ end
266
+
267
+ def visit_WhileStatement(statement)
268
+ test = statement.test
269
+ body = statement.body
270
+
271
+ out = "while (" + accept(test) + ")"
272
+ cuddle(body, out, false)
273
+ out
274
+ end
275
+
276
+ def visit_DoWhileStatement(statement)
277
+ out = "do"
278
+ cuddle(statement.body, out, true)
279
+ out << "while (" + accept(statement.test) + ");"
280
+ end
281
+
282
+ def visit_ForStatement(statement)
283
+ init = statement.init
284
+ test = statement.test
285
+ update = statement.update
286
+ body = statement.body
287
+ out = ""
288
+
289
+ without_newline do
290
+ out << "for (" + accept(init) + ";"
291
+ test = accept(test)
292
+ out << " #{test}" unless test.empty?
293
+ out << ";"
294
+ update = accept(update)
295
+ out << " #{update}" unless update.empty?
296
+ out << ")"
297
+ end
298
+
299
+ cuddle(body, out, false)
300
+ out
301
+ end
302
+
303
+ def visit_VariableDeclaration(decl)
304
+ kind, declarations, semicolon = super
305
+ "#{kind} #{declarations.join(", ")}#{";" if semicolon}"
306
+ end
307
+
308
+ def visit_VariableDeclarator(decl)
309
+ id, init = super
310
+
311
+ out = id
312
+ out << (init ? " = #{init}" : "")
313
+ end
314
+
315
+ def visit_UpdateExpression(expr)
316
+ op, prefix, argument = super
317
+
318
+ op += " " if op =~ /\w/
319
+
320
+ if prefix
321
+ "#{op}#{argument}"
322
+ else
323
+ "#{argument}#{op}"
324
+ end
325
+ end
326
+
327
+ def visit_ForInStatement(statement)
328
+ left = statement.left
329
+ right = statement.right
330
+ body = statement.body
331
+
332
+ out = ""
333
+
334
+ without_newline do
335
+ out << "for (" + accept(left) + " in " + accept(right) + ")"
336
+ end
337
+
338
+ cuddle(body, out, false)
339
+ out
340
+ end
341
+
342
+ def visit_SwitchStatement(statement)
343
+ out = ""
344
+
345
+ without_newline do
346
+ out << "switch (" + accept(statement.discriminant) + ") {" << newline
347
+ indent
348
+ end
349
+
350
+ out << map(statement.cases).join
351
+ outdent
352
+ out << current_indent << "}" << newline
353
+ end
354
+
355
+ def visit_SwitchCase(switch)
356
+ if switch.test
357
+ out = "case #{accept(switch.test)}:" << newline
358
+ else
359
+ out = "default:" << newline
360
+ end
361
+
362
+ indent
363
+ out << map(switch.consequent).join
364
+ outdent
365
+ out
366
+ end
367
+
368
+ def visit_ThrowStatement(statement)
369
+ "throw #{super};"
370
+ end
371
+
372
+ def visit_TryStatement(statement)
373
+ handler = statement.handler
374
+ finalizer = statement.finalizer
375
+
376
+ out = "try"
377
+
378
+ cuddle(statement.block, out, handler || finalizer)
379
+
380
+ if handler
381
+ out << "catch (" + accept(handler.param) + ")"
382
+ cuddle(handler.body, out, finalizer)
383
+ end
384
+
385
+ if finalizer
386
+ out << "finally"
387
+ cuddle(finalizer, out, false)
388
+ end
389
+
390
+ out
391
+ end
392
+
393
+ def visit_FunctionDeclaration(decl)
394
+ id = decl.id
395
+ parameters = decl.params.list
396
+ body = decl.body
397
+
398
+ out = "function " + accept(id) + "("
399
+ out << params(parameters)
400
+ out << ") {" << newline
401
+
402
+ indent
403
+ out << map(decl.body).join
404
+ outdent
405
+ out << current_indent << "}"
406
+ end
407
+
408
+ def labeled(name, label)
409
+ out = name
410
+ out << " #{label}" if label
411
+ out << ";"
412
+ end
413
+
414
+ def visit_ReturnStatement(statement)
415
+ labeled("return", super)
416
+ end
417
+
418
+ def visit_BreakStatement(statement)
419
+ labeled("break", super)
420
+ end
421
+
422
+ def visit_ContinueStatement(statement)
423
+ labeled("continue", super)
424
+ end
425
+
426
+ def visit_ConditionalExpression(expr)
427
+ out = strip_newline(accept(expr.test))
428
+ out << " ? " << strip_newline(accept(expr.consequent))
429
+ out << " : " << accept(expr.alternate)
430
+ end
431
+
432
+ def visit_Comment(comment)
433
+ return "" unless include_comments
434
+ if comment.type == 'singleline'
435
+ "//" + comment.body + newline
436
+ else
437
+ body = comment.body.split("\n")
438
+ first = body.shift
439
+ out = "/*" + first + newline + body.map { |s| "#{current_indent}#{s}" }.join(newline) + "*/"
440
+ out << "\n" if comment.newline
441
+ out
442
+ end
443
+ end
444
+
445
+ private
446
+ def sp
447
+ " "
448
+ end
449
+ end
450
+ end