unparser 0.6.15 → 0.7.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/unparser/anima.rb +11 -0
  3. data/lib/unparser/ast/local_variable_scope.rb +28 -24
  4. data/lib/unparser/ast.rb +18 -22
  5. data/lib/unparser/buffer.rb +44 -2
  6. data/lib/unparser/cli.rb +26 -5
  7. data/lib/unparser/either.rb +6 -6
  8. data/lib/unparser/emitter/array.rb +0 -4
  9. data/lib/unparser/emitter/array_pattern.rb +1 -9
  10. data/lib/unparser/emitter/assignment.rb +7 -8
  11. data/lib/unparser/emitter/begin.rb +0 -6
  12. data/lib/unparser/emitter/binary.rb +1 -1
  13. data/lib/unparser/emitter/block.rb +3 -4
  14. data/lib/unparser/emitter/def.rb +1 -1
  15. data/lib/unparser/emitter/dstr.rb +6 -5
  16. data/lib/unparser/emitter/flow_modifier.rb +0 -6
  17. data/lib/unparser/emitter/for.rb +1 -1
  18. data/lib/unparser/emitter/hash.rb +0 -8
  19. data/lib/unparser/emitter/hash_pattern.rb +1 -1
  20. data/lib/unparser/emitter/index.rb +0 -4
  21. data/lib/unparser/emitter/op_assign.rb +0 -10
  22. data/lib/unparser/emitter/primitive.rb +0 -13
  23. data/lib/unparser/emitter/regexp.rb +5 -17
  24. data/lib/unparser/emitter/rescue.rb +7 -1
  25. data/lib/unparser/emitter/root.rb +2 -9
  26. data/lib/unparser/emitter/send.rb +1 -5
  27. data/lib/unparser/emitter/string.rb +31 -0
  28. data/lib/unparser/emitter.rb +9 -10
  29. data/lib/unparser/generation.rb +8 -14
  30. data/lib/unparser/node_details.rb +1 -0
  31. data/lib/unparser/validation.rb +68 -28
  32. data/lib/unparser/writer/dynamic_string.rb +128 -135
  33. data/lib/unparser/writer/regexp.rb +98 -0
  34. data/lib/unparser/writer/resbody.rb +30 -1
  35. data/lib/unparser/writer/rescue.rb +2 -6
  36. data/lib/unparser/writer/send.rb +8 -14
  37. data/lib/unparser/writer.rb +32 -1
  38. data/lib/unparser.rb +103 -30
  39. metadata +15 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d1d0033d21b1c64de6affcc9c555797edf5206dc2947484ff8391e3c8e739ce
4
- data.tar.gz: 60732ffbcc8a43cd2f04a6cba76e955b7e764ada9d201cd51a0a624d093b3659
3
+ metadata.gz: d687ea37d01a5a689f6c2c909072090603ec406d0a0e0bc933d315362b10d5a3
4
+ data.tar.gz: a49fe55eb25755a6d4158084ca9ccd0df1e62741584af21d9313b8eb63d1d5af
5
5
  SHA512:
6
- metadata.gz: 77db441f3e4da2b584065c597796d4adbb08b07e481b43ee85c80218375cbf8932bae847727afa9abee0be02ee5dea1c2effd5caa70216a5bafa34007d0c3820
7
- data.tar.gz: c180d28c4ac14fe5d6b5756350592b472cd084ce932309fd6cc87f042dffb1f1870dd194260e71b53dbac71fa648bd359c7b4d3c8860c7508f7cc854c9d2ef25
6
+ metadata.gz: 5bc28be9868ed008eb3c7e072a01a9403921f2ee9a28566f2d4a616042cb2f3da659a817a79046462fd4d5a782219d2209072ed21942c54133bac82754f1103d
7
+ data.tar.gz: eaa194a4a148f61ffdefad476cadf99c600fa88f94618dbf408a8fed10027cde2f4151bb6d2ec4b35195ce54c10d07b90d2b58be83f7ef8abf24056aee3d828a
@@ -134,14 +134,25 @@ module Unparser
134
134
  # @param [Class, Module] scope
135
135
  #
136
136
  # @return [undefined]
137
+ #
138
+ # mutant:disable
137
139
  def included(descendant)
138
140
  descendant.instance_exec(self, attribute_names) do |anima, names|
139
141
  # Define anima method
142
+
143
+ class << self
144
+ undef_method(:anima) if method_defined?(:anima)
145
+ end
146
+
140
147
  define_singleton_method(:anima) { anima }
141
148
 
142
149
  # Define instance methods
143
150
  include InstanceMethods
144
151
 
152
+ names.each do |name|
153
+ undef_method(name) if method_defined?(name)
154
+ end
155
+
145
156
  # Define attribute readers
146
157
  attr_reader(*names)
147
158
 
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Unparser
4
- module AST
5
-
4
+ class AST
6
5
  # Calculated local variable scope for a given node
7
6
  class LocalVariableScope
8
- include Enumerable, Adamantium
7
+ include Adamantium, Anima.new(:static_local_variables, :node)
9
8
 
10
9
  # Initialize object
11
10
  #
@@ -13,13 +12,17 @@ module Unparser
13
12
  #
14
13
  # @return [undefined]
15
14
  #
16
- # @api private
17
- #
18
- def initialize(node)
15
+ # mutant:disable
16
+ def initialize(*arguments)
17
+ super
18
+
19
19
  items = []
20
- LocalVariableScopeEnumerator.each(node) do |*scope|
21
- items << scope
22
- end
20
+
21
+ LocalVariableScopeEnumerator.each(
22
+ node: node,
23
+ stack: static_local_variables.dup
24
+ ) { |*scope| items << scope }
25
+
23
26
  @items = items
24
27
  end
25
28
 
@@ -53,6 +56,15 @@ module Unparser
53
56
  end
54
57
  end
55
58
 
59
+ # mutant:disable
60
+ def local_variables_for_node(needle)
61
+ @items.each do |node, current|
62
+ return current if node.equal?(needle)
63
+ end
64
+
65
+ Set.new
66
+ end
67
+
56
68
  # Test if local variables where first assigned in body and read by conditional
57
69
  #
58
70
  # @param [Parser::AST::Node] body
@@ -90,21 +102,13 @@ module Unparser
90
102
  #
91
103
  # @api private
92
104
  #
93
- def initialize
94
- @stack = [Set.new]
105
+ def initialize(stack:)
106
+ @stack = [stack]
95
107
  end
96
108
 
97
109
  # Enumerate each node with its local variable scope
98
- #
99
- # @param [Parser::AST::Node] node
100
- #
101
- # @return [self]
102
- #
103
- # @api private
104
- #
105
- def self.each(node, &block)
106
- new.each(node, &block)
107
- self
110
+ def self.each(node:, stack:, &block)
111
+ new(stack: stack).each(node: node, &block)
108
112
  end
109
113
 
110
114
  # Enumerate local variable scope scope
@@ -117,7 +121,7 @@ module Unparser
117
121
  #
118
122
  # @api private
119
123
  #
120
- def each(node, &block)
124
+ def each(node:, &block)
121
125
  visit(node, &block)
122
126
  end
123
127
 
@@ -132,7 +136,7 @@ module Unparser
132
136
  enter(node)
133
137
  yield node, current.dup, before
134
138
  node.children.each do |child|
135
- visit(child, &block) if child.is_a?(Parser::AST::Node)
139
+ visit(child, &block) if child.instance_of?(Parser::AST::Node)
136
140
  end
137
141
  leave(node)
138
142
  end
@@ -142,7 +146,7 @@ module Unparser
142
146
  when *RESET_NODES
143
147
  push_reset
144
148
  when ASSIGN_NODES
145
- define(node.children.first)
149
+ value = node.children.first and define(value)
146
150
  when *INHERIT_NODES
147
151
  push_inherit
148
152
  end
data/lib/unparser/ast.rb CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  module Unparser
4
4
  # Namespace for AST processing tools
5
- module AST
5
+ class AST
6
+ include Anima.new(:comments, :explicit_encoding, :node, :static_local_variables)
7
+
6
8
  FIRST_CHILD = ->(node) { node.children.first }.freeze
7
- TAUTOLOGY = ->(_node) { true }.freeze
8
9
 
9
10
  RESET_NODES = %i[module class sclass def defs].freeze
10
11
  INHERIT_NODES = [:block].freeze
@@ -16,12 +17,22 @@ module Unparser
16
17
  arg
17
18
  kwarg
18
19
  kwoptarg
20
+ kwrestarg
19
21
  lvasgn
20
22
  optarg
21
- procarg0
22
23
  restarg
23
24
  ].to_set.freeze
24
25
 
26
+ # mutant:disable
27
+ def self.from_node(node:)
28
+ new(
29
+ comments: EMPTY_ARRAY,
30
+ explicit_encoding: nil,
31
+ node:,
32
+ static_local_variables: Set.new
33
+ )
34
+ end
35
+
25
36
  # Test for local variable inherited scope reset
26
37
  #
27
38
  # @param [Parser::AST::Node] node
@@ -69,6 +80,7 @@ module Unparser
69
80
  #
70
81
  # @api private
71
82
  #
83
+ # mutant:disable
72
84
  def self.local_variable_reads(node)
73
85
  Enumerator.new(
74
86
  node,
@@ -80,19 +92,6 @@ module Unparser
80
92
  class Enumerator
81
93
  include Adamantium, Concord.new(:node, :controller), Enumerable
82
94
 
83
- # Return new instance
84
- #
85
- # @param [Parser::AST::Node] node
86
- # @param [#call(node)] controller
87
- #
88
- # @return [Enumerator]
89
- #
90
- # @api private
91
- #
92
- def self.new(node, controller = TAUTOLOGY)
93
- super
94
- end
95
-
96
95
  # Return each node
97
96
  #
98
97
  # @return [Enumerator<Parser::AST::Node>]
@@ -103,8 +102,8 @@ module Unparser
103
102
  #
104
103
  # @api private
105
104
  #
106
- def each(&block)
107
- Walker.call(node, controller, &block)
105
+ def each(&)
106
+ Walker.call(node, controller, &)
108
107
  end
109
108
 
110
109
  # Return nodes selected by types
@@ -168,13 +167,10 @@ module Unparser
168
167
  #
169
168
  # @param [Parser::AST::Node] node
170
169
  #
171
- # @return [self]
172
- #
173
170
  # @api private
174
171
  #
175
- def self.call(node, controller = TAUTOLOGY, &block)
172
+ def self.call(node, controller, &block)
176
173
  new(block, controller).call(node)
177
- self
178
174
  end
179
175
 
180
176
  # Call walker with node
@@ -14,8 +14,10 @@ module Unparser
14
14
  # @api private
15
15
  #
16
16
  def initialize
17
- @content = +''
18
- @indent = 0
17
+ @content = +''
18
+ @heredocs = []
19
+ @indent = 0
20
+ @no_nl = true
19
21
  end
20
22
 
21
23
  # Append string
@@ -34,6 +36,13 @@ module Unparser
34
36
  self
35
37
  end
36
38
 
39
+ # Push to heredoc stack
40
+ #
41
+ # @param [String] heredoc
42
+ def push_heredoc(heredoc)
43
+ @heredocs << heredoc
44
+ end
45
+
37
46
  # Append a string without an indentation prefix
38
47
  #
39
48
  # @param [String] string
@@ -68,6 +77,10 @@ module Unparser
68
77
  self
69
78
  end
70
79
 
80
+ def ensure_nl
81
+ nl unless fresh_line?
82
+ end
83
+
71
84
  # Write newline
72
85
  #
73
86
  # @return [self]
@@ -75,7 +88,27 @@ module Unparser
75
88
  # @api private
76
89
  #
77
90
  def nl
91
+ @no_nl = false
78
92
  write(NL)
93
+ flush_heredocs
94
+ self
95
+ end
96
+
97
+ # Write final newline
98
+ def final_newline
99
+ return if fresh_line? || @no_nl
100
+
101
+ write(NL)
102
+ end
103
+
104
+ def nl_flush_heredocs
105
+ return if @heredocs.empty?
106
+
107
+ if fresh_line?
108
+ flush_heredocs
109
+ else
110
+ nl
111
+ end
79
112
  end
80
113
 
81
114
  def root_indent
@@ -117,6 +150,10 @@ module Unparser
117
150
  self
118
151
  end
119
152
 
153
+ def write_encoding(encoding)
154
+ write("# -*- encoding: #{encoding} -*-\n")
155
+ end
156
+
120
157
  private
121
158
 
122
159
  INDENT_SPACE = ' '.freeze
@@ -125,5 +162,10 @@ module Unparser
125
162
  write(INDENT_SPACE * @indent)
126
163
  end
127
164
 
165
+ def flush_heredocs
166
+ @heredocs.each(&public_method(:write))
167
+ @heredocs = []
168
+ end
169
+
128
170
  end # Buffer
129
171
  end # Unparser
data/lib/unparser/cli.rb CHANGED
@@ -60,6 +60,7 @@ module Unparser
60
60
  #
61
61
  # @api private
62
62
  #
63
+ # mutant:disable
63
64
  def self.run(*arguments)
64
65
  new(*arguments).exit_status
65
66
  end
@@ -71,15 +72,17 @@ module Unparser
71
72
  # @return [undefined]
72
73
  #
73
74
  # @api private
75
+ # mutant:disable
74
76
  def initialize(arguments)
75
77
  @ignore = Set.new
76
78
  @targets = []
77
79
 
78
- @fail_fast = false
79
- @start_with = nil
80
- @success = true
81
- @validation = :validation
82
- @verbose = false
80
+ @fail_fast = false
81
+ @start_with = nil
82
+ @success = true
83
+ @validation = :validation
84
+ @verbose = false
85
+ @ignore_original_syntax_error = false
83
86
 
84
87
  opts = OptionParser.new do |builder|
85
88
  add_options(builder)
@@ -98,6 +101,7 @@ module Unparser
98
101
  #
99
102
  # @api private
100
103
  #
104
+ # mutant:disable
101
105
  # rubocop:disable Metrics/MethodLength
102
106
  def add_options(builder)
103
107
  builder.banner = 'usage: unparse [options] FILE [FILE]'
@@ -114,6 +118,9 @@ module Unparser
114
118
  builder.on('-l', '--literal') do
115
119
  @validation = :literal_validation
116
120
  end
121
+ builder.on('--ignore-original-syntax-error') do
122
+ @ignore_original_syntax_error = true
123
+ end
117
124
  builder.on('--ignore FILE') do |file|
118
125
  @ignore.merge(targets(file))
119
126
  end
@@ -129,6 +136,7 @@ module Unparser
129
136
  #
130
137
  # @api private
131
138
  #
139
+ # mutant:disable
132
140
  def exit_status
133
141
  effective_targets.each do |target|
134
142
  process_target(target)
@@ -140,11 +148,15 @@ module Unparser
140
148
 
141
149
  private
142
150
 
151
+ # mutant:disable
143
152
  def process_target(target)
144
153
  validation = target.public_send(@validation)
145
154
  if validation.success?
146
155
  puts validation.report if @verbose
147
156
  puts "Success: #{validation.identification}"
157
+ elsif ignore_original_syntax_error?(validation)
158
+ exception = validation.original_node.from_left
159
+ puts "#{exception.class}: #{validation.identification} #{exception}"
148
160
  else
149
161
  puts validation.report
150
162
  puts "Error: #{validation.identification}"
@@ -152,6 +164,14 @@ module Unparser
152
164
  end
153
165
  end
154
166
 
167
+ # mutant:disable
168
+ def ignore_original_syntax_error?(validation)
169
+ @ignore_original_syntax_error && validation.original_node.from_left do
170
+ nil
171
+ end.instance_of?(Parser::SyntaxError)
172
+ end
173
+
174
+ # mutant:disable
155
175
  def effective_targets
156
176
  if @start_with
157
177
  reject = true
@@ -167,6 +187,7 @@ module Unparser
167
187
  end.reject(&@ignore.method(:include?))
168
188
  end
169
189
 
190
+ # mutant:disable
170
191
  def targets(file_name)
171
192
  if File.directory?(file_name)
172
193
  Dir.glob(File.join(file_name, '**/*.rb'))
@@ -54,15 +54,15 @@ module Unparser
54
54
  # Evaluate functor block
55
55
  #
56
56
  # @return [Either::Left<Object>]
57
- def fmap(&block)
58
- require_block(&block)
57
+ def fmap(&)
58
+ require_block(&)
59
59
  end
60
60
 
61
61
  # Evaluate applicative block
62
62
  #
63
63
  # @return [Either::Left<Object>]
64
- def bind(&block)
65
- require_block(&block)
64
+ def bind(&)
65
+ require_block(&)
66
66
  end
67
67
 
68
68
  # Unwrap value from left
@@ -137,8 +137,8 @@ module Unparser
137
137
  # Map over left value
138
138
  #
139
139
  # @return [Either::Right<Object>]
140
- def lmap(&block)
141
- require_block(&block)
140
+ def lmap(&)
141
+ require_block(&)
142
142
  end
143
143
 
144
144
  # Evaluate right side of branch
@@ -6,10 +6,6 @@ module Unparser
6
6
  class Array < self
7
7
  handle :array
8
8
 
9
- def emit_heredoc_reminders
10
- emitters.each(&:emit_heredoc_reminders)
11
- end
12
-
13
9
  private
14
10
 
15
11
  def dispatch
@@ -12,18 +12,10 @@ module Unparser
12
12
 
13
13
  def dispatch
14
14
  write('[')
15
- delimited(children, &method(:emit_member))
15
+ delimited(children)
16
16
  write(', ') if node_type.equal?(:array_pattern_with_tail)
17
17
  write(']')
18
18
  end
19
-
20
- def emit_member(node)
21
- if n_match_rest?(node)
22
- writer_with(MatchRest, node).emit_array_pattern
23
- else
24
- visit(node)
25
- end
26
- end
27
19
  end # Pin
28
20
  end # Emitter
29
21
  end # Unparser
@@ -11,12 +11,6 @@ module Unparser
11
11
  true
12
12
  end
13
13
 
14
- def emit_heredoc_reminders
15
- return unless right
16
-
17
- emitter(right).emit_heredoc_reminders
18
- end
19
-
20
14
  private
21
15
 
22
16
  def dispatch
@@ -30,12 +24,17 @@ module Unparser
30
24
  write(' = ')
31
25
 
32
26
  if BINARY_OPERATOR.include?(right.type)
33
- writer_with(Writer::Binary, right).emit_operator
27
+ writer_with(Writer::Binary, node: right).emit_operator
34
28
  else
35
- visit(right)
29
+ right_emitter.write_to_buffer
36
30
  end
37
31
  end
38
32
 
33
+ def right_emitter
34
+ emitter(right)
35
+ end
36
+ memoize :right_emitter
37
+
39
38
  abstract_method :emit_left
40
39
 
41
40
  # Variable assignment emitter
@@ -8,12 +8,6 @@ module Unparser
8
8
  handle :begin
9
9
  children :body
10
10
 
11
- def emit_heredoc_reminders
12
- children.each do |child|
13
- emitter(child).emit_heredoc_reminders
14
- end
15
- end
16
-
17
11
  private
18
12
 
19
13
  def dispatch
@@ -13,7 +13,7 @@ module Unparser
13
13
  end
14
14
 
15
15
  def writer
16
- writer_with(Writer::Binary, node)
16
+ writer_with(Writer::Binary, node:)
17
17
  end
18
18
  memoize :writer
19
19
  end # Binary
@@ -16,7 +16,6 @@ module Unparser
16
16
  ws
17
17
  write_open
18
18
  emit_block_arguments unless n_lambda?(target)
19
- target_writer.emit_heredoc_reminders if n_send?(target)
20
19
  emit_optional_body_ensure_rescue(body)
21
20
  write_close
22
21
  end
@@ -42,7 +41,7 @@ module Unparser
42
41
  end
43
42
 
44
43
  def target_writer
45
- writer_with(Writer::Send::Regular, target)
44
+ writer_with(Writer::Send::Regular, node: target)
46
45
  end
47
46
  memoize :target_writer
48
47
 
@@ -65,7 +64,7 @@ module Unparser
65
64
  end
66
65
 
67
66
  def emit_lambda_arguments
68
- parentheses { writer_with(Args, arguments).emit_lambda_arguments }
67
+ parentheses { writer_with(Args, node: arguments).emit_lambda_arguments }
69
68
  end
70
69
 
71
70
  def numblock?
@@ -78,7 +77,7 @@ module Unparser
78
77
  ws
79
78
 
80
79
  parentheses('|', '|') do
81
- writer_with(Args, arguments).emit_block_arguments
80
+ writer_with(Args, node: arguments).emit_block_arguments
82
81
  end
83
82
  end
84
83
 
@@ -26,7 +26,7 @@ module Unparser
26
26
  return if arguments.children.empty?
27
27
 
28
28
  parentheses do
29
- writer_with(Args, arguments).emit_def_arguments
29
+ writer_with(Args, node: arguments).emit_def_arguments
30
30
  end
31
31
  end
32
32
 
@@ -7,15 +7,16 @@ module Unparser
7
7
 
8
8
  handle :dstr
9
9
 
10
- def emit_heredoc_reminders
11
- writer_with(Writer::DynamicString, node).emit_heredoc_reminder
12
- end
13
-
14
10
  private
15
11
 
16
12
  def dispatch
17
- writer_with(Writer::DynamicString, node).dispatch
13
+ dstr_writer.dispatch
14
+ end
15
+
16
+ def dstr_writer
17
+ writer_with(Writer::DynamicString, node:)
18
18
  end
19
+ memoize :dstr_writer
19
20
 
20
21
  end # DStr
21
22
  end # Emitter
@@ -14,12 +14,6 @@ module Unparser
14
14
 
15
15
  handle(*MAP.keys)
16
16
 
17
- def emit_heredoc_reminders
18
- children.each do |node|
19
- emitter(node).emit_heredoc_reminders
20
- end
21
- end
22
-
23
17
  private
24
18
 
25
19
  def dispatch
@@ -18,7 +18,7 @@ module Unparser
18
18
  end
19
19
 
20
20
  def emit_condition
21
- visit(condition)
21
+ emitter(condition).emit_mlhs
22
22
  write(' in ')
23
23
  visit(assignment)
24
24
  write(' do')
@@ -6,10 +6,6 @@ module Unparser
6
6
  class Hash < self
7
7
  handle :hash
8
8
 
9
- def emit_heredoc_reminders
10
- children.each(&method(:emit_heredoc_reminder_member))
11
- end
12
-
13
9
  private
14
10
 
15
11
  def dispatch
@@ -24,10 +20,6 @@ module Unparser
24
20
  end
25
21
  end
26
22
 
27
- def emit_heredoc_reminder_member(node)
28
- emitter(node.children.last).emit_heredoc_reminders if n_pair?(node)
29
- end
30
-
31
23
  def emit_hash_body
32
24
  delimited(children)
33
25
  end
@@ -32,7 +32,7 @@ module Unparser
32
32
  when :match_var
33
33
  emit_match_var(node)
34
34
  when :match_rest
35
- writer_with(MatchRest, node).emit_hash_pattern
35
+ writer_with(MatchRest, node:).emit_hash_pattern
36
36
  else
37
37
  visit(node)
38
38
  end
@@ -40,10 +40,6 @@ module Unparser
40
40
 
41
41
  private_constant(*constants(false))
42
42
 
43
- def emit_heredoc_reminders
44
- emitter(children.last).emit_heredoc_reminders
45
- end
46
-
47
43
  def dispatch
48
44
  emit_receiver
49
45
  emit_operation(children[VALUE_RANGE])
@@ -14,11 +14,6 @@ module Unparser
14
14
 
15
15
  handle(*MAP.keys)
16
16
 
17
- def emit_heredoc_reminders
18
- emitter(target).emit_heredoc_reminders
19
- emitter(expression).emit_heredoc_reminders
20
- end
21
-
22
17
  private
23
18
 
24
19
  def dispatch
@@ -35,11 +30,6 @@ module Unparser
35
30
 
36
31
  children :target, :operator, :value
37
32
 
38
- def emit_heredoc_reminders
39
- emitter(target).emit_heredoc_reminders
40
- emitter(value).emit_heredoc_reminders
41
- end
42
-
43
33
  private
44
34
 
45
35
  def dispatch