unparser 0.4.9 → 0.5.0
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 +4 -4
- data/bin/unparser +1 -1
- data/lib/unparser.rb +98 -61
- data/lib/unparser/ast.rb +0 -1
- data/lib/unparser/ast/local_variable_scope.rb +6 -76
- data/lib/unparser/buffer.rb +19 -16
- data/lib/unparser/cli.rb +26 -39
- data/lib/unparser/color.rb +0 -3
- data/lib/unparser/comments.rb +0 -26
- data/lib/unparser/constants.rb +4 -53
- data/lib/unparser/diff.rb +0 -17
- data/lib/unparser/dsl.rb +0 -32
- data/lib/unparser/emitter.rb +23 -425
- data/lib/unparser/emitter/alias.rb +2 -8
- data/lib/unparser/emitter/args.rb +45 -0
- data/lib/unparser/emitter/argument.rb +8 -166
- data/lib/unparser/emitter/array.rb +27 -0
- data/lib/unparser/emitter/array_pattern.rb +29 -0
- data/lib/unparser/emitter/assignment.rb +36 -127
- data/lib/unparser/emitter/begin.rb +9 -84
- data/lib/unparser/emitter/binary.rb +7 -20
- data/lib/unparser/emitter/block.rb +57 -41
- data/lib/unparser/emitter/case.rb +6 -48
- data/lib/unparser/emitter/case_guard.rb +27 -0
- data/lib/unparser/emitter/case_match.rb +40 -0
- data/lib/unparser/emitter/cbase.rb +1 -3
- data/lib/unparser/emitter/class.rb +6 -26
- data/lib/unparser/emitter/const_pattern.rb +24 -0
- data/lib/unparser/emitter/def.rb +7 -51
- data/lib/unparser/emitter/defined.rb +2 -12
- data/lib/unparser/emitter/dstr.rb +22 -0
- data/lib/unparser/emitter/dsym.rb +41 -0
- data/lib/unparser/emitter/flipflop.rb +11 -10
- data/lib/unparser/emitter/float.rb +29 -0
- data/lib/unparser/emitter/flow_modifier.rb +8 -55
- data/lib/unparser/emitter/for.rb +5 -19
- data/lib/unparser/emitter/hash.rb +74 -0
- data/lib/unparser/emitter/hash_pattern.rb +67 -0
- data/lib/unparser/emitter/hookexe.rb +5 -11
- data/lib/unparser/emitter/if.rb +9 -73
- data/lib/unparser/emitter/in_match.rb +21 -0
- data/lib/unparser/emitter/in_pattern.rb +34 -0
- data/lib/unparser/emitter/index.rb +21 -88
- data/lib/unparser/emitter/kwbegin.rb +31 -0
- data/lib/unparser/emitter/lambda.rb +0 -8
- data/lib/unparser/emitter/masgn.rb +20 -0
- data/lib/unparser/emitter/match.rb +3 -17
- data/lib/unparser/emitter/match_alt.rb +23 -0
- data/lib/unparser/emitter/match_as.rb +21 -0
- data/lib/unparser/emitter/match_rest.rb +26 -0
- data/lib/unparser/emitter/match_var.rb +19 -0
- data/lib/unparser/emitter/mlhs.rb +40 -0
- data/lib/unparser/emitter/module.rb +3 -9
- data/lib/unparser/emitter/op_assign.rb +12 -27
- data/lib/unparser/emitter/pin.rb +19 -0
- data/lib/unparser/emitter/primitive.rb +93 -0
- data/lib/unparser/emitter/range.rb +35 -0
- data/lib/unparser/emitter/regexp.rb +35 -0
- data/lib/unparser/emitter/repetition.rb +17 -57
- data/lib/unparser/emitter/rescue.rb +1 -97
- data/lib/unparser/emitter/root.rb +17 -1
- data/lib/unparser/emitter/send.rb +10 -219
- data/lib/unparser/emitter/simple.rb +33 -0
- data/lib/unparser/emitter/splat.rb +2 -18
- data/lib/unparser/emitter/super.rb +1 -29
- data/lib/unparser/emitter/undef.rb +1 -9
- data/lib/unparser/emitter/variable.rb +1 -31
- data/lib/unparser/emitter/xstr.rb +72 -0
- data/lib/unparser/emitter/yield.rb +1 -9
- data/lib/unparser/generation.rb +250 -0
- data/lib/unparser/node_details.rb +21 -0
- data/lib/unparser/node_details/send.rb +62 -0
- data/lib/unparser/node_helpers.rb +45 -6
- data/lib/unparser/validation.rb +37 -35
- data/lib/unparser/writer.rb +15 -0
- data/lib/unparser/writer/binary.rb +99 -0
- data/lib/unparser/writer/dynamic_string.rb +229 -0
- data/lib/unparser/writer/resbody.rb +40 -0
- data/lib/unparser/writer/rescue.rb +39 -0
- data/lib/unparser/writer/send.rb +124 -0
- data/lib/unparser/{emitter → writer}/send/attribute_assignment.rb +11 -26
- data/lib/unparser/writer/send/binary.rb +27 -0
- data/lib/unparser/writer/send/conditional.rb +25 -0
- data/lib/unparser/writer/send/regular.rb +33 -0
- data/lib/unparser/{emitter → writer}/send/unary.rb +10 -17
- metadata +63 -31
- data/lib/unparser/emitter/empty.rb +0 -23
- data/lib/unparser/emitter/ensure.rb +0 -37
- data/lib/unparser/emitter/literal.rb +0 -10
- data/lib/unparser/emitter/literal/array.rb +0 -29
- data/lib/unparser/emitter/literal/dynamic.rb +0 -53
- data/lib/unparser/emitter/literal/dynamic_body.rb +0 -132
- data/lib/unparser/emitter/literal/execute_string.rb +0 -38
- data/lib/unparser/emitter/literal/hash.rb +0 -156
- data/lib/unparser/emitter/literal/primitive.rb +0 -145
- data/lib/unparser/emitter/literal/range.rb +0 -36
- data/lib/unparser/emitter/literal/regexp.rb +0 -114
- data/lib/unparser/emitter/literal/singleton.rb +0 -26
- data/lib/unparser/emitter/meta.rb +0 -16
- data/lib/unparser/emitter/redo.rb +0 -25
- data/lib/unparser/emitter/resbody.rb +0 -76
- data/lib/unparser/emitter/retry.rb +0 -25
- data/lib/unparser/emitter/send/binary.rb +0 -57
- data/lib/unparser/emitter/send/conditional.rb +0 -40
- data/lib/unparser/emitter/send/regular.rb +0 -40
- data/lib/unparser/preprocessor.rb +0 -159
data/lib/unparser/color.rb
CHANGED
data/lib/unparser/comments.rb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
module Unparser
|
4
4
|
|
5
5
|
# Holds the comments that remain to be emitted
|
6
|
-
#
|
7
|
-
# ignore :reek:RepeatedConditional
|
8
6
|
class Comments
|
9
7
|
|
10
8
|
# Proxy to singleton
|
@@ -113,39 +111,15 @@ module Unparser
|
|
113
111
|
|
114
112
|
private
|
115
113
|
|
116
|
-
# Take comments while the provided block returns true
|
117
|
-
#
|
118
|
-
# @yield [Parser::Source::Comment]
|
119
|
-
#
|
120
|
-
# @return [Array]
|
121
|
-
#
|
122
|
-
# @api private
|
123
|
-
#
|
124
114
|
def take_while
|
125
115
|
number_to_take = @comments.index { |comment| !yield(comment) } || @comments.size
|
126
116
|
@comments.shift(number_to_take)
|
127
117
|
end
|
128
118
|
|
129
|
-
# Take comments up to the line number
|
130
|
-
#
|
131
|
-
# @param [Fixnum] line
|
132
|
-
#
|
133
|
-
# @return [Array]
|
134
|
-
#
|
135
|
-
# @api private
|
136
|
-
#
|
137
119
|
def take_up_to_line(line)
|
138
120
|
take_while { |comment| comment.location.expression.line <= line }
|
139
121
|
end
|
140
122
|
|
141
|
-
# Unshift document comments and return the rest
|
142
|
-
#
|
143
|
-
# @param [Array] comments
|
144
|
-
#
|
145
|
-
# @return [Array]
|
146
|
-
#
|
147
|
-
# @api private
|
148
|
-
#
|
149
123
|
def unshift_documents(comments)
|
150
124
|
doc_comments, other_comments = comments.partition(&:document?)
|
151
125
|
doc_comments.reverse_each { |comment| @comments.unshift(comment) }
|
data/lib/unparser/constants.rb
CHANGED
@@ -2,66 +2,19 @@
|
|
2
2
|
|
3
3
|
module Unparser
|
4
4
|
# All unparser constants maybe included in other libraries.
|
5
|
-
#
|
6
|
-
# False positive since constants are frozen dynamically
|
7
|
-
# to avoid duplication of `.freeze` calls
|
8
|
-
#
|
9
|
-
# :reek:TooManyConstants
|
10
5
|
module Constants
|
11
6
|
|
12
|
-
# Return frozen symbol set from enumerable
|
13
|
-
#
|
14
|
-
# @param [Enumerable] enumerable
|
15
|
-
#
|
16
|
-
# @return [Set<Symbol>]
|
17
|
-
#
|
18
|
-
# @api private
|
19
|
-
#
|
20
|
-
def self.symbol_set(enumerable)
|
21
|
-
enumerable.map(&:to_sym).freeze
|
22
|
-
end
|
23
|
-
private_class_method :symbol_set
|
24
|
-
|
25
|
-
BRACKETS_CURLY = IceNine.deep_freeze(%w[{ }])
|
26
|
-
BRACKETS_ROUND = IceNine.deep_freeze(%w[( )])
|
27
|
-
BRACKETS_SQUARE = IceNine.deep_freeze(%w([ ]))
|
28
|
-
|
29
7
|
# All unary operators of the ruby language
|
30
|
-
UNARY_OPERATORS =
|
8
|
+
UNARY_OPERATORS = %i[
|
31
9
|
! ~ -@ +@
|
32
|
-
]
|
10
|
+
].to_set.freeze
|
33
11
|
|
34
12
|
# All binary operators of the ruby language
|
35
|
-
BINARY_OPERATORS =
|
13
|
+
BINARY_OPERATORS = %i[
|
36
14
|
+ - * / & | && || << >> ==
|
37
15
|
=== != <= < <=> > >= =~ !~ ^
|
38
16
|
** %
|
39
|
-
]
|
40
|
-
|
41
|
-
COMMENT = '#'
|
42
|
-
|
43
|
-
WS = ' '
|
44
|
-
NL = "\n"
|
45
|
-
T_DOT = '.'
|
46
|
-
T_LT = '<'
|
47
|
-
T_DLT = '<<'
|
48
|
-
T_AMP = '&'
|
49
|
-
T_ASN = '='
|
50
|
-
T_SPLAT = '*'
|
51
|
-
T_DSPLAT = '**'
|
52
|
-
T_ASR = '=>'
|
53
|
-
T_PIPE = '|'
|
54
|
-
T_DCL = '::'
|
55
|
-
T_NEG = '!'
|
56
|
-
T_OR = '||'
|
57
|
-
T_AND = '&&'
|
58
|
-
T_COLON = ':'
|
59
|
-
|
60
|
-
M_PO = '('
|
61
|
-
M_PC = ')'
|
62
|
-
|
63
|
-
SNGL_QUOTE = "'"
|
64
|
-
DBL_QUOTE = '"'
|
17
|
+
].to_set.freeze
|
65
18
|
|
66
19
|
# Keywords
|
67
20
|
K_DO = 'do'
|
@@ -107,8 +60,6 @@ module Unparser
|
|
107
60
|
K_FILE = '__FILE__'
|
108
61
|
K_THEN = 'then'
|
109
62
|
|
110
|
-
DEFAULT_DELIMITER = ', '.freeze
|
111
|
-
|
112
63
|
KEYWORDS = constants.each_with_object([]) do |name, keywords|
|
113
64
|
value = const_get(name).freeze
|
114
65
|
next unless name.to_s.start_with?('K_')
|
data/lib/unparser/diff.rb
CHANGED
@@ -59,25 +59,16 @@ module Unparser
|
|
59
59
|
|
60
60
|
private
|
61
61
|
|
62
|
-
# Diffs between old and new
|
63
|
-
#
|
64
|
-
# @return [Array<Array>]
|
65
62
|
def diffs
|
66
63
|
::Diff::LCS.diff(old, new)
|
67
64
|
end
|
68
65
|
|
69
|
-
# Raw diff-lcs hunks
|
70
|
-
#
|
71
|
-
# @return [Array<Diff::LCS::Hunk>]
|
72
66
|
def hunks
|
73
67
|
diffs.map do |diff|
|
74
68
|
::Diff::LCS::Hunk.new(old.map(&:dup), new, diff, max_length, 0)
|
75
69
|
end
|
76
70
|
end
|
77
71
|
|
78
|
-
# Minimized hunk
|
79
|
-
#
|
80
|
-
# @return Diff::LCS::Hunk
|
81
72
|
def minimized_hunk
|
82
73
|
head, *tail = hunks
|
83
74
|
|
@@ -87,18 +78,10 @@ module Unparser
|
|
87
78
|
end
|
88
79
|
end
|
89
80
|
|
90
|
-
# Max length of source line in new and old
|
91
|
-
#
|
92
|
-
# @return [Integer]
|
93
81
|
def max_length
|
94
82
|
[old, new].map(&:length).max
|
95
83
|
end
|
96
84
|
|
97
|
-
# Colorized a unified diff line
|
98
|
-
#
|
99
|
-
# @param [String] line
|
100
|
-
#
|
101
|
-
# @return [String]
|
102
85
|
def self.colorize_line(line)
|
103
86
|
case line[0]
|
104
87
|
when ADDITION
|
data/lib/unparser/dsl.rb
CHANGED
@@ -6,14 +6,6 @@ module Unparser
|
|
6
6
|
|
7
7
|
private
|
8
8
|
|
9
|
-
# Define remaining children
|
10
|
-
#
|
11
|
-
# @param [Enumerable<Symbol>] names
|
12
|
-
#
|
13
|
-
# @return [undefined]
|
14
|
-
#
|
15
|
-
# @api private
|
16
|
-
#
|
17
9
|
def define_remaining_children(names)
|
18
10
|
range = names.length..-1
|
19
11
|
define_method(:remaining_children) do
|
@@ -22,15 +14,6 @@ module Unparser
|
|
22
14
|
private :remaining_children
|
23
15
|
end
|
24
16
|
|
25
|
-
# Define named child
|
26
|
-
#
|
27
|
-
# @param [Symbol] name
|
28
|
-
# @param [Fixnum] index
|
29
|
-
#
|
30
|
-
# @return [undefined]
|
31
|
-
#
|
32
|
-
# @api private
|
33
|
-
#
|
34
17
|
def define_child(name, index)
|
35
18
|
define_method(name) do
|
36
19
|
children.at(index)
|
@@ -38,15 +21,6 @@ module Unparser
|
|
38
21
|
private name
|
39
22
|
end
|
40
23
|
|
41
|
-
# Define a group of children
|
42
|
-
#
|
43
|
-
# @param [Symbol] name
|
44
|
-
# @param [Range] range
|
45
|
-
#
|
46
|
-
# @return [undefined]
|
47
|
-
#
|
48
|
-
# @api private
|
49
|
-
#
|
50
24
|
def define_group(name, range)
|
51
25
|
define_method(name) do
|
52
26
|
children[range]
|
@@ -55,12 +29,6 @@ module Unparser
|
|
55
29
|
memoize(name)
|
56
30
|
end
|
57
31
|
|
58
|
-
# Create name helpers
|
59
|
-
#
|
60
|
-
# @return [undefined]
|
61
|
-
#
|
62
|
-
# @api private
|
63
|
-
#
|
64
32
|
def children(*names)
|
65
33
|
define_remaining_children(names)
|
66
34
|
|
data/lib/unparser/emitter.rb
CHANGED
@@ -4,34 +4,20 @@ module Unparser
|
|
4
4
|
UnknownNodeError = Class.new(ArgumentError)
|
5
5
|
|
6
6
|
# Emitter base class
|
7
|
-
#
|
8
|
-
# buggy, argument values are sends to self
|
9
|
-
#
|
10
|
-
# ignore :reek:TooManyMethods
|
11
7
|
class Emitter
|
12
|
-
include Adamantium::Flat, AbstractType, Constants, NodeHelpers
|
13
|
-
include
|
14
|
-
extend DSL
|
8
|
+
include Adamantium::Flat, AbstractType, Constants, Generation, NodeHelpers
|
9
|
+
include Anima.new(:buffer, :comments, :node, :local_variable_scope)
|
15
10
|
|
16
|
-
|
17
|
-
REGISTRY = {} # rubocop:disable MutableConstant
|
11
|
+
public :node
|
18
12
|
|
19
|
-
|
13
|
+
extend DSL
|
20
14
|
|
21
|
-
|
22
|
-
|
23
|
-
false
|
24
|
-
end
|
25
|
-
end
|
15
|
+
# Registry for node emitters
|
16
|
+
REGISTRY = {} # rubocop:disable Style/MutableConstant
|
26
17
|
|
27
|
-
|
28
|
-
def terminated?
|
29
|
-
true
|
30
|
-
end
|
31
|
-
end
|
18
|
+
NO_INDENT = %i[ensure rescue].freeze
|
32
19
|
|
33
20
|
module LocalVariableRoot
|
34
|
-
|
35
21
|
# Return local variable root
|
36
22
|
#
|
37
23
|
# @return [Parser::AST::Node]
|
@@ -47,33 +33,8 @@ module Unparser
|
|
47
33
|
memoize :local_variable_scope
|
48
34
|
end
|
49
35
|
end
|
50
|
-
|
51
36
|
end # LocalVariableRoot
|
52
37
|
|
53
|
-
# Return local variable root
|
54
|
-
#
|
55
|
-
# @return [Parser::AST::Node]
|
56
|
-
#
|
57
|
-
# @api private
|
58
|
-
#
|
59
|
-
def local_variable_scope
|
60
|
-
parent.local_variable_scope
|
61
|
-
end
|
62
|
-
|
63
|
-
# Return assigned lvars
|
64
|
-
#
|
65
|
-
# @return [Array<Symbol>]
|
66
|
-
#
|
67
|
-
# @api private
|
68
|
-
#
|
69
|
-
abstract_method :local_variables
|
70
|
-
|
71
|
-
# Return node type
|
72
|
-
#
|
73
|
-
# @return [Symbol]
|
74
|
-
#
|
75
|
-
# @api private
|
76
|
-
#
|
77
38
|
def node_type
|
78
39
|
node.type
|
79
40
|
end
|
@@ -88,25 +49,16 @@ module Unparser
|
|
88
49
|
#
|
89
50
|
def self.handle(*types)
|
90
51
|
types.each do |type|
|
52
|
+
fail "Handler for type: #{type} already registered" if REGISTRY.key?(type)
|
53
|
+
|
91
54
|
REGISTRY[type] = self
|
92
55
|
end
|
93
56
|
end
|
94
57
|
private_class_method :handle
|
95
58
|
|
96
|
-
|
97
|
-
#
|
98
|
-
# @return [self]
|
99
|
-
#
|
100
|
-
# @api private
|
101
|
-
#
|
102
|
-
def write_to_buffer
|
103
|
-
emit_comments_before if buffer.fresh_line?
|
59
|
+
def emit_mlhs
|
104
60
|
dispatch
|
105
|
-
comments.consume(node)
|
106
|
-
emit_eof_comments if parent.is_a?(Root)
|
107
|
-
self
|
108
61
|
end
|
109
|
-
memoize :write_to_buffer
|
110
62
|
|
111
63
|
# Return emitter
|
112
64
|
#
|
@@ -114,384 +66,30 @@ module Unparser
|
|
114
66
|
#
|
115
67
|
# @api private
|
116
68
|
#
|
117
|
-
|
69
|
+
# rubocop:disable Metrics/ParameterLists
|
70
|
+
def self.emitter(buffer:, comments:, node:, local_variable_scope:)
|
118
71
|
type = node.type
|
119
|
-
klass = REGISTRY.fetch(type) do
|
120
|
-
raise UnknownNodeError, "Unknown node type: #{type.inspect}"
|
121
|
-
end
|
122
|
-
klass.new(node, parent)
|
123
|
-
end
|
124
|
-
|
125
|
-
# Dispatch node
|
126
|
-
#
|
127
|
-
# @return [undefined]
|
128
|
-
#
|
129
|
-
# @api private
|
130
|
-
#
|
131
|
-
abstract_method :dispatch
|
132
|
-
|
133
|
-
# Test if node is emitted as terminated expression
|
134
|
-
#
|
135
|
-
# @return [Boolean]
|
136
|
-
#
|
137
|
-
# @api private
|
138
|
-
#
|
139
|
-
abstract_method :terminated?
|
140
72
|
|
141
|
-
|
142
|
-
|
143
|
-
# Return buffer
|
144
|
-
#
|
145
|
-
# @return [Buffer] buffer
|
146
|
-
#
|
147
|
-
# @api private
|
148
|
-
#
|
149
|
-
def buffer
|
150
|
-
parent.buffer
|
151
|
-
end
|
152
|
-
memoize :buffer, freezer: :noop
|
153
|
-
|
154
|
-
# Return comments
|
155
|
-
#
|
156
|
-
# @return [Comments] comments
|
157
|
-
#
|
158
|
-
# @api private
|
159
|
-
#
|
160
|
-
def comments
|
161
|
-
parent.comments
|
162
|
-
end
|
163
|
-
memoize :comments, freezer: :noop
|
164
|
-
|
165
|
-
private
|
166
|
-
|
167
|
-
# Emit contents of block within parentheses
|
168
|
-
#
|
169
|
-
# @return [undefined]
|
170
|
-
#
|
171
|
-
# @api private
|
172
|
-
#
|
173
|
-
def parentheses(open = M_PO, close = M_PC)
|
174
|
-
write(open)
|
175
|
-
yield
|
176
|
-
write(close)
|
177
|
-
end
|
178
|
-
|
179
|
-
# Visit node
|
180
|
-
#
|
181
|
-
# @param [Parser::AST::Node] node
|
182
|
-
#
|
183
|
-
# @return [undefined]
|
184
|
-
#
|
185
|
-
# @api private
|
186
|
-
#
|
187
|
-
def visit_plain(node)
|
188
|
-
emitter = emitter(node)
|
189
|
-
emitter.write_to_buffer
|
190
|
-
end
|
191
|
-
|
192
|
-
# Visit ambiguous node
|
193
|
-
#
|
194
|
-
# @param [Parser::AST::Node] node
|
195
|
-
#
|
196
|
-
# @return [undefined]
|
197
|
-
#
|
198
|
-
# @api private
|
199
|
-
#
|
200
|
-
def visit(node)
|
201
|
-
emitter = emitter(node)
|
202
|
-
conditional_parentheses(!emitter.terminated?) do
|
203
|
-
emitter.write_to_buffer
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
# Visit within parentheses
|
208
|
-
#
|
209
|
-
# @param [Parser::AST::Node] node
|
210
|
-
#
|
211
|
-
# @return [undefined]
|
212
|
-
#
|
213
|
-
# @api private
|
214
|
-
#
|
215
|
-
def visit_parentheses(node, *arguments)
|
216
|
-
parentheses(*arguments) do
|
217
|
-
visit_plain(node)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
# Call block in optional parentheses
|
222
|
-
#
|
223
|
-
# @param [true, false] flag
|
224
|
-
#
|
225
|
-
# @return [undefined]
|
226
|
-
#
|
227
|
-
# @api private
|
228
|
-
#
|
229
|
-
# ignore :reek:ControlParameter
|
230
|
-
def conditional_parentheses(flag)
|
231
|
-
if flag
|
232
|
-
parentheses { yield }
|
233
|
-
else
|
234
|
-
yield
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
# Return emitter for node
|
239
|
-
#
|
240
|
-
# @param [Parser::AST::Node] node
|
241
|
-
#
|
242
|
-
# @return [Emitter]
|
243
|
-
#
|
244
|
-
# @api private
|
245
|
-
#
|
246
|
-
def emitter(node)
|
247
|
-
self.class.emitter(node, self)
|
248
|
-
end
|
249
|
-
|
250
|
-
# Emit delimited body
|
251
|
-
#
|
252
|
-
# @param [Enumerable<Parser::AST::Node>] nodes
|
253
|
-
#
|
254
|
-
# @return [undefined]
|
255
|
-
#
|
256
|
-
# @api private
|
257
|
-
#
|
258
|
-
def delimited_plain(nodes)
|
259
|
-
delimited(nodes, &method(:visit_plain))
|
260
|
-
end
|
261
|
-
|
262
|
-
# Emit delimited body
|
263
|
-
#
|
264
|
-
# @param [Enumerable<Parser::AST::Node>] nodes
|
265
|
-
#
|
266
|
-
# @return [undefined]
|
267
|
-
#
|
268
|
-
# @api private
|
269
|
-
#
|
270
|
-
def delimited(nodes, &block)
|
271
|
-
return if nodes.empty?
|
272
|
-
|
273
|
-
block ||= method(:visit)
|
274
|
-
head, *tail = nodes
|
275
|
-
block.call(head)
|
276
|
-
tail.each do |node|
|
277
|
-
write(DEFAULT_DELIMITER)
|
278
|
-
block.call(node)
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
# Return children of node
|
283
|
-
#
|
284
|
-
# @return [Array<Parser::AST::Node>]
|
285
|
-
#
|
286
|
-
# @api private
|
287
|
-
#
|
288
|
-
def children
|
289
|
-
node.children
|
290
|
-
end
|
291
|
-
|
292
|
-
# Write newline
|
293
|
-
#
|
294
|
-
# @return [undefined]
|
295
|
-
#
|
296
|
-
# @api private
|
297
|
-
#
|
298
|
-
def nl
|
299
|
-
emit_eol_comments
|
300
|
-
buffer.nl
|
301
|
-
end
|
302
|
-
|
303
|
-
# Write comments that appeared before source_part in the source
|
304
|
-
#
|
305
|
-
# @param [Symbol] source_part
|
306
|
-
#
|
307
|
-
# @return [undefined]
|
308
|
-
#
|
309
|
-
# @api private
|
310
|
-
#
|
311
|
-
def emit_comments_before(source_part = :expression)
|
312
|
-
comments_before = comments.take_before(node, source_part)
|
313
|
-
return if comments_before.empty?
|
314
|
-
|
315
|
-
emit_comments(comments_before)
|
316
|
-
buffer.nl
|
317
|
-
end
|
318
|
-
|
319
|
-
# Write end-of-line comments
|
320
|
-
#
|
321
|
-
# @return [undefined]
|
322
|
-
#
|
323
|
-
# @api private
|
324
|
-
#
|
325
|
-
def emit_eol_comments
|
326
|
-
comments.take_eol_comments.each do |comment|
|
327
|
-
write(WS, comment.text)
|
328
|
-
end
|
329
|
-
end
|
330
|
-
|
331
|
-
# Write end-of-file comments
|
332
|
-
#
|
333
|
-
# @return [undefined]
|
334
|
-
#
|
335
|
-
# @api private
|
336
|
-
#
|
337
|
-
def emit_eof_comments
|
338
|
-
emit_eol_comments
|
339
|
-
comments_left = comments.take_all
|
340
|
-
return if comments_left.empty?
|
341
|
-
|
342
|
-
buffer.nl
|
343
|
-
emit_comments(comments_left)
|
344
|
-
end
|
345
|
-
|
346
|
-
# Write each comment to a separate line
|
347
|
-
#
|
348
|
-
# @param [Array] comments
|
349
|
-
#
|
350
|
-
# @return [undefined]
|
351
|
-
#
|
352
|
-
# @api private
|
353
|
-
#
|
354
|
-
def emit_comments(comments)
|
355
|
-
max = comments.size - 1
|
356
|
-
comments.each_with_index do |comment, index|
|
357
|
-
if comment.type.equal?(:document)
|
358
|
-
buffer.append_without_prefix(comment.text.chomp)
|
359
|
-
else
|
360
|
-
write(comment.text)
|
361
|
-
end
|
362
|
-
buffer.nl if index < max
|
363
|
-
end
|
364
|
-
end
|
365
|
-
|
366
|
-
# Write strings into buffer
|
367
|
-
#
|
368
|
-
# @return [undefined]
|
369
|
-
#
|
370
|
-
# @api private
|
371
|
-
#
|
372
|
-
def write(*strings)
|
373
|
-
strings.each do |string|
|
374
|
-
buffer.append(string)
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
|
-
# Write end keyword
|
379
|
-
#
|
380
|
-
# @return [undefined]
|
381
|
-
#
|
382
|
-
# @api private
|
383
|
-
#
|
384
|
-
def k_end
|
385
|
-
buffer.indent
|
386
|
-
emit_comments_before(:end)
|
387
|
-
buffer.unindent
|
388
|
-
write(K_END)
|
389
|
-
end
|
390
|
-
|
391
|
-
# Return first child
|
392
|
-
#
|
393
|
-
# @return [Parser::AST::Node]
|
394
|
-
# if present
|
395
|
-
#
|
396
|
-
# @return [nil]
|
397
|
-
# otherwise
|
398
|
-
#
|
399
|
-
# @api private
|
400
|
-
#
|
401
|
-
def first_child
|
402
|
-
children.first
|
403
|
-
end
|
404
|
-
|
405
|
-
# Write whitespace
|
406
|
-
#
|
407
|
-
# @return [undefined]
|
408
|
-
#
|
409
|
-
# @api private
|
410
|
-
#
|
411
|
-
def ws
|
412
|
-
write(WS)
|
413
|
-
end
|
414
|
-
|
415
|
-
# Call emit contents of block indented
|
416
|
-
#
|
417
|
-
# @return [undefined]
|
418
|
-
#
|
419
|
-
# @api private
|
420
|
-
#
|
421
|
-
# False positive:
|
422
|
-
#
|
423
|
-
def indented
|
424
|
-
buffer = buffer()
|
425
|
-
buffer.indent
|
426
|
-
nl
|
427
|
-
yield
|
428
|
-
nl
|
429
|
-
buffer.unindent
|
430
|
-
end
|
431
|
-
|
432
|
-
# Emit non nil body
|
433
|
-
#
|
434
|
-
# @param [Parser::AST::Node] body
|
435
|
-
#
|
436
|
-
# @return [undefined]
|
437
|
-
#
|
438
|
-
# @api private
|
439
|
-
#
|
440
|
-
# rubocop:disable MethodCallWithoutArgsParentheses
|
441
|
-
def emit_body(body = body())
|
442
|
-
unless body
|
443
|
-
buffer.indent
|
444
|
-
nl
|
445
|
-
buffer.unindent
|
446
|
-
return
|
447
|
-
end
|
448
|
-
visit_indented(body)
|
449
|
-
end
|
450
|
-
# rubocop:enable MethodCallWithoutArgsParentheses
|
451
|
-
|
452
|
-
# Visit indented node
|
453
|
-
#
|
454
|
-
# @param [Parser::AST::Node] node
|
455
|
-
#
|
456
|
-
# @return [undefined]
|
457
|
-
#
|
458
|
-
# @api private
|
459
|
-
#
|
460
|
-
def visit_indented(node)
|
461
|
-
if NOINDENT.include?(node.type)
|
462
|
-
visit_plain(node)
|
463
|
-
else
|
464
|
-
indented { visit_plain(node) }
|
73
|
+
klass = REGISTRY.fetch(type) do
|
74
|
+
fail UnknownNodeError, "Unknown node type: #{type.inspect}"
|
465
75
|
end
|
466
|
-
end
|
467
76
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
# otherwise
|
475
|
-
#
|
476
|
-
# @api private
|
477
|
-
#
|
478
|
-
def parent_type
|
479
|
-
parent.node_type
|
77
|
+
klass.new(
|
78
|
+
buffer: buffer,
|
79
|
+
comments: comments,
|
80
|
+
local_variable_scope: local_variable_scope,
|
81
|
+
node: node
|
82
|
+
)
|
480
83
|
end
|
84
|
+
# rubocop:enable Metrics/ParameterLists
|
481
85
|
|
482
|
-
#
|
483
|
-
#
|
484
|
-
# @param [Class:Emitter] emitter
|
86
|
+
# Dispatch node write as statement
|
485
87
|
#
|
486
88
|
# @return [undefined]
|
487
89
|
#
|
488
90
|
# @api private
|
489
91
|
#
|
490
|
-
|
491
|
-
def run(emitter, node = node())
|
492
|
-
emitter.new(node, self).write_to_buffer
|
493
|
-
end
|
494
|
-
# rubocop:enable MethodCallWithoutArgsParentheses
|
92
|
+
abstract_method :dispatch
|
495
93
|
|
496
94
|
end # Emitter
|
497
95
|
end # Unparser
|