unparser 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.circle.yml +4 -0
- data/.gitignore +2 -0
- data/.rspec +3 -0
- data/.travis.yml +16 -0
- data/Changelog.md +3 -0
- data/Gemfile +6 -0
- data/Gemfile.devtools +60 -0
- data/Guardfile +18 -0
- data/README.md +41 -0
- data/Rakefile +24 -0
- data/TODO +1 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +3 -0
- data/config/reek.yml +107 -0
- data/config/yardstick.yml +2 -0
- data/lib/unparser/buffer.rb +93 -0
- data/lib/unparser/constants.rb +87 -0
- data/lib/unparser/emitter/alias.rb +27 -0
- data/lib/unparser/emitter/argument.rb +154 -0
- data/lib/unparser/emitter/assignment.rb +140 -0
- data/lib/unparser/emitter/begin.rb +179 -0
- data/lib/unparser/emitter/binary.rb +52 -0
- data/lib/unparser/emitter/block.rb +53 -0
- data/lib/unparser/emitter/break.rb +28 -0
- data/lib/unparser/emitter/case.rb +97 -0
- data/lib/unparser/emitter/cbase.rb +22 -0
- data/lib/unparser/emitter/class.rb +72 -0
- data/lib/unparser/emitter/def.rb +107 -0
- data/lib/unparser/emitter/defined.rb +27 -0
- data/lib/unparser/emitter/for.rb +50 -0
- data/lib/unparser/emitter/hookexe.rb +42 -0
- data/lib/unparser/emitter/if.rb +85 -0
- data/lib/unparser/emitter/literal/composed.rb +64 -0
- data/lib/unparser/emitter/literal/dynamic.rb +54 -0
- data/lib/unparser/emitter/literal/dynamic_body.rb +109 -0
- data/lib/unparser/emitter/literal/execute_string.rb +38 -0
- data/lib/unparser/emitter/literal/primitive.rb +49 -0
- data/lib/unparser/emitter/literal/range.rb +33 -0
- data/lib/unparser/emitter/literal/regexp.rb +96 -0
- data/lib/unparser/emitter/literal/singleton.rb +24 -0
- data/lib/unparser/emitter/literal.rb +7 -0
- data/lib/unparser/emitter/module.rb +37 -0
- data/lib/unparser/emitter/next.rb +22 -0
- data/lib/unparser/emitter/not.rb +25 -0
- data/lib/unparser/emitter/op_assign.rb +63 -0
- data/lib/unparser/emitter/redo.rb +22 -0
- data/lib/unparser/emitter/repetition.rb +33 -0
- data/lib/unparser/emitter/retry.rb +22 -0
- data/lib/unparser/emitter/return.rb +38 -0
- data/lib/unparser/emitter/root.rb +7 -0
- data/lib/unparser/emitter/send/binary.rb +100 -0
- data/lib/unparser/emitter/send/index.rb +82 -0
- data/lib/unparser/emitter/send/unary.rb +29 -0
- data/lib/unparser/emitter/send.rb +273 -0
- data/lib/unparser/emitter/splat.rb +24 -0
- data/lib/unparser/emitter/super.rb +46 -0
- data/lib/unparser/emitter/undef.rb +25 -0
- data/lib/unparser/emitter/variable.rb +83 -0
- data/lib/unparser/emitter/yield.rb +28 -0
- data/lib/unparser/emitter.rb +308 -0
- data/lib/unparser.rb +87 -0
- data/spec/integration/unparser/spike_spec.rb +914 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/unit/unparser/buffer/append_spec.rb +23 -0
- data/spec/unit/unparser/buffer/content_spec.rb +38 -0
- data/spec/unit/unparser/buffer/indent_spec.rb +18 -0
- data/spec/unit/unparser/buffer/nl_spec.rb +16 -0
- data/spec/unit/unparser/buffer/unindent_spec.rb +18 -0
- data/spec/unit/unparser/class_methods/unparse_spec.rb +16 -0
- data/spec/unit/unparser/emitter/class_methods/handle_spec.rb +17 -0
- data/spec/unit/unparser/emitter/class_methods/visit_spec.rb +37 -0
- data/spec/unit/unparser/emitter/source_map/class_methods/emit_spec.rb +20 -0
- data/unparser.gemspec +24 -0
- 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
|