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
@@ -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