unparser 0.4.9 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module NodeDetails
5
+ include Constants, NodeHelpers
6
+
7
+ def self.included(descendant)
8
+ descendant.class_eval do
9
+ include Adamantium::Flat, Concord.new(:node)
10
+
11
+ extend DSL
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def children
18
+ node.children
19
+ end
20
+ end # NodeDetails
21
+ end # Unparser
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module NodeDetails
5
+ class Send
6
+ include NodeDetails
7
+
8
+ ASSIGN_SUFFIX = '='.freeze
9
+ NON_ASSIGN_RANGE = (0..-2).freeze
10
+
11
+ private_constant(*constants(false))
12
+
13
+ children :receiver, :selector
14
+
15
+ public :receiver, :selector
16
+
17
+ def selector_binary_operator?
18
+ BINARY_OPERATORS.include?(selector)
19
+ end
20
+
21
+ def binary_syntax_allowed?
22
+ selector_binary_operator? && arguments.one? && !n_splat?(arguments.first)
23
+ end
24
+
25
+ def selector_unary_operator?
26
+ UNARY_OPERATORS.include?(selector)
27
+ end
28
+
29
+ def assignment_operator?
30
+ assignment? && !selector_binary_operator? && !selector_unary_operator?
31
+ end
32
+
33
+ def arguments?
34
+ arguments.any?
35
+ end
36
+
37
+ def non_assignment_selector
38
+ if assignment?
39
+ string_selector[NON_ASSIGN_RANGE]
40
+ else
41
+ string_selector
42
+ end
43
+ end
44
+
45
+ def assignment?
46
+ string_selector[-1].eql?(ASSIGN_SUFFIX)
47
+ end
48
+ memoize :assignment?
49
+
50
+ def arguments
51
+ children[2..-1]
52
+ end
53
+ memoize :arguments
54
+
55
+ def string_selector
56
+ selector.to_s
57
+ end
58
+ memoize :string_selector
59
+
60
+ end # Send
61
+ end # NodeDetails
62
+ end # Unparser
@@ -11,9 +11,6 @@ module Unparser
11
11
  # @return [Parser::AST::Node]
12
12
  #
13
13
  # @api private
14
- #
15
- # ignore :reek:UncommunicativeMethodName
16
- # ignore :reek:UtilityFunction
17
14
  def s(type, *children)
18
15
  Parser::AST::Node.new(type, children)
19
16
  end
@@ -26,12 +23,54 @@ module Unparser
26
23
  # @param [Array] children
27
24
  #
28
25
  # @api private
29
- #
30
- # ignore :reek:UncommunicativeMethodName
31
- # ignore :reek:UtilityFunction
32
26
  def n(type, children = [])
33
27
  Parser::AST::Node.new(type, children)
34
28
  end
35
29
 
30
+ def n?(type, node)
31
+ node.type.equal?(type)
32
+ end
33
+
34
+ %i[
35
+ arg
36
+ args
37
+ array
38
+ array_pattern
39
+ empty_else
40
+ begin
41
+ block
42
+ cbase
43
+ const
44
+ dstr
45
+ ensure
46
+ hash
47
+ hash_pattern
48
+ in_pattern
49
+ int
50
+ kwsplat
51
+ lambda
52
+ match_rest
53
+ pair
54
+ rescue
55
+ send
56
+ shadowarg
57
+ splat
58
+ str
59
+ sym
60
+ ].each do |type|
61
+ name = "n_#{type}?"
62
+ define_method(name) do |node|
63
+ n?(type, node)
64
+ end
65
+ private(name)
66
+ end
67
+
68
+ def unwrap_single_begin(node)
69
+ if n_begin?(node) && node.children.one?
70
+ node.children.first
71
+ else
72
+ node
73
+ end
74
+ end
36
75
  end # NodeHelpers
37
76
  end # Unparser
@@ -2,8 +2,6 @@
2
2
 
3
3
  module Unparser
4
4
  # Validation of unparser results
5
- #
6
- # ignore :reek:TooManyMethods
7
5
  class Validation
8
6
  include Adamantium::Flat, Anima.new(
9
7
  :generated_node,
@@ -25,7 +23,7 @@ module Unparser
25
23
  original_node,
26
24
  generated_source,
27
25
  generated_node
28
- ].all?(&:right?) && generated_node.from_right.eql?(original_node.from_right)
26
+ ].all?(&:right?) && generated_node.from_right.==(original_node.from_right)
29
27
  end
30
28
 
31
29
  # Return error report
@@ -55,16 +53,14 @@ module Unparser
55
53
  def self.from_string(original_source)
56
54
  original_node = Unparser
57
55
  .parse_either(original_source)
58
- .fmap(&Preprocessor.method(:run))
59
56
 
60
57
  generated_source = original_node
61
58
  .lmap(&method(:const_unit))
62
- .bind(&method(:unparse_either))
59
+ .bind(&Unparser.method(:unparse_either))
63
60
 
64
61
  generated_node = generated_source
65
62
  .lmap(&method(:const_unit))
66
63
  .bind(&Unparser.method(:parse_either))
67
- .fmap(&Preprocessor.method(:run))
68
64
 
69
65
  new(
70
66
  identification: '(string)',
@@ -86,21 +82,10 @@ module Unparser
86
82
 
87
83
  private
88
84
 
89
- # Create a labeled report from
90
- #
91
- # @param [String] label
92
- # @param [Symbol] attribute_name
93
- #
94
- # @return [Array<String>]
95
85
  def make_report(label, attribute_name)
96
86
  ["#{label}:"].concat(public_send(attribute_name).either(method(:report_exception), ->(value) { [value] }))
97
87
  end
98
88
 
99
- # Report optional exception
100
- #
101
- # @param [Exception, nil] exception
102
- #
103
- # @return [Array<String>]
104
89
  def report_exception(exception)
105
90
  if exception
106
91
  [exception.inspect].concat(exception.backtrace.take(20))
@@ -109,9 +94,6 @@ module Unparser
109
94
  end
110
95
  end
111
96
 
112
- # Report the node diff
113
- #
114
- # @return [Array<String>]
115
97
  def node_diff_report
116
98
  diff = nil
117
99
 
@@ -127,23 +109,43 @@ module Unparser
127
109
  diff ? ['Node-Diff:', diff] : []
128
110
  end
129
111
 
130
- # Create unit represented as nil
131
- #
132
- # @param [Object] _value
133
- #
134
- # @return [nil]
135
112
  def self.const_unit(_value); end
136
113
  private_class_method :const_unit
137
114
 
138
- # Unparse capturing errors
139
- #
140
- # @param [Parser::AST::Node] node
141
- #
142
- # @return [Either<RuntimeError, String>]
143
- def self.unparse_either(node)
144
- MPrelude::Either
145
- .wrap_error(RuntimeError) { Unparser.unparse(node) }
146
- end
147
- private_class_method :unparse_either
115
+ class Literal < self
116
+ def success?
117
+ original_source.eql?(generated_source)
118
+ end
119
+
120
+ def report
121
+ message = [identification]
122
+
123
+ message.concat(make_report('Original-Source', :original_source))
124
+ message.concat(make_report('Generated-Source', :generated_source))
125
+ message.concat(make_report('Original-Node', :original_node))
126
+ message.concat(make_report('Generated-Node', :generated_node))
127
+ message.concat(node_diff_report)
128
+ message.concat(source_diff_report)
129
+
130
+ message.join("\n")
131
+ end
132
+
133
+ private
134
+
135
+ def source_diff_report
136
+ diff = nil
137
+
138
+ original_source.fmap do |original|
139
+ generated_source.fmap do |generated|
140
+ diff = Diff.new(
141
+ original.split("\n", -1),
142
+ generated.split("\n", -1)
143
+ ).colorized_diff
144
+ end
145
+ end
146
+
147
+ diff ? ['Source-Diff:', diff] : []
148
+ end
149
+ end # Literal
148
150
  end # Validation
149
151
  end # Unparser
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ include Generation, NodeHelpers
6
+
7
+ def self.included(descendant)
8
+ descendant.class_eval do
9
+ include Anima.new(:buffer, :comments, :node, :local_variable_scope)
10
+
11
+ extend DSL
12
+ end
13
+ end
14
+ end # Writer
15
+ end # Unparser
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ class Binary
6
+ include Writer, Adamantium::Flat
7
+
8
+ children :left, :right
9
+
10
+ OPERATOR_TOKENS =
11
+ {
12
+ and: '&&',
13
+ or: '||'
14
+ }.freeze
15
+
16
+ KEYWORD_TOKENS =
17
+ {
18
+ and: 'and',
19
+ or: 'or'
20
+ }.freeze
21
+
22
+ KEYWORD_SYMBOLS =
23
+ {
24
+ and: :kAND,
25
+ or: :kOR
26
+ }.freeze
27
+
28
+ OPERATOR_SYMBOLS =
29
+ {
30
+ and: :tANDOP,
31
+ or: :tOROP
32
+ }.freeze
33
+
34
+ MAP =
35
+ {
36
+ kAND: 'and',
37
+ kOR: 'or',
38
+ tOROP: '||',
39
+ tANDOP: '&&'
40
+ }.freeze
41
+
42
+ NEED_KEYWORD = %i[return break next].freeze
43
+
44
+ private_constant(*constants(false))
45
+
46
+ def emit_operator
47
+ emit_with(OPERATOR_TOKENS)
48
+ end
49
+
50
+ def symbol_name
51
+ true
52
+ end
53
+
54
+ def dispatch
55
+ left_emitter.write_to_buffer
56
+ write(' ', MAP.fetch(effective_symbol), ' ')
57
+ visit(right)
58
+ end
59
+
60
+ private
61
+
62
+ def effective_symbol
63
+ if NEED_KEYWORD.include?(right.type) || NEED_KEYWORD.include?(left.type)
64
+ return keyword_symbol
65
+ end
66
+
67
+ unless left_emitter.symbol_name
68
+ return operator_symbol
69
+ end
70
+
71
+ keyword_symbol
72
+ end
73
+
74
+ def emit_with(map)
75
+ visit(left)
76
+ write(' ', map.fetch(node.type), ' ')
77
+ visit(right)
78
+ end
79
+
80
+ def keyword_symbol
81
+ KEYWORD_SYMBOLS.fetch(node.type)
82
+ end
83
+
84
+ def operator_symbol
85
+ OPERATOR_SYMBOLS.fetch(node.type)
86
+ end
87
+
88
+ def left_emitter
89
+ emitter(left)
90
+ end
91
+ memoize :left_emitter
92
+
93
+ def right_emitter
94
+ emitter(right)
95
+ end
96
+ memoize :right_emitter
97
+ end # Binary
98
+ end # Writer
99
+ end # Unparser
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ class DynamicString
6
+ include Writer, Adamantium::Flat
7
+
8
+ PATTERNS_2 = IceNine.deep_freeze(
9
+ [
10
+ %i[str_empty begin],
11
+ %i[begin str_nl]
12
+ ]
13
+ )
14
+
15
+ PATTERNS_3 = IceNine.deep_freeze(
16
+ [
17
+ %i[begin str_nl_eol str_nl_eol],
18
+ %i[str_nl_eol begin str_nl_eol],
19
+ %i[str_ws begin str_nl_eol]
20
+ ]
21
+ )
22
+
23
+ FLAT_INTERPOLATION = %i[ivar cvar gvar].to_set.freeze
24
+
25
+ private_constant(*constants(false))
26
+
27
+ def emit_heredoc_reminder
28
+ return unless heredoc?
29
+
30
+ emit_heredoc_body
31
+ emit_heredoc_footer
32
+ end
33
+
34
+ def dispatch
35
+ if heredoc?
36
+ emit_heredoc_header
37
+ else
38
+ emit_dstr
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def heredoc_header
45
+ need_squiggly? ? '<<~HEREDOC' : '<<-HEREDOC'
46
+ end
47
+
48
+ def heredoc?
49
+ children.empty? || (nl_last_child? && heredoc_pattern?)
50
+ end
51
+
52
+ def emit_heredoc_header
53
+ write(heredoc_header)
54
+ end
55
+
56
+ def emit_heredoc_body
57
+ nl
58
+ if need_squiggly?
59
+ emit_squiggly_heredoc_body
60
+ else
61
+ emit_normal_heredoc_body
62
+ end
63
+ end
64
+
65
+ def emit_heredoc_footer
66
+ write('HEREDOC')
67
+ end
68
+
69
+ def classify(node)
70
+ if n_str?(node)
71
+ classify_str(node)
72
+ else
73
+ node.type
74
+ end
75
+ end
76
+
77
+ def classify_str(node)
78
+ if str_nl?(node)
79
+ :str_nl
80
+ elsif node.children.first.end_with?("\n")
81
+ :str_nl_eol
82
+ elsif str_ws?(node)
83
+ :str_ws
84
+ elsif str_empty?(node)
85
+ :str_empty
86
+ end
87
+ end
88
+
89
+ def str_nl?(node)
90
+ node.eql?(s(:str, "\n"))
91
+ end
92
+
93
+ def str_empty?(node)
94
+ node.eql?(s(:str, ''))
95
+ end
96
+
97
+ def str_ws?(node)
98
+ /\A( |\t)+\z/.match?(node.children.first)
99
+ end
100
+
101
+ def heredoc_pattern?
102
+ heredoc_pattern_2? || heredoc_pattern_3?
103
+ end
104
+
105
+ def heredoc_pattern_3?
106
+ children.each_cons(3).any? do |group|
107
+ PATTERNS_3.include?(group.map(&method(:classify)))
108
+ end
109
+ end
110
+
111
+ def heredoc_pattern_2?
112
+ children.each_cons(2).any? do |group|
113
+ PATTERNS_2.include?(group.map(&method(:classify)))
114
+ end
115
+ end
116
+
117
+ def nl_last_child?
118
+ last = children.last
119
+ n_str?(last) && last.children.first[-1].eql?("\n")
120
+ end
121
+
122
+ def need_squiggly?
123
+ children.any?(s(:str, ''))
124
+ end
125
+
126
+ def emit_squiggly_heredoc_body
127
+ buffer.indent
128
+ children.each do |child|
129
+ if n_str?(child)
130
+ write(escape_dynamic(child.children.first))
131
+ else
132
+ emit_dynamic(child)
133
+ end
134
+ end
135
+ buffer.unindent
136
+ end
137
+
138
+ def emit_normal_heredoc_body
139
+ buffer.root_indent do
140
+ children.each do |child|
141
+ if n_str?(child)
142
+ write(escape_dynamic(child.children.first))
143
+ else
144
+ emit_dynamic(child)
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ def escape_dynamic(string)
151
+ string.gsub('#', '\#')
152
+ end
153
+
154
+ def emit_dynamic(child)
155
+ if FLAT_INTERPOLATION.include?(child.type)
156
+ write('#')
157
+ visit(child)
158
+ elsif n_dstr?(child)
159
+ emit_body(child.children)
160
+ else
161
+ write('#{')
162
+ emit_dynamic_component(child.children.first)
163
+ write('}')
164
+ end
165
+ end
166
+
167
+ def emit_dynamic_component(node)
168
+ visit(node) if node
169
+ end
170
+
171
+ def emit_dstr
172
+ segments.each_with_index do |children, index|
173
+ emit_segment(children, index)
174
+ end
175
+ end
176
+
177
+ def breakpoint?(child, current)
178
+ last_type = current.last&.type
179
+
180
+ [
181
+ n_str?(child) && last_type.equal?(:str) && current.none?(&method(:n_begin?)),
182
+ last_type.equal?(:dstr),
183
+ n_dstr?(child) && last_type
184
+ ].any?
185
+ end
186
+
187
+ def segments
188
+ segments = []
189
+
190
+ segments << current = []
191
+
192
+ children.each do |child|
193
+ if breakpoint?(child, current)
194
+ segments << current = []
195
+ end
196
+
197
+ current << child
198
+ end
199
+
200
+ segments
201
+ end
202
+
203
+ def emit_segment(children, index)
204
+ write(' ') unless index.zero?
205
+
206
+ write('"')
207
+ emit_body(children)
208
+ write('"')
209
+ end
210
+
211
+ def emit_body(children)
212
+ buffer.root_indent do
213
+ children.each_with_index do |child, index|
214
+ if n_str?(child)
215
+ string = child.children.first
216
+ if string.eql?("\n") && children.fetch(index.pred).type.equal?(:begin)
217
+ write("\n")
218
+ else
219
+ write(string.inspect[1..-2])
220
+ end
221
+ else
222
+ emit_dynamic(child)
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end # DynamicString
228
+ end # Writer
229
+ end # Unparser