Class: ParseJS::Stringifier

Inherits:
Visitor
  • Object
show all
Defined in:
lib/parsejs/stringifier.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Visitor

#map, #visit, #visit_CatchClause, #visit_EmptyStatement, #visit_FunctionExpression, #visit_Identifier, #visit_LogicalExpression, #visit_Number, #visit_ParameterList, #visit_ThisExpression, #visit_WithStatement

Constructor Details

#initialize(ast) ⇒ Stringifier

Returns a new instance of Stringifier.



16
17
18
19
20
# File 'lib/parsejs/stringifier.rb', line 16

def initialize(ast)
  @ast = ast
  @indent = 0
  @include_comments = false
end

Instance Attribute Details

#include_commentsObject

Returns the value of attribute include_comments.



14
15
16
# File 'lib/parsejs/stringifier.rb', line 14

def include_comments
  @include_comments
end

Class Method Details

.to_string(ast) {|stringifier| ... } ⇒ Object

Yields:

  • (stringifier)


8
9
10
11
12
# File 'lib/parsejs/stringifier.rb', line 8

def self.to_string(ast)
  stringifier = new(ast)
  yield stringifier if block_given?
  stringifier.to_string
end

Instance Method Details

#accept(node) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/parsejs/stringifier.rb', line 22

def accept(node)
  return "" if node.nil?

  nl = @newline

  out = if node.cuddly?
    " " << super
  elsif @newline
    @newline = false
    current_indent << super
  else
    super
  end

  out << newline if node.needs_newline? && needs_newline?(out)

  out
end

#cuddle(node, out, more = false) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/parsejs/stringifier.rb', line 58

def cuddle(node, out, more=false)
  if node.cuddly?
    node.cuddle! if more
  else
    indent
    out << newline
  end

  out << accept(node)

  if more && node.cuddly?
    out << " "
    @newline = false
  end

  unless node.cuddly?
    outdent
    out << current_indent if more
  end
end

#current_indentObject



49
50
51
# File 'lib/parsejs/stringifier.rb', line 49

def current_indent
  "  " * @indent
end

#each(node) ⇒ Object



103
104
105
# File 'lib/parsejs/stringifier.rb', line 103

def each(node)
  node.each { |element| accept(element) }
end

#indentObject



41
42
43
# File 'lib/parsejs/stringifier.rb', line 41

def indent
  @indent += 1
end

#labeled(name, label) ⇒ Object



408
409
410
411
412
# File 'lib/parsejs/stringifier.rb', line 408

def labeled(name, label)
  out = name
  out << " #{label}" if label
  out << ";"
end

#needs_newline?(out) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
# File 'lib/parsejs/stringifier.rb', line 86

def needs_newline?(out)
  out !~ /\n$/ && !@skip_newline
end

#newlineObject



53
54
55
56
# File 'lib/parsejs/stringifier.rb', line 53

def newline
  @newline = true
  "\n"
end

#outdentObject



45
46
47
# File 'lib/parsejs/stringifier.rb', line 45

def outdent
  @indent -= 1
end

#params(node) ⇒ Object



99
100
101
# File 'lib/parsejs/stringifier.rb', line 99

def params(node)
  map(node).join(", ")
end

#strip_newline(str) ⇒ Object



90
91
92
93
# File 'lib/parsejs/stringifier.rb', line 90

def strip_newline(str)
  @newline = false
  str.sub(/\n$/, '')
end

#to_stringObject



95
96
97
# File 'lib/parsejs/stringifier.rb', line 95

def to_string
  accept @ast
end

#visit_ArrayExpression(expr) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/parsejs/stringifier.rb', line 166

def visit_ArrayExpression(expr)

  "[" + begin

    last, out = expr.elements.size - 1, ""
    expr.elements.each_with_index do |element, i|
      if element.nil?
        out << ","
        out << " " unless i == last
      else
        out << accept(element)
        out << ", " unless i == last
      end
    end
    out

  end + "]"
end

#visit_AssignmentExpression(expr) ⇒ Object



154
155
156
157
# File 'lib/parsejs/stringifier.rb', line 154

def visit_AssignmentExpression(expr)
  left, op, right = super
  "#{left} #{op} #{right}"
end

#visit_BinaryExpression(expr) ⇒ Object



234
235
236
237
238
239
# File 'lib/parsejs/stringifier.rb', line 234

def visit_BinaryExpression(expr)
  left = strip_newline(accept(expr.left))
  right = accept(expr.right)

  "#{left} #{expr.op} #{right}"
end

#visit_BlockStatement(statement) ⇒ Object



241
242
243
244
245
246
247
248
249
# File 'lib/parsejs/stringifier.rb', line 241

def visit_BlockStatement(statement)
  out = "{" << newline
  indent
  out << super.join
  outdent
  out << current_indent << "}"
  @newline = false unless statement.cuddly
  out
end

#visit_BreakStatement(statement) ⇒ Object



418
419
420
# File 'lib/parsejs/stringifier.rb', line 418

def visit_BreakStatement(statement)
  labeled("break", super)
end

#visit_CallExpression(expr) ⇒ Object



159
160
161
162
163
164
# File 'lib/parsejs/stringifier.rb', line 159

def visit_CallExpression(expr)
  out = strip_newline(accept(expr.callee))
  args = params(expr.args)
  args = strip_newline(args)
  out << "(" + args + ")"
end

#visit_Comment(comment) ⇒ Object



432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/parsejs/stringifier.rb', line 432

def visit_Comment(comment)
  return "" unless include_comments
  if comment.type == 'singleline'
    "//" + comment.body + newline
  else
    body = comment.body.split("\n")
    first = body.shift
    out = "/*" + first + newline + body.map { |s| "#{current_indent}#{s}" }.join(newline) + "*/"
    out << "\n" if comment.newline
    out
  end
end

#visit_CommentedStatement(statement) ⇒ Object



210
211
212
213
# File 'lib/parsejs/stringifier.rb', line 210

def visit_CommentedStatement(statement)
  comments, statement = super
  "#{comments.join}#{statement}"
end

#visit_ConditionalExpression(expr) ⇒ Object



426
427
428
429
430
# File 'lib/parsejs/stringifier.rb', line 426

def visit_ConditionalExpression(expr)
  out = strip_newline(accept(expr.test))
  out << " ? " << strip_newline(accept(expr.consequent))
  out << " : " << accept(expr.alternate)
end

#visit_ContinueStatement(statement) ⇒ Object



422
423
424
# File 'lib/parsejs/stringifier.rb', line 422

def visit_ContinueStatement(statement)
  labeled("continue", super)
end

#visit_DebuggerStatement(expr) ⇒ Object



144
145
146
# File 'lib/parsejs/stringifier.rb', line 144

def visit_DebuggerStatement(expr)
  "#{super};"
end

#visit_DoWhileStatement(statement) ⇒ Object



276
277
278
279
280
# File 'lib/parsejs/stringifier.rb', line 276

def visit_DoWhileStatement(statement)
  out = "do"
  cuddle(statement.body, out, true)
  out << "while (" + accept(statement.test) + ");"
end

#visit_ExpressionStatement(statement) ⇒ Object



111
112
113
# File 'lib/parsejs/stringifier.rb', line 111

def visit_ExpressionStatement(statement)
  accept(statement.expression) + ";"
end

#visit_ForInStatement(statement) ⇒ Object



327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/parsejs/stringifier.rb', line 327

def visit_ForInStatement(statement)
  left = statement.left
  right = statement.right
  body = statement.body

  out = ""

  without_newline do
    out << "for (" + accept(left) + " in " + accept(right) + ")"
  end

  cuddle(body, out, false)
  out
end

#visit_ForStatement(statement) ⇒ Object



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/parsejs/stringifier.rb', line 282

def visit_ForStatement(statement)
  init = statement.init
  test = statement.test
  update = statement.update
  body = statement.body
  out = ""

  without_newline do
    out << "for (" + accept(init) + ";"
    test = accept(test)
    out << " #{test}" unless test.empty?
    out << ";"
    update = accept(update)
    out << " #{update}" unless update.empty?
    out << ")"
  end

  cuddle(body, out, false)
  out
end

#visit_FunctionDeclaration(decl) ⇒ Object



393
394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/parsejs/stringifier.rb', line 393

def visit_FunctionDeclaration(decl)
  id = decl.id
  parameters = decl.params.list
  body = decl.body

  out = "function " + accept(id) + "("
  out << params(parameters)
  out << ") {" << newline

  indent
  out << map(decl.body).join
  outdent
  out << current_indent << "}"
end

#visit_IfStatement(statement) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/parsejs/stringifier.rb', line 251

def visit_IfStatement(statement)
  consequent = statement.consequent
  alternate = statement.alternate

  out = "if (" + accept(statement.test) + ")"

  cuddle(consequent, out, alternate)

  if alternate
    out << "else"
    cuddle(alternate, out, false)
  end

  out
end

#visit_Literal(literal) ⇒ Object



125
126
127
128
129
130
131
132
133
134
# File 'lib/parsejs/stringifier.rb', line 125

def visit_Literal(literal)
  case val = literal.val
  when nil
    "null"
  when ParseJS::AST::Node
    accept val
  else
    val.inspect
  end
end

#visit_MemberExpression(expr) ⇒ Object



215
216
217
218
219
220
221
222
223
224
# File 'lib/parsejs/stringifier.rb', line 215

def visit_MemberExpression(expr)
  left = strip_newline(accept(expr.object))
  right = accept(expr.property)

  if expr.computed
    "#{left}[#{right}]"
  else
    "#{left}.#{right}"
  end
end

#visit_NewExpression(expr) ⇒ Object



226
227
228
229
230
231
232
# File 'lib/parsejs/stringifier.rb', line 226

def visit_NewExpression(expr)
  callee, args = super

  left = "new #{callee}"
  arg_string = "(#{args.join(", ")})" if args
  return "#{left}#{arg_string}"
end

#visit_ObjectExpression(expr) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/parsejs/stringifier.rb', line 185

def visit_ObjectExpression(expr)
  if expr.properties.length > 2
    out = "{" << newline
    indent

    last = expr.properties.size - 1
    expr.properties.each_with_index do |prop, i|
      out << strip_newline(accept(prop))
      out << "," unless last == i
      out << newline
    end

    outdent
    out << current_indent << "}"
  else
    "{#{params(expr.properties)}}"
  end
end

#visit_Program(program) ⇒ Object



107
108
109
# File 'lib/parsejs/stringifier.rb', line 107

def visit_Program(program)
  map(program.elements).join("")
end

#visit_Property(property) ⇒ Object



204
205
206
207
208
# File 'lib/parsejs/stringifier.rb', line 204

def visit_Property(property)
  comments, key, value = super

  "#{comments.join}#{key}: #{value}"
end

#visit_RegExp(regex) ⇒ Object



140
141
142
# File 'lib/parsejs/stringifier.rb', line 140

def visit_RegExp(regex)
  "/#{super.join("/")}"
end

#visit_ReturnStatement(statement) ⇒ Object



414
415
416
# File 'lib/parsejs/stringifier.rb', line 414

def visit_ReturnStatement(statement)
  labeled("return", super)
end

#visit_SequenceExpression(expression) ⇒ Object



115
116
117
118
119
120
121
122
123
# File 'lib/parsejs/stringifier.rb', line 115

def visit_SequenceExpression(expression)
  out = ""
  out << "(" if expression.parens
  exprs = params(expression.expressions)
  exprs = strip_newline(exprs) if expression.parens
  out << exprs
  out << ")" if expression.parens
  out
end

#visit_String(string) ⇒ Object



136
137
138
# File 'lib/parsejs/stringifier.rb', line 136

def visit_String(string)
  string.quote + super + string.quote
end

#visit_SwitchCase(switch) ⇒ Object



355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/parsejs/stringifier.rb', line 355

def visit_SwitchCase(switch)
  if switch.test
    out = "case #{accept(switch.test)}:" << newline
  else
    out = "default:" << newline
  end

  indent
  out << map(switch.consequent).join
  outdent
  out
end

#visit_SwitchStatement(statement) ⇒ Object



342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/parsejs/stringifier.rb', line 342

def visit_SwitchStatement(statement)
  out = ""

  without_newline do
    out << "switch (" + accept(statement.discriminant) + ") {" << newline
    indent
  end

  out << map(statement.cases).join
  outdent
  out << current_indent << "}" << newline
end

#visit_ThrowStatement(statement) ⇒ Object



368
369
370
# File 'lib/parsejs/stringifier.rb', line 368

def visit_ThrowStatement(statement)
  "throw #{super};"
end

#visit_TryStatement(statement) ⇒ Object



372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/parsejs/stringifier.rb', line 372

def visit_TryStatement(statement)
  handler = statement.handler
  finalizer = statement.finalizer

  out = "try"

  cuddle(statement.block, out, handler || finalizer)

  if handler
    out << "catch (" + accept(handler.param) + ")"
    cuddle(handler.body, out, finalizer)
  end

  if finalizer
    out << "finally"
    cuddle(finalizer, out, false)
  end

  out
end

#visit_UnaryExpression(unary) ⇒ Object



148
149
150
151
152
# File 'lib/parsejs/stringifier.rb', line 148

def visit_UnaryExpression(unary)
  op, argument = super
  space = op =~ /\w/ ? sp : ""
  "#{op}#{space}#{argument}"
end

#visit_UpdateExpression(expr) ⇒ Object



315
316
317
318
319
320
321
322
323
324
325
# File 'lib/parsejs/stringifier.rb', line 315

def visit_UpdateExpression(expr)
  op, prefix, argument = super

  op += " " if op =~ /\w/

  if prefix
    "#{op}#{argument}"
  else
    "#{argument}#{op}"
  end
end

#visit_VariableDeclaration(decl) ⇒ Object



303
304
305
306
# File 'lib/parsejs/stringifier.rb', line 303

def visit_VariableDeclaration(decl)
  kind, declarations, semicolon = super
  "#{kind} #{declarations.join(", ")}#{";" if semicolon}"
end

#visit_VariableDeclarator(decl) ⇒ Object



308
309
310
311
312
313
# File 'lib/parsejs/stringifier.rb', line 308

def visit_VariableDeclarator(decl)
  id, init = super

  out = id
  out << (init ? " = #{init}" : "")
end

#visit_WhileStatement(statement) ⇒ Object



267
268
269
270
271
272
273
274
# File 'lib/parsejs/stringifier.rb', line 267

def visit_WhileStatement(statement)
  test = statement.test
  body = statement.body

  out = "while (" + accept(test) + ")"
  cuddle(body, out, false)
  out
end

#without_newlineObject



79
80
81
82
83
84
# File 'lib/parsejs/stringifier.rb', line 79

def without_newline
  old, @skip_newline = @skip_newline, true
  ret = yield
  @skip_newline = old
  ret
end