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
@@ -4,17 +4,30 @@ module Unparser
4
4
  # All unparser constants maybe included in other libraries.
5
5
  module Constants
6
6
 
7
+ # Return frozen symbol set from enumerable
8
+ #
9
+ # @param [Enumerable]
10
+ #
11
+ # @return [Set<Symbol>]
12
+ #
13
+ # @api private
14
+ #
15
+ def self.symbol_set(enumerable)
16
+ enumerable.map(&:to_sym).freeze
17
+ end
18
+ private_class_method :symbol_set
19
+
7
20
  # All unary operators of the ruby language
8
- UNARY_OPERATORS = %w(
21
+ UNARY_OPERATORS = symbol_set %w(
9
22
  ! ~ -@ +@
10
- ).map(&:to_sym).to_set
23
+ )
11
24
 
12
25
  # All binary operators of the ruby language
13
- BINARY_OPERATORS = %w(
26
+ BINARY_OPERATORS = symbol_set %w(
14
27
  + - * / & | && || << >> ==
15
28
  === != <= < <=> > >= =~ !~ ^
16
29
  ** %
17
- ).map(&:to_sym).to_set
30
+ )
18
31
 
19
32
  COMMENT = '#'
20
33
 
@@ -89,12 +102,16 @@ module Unparser
89
102
  #
90
103
  # These nodes dont require parentheses to be exactly reproduced in context of a more complex expression.
91
104
  #
92
- TERMINATED = %w(
105
+ TERMINATED = symbol_set %w(
93
106
  int float self kwbegin const regexp args lvar
94
107
  ivar gvar cvar if case module class sclass super
95
108
  yield zsuper break next defined? str block while loop until
96
109
  def defs true false nil array hash sym return
97
- ).map(&:to_sym).to_set
110
+ )
111
+
112
+ DEFAULT_DELIMITER = ', '.freeze
113
+
114
+ CURLY_BRACKETS = IceNine.deep_freeze(%w({ }))
98
115
 
99
116
  KEYWORDS = constants.each_with_object([]) do |name, keywords|
100
117
  value = const_get(name).freeze
@@ -7,6 +7,30 @@ module Unparser
7
7
  include Adamantium::Flat, AbstractType, Constants, NodeHelpers
8
8
  include Concord.new(:node, :parent)
9
9
 
10
+ module LocalVariableRoot
11
+
12
+ # Return local variable root
13
+ #
14
+ # @return [Parser::AST::Node]
15
+ #
16
+ # @api private
17
+ #
18
+ def local_variable_root
19
+ node
20
+ end
21
+
22
+ end
23
+
24
+ # Return local variable root
25
+ #
26
+ # @return [Parser::AST::Node]
27
+ #
28
+ # @api private
29
+ #
30
+ def local_variable_root
31
+ parent.local_variable_root
32
+ end
33
+
10
34
  # Registry for node emitters
11
35
  REGISTRY = {}
12
36
 
@@ -16,6 +40,14 @@ module Unparser
16
40
 
17
41
  CURLY_BRACKETS = IceNine.deep_freeze(%w({ }))
18
42
 
43
+ # Return assigned lvars
44
+ #
45
+ # @return [Array<Symbol>]
46
+ #
47
+ # @api private
48
+ #
49
+ abstract_method :local_variables
50
+
19
51
  # Return node type
20
52
  #
21
53
  # @return [Symbol]
@@ -30,6 +30,8 @@ module Unparser
30
30
 
31
31
  handle :args
32
32
 
33
+ SHADOWARGS = ->(node) { node.type == :shadowarg }.freeze
34
+
33
35
  private
34
36
 
35
37
  # Perform dispatch
@@ -39,16 +41,40 @@ module Unparser
39
41
  # @api private
40
42
  #
41
43
  def dispatch
42
- mapped = children.map do |child|
44
+ delimited(normal_arguments)
45
+ unless shadowargs.empty?
46
+ write('; ')
47
+ delimited(shadowargs)
48
+ end
49
+ end
50
+
51
+ # Return normal arguments
52
+ #
53
+ # @return [Enumerable<Parser::AST::Node>]
54
+ #
55
+ # @api private
56
+ #
57
+ def normal_arguments
58
+ children.reject(&SHADOWARGS).map do |child|
43
59
  if child.type == :mlhs
44
60
  Parser::AST::Node.new(:arg_expr, [child])
45
61
  else
46
62
  child
47
63
  end
48
64
  end
49
- delimited(mapped)
50
65
  end
51
66
 
67
+ # Return shadow args
68
+ #
69
+ # @return [Enumerable<Parser::AST::Node>]
70
+ #
71
+ # @api private
72
+ #
73
+ def shadowargs
74
+ children.select(&SHADOWARGS)
75
+ end
76
+ memoize :shadowargs
77
+
52
78
  end # Arguments
53
79
 
54
80
  # Emitter for block arguments
@@ -178,7 +204,7 @@ module Unparser
178
204
  # Argument emitter
179
205
  class Argument < self
180
206
 
181
- handle :arg
207
+ handle :arg, :shadowarg
182
208
 
183
209
  children :name
184
210
 
@@ -213,7 +239,7 @@ module Unparser
213
239
  #
214
240
  def dispatch
215
241
  write(T_AMP)
216
- visit(name)
242
+ visit_terminated(name)
217
243
  end
218
244
 
219
245
  end # BlockPass
@@ -90,6 +90,8 @@ module Unparser
90
90
 
91
91
  handle :masgn
92
92
 
93
+ PARENS = IceNine.deep_freeze(%w([ ]))
94
+
93
95
  private
94
96
 
95
97
  # Emit left
@@ -113,7 +115,10 @@ module Unparser
113
115
  right = children.last
114
116
  case right.type
115
117
  when :array
116
- delimited(right.children)
118
+ children = right.children
119
+ parentheses(*PARENS) do
120
+ delimited(children)
121
+ end
117
122
  else
118
123
  visit(right)
119
124
  end
@@ -128,6 +133,8 @@ module Unparser
128
133
 
129
134
  private
130
135
 
136
+ NO_COMMA = [:splat, :restarg, :mlhs]
137
+
131
138
  # Perform dispatch
132
139
  #
133
140
  # @return [undefined]
@@ -138,6 +145,10 @@ module Unparser
138
145
  conditional_parentheses(parent_type == :mlhs) do
139
146
  delimited(children)
140
147
  end
148
+
149
+ if children.one? && !NO_COMMA.include?(children.first.type)
150
+ write(',')
151
+ end
141
152
  end
142
153
 
143
154
  end # MLHS
@@ -25,6 +25,8 @@ module Unparser
25
25
 
26
26
  handle :begin
27
27
 
28
+ NON_EMPTY_PARENS = [:root, :dstr, :dyn_str_body].to_set.freeze
29
+
28
30
  private
29
31
 
30
32
  # Perform dispatch
@@ -34,7 +36,23 @@ module Unparser
34
36
  # @api private
35
37
  #
36
38
  def dispatch
37
- emit_inner
39
+ if children.empty? && !NON_EMPTY_PARENS.include?(parent_type)
40
+ write('()')
41
+ else
42
+ conditional_parentheses(parent_type == :optarg) do
43
+ emit_inner
44
+ end
45
+ end
46
+ end
47
+
48
+ # Emit inner delimited by semicolons
49
+ #
50
+ # @return [undefined]
51
+ #
52
+ # @api private
53
+ #
54
+ def emit_inner_semi
55
+ delimited(children, ';')
38
56
  end
39
57
 
40
58
  end # Implicit
@@ -65,7 +83,10 @@ module Unparser
65
83
  # @api private
66
84
  #
67
85
  def emit_body
68
- if NOINDENT.include?(body.type)
86
+ case
87
+ when body.nil?
88
+ nl
89
+ when NOINDENT.include?(body.type)
69
90
  emit_inner
70
91
  else
71
92
  indented { emit_inner }
@@ -60,7 +60,7 @@ module Unparser
60
60
  def emit_condition
61
61
  return unless condition
62
62
  write(WS)
63
- visit(condition)
63
+ visit_terminated(condition)
64
64
  end
65
65
 
66
66
  end # Case
@@ -4,6 +4,7 @@ module Unparser
4
4
  class Emitter
5
5
  # Emitter for class nodes
6
6
  class Class < self
7
+ include LocalVariableRoot
7
8
 
8
9
  handle :class
9
10
 
@@ -4,6 +4,7 @@ module Unparser
4
4
  class Emitter
5
5
  # Emitter for def node
6
6
  class Def < self
7
+ include LocalVariableRoot
7
8
 
8
9
  private
9
10
 
@@ -88,10 +89,36 @@ module Unparser
88
89
  # @api private
89
90
  #
90
91
  def emit_name
91
- visit(subject)
92
+ conditional_parentheses(!subject_without_parens?) do
93
+ visit(subject)
94
+ end
92
95
  write(T_DOT, name.to_s)
93
96
  end
94
97
 
98
+ # Test if subject needs parentheses
99
+ #
100
+ # @return [true]
101
+ # if subject needs parentheses
102
+ #
103
+ # @return [false]
104
+ # otherwise
105
+ #
106
+ # @api private
107
+ #
108
+ def subject_without_parens?
109
+ case subject.type
110
+ when :self
111
+ true
112
+ when :const
113
+ !subject.children.first
114
+ when :send
115
+ receiver, _selector, *arguments = *subject
116
+ !receiver && arguments.empty?
117
+ else
118
+ false
119
+ end
120
+ end
121
+
95
122
  end # Singleton
96
123
  end # Def
97
124
  end # Emitter
@@ -19,7 +19,9 @@ module Unparser
19
19
  #
20
20
  def dispatch
21
21
  write(K_DEFINED)
22
- visit_parentheses(subject)
22
+ parentheses do
23
+ visit_terminated(subject)
24
+ end
23
25
  end
24
26
 
25
27
  end # Defined
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ module Unparser
4
+ class Emitter
5
+ # Emitter control flow modifiers
6
+ class FlowModifier < self
7
+
8
+ MAP = {
9
+ return: K_RETURN,
10
+ next: K_NEXT,
11
+ break: K_BREAK,
12
+ }
13
+
14
+ handle(*MAP.keys)
15
+
16
+ private
17
+
18
+ # Perform dispatch
19
+ #
20
+ # @return [undefined]
21
+ #
22
+ # @api private
23
+ #
24
+ def dispatch
25
+ conditional_parentheses((parent_type == :or || parent_type == :and) && children.any?) do
26
+ write(MAP.fetch(node.type))
27
+ emit_arguments if children.any?
28
+ end
29
+ end
30
+
31
+ # Emit break or return arguments
32
+ #
33
+ # @return [undefined]
34
+ #
35
+ # @api private
36
+ #
37
+ def emit_arguments
38
+ ws
39
+ head, *tail = children
40
+ emit_argument(head)
41
+ tail.each do |node|
42
+ write(DEFAULT_DELIMITER)
43
+ emit_argument(node)
44
+ end
45
+ end
46
+
47
+ PARENS = [:if, :case, :begin].to_set.freeze
48
+
49
+ # Emit argument
50
+ #
51
+ # @param [Parser::AST::Node] node
52
+ #
53
+ # @api private
54
+ #
55
+ def emit_argument(node)
56
+ conditional_parentheses(PARENS.include?(node.type)) do
57
+ visit(node)
58
+ end
59
+ end
60
+
61
+ end # Return
62
+ end # Emitter
63
+ end # Unparser
@@ -18,6 +18,50 @@ module Unparser
18
18
  # @api private
19
19
  #
20
20
  def dispatch
21
+ if postcondition?
22
+ emit_postcondition
23
+ else
24
+ emit_normal
25
+ end
26
+ end
27
+
28
+ # Test for postcondition
29
+ #
30
+ # @return [true]
31
+ # if node must be emitted in postconditin style
32
+ #
33
+ # @return [false]
34
+ # otherwise
35
+ #
36
+ # @api private
37
+ #
38
+ def postcondition?
39
+ return false unless !!if_branch ^ !!else_branch
40
+
41
+ body = if_branch || else_branch
42
+
43
+ AST.first_assignment_in_body_and_used_in_condition?(local_variable_root, body, condition)
44
+ end
45
+
46
+ # Emit in postcondition style
47
+ #
48
+ # @return [undefined]
49
+ #
50
+ # @api private
51
+ #
52
+ def emit_postcondition
53
+ visit(if_branch || else_branch)
54
+ write(WS, keyword, WS)
55
+ emit_condition
56
+ end
57
+
58
+ # Emit in normal style
59
+ #
60
+ # @return [undefined]
61
+ #
62
+ # @api private
63
+ #
64
+ def emit_normal
21
65
  write(keyword, WS)
22
66
  emit_condition
23
67
  emit_if_branch