unparser 0.1.7 → 0.1.8

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -0
  3. data/.travis.yml +3 -0
  4. data/Changelog.md +4 -0
  5. data/README.md +4 -2
  6. data/config/flay.yml +1 -1
  7. data/config/flog.yml +1 -1
  8. data/config/reek.yml +24 -19
  9. data/config/rubocop.yml +2 -3
  10. data/lib/unparser.rb +8 -22
  11. data/lib/unparser/ast.rb +232 -0
  12. data/lib/unparser/ast/local_variable_scope.rb +198 -0
  13. data/lib/unparser/cli.rb +41 -24
  14. data/lib/unparser/cli/differ.rb +38 -16
  15. data/lib/unparser/cli/source.rb +46 -17
  16. data/lib/unparser/constants.rb +23 -6
  17. data/lib/unparser/emitter.rb +32 -0
  18. data/lib/unparser/emitter/argument.rb +30 -4
  19. data/lib/unparser/emitter/assignment.rb +12 -1
  20. data/lib/unparser/emitter/begin.rb +23 -2
  21. data/lib/unparser/emitter/case.rb +1 -1
  22. data/lib/unparser/emitter/class.rb +1 -0
  23. data/lib/unparser/emitter/def.rb +28 -1
  24. data/lib/unparser/emitter/defined.rb +3 -1
  25. data/lib/unparser/emitter/flow_modifier.rb +63 -0
  26. data/lib/unparser/emitter/if.rb +44 -0
  27. data/lib/unparser/emitter/literal/dynamic.rb +25 -1
  28. data/lib/unparser/emitter/literal/hash.rb +3 -3
  29. data/lib/unparser/emitter/literal/primitive.rb +9 -47
  30. data/lib/unparser/emitter/literal/regexp.rb +5 -16
  31. data/lib/unparser/emitter/module.rb +1 -0
  32. data/lib/unparser/emitter/repetition.rb +52 -0
  33. data/lib/unparser/emitter/resbody.rb +4 -2
  34. data/lib/unparser/emitter/rescue.rb +12 -2
  35. data/lib/unparser/emitter/root.rb +2 -11
  36. data/lib/unparser/emitter/send.rb +19 -2
  37. data/lib/unparser/emitter/send/index.rb +42 -4
  38. data/lib/unparser/emitter/send/unary.rb +4 -0
  39. data/lib/unparser/emitter/undef.rb +1 -3
  40. data/lib/unparser/node_helpers.rb +13 -1
  41. data/lib/unparser/preprocessor.rb +226 -0
  42. data/lib/unparser/strip_helper.rb +23 -0
  43. data/rubyspec.sh +20 -0
  44. data/spec/spec_helper.rb +2 -0
  45. data/spec/unit/unparser_spec.rb +390 -151
  46. data/unparser.gemspec +1 -1
  47. metadata +27 -24
  48. data/lib/unparser/cli/preprocessor.rb +0 -197
  49. data/lib/unparser/emitter/break.rb +0 -27
  50. data/lib/unparser/emitter/next.rb +0 -28
  51. data/lib/unparser/emitter/return.rb +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bbd1a7e700a15704cf6045d49b60c819fa098a56
4
- data.tar.gz: c4309776665a4ee2703ff6569d57423d17b20bd7
3
+ metadata.gz: d247e782f83bba8d99fdba92eb8cf9fb010ea6cf
4
+ data.tar.gz: 51ead40ddaae1b6424a80d9e9ade1617b3189209
5
5
  SHA512:
6
- metadata.gz: 96cad113e6bd2c968267edb3f3008e64bcb7c1e217f7256b182ebb3e7412a51a8bb7675204a9b897a926da7e3cdb8ed49a9f34c2516f2db901adcef65c302c68
7
- data.tar.gz: a9a4694e7475058e25e97f107a835cf704533937ea9bfa08985593abb85dfd150daa48633497d354f1ed53ebee3c27bb8fbc04bf1833d2e096933adcad29b214
6
+ metadata.gz: 3a5d334a76e0cab7091514c88ad8abcb12b56c2a7553eb22e4f74f79b893353a6bbbbf68fd04f3fd36528320731a70bb1517b0f8116c994d69a0643881d79fe4
7
+ data.tar.gz: 7549efa3f744f4190aef927f38f1467667d911a0b123ff4d37e014506d7e93c854a892863a22f8b26c46a4e4f8f36bfe384568a2355bcf485e316f350508b00e
data/.rubocop.yml ADDED
@@ -0,0 +1,7 @@
1
+ AllCops:
2
+ Includes:
3
+ - 'Gemfile'
4
+ Excludes:
5
+ - 'Gemfile.devtools'
6
+ - 'vendor/**'
7
+ - 'benchmarks/**'
data/.travis.yml CHANGED
@@ -6,6 +6,9 @@ rvm:
6
6
  - ruby
7
7
  - jruby
8
8
  - rbx
9
+ matrix:
10
+ allowed_failures:
11
+ rvm: rbx
9
12
  notifications:
10
13
  irc:
11
14
  channels:
data/Changelog.md CHANGED
@@ -1,3 +1,7 @@
1
+ # v0.1.8 2013-01-xx
2
+
3
+ * Fix all bugs found while round tripping rubyspec.
4
+
1
5
  # v0.1.7 2013-12-31
2
6
 
3
7
  * Add back support for root nodes of type resbody https://github.com/mbj/unparser/issues/24
data/README.md CHANGED
@@ -5,12 +5,14 @@ unparser
5
5
  [![Dependency Status](https://gemnasium.com/mbj/unparser.png)](https://gemnasium.com/mbj/unparser)
6
6
  [![Code Climate](https://codeclimate.com/github/mbj/unparser.png)](https://codeclimate.com/github/mbj/unparser)
7
7
 
8
- Generate equivalent source for ASTs from whitequarks awesome [parser](https://github.com/whitequark/parser).
8
+ Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser).
9
9
 
10
10
  This library is able to reproduce 100% of ruby 1.9 and 2.0 syntax. Including its own source code.
11
+
11
12
  It serves well for [mutant](https://github.cm/mbj/mutant) mutators and the in-memory vendoring for self hosting.
12
13
 
13
- This library dropped the reproduction of 1.8 syntax in the 0.1.0 release. It currently does not have support for 2.1 specific syntax.
14
+ This library dropped the reproduction of 1.8 syntax in the 0.1.0 release.
15
+ It currently does not have support for 2.1 specific syntax.
14
16
 
15
17
  Usage
16
18
  -----
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 13
3
- total_score: 497
3
+ total_score: 700
data/config/flog.yml CHANGED
@@ -1,2 +1,2 @@
1
1
  ---
2
- threshold: 24.5
2
+ threshold: 20.7
data/config/reek.yml CHANGED
@@ -10,7 +10,8 @@ UncommunicativeParameterName:
10
10
  - !ruby/regexp /[0-9]$/
11
11
  - !ruby/regexp /[A-Z]/
12
12
  TooManyInstanceVariables:
13
- exclude: []
13
+ exclude:
14
+ - Unparser::CLI
14
15
  enabled: true
15
16
  max_instance_variables: 3
16
17
  TooManyMethods:
@@ -21,6 +22,7 @@ TooManyMethods:
21
22
  UncommunicativeMethodName:
22
23
  accept:
23
24
  - s
25
+ - n
24
26
  exclude: []
25
27
  enabled: true
26
28
  reject:
@@ -35,14 +37,12 @@ LongParameterList:
35
37
  overrides: {}
36
38
  FeatureEnvy:
37
39
  exclude:
38
- - Unparser::Emitter::Literal::Regexp#escape # TODO Fixme!
39
- - Unparser::Emitter::Send::Arguments#effective_arguments # False positive
40
- - Unparser::Emitter::Send#binary_receiver?
41
- - Unparser::Emitter#visit_terminated
42
- - Unparser::CLI#add_options
43
- - Unparser::CLI::Source#error_report
44
- - Unparser::CLI::Preprocessor::Dstr#collapsed_children
45
- - Unparser::Comments#take_before
40
+ # False positives
41
+ - Unparser::CLI::Differ#collapsed_hunks
42
+ - Unparser::Emitter::Send::Arguments#effective_arguments
43
+ # Helper methods, false positive
44
+ - Unparser::StripHelper#strip
45
+ - Unparser::CLI#sources
46
46
  enabled: true
47
47
  ClassVariable:
48
48
  exclude: []
@@ -63,10 +63,10 @@ UncommunicativeModuleName:
63
63
  NestedIterators:
64
64
  ignore_iterators: []
65
65
  exclude:
66
- - Unparser::Emitter#self.children
66
+ # Acceptable cases:
67
67
  - Unparser::Emitter::Literal::Regexp#dispatch
68
- - Unparser::Emitter::Return#emit_break_return_arguments
69
68
  - Unparser::CLI::Preprocessor::Dstr#collapsed_children
69
+ - Unparser::Preprocessor::CollapseStrChildren#collapsed_children
70
70
  enabled: true
71
71
  max_allowed_nesting: 1
72
72
  TooManyStatements:
@@ -81,7 +81,10 @@ DuplicateMethodCall:
81
81
  max_calls: 1
82
82
  UtilityFunction:
83
83
  max_helper_calls: 1
84
- exclude: []
84
+ exclude:
85
+ # Intent to be helper methods
86
+ - Unparser::StripHelper#strip
87
+ - Unparser::CLI#sources
85
88
  enabled: true
86
89
  Attribute:
87
90
  exclude: []
@@ -96,11 +99,11 @@ UncommunicativeVariableName:
96
99
  - !ruby/regexp /[A-Z]/
97
100
  RepeatedConditional:
98
101
  exclude:
99
- - Unparser::Emitter # FIXME
100
- - Unparser::Comments # FIXME
101
- - Unparser::Emitter::Send # TODO Fixme
102
+ # TODO:
103
+ - Unparser::Comments
104
+ # False positives
102
105
  - Unparser::Emitter::If
103
- - Unparser::CLI # False positive
106
+ - Unparser::CLI
104
107
  enabled: true
105
108
  max_ifs: 1
106
109
  DataClump:
@@ -110,12 +113,14 @@ DataClump:
110
113
  min_clump_size: 3
111
114
  ControlParameter:
112
115
  exclude:
113
- - Unparser::Emitter#emit_body # false positive
114
- - Unparser::Emitter#conditional_parentheses # Intentionally a control structure like helper
116
+ # False positive:
117
+ - Unparser::Emitter#emit_body
118
+ - Unparser::Emitter#conditional_parentheses
115
119
  enabled: true
116
120
  NilCheck:
117
121
  enabled: false
118
122
  LongYieldList:
119
123
  max_params: 1
120
- exclude: []
124
+ exclude:
125
+ - Unparser::AST::LocalVariableScope#visit
121
126
  enabled: true
data/config/rubocop.yml CHANGED
@@ -1,11 +1,10 @@
1
+ inherit_from: ../.rubocop.yml
2
+
1
3
  AllCops:
2
4
  Includes:
3
5
  - '**/*.rake'
4
6
  - 'Gemfile'
5
7
  - 'Gemfile.triage'
6
- Excludes:
7
- - '**/vendor/**'
8
- - '**/benchmarks/**'
9
8
 
10
9
  # Avoid parameter lists longer than five parameters.
11
10
  ParameterLists:
data/lib/unparser.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'set'
3
4
  require 'abstract_type'
4
5
  require 'procto'
5
6
  require 'concord'
@@ -23,37 +24,24 @@ module Unparser
23
24
  # @api private
24
25
  #
25
26
  def self.unparse(node, comment_array = [])
26
- if node.nil?
27
- node = Parser::AST::Node.new(:empty)
28
- end
27
+ node = Preprocessor.run(node)
29
28
  buffer = Buffer.new
30
29
  comments = Comments.new(comment_array)
31
- root = Emitter::Root.new(buffer, comments)
30
+ root = Emitter::Root.new(Parser::AST::Node.new(:root, [node]), buffer, comments)
32
31
  Emitter.emitter(node, root).write_to_buffer
33
32
  buffer.content
34
33
  end
35
34
 
36
- # Transquote string
37
- #
38
- # @param [String] raw_quoted
39
- # @param [String] current_delimiter
40
- # @param [String] target_delimiter
41
- #
42
- # @return [String]
43
- #
44
- # @api private
45
- #
46
- def self.transquote(raw_quoted, current_delimiter, target_delimiter)
47
- raw = raw_quoted.gsub("\\#{current_delimiter}", current_delimiter)
48
- raw.gsub(target_delimiter, "\\#{target_delimiter}").freeze
49
- end
50
-
51
35
  end # Unparser
52
36
 
37
+ require 'unparser/strip_helper'
53
38
  require 'unparser/buffer'
54
39
  require 'unparser/node_helpers'
40
+ require 'unparser/preprocessor'
55
41
  require 'unparser/comments'
56
42
  require 'unparser/constants'
43
+ require 'unparser/ast'
44
+ require 'unparser/ast/local_variable_scope'
57
45
  require 'unparser/emitter'
58
46
  require 'unparser/emitter/literal'
59
47
  require 'unparser/emitter/literal/primitive'
@@ -79,7 +67,7 @@ require 'unparser/emitter/splat'
79
67
  require 'unparser/emitter/cbase'
80
68
  require 'unparser/emitter/argument'
81
69
  require 'unparser/emitter/begin'
82
- require 'unparser/emitter/return'
70
+ require 'unparser/emitter/flow_modifier'
83
71
  require 'unparser/emitter/undef'
84
72
  require 'unparser/emitter/def'
85
73
  require 'unparser/emitter/class'
@@ -88,10 +76,8 @@ require 'unparser/emitter/op_assign'
88
76
  require 'unparser/emitter/defined'
89
77
  require 'unparser/emitter/hookexe'
90
78
  require 'unparser/emitter/super'
91
- require 'unparser/emitter/break'
92
79
  require 'unparser/emitter/retry'
93
80
  require 'unparser/emitter/redo'
94
- require 'unparser/emitter/next'
95
81
  require 'unparser/emitter/if'
96
82
  require 'unparser/emitter/alias'
97
83
  require 'unparser/emitter/yield'
@@ -0,0 +1,232 @@
1
+ # encoding: UTF-8
2
+
3
+ module Unparser
4
+ # Namespace for AST processing tools
5
+ module AST
6
+
7
+ FIRST_CHILD = ->(node) { node.children.first }.freeze
8
+ TAUTOLOGY = ->(node) { true }.freeze
9
+
10
+ # Test if local variable was first at given assignment
11
+ #
12
+ # @param [Parser::AST::Node] root
13
+ # @param [Parser::AST::Node] assignment
14
+ #
15
+ # @return [true]
16
+ # if local variable was firstly introduced in body
17
+ #
18
+ # @return [false]
19
+ # otherwise
20
+ #
21
+ # @api private
22
+ #
23
+ def self.first_assignment?(root, assignment)
24
+ name = assignment.children.first
25
+ AST::LocalVariableScope.each(root) do |node, current, before|
26
+ if node.equal?(assignment) && current.include?(name) && !before.include?(name)
27
+ return true
28
+ end
29
+ end
30
+
31
+ false
32
+ end
33
+
34
+ # Test if local variable is defined for given node
35
+ #
36
+ # @param [Parser::AST::Node] root
37
+ # @param [Parser::AST::Node] node
38
+ # @param [Symbol] name
39
+ #
40
+ # @return [true]
41
+ # if local variable is defined
42
+ #
43
+ # @return [false]
44
+ # otherwise
45
+ #
46
+ # @api private
47
+ #
48
+ def self.local_variable_defined_for_node?(root, node, name)
49
+ AST::LocalVariableScope.each(root) do |child, current, before|
50
+ if child.equal?(node) && current.include?(name)
51
+ return true
52
+ end
53
+ end
54
+
55
+ false
56
+ end
57
+
58
+ # Test if local variables where first assigned in body and read by conditional
59
+ #
60
+ # @param [Parser::AST::Node] root
61
+ # @param [Parser::AST::Node] conditional
62
+ # @param [Parser::AST::Node] body
63
+ #
64
+ # @api private
65
+ #
66
+ def self.first_assignment_in_body_and_used_in_condition?(root, body, condition)
67
+ condition_reads = local_variables_read_in_scope(condition)
68
+
69
+ candidates = AST.local_variable_assignments_in_scope(body).select do |node|
70
+ name = node.children.first
71
+ condition_reads.include?(name)
72
+ end
73
+
74
+ candidates.any? do |node|
75
+ first_assignment?(root, node)
76
+ end
77
+ end
78
+
79
+ # Return local variables that get assigned in scope
80
+ #
81
+ # @param [Parser::AST::Node]
82
+ #
83
+ # @return [Set<Symbol>]
84
+ #
85
+ # @api private
86
+ #
87
+ def self.local_variable_assignments_in_scope(node)
88
+ Enumerator.new(
89
+ node,
90
+ LocalVariableScope.method(:not_reset_scope?),
91
+ ).types(LocalVariableScope::ASSIGN_NODES)
92
+ end
93
+
94
+ # Return local variables read
95
+ #
96
+ # @param [Parser::AST::Node] node
97
+ #
98
+ # @return [Set<Symbol>]
99
+ #
100
+ # @api private
101
+ #
102
+ def self.local_variables_read_in_scope(node)
103
+ Enumerator.new(
104
+ node,
105
+ LocalVariableScope.method(:not_close_scope?),
106
+ ).type(:lvar).map(&FIRST_CHILD).to_set
107
+ end
108
+
109
+ # AST enumerator
110
+ class Enumerator
111
+ include Adamantium::Flat, Concord.new(:node, :controller), Enumerable
112
+
113
+ # Return new instance
114
+ #
115
+ # @param [Parser::AST::Node] node
116
+ # @param [#call(node)] controller
117
+ #
118
+ # @return [Enumerator]
119
+ #
120
+ # @api private
121
+ #
122
+ def self.new(node, controller = TAUTOLOGY)
123
+ super
124
+ end
125
+
126
+ # Return each node
127
+ #
128
+ # @return [Enumerator<Parser::AST::Node>]
129
+ # if no block given
130
+ #
131
+ # @return [self]
132
+ # otherwise
133
+ #
134
+ # @api private
135
+ #
136
+ def each(&block)
137
+ return to_enum unless block_given?
138
+ Walker.call(node, controller, &block)
139
+ end
140
+
141
+ # Return nodes selected by types
142
+ #
143
+ # @param [Enumerable<Symbol>] types
144
+ #
145
+ # @return [Enumerable<Parser::AST::Node>]
146
+ #
147
+ # @api private
148
+ #
149
+ def types(types)
150
+ select { |node| types.include?(node.type) }
151
+ end
152
+
153
+ # Return nodes selected by type
154
+ #
155
+ # @param [Symbol] type
156
+ #
157
+ # @return [Enumerable<Parser::AST::Node>]
158
+ #
159
+ # @api private
160
+ #
161
+ def type(type)
162
+ select { |node| node.type == type }
163
+ end
164
+
165
+ # Return frozne set of objects
166
+ #
167
+ # @param [Enumerable] enumerable
168
+ #
169
+ # @return [Set]
170
+ #
171
+ # @api private
172
+ #
173
+ def self.set(enumerable)
174
+ enumerable.to_set.freeze
175
+ end
176
+ private_class_method :set
177
+
178
+ # Return nodes of type
179
+ #
180
+ # @param [Parser::AST::Node] node
181
+ # @param [Symbol] type
182
+ #
183
+ # @return [Enumerable<Parser::AST::Node]
184
+ #
185
+ # @api private
186
+ #
187
+ def self.type(node, type)
188
+ new(node).type(type)
189
+ end
190
+ private_class_method :type
191
+
192
+ end # Enumerator
193
+
194
+ # Controlled AST walker walking the AST in deeth first search with pre order
195
+ class Walker
196
+ include Concord.new(:block, :controller)
197
+
198
+ # Call ast walker
199
+ #
200
+ # @param [Parser::AST::Node] node
201
+ #
202
+ # @return [self]
203
+ #
204
+ # @api private
205
+ #
206
+ def self.call(node, controller = TAUTOLOGY, &block)
207
+ new(block, controller).call(node)
208
+ self
209
+ end
210
+
211
+ # Call walker with node
212
+ #
213
+ # @param [Parser::AST::Node] node
214
+ #
215
+ # @return [self]
216
+ #
217
+ # @api private
218
+ #
219
+ def call(node)
220
+ return unless controller.call(node)
221
+ block.call(node)
222
+ node.children.each do |child|
223
+ if child.kind_of?(Parser::AST::Node)
224
+ call(child)
225
+ end
226
+ end
227
+ self
228
+ end
229
+
230
+ end # Walker
231
+ end # AST
232
+ end # Unparser