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
@@ -20,7 +20,7 @@ module Unparser
20
20
  if interpolation?
21
21
  visit_parentheses(dynamic_body, util::OPEN, util::CLOSE)
22
22
  else
23
- delimited(children, WS)
23
+ emit_non_interpolated
24
24
  end
25
25
  end
26
26
 
@@ -54,6 +54,18 @@ module Unparser
54
54
  OPEN = CLOSE = '"'.freeze
55
55
  handle :dstr
56
56
 
57
+ private
58
+
59
+ # Emit non interpolated form
60
+ #
61
+ # @return [undefined]
62
+ #
63
+ # @api private
64
+ #
65
+ def emit_non_interpolated
66
+ delimited(children, WS)
67
+ end
68
+
57
69
  end # String
58
70
 
59
71
  # Dynamic symbol literal emitter
@@ -64,6 +76,18 @@ module Unparser
64
76
 
65
77
  handle :dsym
66
78
 
79
+ private
80
+
81
+ # Emit non interpolated form
82
+ #
83
+ # @return [undefined]
84
+ #
85
+ # @api private
86
+ #
87
+ def emit_non_interpolated
88
+ visit_parentheses(dynamic_body, OPEN, CLOSE)
89
+ end
90
+
67
91
  end # Symbol
68
92
 
69
93
  end
@@ -45,7 +45,7 @@ module Unparser
45
45
  #
46
46
  def dispatch
47
47
  write(key.children.first.to_s, COLON)
48
- visit(value)
48
+ visit_terminated(value)
49
49
  end
50
50
 
51
51
  end # Colon
@@ -82,9 +82,9 @@ module Unparser
82
82
  children.map do |pair|
83
83
  key, _value = *pair
84
84
  if key.type == :sym && key.children.first.to_s =~ BAREWORD
85
- s(:pair_colon, pair.children)
85
+ n(:pair_colon, pair.children)
86
86
  else
87
- s(:pair_rocket, pair.children)
87
+ n(:pair_rocket, pair.children)
88
88
  end
89
89
  end
90
90
  end
@@ -14,7 +14,7 @@ module Unparser
14
14
  # Emitter for primitives based on Object#inspect
15
15
  class Inspect < self
16
16
 
17
- handle :float, :int, :sym
17
+ handle :sym, :str
18
18
 
19
19
  private
20
20
 
@@ -30,64 +30,26 @@ module Unparser
30
30
 
31
31
  end # Inspect
32
32
 
33
- # Literal emitter with macro regeneration base class
34
- class MacroSafe < self
33
+ # Emiter for numeric literals
34
+ class Numeric < self
35
+
36
+ handle :int, :float
35
37
 
36
38
  private
37
39
 
38
- # Perform dispatch
40
+ # Dispatch value
39
41
  #
40
42
  # @return [undefined]
41
43
  #
42
44
  # @api private
43
45
  #
44
46
  def dispatch
45
- if source == macro
46
- write(macro)
47
- return
47
+ conditional_parentheses(parent.kind_of?(Emitter::Send) && value < 0) do
48
+ write(value.inspect)
48
49
  end
49
- write(value.inspect)
50
- end
51
-
52
- # Return source, if present
53
- #
54
- # @return [String]
55
- # if present
56
- #
57
- # @return [nil]
58
- # otherwise
59
- #
60
- # @api private
61
- #
62
- def source
63
- location = node.location || return
64
- expression = location.expression || return
65
- expression.source
66
50
  end
67
51
 
68
- # Return marco
69
- #
70
- # @return [String]
71
- #
72
- # @api private
73
- #
74
- def macro
75
- self.class::MACRO
76
- end
77
-
78
- # String macro safe emitter
79
- class String < self
80
- MACRO = '__FILE__'.freeze
81
- handle :str
82
- end # String
83
-
84
- # Integer macro safe emitter
85
- class Integer < self
86
- MACRO = '__LINE__'.freeze
87
- handle :int
88
- end # Integer
89
-
90
- end # MacroSave
52
+ end # Numeric
91
53
  end # Primitive
92
54
  end # Literal
93
55
  end # Emitter
@@ -7,6 +7,7 @@ module Unparser
7
7
  # Emitter for regexp literals
8
8
  class Regexp < self
9
9
  DELIMITER = '/'.freeze
10
+ ESCAPED_DELIMITER = '\/'.freeze
10
11
 
11
12
  handle :regexp
12
13
 
@@ -50,7 +51,7 @@ module Unparser
50
51
  when :str
51
52
  buffer.append_without_prefix(escape(node).children.first)
52
53
  else
53
- visit(s(:interpolated, [node]))
54
+ visit(s(:interpolated, node))
54
55
  end
55
56
  end
56
57
 
@@ -78,28 +79,16 @@ module Unparser
78
79
 
79
80
  # Return escaped child
80
81
  #
82
+ # @param [Parser::AST::Node] child
83
+ #
81
84
  # @return [Parser::AST::Node]
82
85
  #
83
86
  # @api private
84
87
  #
85
88
  def escape(child)
86
- return child unless child.type == :str
87
89
  source = child.children.first
88
- Parser::AST::Node.new(:str, [Unparser.transquote(source, delimiter, DELIMITER)])
89
- end
90
-
91
- # Return closing delimiter
92
- #
93
- # @return [String]
94
- #
95
- # @api private
96
- #
97
- def delimiter
98
- location = node.location
99
- return DELIMITER unless location
100
- location.end.source[-1]
90
+ s(:str, source.gsub(DELIMITER, ESCAPED_DELIMITER))
101
91
  end
102
- memoize :delimiter
103
92
 
104
93
  end # Regexp
105
94
 
@@ -4,6 +4,7 @@ module Unparser
4
4
  class Emitter
5
5
  # Emitter for module nodes
6
6
  class Module < self
7
+ include LocalVariableRoot
7
8
 
8
9
  handle :module
9
10
 
@@ -51,12 +51,64 @@ module Unparser
51
51
  # @api private
52
52
  #
53
53
  def dispatch
54
+ if postcontrol?
55
+ emit_postcontrol
56
+ else
57
+ emit_normal
58
+ end
59
+ end
60
+
61
+ # Test for postcontrol
62
+ #
63
+ # @return [true]
64
+ # if repetition must be emitted as post control
65
+ #
66
+ # @return [false]
67
+ # otherwise
68
+ #
69
+ # @api private
70
+ #
71
+ def postcontrol?
72
+ return false unless body
73
+ AST.first_assignment_in_body_and_used_in_condition?(local_variable_root, body, condition)
74
+ end
75
+
76
+ # Emit keyword
77
+ #
78
+ # @return [undefined]
79
+ #
80
+ # @api private
81
+ #
82
+ def emit_keyword
54
83
  write(MAP.fetch(node.type), WS)
84
+ end
85
+
86
+ # Emit embedded
87
+ #
88
+ # @return [undefned]
89
+ #
90
+ # @api private
91
+ #
92
+ def emit_normal
93
+ emit_keyword
55
94
  visit(condition)
56
95
  emit_body
57
96
  k_end
58
97
  end
59
98
 
99
+ # Emit postcontrol
100
+ #
101
+ # @return [undefined]
102
+ #
103
+ # @api private
104
+ #
105
+ def emit_postcontrol
106
+ visit(body)
107
+ ws
108
+ emit_keyword
109
+ visit(condition)
110
+ end
111
+
60
112
  end # Repetition
61
113
  end # Emitter
62
114
  end # Unparser
@@ -7,10 +7,9 @@ module Unparser
7
7
 
8
8
  children :exception, :assignment, :body
9
9
 
10
+ # Emitter for resbody in standalone form
10
11
  class Standalone < self
11
12
 
12
- handle :resbody
13
-
14
13
  private
15
14
 
16
15
  # Perform dispatch
@@ -49,8 +48,11 @@ module Unparser
49
48
  end
50
49
  end
51
50
 
51
+ # Emitter for resbody in keyworkd-embedded form
52
52
  class Embedded < self
53
53
 
54
+ handle :resbody
55
+
54
56
  private
55
57
 
56
58
  # Perform dispatch
@@ -13,6 +13,8 @@ module Unparser
13
13
 
14
14
  EMBEDDED_TYPES = [:def, :defs, :kwbegin, :ensure].to_set.freeze
15
15
 
16
+ NOINDENT_STANDALONE_RESCUE = [:root, :begin, :pair_rocket, :pair_colon, :lvasgn, :ivasgn].to_set.freeze
17
+
16
18
  private
17
19
 
18
20
  # Perform dispatch
@@ -23,7 +25,11 @@ module Unparser
23
25
  #
24
26
  def dispatch
25
27
  if standalone?
26
- emit_standalone
28
+ if NOINDENT_STANDALONE_RESCUE.include?(parent_type)
29
+ emit_standalone
30
+ else
31
+ indented { emit_standalone }
32
+ end
27
33
  else
28
34
  emit_embedded
29
35
  end
@@ -40,7 +46,11 @@ module Unparser
40
46
  # @api private
41
47
  #
42
48
  def standalone?
43
- !EMBEDDED_TYPES.include?(parent_type) && body
49
+ if parent_type == :ensure
50
+ !parent.node.children.first.equal?(node)
51
+ else
52
+ !EMBEDDED_TYPES.include?(parent_type) && body
53
+ end
44
54
  end
45
55
 
46
56
  # Emit standalone form
@@ -4,17 +4,8 @@ module Unparser
4
4
  class Emitter
5
5
  # Root emitter a special case
6
6
  class Root < self
7
- include Concord::Public.new(:buffer, :comments)
8
-
9
- # Return root node type
10
- #
11
- # @return [nil]
12
- #
13
- # @api private
14
- #
15
- def node_type
16
- nil
17
- end
7
+ include Concord::Public.new(:node, :buffer, :comments)
8
+ include LocalVariableRoot
18
9
 
19
10
  end # Root
20
11
  end # Emitter
@@ -30,7 +30,6 @@ module Unparser
30
30
  #
31
31
  def terminated?
32
32
  [
33
- Unary,
34
33
  Index::Reference,
35
34
  Regular
36
35
  ].include?(effective_emitter)
@@ -212,7 +211,25 @@ module Unparser
212
211
  # @api private
213
212
  #
214
213
  def emit_arguments
215
- run(Arguments, s(:arguments, arguments))
214
+ if arguments.empty? && receiver.nil? && local_variable_clash?
215
+ write('()')
216
+ else
217
+ run(Arguments, n(:arguments, arguments))
218
+ end
219
+ end
220
+
221
+ # Test for local variable clash
222
+ #
223
+ # @return [true]
224
+ # if selector clashes with a local variable
225
+ #
226
+ # @return [false]
227
+ # otherwise
228
+ #
229
+ # @api private
230
+ #
231
+ def local_variable_clash?
232
+ AST.local_variable_defined_for_node?(local_variable_root, node, selector)
216
233
  end
217
234
 
218
235
  end # Send
@@ -57,13 +57,51 @@ module Unparser
57
57
  # @api private
58
58
  #
59
59
  def emit_arguments
60
- index, *assignment = arguments
60
+ case arguments.length
61
+ when 0
62
+ emit_regular_with_empty_args
63
+ when 1
64
+ emit_mlhs_arguments
65
+ else
66
+ emit_normal_arguments
67
+ end
68
+ end
69
+
70
+ # Emit mlhs arguments
71
+ #
72
+ # @return [undefined]
73
+ #
74
+ # @api private
75
+ #
76
+ def emit_mlhs_arguments
61
77
  parentheses(*INDEX_PARENS) do
62
- delimited([index])
78
+ delimited(arguments)
79
+ end
80
+ end
81
+
82
+ # Emit normal arguments
83
+ #
84
+ # @return [undefined]
85
+ #
86
+ # @api private
87
+ #
88
+ def emit_normal_arguments
89
+ *indices, value = arguments
90
+ parentheses(*INDEX_PARENS) do
91
+ delimited(indices)
63
92
  end
64
- return if assignment.empty? # mlhs
65
93
  write(WS, T_ASN, WS)
66
- delimited(assignment)
94
+ visit(value)
95
+ end
96
+
97
+ # Emit regular with empty ars
98
+ #
99
+ # @return [undefined]
100
+ #
101
+ # @api private
102
+ #
103
+ def emit_regular_with_empty_args
104
+ write(T_DOT, '[]=()')
67
105
  end
68
106
 
69
107
  end # Assign
@@ -22,6 +22,10 @@ module Unparser
22
22
  def dispatch
23
23
  name = selector
24
24
  write(MAP.fetch(name, name).to_s)
25
+ if receiver.type == :int && selector == :'+@' && receiver.children.first > 0
26
+ write('+')
27
+ end
28
+
25
29
  visit_terminated(receiver)
26
30
  end
27
31