unparser 0.6.5 → 0.8.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -6
  3. data/bin/unparser +1 -1
  4. data/lib/unparser/adamantium.rb +3 -1
  5. data/lib/unparser/anima.rb +11 -0
  6. data/lib/unparser/ast/local_variable_scope.rb +28 -25
  7. data/lib/unparser/ast.rb +18 -22
  8. data/lib/unparser/buffer.rb +43 -15
  9. data/lib/unparser/cli.rb +30 -7
  10. data/lib/unparser/color.rb +5 -0
  11. data/lib/unparser/either.rb +6 -6
  12. data/lib/unparser/emitter/args.rb +5 -1
  13. data/lib/unparser/emitter/argument.rb +6 -4
  14. data/lib/unparser/emitter/array.rb +0 -4
  15. data/lib/unparser/emitter/array_pattern.rb +1 -9
  16. data/lib/unparser/emitter/assignment.rb +17 -8
  17. data/lib/unparser/emitter/begin.rb +0 -6
  18. data/lib/unparser/emitter/binary.rb +1 -1
  19. data/lib/unparser/emitter/block.rb +13 -6
  20. data/lib/unparser/emitter/def.rb +1 -1
  21. data/lib/unparser/emitter/dstr.rb +6 -5
  22. data/lib/unparser/emitter/dsym.rb +1 -1
  23. data/lib/unparser/emitter/ensure.rb +16 -0
  24. data/lib/unparser/emitter/flipflop.rb +7 -2
  25. data/lib/unparser/emitter/flow_modifier.rb +1 -7
  26. data/lib/unparser/emitter/for.rb +1 -1
  27. data/lib/unparser/emitter/hash.rb +0 -16
  28. data/lib/unparser/emitter/hash_pattern.rb +1 -1
  29. data/lib/unparser/emitter/in_pattern.rb +9 -1
  30. data/lib/unparser/emitter/index.rb +0 -4
  31. data/lib/unparser/emitter/kwbegin.rb +1 -1
  32. data/lib/unparser/emitter/match_pattern.rb +7 -11
  33. data/lib/unparser/emitter/match_pattern_p.rb +6 -1
  34. data/lib/unparser/emitter/mlhs.rb +7 -1
  35. data/lib/unparser/emitter/op_assign.rb +0 -5
  36. data/lib/unparser/emitter/pair.rb +31 -5
  37. data/lib/unparser/emitter/primitive.rb +19 -6
  38. data/lib/unparser/emitter/range.rb +23 -2
  39. data/lib/unparser/emitter/regexp.rb +5 -17
  40. data/lib/unparser/emitter/rescue.rb +7 -1
  41. data/lib/unparser/emitter/root.rb +2 -9
  42. data/lib/unparser/emitter/send.rb +1 -5
  43. data/lib/unparser/emitter/string.rb +31 -0
  44. data/lib/unparser/emitter/xstr.rb +8 -1
  45. data/lib/unparser/emitter.rb +9 -10
  46. data/lib/unparser/generation.rb +33 -29
  47. data/lib/unparser/node_details/send.rb +4 -3
  48. data/lib/unparser/node_details.rb +1 -0
  49. data/lib/unparser/node_helpers.rb +19 -9
  50. data/lib/unparser/util.rb +23 -0
  51. data/lib/unparser/validation.rb +70 -28
  52. data/lib/unparser/writer/array.rb +51 -0
  53. data/lib/unparser/writer/binary.rb +8 -4
  54. data/lib/unparser/writer/dynamic_string.rb +127 -146
  55. data/lib/unparser/writer/regexp.rb +101 -0
  56. data/lib/unparser/writer/resbody.rb +37 -3
  57. data/lib/unparser/writer/rescue.rb +3 -7
  58. data/lib/unparser/writer/send/unary.rb +9 -4
  59. data/lib/unparser/writer/send.rb +8 -14
  60. data/lib/unparser/writer.rb +31 -1
  61. data/lib/unparser.rb +149 -38
  62. metadata +38 -20
data/lib/unparser.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require 'diff/lcs'
4
4
  require 'diff/lcs/hunk'
5
5
  require 'optparse'
6
- require 'parser/current'
7
6
  require 'set'
8
7
 
9
8
  require 'unparser/equalizer'
@@ -18,18 +17,53 @@ require 'unparser/anima/attribute'
18
17
  require 'unparser/anima/error'
19
18
 
20
19
  # Library namespace
21
- module Unparser
20
+ module Unparser # rubocop:disable Metrics/ModuleLength
22
21
  # Unparser specific AST builder defaulting to modern AST format
23
- class Builder < Parser::Builders::Default
24
- modernize
22
+ if Gem::Version.new(RUBY_VERSION) <= '3.4'
23
+ require 'parser/current'
24
+ class Builder < Parser::Builders::Default
25
+ modernize
25
26
 
26
- def initialize
27
- super
27
+ # mutant:disable
28
+ def initialize
29
+ super
28
30
 
29
- self.emit_file_line_as_literals = false
31
+ self.emit_file_line_as_literals = false
32
+ end
33
+ end
34
+ else
35
+ require 'prism'
36
+ class Builder < Prism::Translation::Parser::Builder
37
+ modernize
38
+
39
+ # mutant:disable
40
+ def initialize
41
+ super
42
+
43
+ self.emit_file_line_as_literals = false
44
+ end
30
45
  end
31
46
  end
32
47
 
48
+ PARSER_CLASS =
49
+ if Gem::Version.new(RUBY_VERSION) <= '3.4'
50
+ Class.new(Parser::CurrentRuby) do
51
+ def declare_local_variable(local_variable)
52
+ static_env.declare(local_variable)
53
+ end
54
+ end
55
+ else
56
+ Class.new(Prism::Translation::Parser34) do
57
+ def declare_local_variable(local_variable)
58
+ (@local_variables ||= Set.new) << local_variable
59
+ end
60
+
61
+ def prism_options
62
+ super.merge(scopes: [@local_variables.to_a])
63
+ end
64
+ end
65
+ end
66
+
33
67
  EMPTY_STRING = ''.freeze
34
68
  EMPTY_ARRAY = [].freeze
35
69
 
@@ -44,57 +78,118 @@ module Unparser
44
78
  @node = node
45
79
  freeze
46
80
  end
47
- end
81
+ end # InvalidNodeError
82
+
83
+ # Error raised when unparser encounders AST it cannot generate source for that would parse to the same AST.
84
+ class UnsupportedNodeError < RuntimeError
85
+ end # UnsupportedNodeError
48
86
 
49
87
  # Unparse an AST (and, optionally, comments) into a string
50
88
  #
51
89
  # @param [Parser::AST::Node, nil] node
52
- # @param [Array] comment_array
90
+ # @param [Array] comments
91
+ # @param [Encoding, nil] explicit_encoding
92
+ # @param [Set<Symbol>] static_local_variables
93
+ #
94
+ # @return [String]
95
+ #
96
+ # @raise InvalidNodeError
97
+ # if the node passed is invalid
98
+ #
99
+ # @api public
100
+ #
101
+ # mutant:disable
102
+ # rubocop:disable Metrics/ParameterLists
103
+ def self.unparse(
104
+ node,
105
+ comments: EMPTY_ARRAY,
106
+ explicit_encoding: nil,
107
+ static_local_variables: Set.new
108
+ )
109
+ unparse_ast(
110
+ AST.new(
111
+ comments: comments,
112
+ explicit_encoding: explicit_encoding,
113
+ node: node,
114
+ static_local_variables: static_local_variables
115
+ )
116
+ )
117
+ end
118
+ # rubocop:enable Metrics/ParameterLists
119
+
120
+ # Unparse an AST
121
+ #
122
+ # @param [AST] ast
53
123
  #
54
124
  # @return [String]
55
125
  #
56
126
  # @raise InvalidNodeError
57
127
  # if the node passed is invalid
58
128
  #
129
+ # @raise UnsupportedNodeError
130
+ # if the node passed is valid but unparser cannot unparse it
131
+ #
59
132
  # @api public
60
- def self.unparse(node, comment_array = [])
61
- return '' if node.nil?
133
+ def self.unparse_ast(ast)
134
+ return EMPTY_STRING if ast.node.nil?
135
+
136
+ local_variable_scope = AST::LocalVariableScope.new(
137
+ node: ast.node,
138
+ static_local_variables: ast.static_local_variables
139
+ )
62
140
 
63
141
  Buffer.new.tap do |buffer|
64
142
  Emitter::Root.new(
65
- buffer,
66
- node,
67
- Comments.new(comment_array)
143
+ buffer: buffer,
144
+ comments: Comments.new(ast.comments),
145
+ explicit_encoding: ast.explicit_encoding,
146
+ local_variable_scope: local_variable_scope,
147
+ node: ast.node
68
148
  ).write_to_buffer
69
149
  end.content
70
150
  end
71
151
 
72
- # Unparse with validation
152
+ # Unparse AST either
73
153
  #
74
- # @param [Parser::AST::Node, nil] node
75
- # @param [Array] comment_array
154
+ # @param [AST] ast
76
155
  #
77
- # @return [Either<Validation,String>]
78
- def self.unparse_validate(node, comment_array = [])
79
- generated = unparse(node, comment_array)
80
- validation = Validation.from_string(generated)
156
+ # @return [Either<Exception,String>]
157
+ def self.unparse_ast_either(ast)
158
+ Either.wrap_error(Exception) { unparse_ast(ast) }
159
+ end
160
+
161
+ # Unparse AST either
162
+ #
163
+ # @param [AST] ast
164
+ #
165
+ # @return [Either<Exception,String>]
166
+ #
167
+ # mutant:disable
168
+ def self.unparse_validate_ast_either(ast:)
169
+ validation = Validation.from_ast(ast:)
81
170
 
82
171
  if validation.success?
83
- Either::Right.new(generated)
172
+ Either::Right.new(validation.generated_source.from_right)
84
173
  else
85
174
  Either::Left.new(validation)
86
175
  end
87
176
  end
88
177
 
89
- # Unparse capturing errors
90
- #
91
- # This is mostly useful for writing testing tools against unparser.
178
+ # Unparse with validation
92
179
  #
93
180
  # @param [Parser::AST::Node, nil] node
181
+ # @param [Array] comments
94
182
  #
95
- # @return [Either<Exception, String>]
96
- def self.unparse_either(node)
97
- Either.wrap_error(Exception) { unparse(node) }
183
+ # @return [Either<Validation,String>]
184
+ def self.unparse_validate(node, comments: EMPTY_ARRAY)
185
+ generated = unparse(node, comments:)
186
+ validation = Validation.from_string(generated)
187
+
188
+ if validation.success?
189
+ Either::Right.new(generated)
190
+ else
191
+ Either::Left.new(validation)
192
+ end
98
193
  end
99
194
 
100
195
  # Parse string into AST
@@ -103,27 +198,37 @@ module Unparser
103
198
  #
104
199
  # @return [Parser::AST::Node, nil]
105
200
  def self.parse(source)
106
- parser.parse(buffer(source))
201
+ parse_ast(source).node
107
202
  end
108
203
 
109
204
  # Parse string into either syntax error or AST
110
205
  #
111
206
  # @param [String] source
112
207
  #
113
- # @return [Either<Parser::SyntaxError, (Parser::ASTNode, nil)>]
114
- def self.parse_either(source)
115
- Either.wrap_error(Parser::SyntaxError) do
116
- parser.parse(buffer(source))
208
+ # @return [Either<Exception, (Parser::ASTNode, nil)>]
209
+ def self.parse_ast_either(source)
210
+ Either.wrap_error(Exception) do
211
+ parse_ast(source)
117
212
  end
118
213
  end
119
214
 
120
- # Parse string into AST, with comments
215
+ # Parse source with ast details
121
216
  #
122
217
  # @param [String] source
123
218
  #
124
- # @return [Parser::AST::Node]
125
- def self.parse_with_comments(source)
126
- parser.parse_with_comments(buffer(source))
219
+ # @return [AST]
220
+ #
221
+ # mutant:disable
222
+ def self.parse_ast(source, static_local_variables: Set.new)
223
+ explicit_encoding = Parser::Source::Buffer.recognize_encoding(source.dup.force_encoding(Encoding::BINARY))
224
+ node, comments = parser.parse_with_comments(buffer(source))
225
+
226
+ AST.new(
227
+ comments: comments,
228
+ explicit_encoding: explicit_encoding,
229
+ node: node,
230
+ static_local_variables: static_local_variables
231
+ )
127
232
  end
128
233
 
129
234
  # Parser instance that produces AST unparser understands
@@ -131,8 +236,9 @@ module Unparser
131
236
  # @return [Parser::Base]
132
237
  #
133
238
  # @api private
239
+ # mutant:disable
134
240
  def self.parser
135
- Parser::CurrentRuby.new(Builder.new).tap do |parser|
241
+ PARSER_CLASS.new(Builder.new).tap do |parser|
136
242
  parser.diagnostics.tap do |diagnostics|
137
243
  diagnostics.all_errors_are_fatal = true
138
244
  end
@@ -179,6 +285,7 @@ require 'unparser/emitter/def'
179
285
  require 'unparser/emitter/defined'
180
286
  require 'unparser/emitter/dstr'
181
287
  require 'unparser/emitter/dsym'
288
+ require 'unparser/emitter/ensure'
182
289
  require 'unparser/emitter/flipflop'
183
290
  require 'unparser/emitter/float'
184
291
  require 'unparser/emitter/flow_modifier'
@@ -210,6 +317,7 @@ require 'unparser/emitter/rescue'
210
317
  require 'unparser/emitter/root'
211
318
  require 'unparser/emitter/send'
212
319
  require 'unparser/emitter/simple'
320
+ require 'unparser/emitter/string'
213
321
  require 'unparser/emitter/splat'
214
322
  require 'unparser/emitter/super'
215
323
  require 'unparser/emitter/undef'
@@ -222,8 +330,10 @@ require 'unparser/emitter/find_pattern'
222
330
  require 'unparser/emitter/match_pattern'
223
331
  require 'unparser/emitter/match_pattern_p'
224
332
  require 'unparser/writer'
333
+ require 'unparser/writer/array'
225
334
  require 'unparser/writer/binary'
226
335
  require 'unparser/writer/dynamic_string'
336
+ require 'unparser/writer/regexp'
227
337
  require 'unparser/writer/resbody'
228
338
  require 'unparser/writer/rescue'
229
339
  require 'unparser/writer/send'
@@ -234,6 +344,7 @@ require 'unparser/writer/send/unary'
234
344
  require 'unparser/node_details'
235
345
  require 'unparser/node_details/send'
236
346
  require 'unparser/cli'
347
+ require 'unparser/util'
237
348
 
238
349
  require 'unparser/validation'
239
350
  # make it easy for zombie
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unparser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-04-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: diff-lcs
@@ -16,84 +15,98 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '1.3'
18
+ version: '1.6'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: '1.3'
25
+ version: '1.6'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: parser
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: 3.1.0
32
+ version: 3.3.0
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
- version: 3.1.0
39
+ version: 3.3.0
40
+ - !ruby/object:Gem::Dependency
41
+ name: prism
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.4'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '1.4'
41
54
  - !ruby/object:Gem::Dependency
42
55
  name: mutant
43
56
  requirement: !ruby/object:Gem::Requirement
44
57
  requirements:
45
58
  - - "~>"
46
59
  - !ruby/object:Gem::Version
47
- version: 0.11.4
60
+ version: 0.13.0
48
61
  type: :development
49
62
  prerelease: false
50
63
  version_requirements: !ruby/object:Gem::Requirement
51
64
  requirements:
52
65
  - - "~>"
53
66
  - !ruby/object:Gem::Version
54
- version: 0.11.4
67
+ version: 0.13.0
55
68
  - !ruby/object:Gem::Dependency
56
69
  name: mutant-rspec
57
70
  requirement: !ruby/object:Gem::Requirement
58
71
  requirements:
59
72
  - - "~>"
60
73
  - !ruby/object:Gem::Version
61
- version: 0.11.4
74
+ version: 0.13.0
62
75
  type: :development
63
76
  prerelease: false
64
77
  version_requirements: !ruby/object:Gem::Requirement
65
78
  requirements:
66
79
  - - "~>"
67
80
  - !ruby/object:Gem::Version
68
- version: 0.11.4
81
+ version: 0.13.0
69
82
  - !ruby/object:Gem::Dependency
70
83
  name: rspec
71
84
  requirement: !ruby/object:Gem::Requirement
72
85
  requirements:
73
86
  - - "~>"
74
87
  - !ruby/object:Gem::Version
75
- version: '3.9'
88
+ version: '3.13'
76
89
  type: :development
77
90
  prerelease: false
78
91
  version_requirements: !ruby/object:Gem::Requirement
79
92
  requirements:
80
93
  - - "~>"
81
94
  - !ruby/object:Gem::Version
82
- version: '3.9'
95
+ version: '3.13'
83
96
  - !ruby/object:Gem::Dependency
84
97
  name: rspec-core
85
98
  requirement: !ruby/object:Gem::Requirement
86
99
  requirements:
87
100
  - - "~>"
88
101
  - !ruby/object:Gem::Version
89
- version: '3.9'
102
+ version: '3.13'
90
103
  type: :development
91
104
  prerelease: false
92
105
  version_requirements: !ruby/object:Gem::Requirement
93
106
  requirements:
94
107
  - - "~>"
95
108
  - !ruby/object:Gem::Version
96
- version: '3.9'
109
+ version: '3.13'
97
110
  - !ruby/object:Gem::Dependency
98
111
  name: rspec-its
99
112
  requirement: !ruby/object:Gem::Requirement
@@ -184,6 +197,7 @@ files:
184
197
  - lib/unparser/emitter/defined.rb
185
198
  - lib/unparser/emitter/dstr.rb
186
199
  - lib/unparser/emitter/dsym.rb
200
+ - lib/unparser/emitter/ensure.rb
187
201
  - lib/unparser/emitter/find_pattern.rb
188
202
  - lib/unparser/emitter/flipflop.rb
189
203
  - lib/unparser/emitter/float.rb
@@ -221,6 +235,7 @@ files:
221
235
  - lib/unparser/emitter/send.rb
222
236
  - lib/unparser/emitter/simple.rb
223
237
  - lib/unparser/emitter/splat.rb
238
+ - lib/unparser/emitter/string.rb
224
239
  - lib/unparser/emitter/super.rb
225
240
  - lib/unparser/emitter/undef.rb
226
241
  - lib/unparser/emitter/variable.rb
@@ -232,10 +247,13 @@ files:
232
247
  - lib/unparser/node_details.rb
233
248
  - lib/unparser/node_details/send.rb
234
249
  - lib/unparser/node_helpers.rb
250
+ - lib/unparser/util.rb
235
251
  - lib/unparser/validation.rb
236
252
  - lib/unparser/writer.rb
253
+ - lib/unparser/writer/array.rb
237
254
  - lib/unparser/writer/binary.rb
238
255
  - lib/unparser/writer/dynamic_string.rb
256
+ - lib/unparser/writer/regexp.rb
239
257
  - lib/unparser/writer/resbody.rb
240
258
  - lib/unparser/writer/rescue.rb
241
259
  - lib/unparser/writer/send.rb
@@ -249,9 +267,10 @@ licenses:
249
267
  - MIT
250
268
  metadata:
251
269
  bug_tracker_uri: https://github.com/mbj/unparser/issues
252
- changelog_uri: https://github.com/mbj/unparser/blob/master/Changelog.md
270
+ changelog_uri: https://github.com/mbj/unparser/blob/main/Changelog.md
253
271
  funding_uri: https://github.com/sponsors/mbj
254
- post_install_message:
272
+ source_code_uri: https://github.com/mbj/unparser
273
+ rubygems_mfa_required: 'true'
255
274
  rdoc_options: []
256
275
  require_paths:
257
276
  - lib
@@ -259,15 +278,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
259
278
  requirements:
260
279
  - - ">="
261
280
  - !ruby/object:Gem::Version
262
- version: '2.7'
281
+ version: '3.1'
263
282
  required_rubygems_version: !ruby/object:Gem::Requirement
264
283
  requirements:
265
284
  - - ">="
266
285
  - !ruby/object:Gem::Version
267
286
  version: '0'
268
287
  requirements: []
269
- rubygems_version: 3.1.6
270
- signing_key:
288
+ rubygems_version: 3.6.7
271
289
  specification_version: 4
272
290
  summary: Generate equivalent source for parser gem AST nodes
273
291
  test_files: []