unparser 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|