unparser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.circle.yml +4 -0
  3. data/.gitignore +2 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +16 -0
  6. data/Changelog.md +3 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.devtools +60 -0
  9. data/Guardfile +18 -0
  10. data/README.md +41 -0
  11. data/Rakefile +24 -0
  12. data/TODO +1 -0
  13. data/config/devtools.yml +2 -0
  14. data/config/flay.yml +3 -0
  15. data/config/flog.yml +2 -0
  16. data/config/mutant.yml +3 -0
  17. data/config/reek.yml +107 -0
  18. data/config/yardstick.yml +2 -0
  19. data/lib/unparser/buffer.rb +93 -0
  20. data/lib/unparser/constants.rb +87 -0
  21. data/lib/unparser/emitter/alias.rb +27 -0
  22. data/lib/unparser/emitter/argument.rb +154 -0
  23. data/lib/unparser/emitter/assignment.rb +140 -0
  24. data/lib/unparser/emitter/begin.rb +179 -0
  25. data/lib/unparser/emitter/binary.rb +52 -0
  26. data/lib/unparser/emitter/block.rb +53 -0
  27. data/lib/unparser/emitter/break.rb +28 -0
  28. data/lib/unparser/emitter/case.rb +97 -0
  29. data/lib/unparser/emitter/cbase.rb +22 -0
  30. data/lib/unparser/emitter/class.rb +72 -0
  31. data/lib/unparser/emitter/def.rb +107 -0
  32. data/lib/unparser/emitter/defined.rb +27 -0
  33. data/lib/unparser/emitter/for.rb +50 -0
  34. data/lib/unparser/emitter/hookexe.rb +42 -0
  35. data/lib/unparser/emitter/if.rb +85 -0
  36. data/lib/unparser/emitter/literal/composed.rb +64 -0
  37. data/lib/unparser/emitter/literal/dynamic.rb +54 -0
  38. data/lib/unparser/emitter/literal/dynamic_body.rb +109 -0
  39. data/lib/unparser/emitter/literal/execute_string.rb +38 -0
  40. data/lib/unparser/emitter/literal/primitive.rb +49 -0
  41. data/lib/unparser/emitter/literal/range.rb +33 -0
  42. data/lib/unparser/emitter/literal/regexp.rb +96 -0
  43. data/lib/unparser/emitter/literal/singleton.rb +24 -0
  44. data/lib/unparser/emitter/literal.rb +7 -0
  45. data/lib/unparser/emitter/module.rb +37 -0
  46. data/lib/unparser/emitter/next.rb +22 -0
  47. data/lib/unparser/emitter/not.rb +25 -0
  48. data/lib/unparser/emitter/op_assign.rb +63 -0
  49. data/lib/unparser/emitter/redo.rb +22 -0
  50. data/lib/unparser/emitter/repetition.rb +33 -0
  51. data/lib/unparser/emitter/retry.rb +22 -0
  52. data/lib/unparser/emitter/return.rb +38 -0
  53. data/lib/unparser/emitter/root.rb +7 -0
  54. data/lib/unparser/emitter/send/binary.rb +100 -0
  55. data/lib/unparser/emitter/send/index.rb +82 -0
  56. data/lib/unparser/emitter/send/unary.rb +29 -0
  57. data/lib/unparser/emitter/send.rb +273 -0
  58. data/lib/unparser/emitter/splat.rb +24 -0
  59. data/lib/unparser/emitter/super.rb +46 -0
  60. data/lib/unparser/emitter/undef.rb +25 -0
  61. data/lib/unparser/emitter/variable.rb +83 -0
  62. data/lib/unparser/emitter/yield.rb +28 -0
  63. data/lib/unparser/emitter.rb +308 -0
  64. data/lib/unparser.rb +87 -0
  65. data/spec/integration/unparser/spike_spec.rb +914 -0
  66. data/spec/spec_helper.rb +13 -0
  67. data/spec/unit/unparser/buffer/append_spec.rb +23 -0
  68. data/spec/unit/unparser/buffer/content_spec.rb +38 -0
  69. data/spec/unit/unparser/buffer/indent_spec.rb +18 -0
  70. data/spec/unit/unparser/buffer/nl_spec.rb +16 -0
  71. data/spec/unit/unparser/buffer/unindent_spec.rb +18 -0
  72. data/spec/unit/unparser/class_methods/unparse_spec.rb +16 -0
  73. data/spec/unit/unparser/emitter/class_methods/handle_spec.rb +17 -0
  74. data/spec/unit/unparser/emitter/class_methods/visit_spec.rb +37 -0
  75. data/spec/unit/unparser/emitter/source_map/class_methods/emit_spec.rb +20 -0
  76. data/unparser.gemspec +24 -0
  77. metadata +201 -0
@@ -0,0 +1,308 @@
1
+ module Unparser
2
+
3
+ # Emitter base class
4
+ class Emitter
5
+ include Adamantium::Flat, AbstractType, Constants
6
+ include Equalizer.new(:node, :buffer, :parent)
7
+
8
+ # Registry for node emitters
9
+ REGISTRY = {}
10
+
11
+ DEFAULT_DELIMITER = ', '.freeze
12
+
13
+ CURLY_BRACKETS = IceNine.deep_freeze(%w({ }))
14
+
15
+ # Create name helpers
16
+ #
17
+ # @return [undefined]
18
+ #
19
+ # @api private
20
+ #
21
+ def self.children(*names)
22
+ names.each_with_index do |name, index|
23
+ define_method(name) do
24
+ children[index]
25
+ end
26
+ private name
27
+ end
28
+ end
29
+ private_class_method :children
30
+
31
+ # Register emitter for type
32
+ #
33
+ # @param [Symbol] type
34
+ #
35
+ # @return [undefined]
36
+ #
37
+ # @api private
38
+ #
39
+ def self.handle(*types)
40
+ types.each do |type|
41
+ REGISTRY[type] = self
42
+ end
43
+ end
44
+ private_class_method :handle
45
+
46
+ # Emit node into buffer
47
+ #
48
+ # @return [self]
49
+ #
50
+ # @api private
51
+ #
52
+ def self.emit(*arguments)
53
+ new(*arguments)
54
+ self
55
+ end
56
+
57
+ # Initialize object
58
+ #
59
+ # @param [Parser::AST::Node] node
60
+ # @param [Buffer] buffer
61
+ #
62
+ # @return [undefined]
63
+ #
64
+ # @api private
65
+ #
66
+ def initialize(node, buffer, parent)
67
+ @node, @buffer, @parent = node, buffer, parent
68
+ dispatch
69
+ end
70
+
71
+ private_class_method :new
72
+
73
+ # Visit node
74
+ #
75
+ # @param [Parser::AST::Node] node
76
+ # @param [Buffer] buffer
77
+ #
78
+ # @return [Emitter]
79
+ #
80
+ # @api private
81
+ #
82
+ def self.visit(node, buffer, parent = Root)
83
+ type = node.type
84
+ emitter = REGISTRY.fetch(type) do
85
+ raise ArgumentError, "No emitter for node: #{type.inspect}"
86
+ end
87
+ emitter.emit(node, buffer, parent)
88
+ self
89
+ end
90
+
91
+ # Test if node needs begin
92
+ #
93
+ # @return [true]
94
+ # if if node needs begin
95
+ #
96
+ # @return [false]
97
+ # otherwise
98
+ #
99
+ # @api private
100
+ #
101
+ def needs_begin?
102
+ false
103
+ end
104
+
105
+ # Return node
106
+ #
107
+ # @return [Parser::AST::Node] node
108
+ #
109
+ # @api private
110
+ #
111
+ attr_reader :node
112
+
113
+ private
114
+
115
+ # Return buffer
116
+ #
117
+ # @return [Buffer] buffer
118
+ #
119
+ # @api private
120
+ #
121
+ attr_reader :buffer
122
+ protected :buffer
123
+
124
+ # Return parent emitter
125
+ #
126
+ # @return [Parent]
127
+ #
128
+ # @api private
129
+ #
130
+ attr_reader :parent
131
+ protected :parent
132
+
133
+ # Emit contents of block within parentheses
134
+ #
135
+ # @return [undefined]
136
+ #
137
+ # @api private
138
+ #
139
+ def parentheses(open=M_PO, close=M_PC)
140
+ write(open)
141
+ yield
142
+ write(close)
143
+ end
144
+
145
+ # Emit nodes source map
146
+ #
147
+ # @return [undefined]
148
+ #
149
+ # @api private
150
+ #
151
+ def emit_source_map
152
+ SourceMap.emit(node, buffer)
153
+ end
154
+
155
+ # Dispatch helper
156
+ #
157
+ # @param [Parser::AST::Node] node
158
+ #
159
+ # @return [undefined]
160
+ #
161
+ # @api private
162
+ #
163
+ def visit(node)
164
+ self.class.visit(node, buffer, self)
165
+ end
166
+
167
+ # Emit delimited body
168
+ #
169
+ # @param [Enumerable<Parser::AST::Node>] nodes
170
+ # @param [String] delimiter
171
+ #
172
+ # @return [undefined]
173
+ #
174
+ # @api private
175
+ #
176
+ def delimited(nodes, delimiter = DEFAULT_DELIMITER)
177
+ max = nodes.length - 1
178
+ nodes.each_with_index do |node, index|
179
+ visit(node)
180
+ write(delimiter) if index < max
181
+ end
182
+ end
183
+
184
+ # Return children of node
185
+ #
186
+ # @return [Array<Parser::AST::Node>]
187
+ #
188
+ # @api private
189
+ #
190
+ def children
191
+ node.children
192
+ end
193
+
194
+ # Write newline
195
+ #
196
+ # @return [undefined]
197
+ #
198
+ # @api private
199
+ #
200
+ def nl
201
+ buffer.nl
202
+ end
203
+
204
+ # Write strings into buffer
205
+ #
206
+ # @return [undefined]
207
+ #
208
+ # @api private
209
+ #
210
+ def write(*strings)
211
+ strings.each do |string|
212
+ buffer.append(string)
213
+ end
214
+ end
215
+
216
+ # Write begin keyword
217
+ #
218
+ # @return [undefined]
219
+ #
220
+ # @api private
221
+ #
222
+ def k_begin
223
+ write(K_BEGIN)
224
+ end
225
+
226
+ # Write end keyword
227
+ #
228
+ # @return [undefined]
229
+ #
230
+ # @api private
231
+ #
232
+ def k_end
233
+ write(K_END)
234
+ end
235
+
236
+ # Return first child
237
+ #
238
+ # @return [Parser::AST::Node]
239
+ # if present
240
+ #
241
+ # @return [nil]
242
+ # otherwise
243
+ #
244
+ # @api private
245
+ #
246
+ def first_child
247
+ children.first
248
+ end
249
+
250
+ # Write whitespace
251
+ #
252
+ # @return [undefined]
253
+ #
254
+ # @api private
255
+ #
256
+ def ws
257
+ write(WS)
258
+ end
259
+
260
+ # Call emit contents of block indented
261
+ #
262
+ # @return [undefined]
263
+ #
264
+ # @api private
265
+ #
266
+ def indented
267
+ buffer = self.buffer
268
+ buffer.indent
269
+ yield
270
+ buffer.unindent
271
+ end
272
+
273
+ # Emit non nil body
274
+ #
275
+ # @param [Parser::AST::Node] node
276
+ #
277
+ # @return [undefined]
278
+ #
279
+ # @api private
280
+ #
281
+ def emit_non_nil_body(node)
282
+ if node.type == :nil
283
+ nl
284
+ return
285
+ end
286
+ indented { visit(node) }
287
+ end
288
+
289
+ # Emitter that fully relies on parser source maps
290
+ class SourceMap < self
291
+
292
+ # Perform dispatch
293
+ #
294
+ # @param [Node] node
295
+ # @param [Buffer] buffer
296
+ #
297
+ # @return [self]
298
+ #
299
+ # @api private
300
+ #
301
+ def self.emit(node, buffer)
302
+ buffer.append(node.location.expression.source)
303
+ self
304
+ end
305
+
306
+ end # SourceMap
307
+ end # Emitter
308
+ end # Unparser
data/lib/unparser.rb ADDED
@@ -0,0 +1,87 @@
1
+ require 'abstract_type'
2
+ require 'concord'
3
+ require 'parser/all'
4
+
5
+ # Library namespace
6
+ module Unparser
7
+
8
+ EMPTY_STRING = ''.freeze
9
+
10
+ # Unparse ast into string
11
+ #
12
+ # @param [Parser::Node] node
13
+ #
14
+ # @return [String]
15
+ #
16
+ # @api private
17
+ #
18
+ def self.unparse(node)
19
+ buffer = Buffer.new
20
+ Emitter.visit(node, buffer)
21
+ buffer.content
22
+ end
23
+
24
+ # Transquote string
25
+ #
26
+ # @param [String] raw_quoted
27
+ # @param [String] current_delimiter
28
+ # @param [String] target_delimiter
29
+ #
30
+ # @return [String]
31
+ #
32
+ # @api private
33
+ #
34
+ def self.transquote(raw_quoted, current_delimiter, target_delimiter)
35
+ raw = raw_quoted.gsub("\\#{current_delimiter}", current_delimiter)
36
+ raw.gsub(target_delimiter, "\\#{target_delimiter}").freeze
37
+ end
38
+
39
+ end # Unparser
40
+
41
+ require 'unparser/buffer'
42
+ require 'unparser/constants'
43
+ require 'unparser/emitter'
44
+ require 'unparser/emitter/literal'
45
+ require 'unparser/emitter/literal/primitive'
46
+ require 'unparser/emitter/literal/singleton'
47
+ require 'unparser/emitter/literal/dynamic'
48
+ require 'unparser/emitter/literal/regexp'
49
+ require 'unparser/emitter/literal/composed'
50
+ require 'unparser/emitter/literal/range'
51
+ require 'unparser/emitter/literal/dynamic_body'
52
+ require 'unparser/emitter/literal/execute_string'
53
+ require 'unparser/emitter/send'
54
+ require 'unparser/emitter/send/unary'
55
+ require 'unparser/emitter/send/binary'
56
+ require 'unparser/emitter/send/index'
57
+ require 'unparser/emitter/block'
58
+ require 'unparser/emitter/assignment'
59
+ require 'unparser/emitter/variable'
60
+ require 'unparser/emitter/splat'
61
+ require 'unparser/emitter/cbase'
62
+ require 'unparser/emitter/argument'
63
+ require 'unparser/emitter/begin'
64
+ require 'unparser/emitter/return'
65
+ require 'unparser/emitter/undef'
66
+ require 'unparser/emitter/def'
67
+ require 'unparser/emitter/class'
68
+ require 'unparser/emitter/module'
69
+ require 'unparser/emitter/op_assign'
70
+ require 'unparser/emitter/defined'
71
+ require 'unparser/emitter/hookexe'
72
+ require 'unparser/emitter/super'
73
+ require 'unparser/emitter/break'
74
+ require 'unparser/emitter/retry'
75
+ require 'unparser/emitter/redo'
76
+ require 'unparser/emitter/next'
77
+ require 'unparser/emitter/if'
78
+ require 'unparser/emitter/alias'
79
+ require 'unparser/emitter/yield'
80
+ require 'unparser/emitter/not'
81
+ require 'unparser/emitter/binary'
82
+ require 'unparser/emitter/case'
83
+ require 'unparser/emitter/for'
84
+ require 'unparser/emitter/repetition'
85
+ require 'unparser/emitter/root'
86
+
87
+ Unparser::Emitter::REGISTRY.freeze