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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/bin/unparser +1 -1
  3. data/lib/unparser.rb +98 -61
  4. data/lib/unparser/ast.rb +0 -1
  5. data/lib/unparser/ast/local_variable_scope.rb +6 -76
  6. data/lib/unparser/buffer.rb +19 -16
  7. data/lib/unparser/cli.rb +26 -39
  8. data/lib/unparser/color.rb +0 -3
  9. data/lib/unparser/comments.rb +0 -26
  10. data/lib/unparser/constants.rb +4 -53
  11. data/lib/unparser/diff.rb +0 -17
  12. data/lib/unparser/dsl.rb +0 -32
  13. data/lib/unparser/emitter.rb +23 -425
  14. data/lib/unparser/emitter/alias.rb +2 -8
  15. data/lib/unparser/emitter/args.rb +45 -0
  16. data/lib/unparser/emitter/argument.rb +8 -166
  17. data/lib/unparser/emitter/array.rb +27 -0
  18. data/lib/unparser/emitter/array_pattern.rb +29 -0
  19. data/lib/unparser/emitter/assignment.rb +36 -127
  20. data/lib/unparser/emitter/begin.rb +9 -84
  21. data/lib/unparser/emitter/binary.rb +7 -20
  22. data/lib/unparser/emitter/block.rb +57 -41
  23. data/lib/unparser/emitter/case.rb +6 -48
  24. data/lib/unparser/emitter/case_guard.rb +27 -0
  25. data/lib/unparser/emitter/case_match.rb +40 -0
  26. data/lib/unparser/emitter/cbase.rb +1 -3
  27. data/lib/unparser/emitter/class.rb +6 -26
  28. data/lib/unparser/emitter/const_pattern.rb +24 -0
  29. data/lib/unparser/emitter/def.rb +7 -51
  30. data/lib/unparser/emitter/defined.rb +2 -12
  31. data/lib/unparser/emitter/dstr.rb +22 -0
  32. data/lib/unparser/emitter/dsym.rb +41 -0
  33. data/lib/unparser/emitter/flipflop.rb +11 -10
  34. data/lib/unparser/emitter/float.rb +29 -0
  35. data/lib/unparser/emitter/flow_modifier.rb +8 -55
  36. data/lib/unparser/emitter/for.rb +5 -19
  37. data/lib/unparser/emitter/hash.rb +74 -0
  38. data/lib/unparser/emitter/hash_pattern.rb +67 -0
  39. data/lib/unparser/emitter/hookexe.rb +5 -11
  40. data/lib/unparser/emitter/if.rb +9 -73
  41. data/lib/unparser/emitter/in_match.rb +21 -0
  42. data/lib/unparser/emitter/in_pattern.rb +34 -0
  43. data/lib/unparser/emitter/index.rb +21 -88
  44. data/lib/unparser/emitter/kwbegin.rb +31 -0
  45. data/lib/unparser/emitter/lambda.rb +0 -8
  46. data/lib/unparser/emitter/masgn.rb +20 -0
  47. data/lib/unparser/emitter/match.rb +3 -17
  48. data/lib/unparser/emitter/match_alt.rb +23 -0
  49. data/lib/unparser/emitter/match_as.rb +21 -0
  50. data/lib/unparser/emitter/match_rest.rb +26 -0
  51. data/lib/unparser/emitter/match_var.rb +19 -0
  52. data/lib/unparser/emitter/mlhs.rb +40 -0
  53. data/lib/unparser/emitter/module.rb +3 -9
  54. data/lib/unparser/emitter/op_assign.rb +12 -27
  55. data/lib/unparser/emitter/pin.rb +19 -0
  56. data/lib/unparser/emitter/primitive.rb +93 -0
  57. data/lib/unparser/emitter/range.rb +35 -0
  58. data/lib/unparser/emitter/regexp.rb +35 -0
  59. data/lib/unparser/emitter/repetition.rb +17 -57
  60. data/lib/unparser/emitter/rescue.rb +1 -97
  61. data/lib/unparser/emitter/root.rb +17 -1
  62. data/lib/unparser/emitter/send.rb +10 -219
  63. data/lib/unparser/emitter/simple.rb +33 -0
  64. data/lib/unparser/emitter/splat.rb +2 -18
  65. data/lib/unparser/emitter/super.rb +1 -29
  66. data/lib/unparser/emitter/undef.rb +1 -9
  67. data/lib/unparser/emitter/variable.rb +1 -31
  68. data/lib/unparser/emitter/xstr.rb +72 -0
  69. data/lib/unparser/emitter/yield.rb +1 -9
  70. data/lib/unparser/generation.rb +250 -0
  71. data/lib/unparser/node_details.rb +21 -0
  72. data/lib/unparser/node_details/send.rb +62 -0
  73. data/lib/unparser/node_helpers.rb +45 -6
  74. data/lib/unparser/validation.rb +37 -35
  75. data/lib/unparser/writer.rb +15 -0
  76. data/lib/unparser/writer/binary.rb +99 -0
  77. data/lib/unparser/writer/dynamic_string.rb +229 -0
  78. data/lib/unparser/writer/resbody.rb +40 -0
  79. data/lib/unparser/writer/rescue.rb +39 -0
  80. data/lib/unparser/writer/send.rb +124 -0
  81. data/lib/unparser/{emitter → writer}/send/attribute_assignment.rb +11 -26
  82. data/lib/unparser/writer/send/binary.rb +27 -0
  83. data/lib/unparser/writer/send/conditional.rb +25 -0
  84. data/lib/unparser/writer/send/regular.rb +33 -0
  85. data/lib/unparser/{emitter → writer}/send/unary.rb +10 -17
  86. metadata +63 -31
  87. data/lib/unparser/emitter/empty.rb +0 -23
  88. data/lib/unparser/emitter/ensure.rb +0 -37
  89. data/lib/unparser/emitter/literal.rb +0 -10
  90. data/lib/unparser/emitter/literal/array.rb +0 -29
  91. data/lib/unparser/emitter/literal/dynamic.rb +0 -53
  92. data/lib/unparser/emitter/literal/dynamic_body.rb +0 -132
  93. data/lib/unparser/emitter/literal/execute_string.rb +0 -38
  94. data/lib/unparser/emitter/literal/hash.rb +0 -156
  95. data/lib/unparser/emitter/literal/primitive.rb +0 -145
  96. data/lib/unparser/emitter/literal/range.rb +0 -36
  97. data/lib/unparser/emitter/literal/regexp.rb +0 -114
  98. data/lib/unparser/emitter/literal/singleton.rb +0 -26
  99. data/lib/unparser/emitter/meta.rb +0 -16
  100. data/lib/unparser/emitter/redo.rb +0 -25
  101. data/lib/unparser/emitter/resbody.rb +0 -76
  102. data/lib/unparser/emitter/retry.rb +0 -25
  103. data/lib/unparser/emitter/send/binary.rb +0 -57
  104. data/lib/unparser/emitter/send/conditional.rb +0 -40
  105. data/lib/unparser/emitter/send/regular.rb +0 -40
  106. data/lib/unparser/preprocessor.rb +0 -159
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ class Emitter
5
+ # Range emitters
6
+ class Range < self
7
+ TOKENS = {
8
+ irange: '..',
9
+ erange: '...'
10
+ }.freeze
11
+
12
+ SYMBOLS = {
13
+ erange: :tDOT3,
14
+ irange: :tDOT2
15
+ }.freeze
16
+
17
+ def symbol_name
18
+ true
19
+ end
20
+
21
+ handle(*TOKENS.keys)
22
+
23
+ children :begin_node, :end_node
24
+
25
+ private
26
+
27
+ def dispatch
28
+ visit(begin_node) if begin_node
29
+ write(TOKENS.fetch(node.type))
30
+ visit(end_node) if end_node
31
+ end
32
+
33
+ end # Range
34
+ end # Emitter
35
+ end # Unparser
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ class Emitter
5
+ # Emitter for regexp literals
6
+ class Regexp < self
7
+ handle :regexp
8
+
9
+ define_group(:body, 0..-2)
10
+
11
+ private
12
+
13
+ def dispatch
14
+ parentheses('/', '/') do
15
+ body.each(&method(:emit_body))
16
+ end
17
+ emit_options
18
+ end
19
+
20
+ def emit_options
21
+ write(children.last.children.join)
22
+ end
23
+
24
+ def emit_body(node)
25
+ if n_begin?(node)
26
+ write('#{')
27
+ node.children.each(&method(:visit))
28
+ write('}')
29
+ else
30
+ buffer.append_without_prefix(node.children.first.gsub('/', '\/'))
31
+ end
32
+ end
33
+ end # Regexp
34
+ end # Emitter
35
+ end # Unparser
@@ -5,39 +5,29 @@ module Unparser
5
5
 
6
6
  # Emitter for postconditions
7
7
  class Post < self
8
- include Unterminated
9
-
10
- handle :while_post, :until_post
11
-
12
8
  children :condition, :body
13
9
 
14
10
  MAP = {
15
- while_post: K_WHILE,
16
- until_post: K_UNTIL
11
+ while_post: 'while',
12
+ until_post: 'until'
17
13
  }.freeze
18
14
 
19
15
  handle(*MAP.keys)
20
16
 
21
- # Perform dispatch
22
- #
23
- # @return [undefined]
24
- #
25
- # @api private
26
- #
17
+ private
18
+
27
19
  def dispatch
28
20
  visit(body)
29
- write(WS, MAP.fetch(node.type), WS)
21
+ write(' ', MAP.fetch(node.type), ' ')
30
22
  visit(condition)
31
23
  end
32
24
  end
33
25
 
34
- # Base class for while and until emitters
26
+ # Emitter for while and until nodes
35
27
  class Repetition < self
36
- include Terminated
37
-
38
28
  MAP = {
39
- while: K_WHILE,
40
- until: K_UNTIL
29
+ while: 'while',
30
+ until: 'until'
41
31
  }.freeze
42
32
 
43
33
  handle(*MAP.keys)
@@ -46,12 +36,6 @@ module Unparser
46
36
 
47
37
  private
48
38
 
49
- # Perform dispatch
50
- #
51
- # @return [undefined]
52
- #
53
- # @api private
54
- #
55
39
  def dispatch
56
40
  if postcontrol?
57
41
  emit_postcontrol
@@ -60,54 +44,30 @@ module Unparser
60
44
  end
61
45
  end
62
46
 
63
- # Test if node must be emitted in postcontrol form
64
- #
65
- # @return [Boolean]
66
- #
67
- # @api private
68
- #
69
47
  def postcontrol?
70
- return nil unless body # greez from falsyness
71
-
72
- local_variable_scope.first_assignment_in_body_and_used_in_condition?(body, condition)
48
+ body && local_variable_scope.first_assignment_in?(body, condition)
73
49
  end
74
50
 
75
- # Emit keyword
76
- #
77
- # @return [undefined]
78
- #
79
- # @api private
80
- #
81
51
  def emit_keyword
82
- write(MAP.fetch(node.type), WS)
52
+ write(MAP.fetch(node.type), ' ')
83
53
  end
84
54
 
85
- # Emit embedded
86
- #
87
- # @return [undefned]
88
- #
89
- # @api private
90
- #
91
55
  def emit_normal
92
56
  emit_keyword
93
- conditional_parentheses(condition.type.equal?(:block)) do
94
- visit(condition)
57
+ visit(condition)
58
+ if body
59
+ emit_body(body)
60
+ else
61
+ nl
95
62
  end
96
- emit_body
97
63
  k_end
98
64
  end
99
65
 
100
- # Emit postcontrol
101
- #
102
- # @return [undefined]
103
- #
104
- # @api private
105
- #
106
66
  def emit_postcontrol
107
- visit_plain(body)
67
+ visit(body)
108
68
  ws
109
69
  emit_keyword
110
- visit_plain(condition)
70
+ visit(condition)
111
71
  end
112
72
 
113
73
  end # Repetition
@@ -4,109 +4,13 @@ module Unparser
4
4
  class Emitter
5
5
  # Emitter for rescue nodes
6
6
  class Rescue < self
7
- include Unterminated
8
-
9
7
  handle :rescue
10
8
 
11
- children :body, :rescue_body
12
-
13
- define_group :rescue_bodies, 1..-2
14
-
15
- EMBEDDED_TYPES = %i[block def defs kwbegin ensure].to_set.freeze
16
-
17
- NOINDENT_STANDALONE_RESCUE = %i[root begin pair_rocket pair_colon lvasgn ivasgn].to_set.freeze
18
-
19
9
  private
20
10
 
21
- # Perform dispatch
22
- #
23
- # @return [undefined]
24
- #
25
- # @api private
26
- #
27
11
  def dispatch
28
- if standalone?
29
- if NOINDENT_STANDALONE_RESCUE.include?(parent_type)
30
- emit_standalone
31
- else
32
- indented { emit_standalone }
33
- end
34
- else
35
- emit_embedded
36
- end
37
- end
38
-
39
- # Test if rescue node ist standalone
40
- #
41
- # @return [Boolean]
42
- #
43
- # @api private
44
- #
45
- def standalone?
46
- if parent_type.equal?(:ensure)
47
- !parent.node.children.first.equal?(node)
48
- else
49
- !EMBEDDED_TYPES.include?(parent_type)
50
- end
51
- end
52
-
53
- # Emit standalone form
54
- #
55
- # @return [undefined]
56
- #
57
- # @api private
58
- #
59
- def emit_standalone
60
- visit_plain(body)
61
- ws
62
- run(Resbody::Standalone, rescue_body)
12
+ emit_rescue_postcontrol(node)
63
13
  end
64
-
65
- # Emit embedded form
66
- #
67
- # @return [undefined]
68
- #
69
- # @api private
70
- #
71
- def emit_embedded
72
- if body
73
- visit_indented(body)
74
- else
75
- nl
76
- end
77
- rescue_bodies.each do |child|
78
- run(Resbody::Embedded, child)
79
- end
80
- emit_else
81
- end
82
-
83
- # Emit else
84
- #
85
- # @return [undefined]
86
- #
87
- # @api private
88
- #
89
- def emit_else
90
- return unless else_branch
91
-
92
- write(K_ELSE)
93
- visit_indented(else_branch)
94
- end
95
-
96
- # Return else body
97
- #
98
- # @return [Parser::AST::Node]
99
- # if else body is present
100
- #
101
- # @return [nil]
102
- # otherwise
103
- #
104
- # @api private
105
- #
106
- def else_branch
107
- children.last
108
- end
109
-
110
14
  end # Rescue
111
15
  end # Emitter
112
16
  end # Unparser
@@ -4,8 +4,24 @@ module Unparser
4
4
  class Emitter
5
5
  # Root emitter a special case
6
6
  class Root < self
7
- include Concord::Public.new(:node, :buffer, :comments)
7
+ include Concord::Public.new(:buffer, :node, :comments)
8
8
  include LocalVariableRoot
9
+
10
+ END_NL = %i[class sclass module begin].freeze
11
+
12
+ private_constant(*constants(false))
13
+
14
+ def dispatch
15
+ if children.any?
16
+ emit_body(node, indent: false)
17
+ else
18
+ visit_deep(node)
19
+ end
20
+
21
+ emit_eof_comments
22
+
23
+ nl if END_NL.include?(node.type) && !buffer.fresh_line?
24
+ end
9
25
  end # Root
10
26
  end # Emitter
11
27
  end # Unparser
@@ -3,236 +3,27 @@
3
3
  module Unparser
4
4
  class Emitter
5
5
  # Emitter for send
6
- # ignore :reek:TooManyMethods
7
6
  class Send < self
7
+ handle :csend, :send
8
8
 
9
- handle :send
10
-
11
- ASSIGN_SUFFIX = '='.freeze
12
- INDEX_ASSIGN = :'[]='
13
- INDEX_REFERENCE = :'[]'
14
- NON_ASSIGN_RANGE = (0..-2).freeze
15
-
16
- children :receiver, :selector
9
+ def emit_mlhs
10
+ writer.emit_mlhs
11
+ end
17
12
 
18
- def terminated?
19
- effective_emitter.terminated?
13
+ def emit_heredoc_reminders
14
+ writer.emit_heredoc_reminders
20
15
  end
21
16
 
22
17
  private
23
18
 
24
- # Perform dispatch
25
- #
26
- # @return [undefined]
27
- #
28
- # @api private
29
- #
30
19
  def dispatch
31
- effective_emitter.write_to_buffer
32
- end
33
-
34
- # Return effective emitter
35
- #
36
- # @return [Emitter]
37
- #
38
- # @api private
39
- #
40
- def effective_emitter
41
- effective_emitter_class.new(node, parent)
42
- end
43
-
44
- # Return effective emitter
45
- #
46
- # @return [Class:Emitter]
47
- #
48
- # @api private
49
- #
50
- def effective_emitter_class
51
- if binary_operator?
52
- Binary
53
- elsif unary_operator?
54
- Unary
55
- elsif attribute_assignment?
56
- AttributeAssignment
57
- else
58
- Regular
59
- end
60
- end
61
-
62
- # Return string selector
63
- #
64
- # @return [String]
65
- #
66
- # @api private
67
- #
68
- def string_selector
69
- selector.to_s
70
- end
71
-
72
- # Test for unary operator implemented as method
73
- #
74
- # @return [Boolean]
75
- #
76
- # @api private
77
- #
78
- def unary_operator?
79
- UNARY_OPERATORS.include?(selector)
80
- end
81
-
82
- # Test for binary operator implemented as method
83
- #
84
- # @return [Boolean]
85
- #
86
- # @api private
87
- #
88
- def binary_operator?
89
- BINARY_OPERATORS.include?(selector) && arguments.one? && !arguments.first.type.equal?(:splat)
90
- end
91
-
92
- # Emit selector
93
- #
94
- # @return [undefined]
95
- #
96
- # @api private
97
- #
98
- def emit_selector
99
- write(mlhs? ? non_assignment_selector : string_selector)
100
- end
101
-
102
- # Test for mlhs
103
- #
104
- # @return [Boolean]
105
- #
106
- # @api private
107
- #
108
- def mlhs?
109
- parent_type.equal?(:mlhs)
110
- end
111
-
112
- # Test for assignment
113
- #
114
- # FIXME: This also returns true for <= operator!
115
- #
116
- # @return [Boolean]
117
- #
118
- # @api private
119
- #
120
- def assignment?
121
- string_selector[-1].eql?(ASSIGN_SUFFIX)
122
- end
123
-
124
- # Test for attribute assignment
125
- #
126
- # @return [Boolean]
127
- #
128
- # @api private
129
- #
130
- def attribute_assignment?
131
- !BINARY_OPERATORS.include?(selector) && !UNARY_OPERATORS.include?(selector) && assignment? && !mlhs?
132
- end
133
-
134
- # Test for empty arguments
135
- #
136
- # @return [Boolean]
137
- #
138
- # @api private
139
- #
140
- def arguments?
141
- arguments.any?
142
- end
143
-
144
- # Return argument nodes
145
- #
146
- # @return [Array<Parser::AST::Node>]
147
- #
148
- # @api private
149
- #
150
- def arguments
151
- children[2..-1]
152
- end
153
- memoize :arguments
154
-
155
- # Emit arguments
156
- #
157
- # @return [undefined]
158
- #
159
- # @api private
160
- #
161
- def emit_arguments
162
- if arguments.empty?
163
- write('()') if receiver.nil? && avoid_clash?
164
- else
165
- normal_arguments
166
- end
167
- end
168
-
169
- # Emit normal arguments
170
- #
171
- # @return [undefined]
172
- #
173
- # @api private
174
- #
175
- def normal_arguments
176
- parentheses do
177
- delimited_plain(effective_arguments)
178
- end
179
- end
180
-
181
- # The effective arguments
182
- #
183
- # @return [Parser::AST::Node]
184
- #
185
- # @api private
186
- #
187
- def effective_arguments
188
- last = arguments.length - 1
189
- arguments.each_with_index.map do |argument, index|
190
- if last.equal?(index) && argument.type.equal?(:hash) && argument.children.any?
191
- argument.updated(:hash_body)
192
- else
193
- argument
194
- end
195
- end
196
- end
197
-
198
- # Test if clash with local variable or constant needs to be avoided
199
- #
200
- # @return [Boolean]
201
- #
202
- # @api private
203
- #
204
- def avoid_clash?
205
- local_variable_clash? || parses_as_constant?
206
- end
207
-
208
- # Test for local variable clash
209
- #
210
- # @return [Boolean]
211
- #
212
- # @api private
213
- #
214
- def local_variable_clash?
215
- local_variable_scope.local_variable_defined_for_node?(node, selector)
216
- end
217
-
218
- # The non assignment selector
219
- #
220
- # @return [String]
221
- #
222
- # @api private
223
- def non_assignment_selector
224
- string_selector[NON_ASSIGN_RANGE]
20
+ writer.dispatch
225
21
  end
226
22
 
227
- # Test if selector parses as constant
228
- #
229
- # @return [Boolean]
230
- #
231
- # @api private
232
- #
233
- def parses_as_constant?
234
- Unparser.parse(selector.to_s).type.equal?(:const)
23
+ def writer
24
+ writer_with(Writer::Send, node)
235
25
  end
26
+ memoize :writer
236
27
  end # Send
237
28
  end # Emitter
238
29
  end # Unparser