unparser 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
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