unparser 0.5.5 → 0.6.1

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -2
  3. data/lib/unparser/abstract_type.rb +121 -0
  4. data/lib/unparser/adamantium/method_builder.rb +111 -0
  5. data/lib/unparser/adamantium.rb +150 -0
  6. data/lib/unparser/anima/attribute.rb +59 -0
  7. data/lib/unparser/anima/error.rb +23 -0
  8. data/lib/unparser/anima.rb +184 -0
  9. data/lib/unparser/ast.rb +1 -2
  10. data/lib/unparser/buffer.rb +2 -2
  11. data/lib/unparser/cli.rb +1 -0
  12. data/lib/unparser/color.rb +1 -1
  13. data/lib/unparser/concord.rb +114 -0
  14. data/lib/unparser/diff.rb +1 -1
  15. data/lib/unparser/either.rb +153 -0
  16. data/lib/unparser/emitter/flow_modifier.rb +6 -0
  17. data/lib/unparser/emitter/hash.rb +1 -31
  18. data/lib/unparser/emitter/hash_pattern.rb +1 -1
  19. data/lib/unparser/emitter/index.rb +1 -1
  20. data/lib/unparser/emitter/kwargs.rb +13 -0
  21. data/lib/unparser/emitter/match_pattern.rb +22 -0
  22. data/lib/unparser/emitter/op_assign.rb +2 -2
  23. data/lib/unparser/emitter/pair.rb +33 -0
  24. data/lib/unparser/emitter/primitive.rb +2 -2
  25. data/lib/unparser/emitter/simple.rb +2 -2
  26. data/lib/unparser/emitter.rb +1 -1
  27. data/lib/unparser/equalizer.rb +98 -0
  28. data/lib/unparser/node_details/send.rb +5 -2
  29. data/lib/unparser/node_details.rb +1 -1
  30. data/lib/unparser/node_helpers.rb +3 -1
  31. data/lib/unparser/validation.rb +3 -3
  32. data/lib/unparser/writer/binary.rb +1 -1
  33. data/lib/unparser/writer/dynamic_string.rb +12 -22
  34. data/lib/unparser/writer/rescue.rb +1 -1
  35. data/lib/unparser/writer/send/unary.rb +1 -1
  36. data/lib/unparser/writer/send.rb +9 -18
  37. data/lib/unparser.rb +34 -24
  38. metadata +30 -113
@@ -114,9 +114,9 @@ module Unparser
114
114
  # @api private
115
115
  #
116
116
  def capture_content
117
- size_before = @content.size
117
+ size_before = content.size
118
118
  yield
119
- @content[size_before..-1]
119
+ content[size_before..]
120
120
  end
121
121
 
122
122
  # Write raw fragment to buffer
data/lib/unparser/cli.rb CHANGED
@@ -76,6 +76,7 @@ module Unparser
76
76
  @targets = []
77
77
 
78
78
  @fail_fast = false
79
+ @start_with = nil
79
80
  @success = true
80
81
  @validation = :validation
81
82
  @verbose = false
@@ -3,7 +3,7 @@
3
3
  module Unparser
4
4
  # Class to colorize strings
5
5
  class Color
6
- include Adamantium::Flat, Concord.new(:code)
6
+ include Adamantium, Concord.new(:code)
7
7
 
8
8
  # Format text with color
9
9
  #
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ # A mixin to define a composition
5
+ #
6
+ # Original code before vendoring and reduction from: https://github.com/mbj/concord.
7
+ class Concord < Module
8
+ include Adamantium, Equalizer.new(:names)
9
+
10
+ # The maximum number of objects the hosting class is composed of
11
+ MAX_NR_OF_OBJECTS = 3
12
+
13
+ # Return names
14
+ #
15
+ # @return [Enumerable<Symbol>]
16
+ #
17
+ # @api private
18
+ #
19
+ attr_reader :names
20
+
21
+ private
22
+
23
+ # Initialize object
24
+ #
25
+ # @return [undefined]
26
+ #
27
+ # @api private
28
+ #
29
+ # rubocop:disable Lint/MissingSuper
30
+ def initialize(*names)
31
+ if names.length > MAX_NR_OF_OBJECTS
32
+ fail "Composition of more than #{MAX_NR_OF_OBJECTS} objects is not allowed"
33
+ end
34
+
35
+ @names = names
36
+ define_initialize
37
+ define_readers
38
+ define_equalizer
39
+ end
40
+ # rubocop:enable Lint/MissingSuper
41
+
42
+ # Define equalizer
43
+ #
44
+ # @return [undefined]
45
+ #
46
+ # @api private
47
+ #
48
+ def define_equalizer
49
+ include(Equalizer.new(*names))
50
+ end
51
+
52
+ # Define readers
53
+ #
54
+ # @return [undefined]
55
+ #
56
+ # @api private
57
+ #
58
+ def define_readers
59
+ attribute_names = names
60
+ attr_reader(*attribute_names)
61
+
62
+ protected(*attribute_names) if attribute_names.any?
63
+ end
64
+
65
+ # Define initialize method
66
+ #
67
+ # @return [undefined]
68
+ #
69
+ # @api private
70
+ #
71
+ #
72
+ def define_initialize
73
+ ivars = instance_variable_names
74
+ size = names.size
75
+
76
+ define_method :initialize do |*args|
77
+ args_size = args.size
78
+ unless args_size.equal?(size)
79
+ fail ArgumentError, "wrong number of arguments (#{args_size} for #{size})"
80
+ end
81
+
82
+ ivars.zip(args) { |ivar, arg| instance_variable_set(ivar, arg) }
83
+ end
84
+ end
85
+
86
+ # Return instance variable names
87
+ #
88
+ # @return [String]
89
+ #
90
+ # @api private
91
+ #
92
+ def instance_variable_names
93
+ names.map { |name| "@#{name}" }
94
+ end
95
+
96
+ # Mixin for public attribute readers
97
+ class Public < self
98
+
99
+ # Hook called when module is included
100
+ #
101
+ # @param [Class,Module] descendant
102
+ #
103
+ # @return [undefined]
104
+ #
105
+ # @api private
106
+ #
107
+ def included(descendant)
108
+ names.each do |name|
109
+ descendant.__send__(:public, name)
110
+ end
111
+ end
112
+ end # Public
113
+ end # Concord
114
+ end # Unparser
data/lib/unparser/diff.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Unparser
4
4
  # Class to create diffs from source code
5
5
  class Diff
6
- include Adamantium::Flat, Concord.new(:old, :new)
6
+ include Adamantium, Concord.new(:old, :new)
7
7
 
8
8
  ADDITION = '+'
9
9
  DELETION = '-'
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module RequireBlock
5
+
6
+ private
7
+
8
+ # Raise error unless block is provided
9
+ #
10
+ # @raise [MissingBlockError]
11
+ # if no block is given
12
+ #
13
+ # @return [self]
14
+ def require_block
15
+ fail LocalJumpError unless block_given?
16
+
17
+ self
18
+ end
19
+ end # RequireBLock
20
+
21
+ class Either
22
+ include(
23
+ Adamantium,
24
+ Concord.new(:value),
25
+ RequireBlock
26
+ )
27
+
28
+ # Execute block and wrap error in left
29
+ #
30
+ # @param [Class<Exception>] exception
31
+ #
32
+ # @return [Either<Exception, Object>]
33
+ def self.wrap_error(*exceptions)
34
+ Right.new(yield)
35
+ rescue *exceptions => error
36
+ Left.new(error)
37
+ end
38
+
39
+ # Test for left constructor
40
+ #
41
+ # @return [Boolean]
42
+ def left?
43
+ instance_of?(Left)
44
+ end
45
+
46
+ # Test for right constructor
47
+ #
48
+ # @return [Boolean]
49
+ def right?
50
+ instance_of?(Right)
51
+ end
52
+
53
+ class Left < self
54
+ # Evaluate functor block
55
+ #
56
+ # @return [Either::Left<Object>]
57
+ def fmap(&block)
58
+ require_block(&block)
59
+ end
60
+
61
+ # Evaluate applicative block
62
+ #
63
+ # @return [Either::Left<Object>]
64
+ def bind(&block)
65
+ require_block(&block)
66
+ end
67
+
68
+ # Unwrap value from left
69
+ #
70
+ # @return [Object]
71
+ def from_left
72
+ value
73
+ end
74
+
75
+ # Unwrap value from right
76
+ #
77
+ # @return [Object]
78
+ #
79
+ def from_right
80
+ if block_given?
81
+ yield(value)
82
+ else
83
+ fail "Expected right value, got #{inspect}"
84
+ end
85
+ end
86
+
87
+ # Map over left value
88
+ #
89
+ # @return [Either::Right<Object>]
90
+ def lmap
91
+ Left.new(yield(value))
92
+ end
93
+
94
+ # Evaluate left side of branch
95
+ #
96
+ # @param [#call] left
97
+ # @param [#call] _right
98
+ def either(left, _right)
99
+ left.call(value)
100
+ end
101
+ end # Left
102
+
103
+ class Right < self
104
+ # Evaluate functor block
105
+ #
106
+ # @return [Either::Right<Object>]
107
+ def fmap
108
+ Right.new(yield(value))
109
+ end
110
+
111
+ # Evaluate applicative block
112
+ #
113
+ # @return [Either<Object>]
114
+ def bind
115
+ yield(value)
116
+ end
117
+
118
+ # Unwrap value from left
119
+ #
120
+ # @return [Object]
121
+ #
122
+ def from_left
123
+ if block_given?
124
+ yield(value)
125
+ else
126
+ fail "Expected left value, got #{inspect}"
127
+ end
128
+ end
129
+
130
+ # Unwrap value from right
131
+ #
132
+ # @return [Object]
133
+ def from_right
134
+ value
135
+ end
136
+
137
+ # Map over left value
138
+ #
139
+ # @return [Either::Right<Object>]
140
+ def lmap(&block)
141
+ require_block(&block)
142
+ end
143
+
144
+ # Evaluate right side of branch
145
+ #
146
+ # @param [#call] _left
147
+ # @param [#call] right
148
+ def either(_left, right)
149
+ right.call(value)
150
+ end
151
+ end # Right
152
+ end # Either
153
+ end # Unparser
@@ -14,6 +14,12 @@ module Unparser
14
14
 
15
15
  handle(*MAP.keys)
16
16
 
17
+ def emit_heredoc_reminders
18
+ children.each do |node|
19
+ emitter(node).emit_heredoc_reminders
20
+ end
21
+ end
22
+
17
23
  private
18
24
 
19
25
  def dispatch
@@ -4,10 +4,6 @@ module Unparser
4
4
  class Emitter
5
5
  # Emitter for Hash literals
6
6
  class Hash < self
7
- BAREWORD = /\A[A-Za-z_][A-Za-z_0-9]*[?!]?\z/.freeze
8
-
9
- private_constant(*constants(false))
10
-
11
7
  handle :hash
12
8
 
13
9
  def emit_last_argument_hash
@@ -41,34 +37,8 @@ module Unparser
41
37
  end
42
38
 
43
39
  def emit_hash_body
44
- delimited(children, &method(:emit_hash_member))
45
- end
46
-
47
- def emit_hash_member(node)
48
- if n_kwsplat?(node)
49
- visit(node)
50
- else
51
- emit_pair(node)
52
- end
40
+ delimited(children)
53
41
  end
54
-
55
- def emit_pair(pair)
56
- key, value = *pair.children
57
-
58
- if colon?(key)
59
- write(key.children.first.to_s, ': ')
60
- else
61
- visit(key)
62
- write(' => ')
63
- end
64
-
65
- visit(value)
66
- end
67
-
68
- def colon?(key)
69
- n_sym?(key) && BAREWORD.match?(key.children.first)
70
- end
71
-
72
42
  end # Hash
73
43
  end # Emitter
74
44
  end # Unparser
@@ -60,7 +60,7 @@ module Unparser
60
60
  end
61
61
 
62
62
  def write_symbol_body(symbol)
63
- write(symbol.inspect[1..-1])
63
+ write(symbol.inspect[1..])
64
64
  end
65
65
  end # Pin
66
66
  end # Emitter
@@ -36,7 +36,7 @@ module Unparser
36
36
  handle :indexasgn
37
37
 
38
38
  VALUE_RANGE = (1..-2).freeze
39
- NO_VALUE_PARENT = IceNine.deep_freeze(%i[and_asgn op_asgn or_asgn].to_set)
39
+ NO_VALUE_PARENT = %i[and_asgn op_asgn or_asgn].to_set.freeze
40
40
 
41
41
  private_constant(*constants(false))
42
42
 
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ class Emitter
5
+ class Kwargs < self
6
+ handle :kwargs
7
+
8
+ def dispatch
9
+ delimited(children)
10
+ end
11
+ end # Kwargs
12
+ end # Emitter
13
+ end # Unparser
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ class Emitter
5
+ # Emitter for in pattern nodes
6
+ class MatchPattern < self
7
+
8
+ handle :match_pattern
9
+ handle :match_pattern_p
10
+
11
+ children :target, :pattern
12
+
13
+ private
14
+
15
+ def dispatch
16
+ visit(target)
17
+ write(' in ')
18
+ visit(pattern)
19
+ end
20
+ end # InPattern
21
+ end # Emitter
22
+ end # Unparser
@@ -7,10 +7,10 @@ module Unparser
7
7
  class BinaryAssign < self
8
8
  children :target, :expression
9
9
 
10
- MAP = IceNine.deep_freeze(
10
+ MAP = {
11
11
  and_asgn: '&&=',
12
12
  or_asgn: '||='
13
- )
13
+ }.freeze
14
14
 
15
15
  handle(*MAP.keys)
16
16
 
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ class Emitter
5
+ # Emitter for key value pairs in hash literals or kwargs
6
+ class Pair < self
7
+ BAREWORD = /\A[A-Za-z_][A-Za-z_0-9]*[?!]?\z/.freeze
8
+
9
+ private_constant(*constants(false))
10
+
11
+ handle :pair
12
+
13
+ children :key, :value
14
+
15
+ private
16
+
17
+ def dispatch
18
+ if colon?(key)
19
+ write(key.children.first.to_s, ': ')
20
+ else
21
+ visit(key)
22
+ write(' => ')
23
+ end
24
+
25
+ visit(value)
26
+ end
27
+
28
+ def colon?(key)
29
+ n_sym?(key) && BAREWORD.match?(key.children.first)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -28,11 +28,11 @@ module Unparser
28
28
  RATIONAL_FORMAT = 'i'.freeze
29
29
 
30
30
  MAP =
31
- IceNine.deep_freeze(
31
+ {
32
32
  ::Float => :float,
33
33
  ::Rational => :rational,
34
34
  ::Integer => :int
35
- )
35
+ }.freeze
36
36
 
37
37
  private
38
38
 
@@ -4,7 +4,7 @@ module Unparser
4
4
  class Emitter
5
5
  # Emitter for simple nodes that generate a single token
6
6
  class Simple < self
7
- MAP = IceNine.deep_freeze(
7
+ MAP = {
8
8
  __ENCODING__: '__ENCODING__',
9
9
  __FILE__: '__FILE__',
10
10
  __LINE__: '__LINE__',
@@ -19,7 +19,7 @@ module Unparser
19
19
  self: 'self',
20
20
  true: 'true',
21
21
  zsuper: 'super'
22
- )
22
+ }.freeze
23
23
 
24
24
  handle(*MAP.keys)
25
25
 
@@ -5,7 +5,7 @@ module Unparser
5
5
 
6
6
  # Emitter base class
7
7
  class Emitter
8
- include Adamantium::Flat, AbstractType, Constants, Generation, NodeHelpers
8
+ include Adamantium, AbstractType, Constants, Generation, NodeHelpers
9
9
  include Anima.new(:buffer, :comments, :node, :local_variable_scope)
10
10
 
11
11
  public :node
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ # Define equality, equivalence and inspection methods
5
+ #
6
+ # Original code before vendoring and reduction from: https://github.com/dkubb/equalizer.
7
+ class Equalizer < Module
8
+ # Initialize an Equalizer with the given keys
9
+ #
10
+ # Will use the keys with which it is initialized to define #cmp?,
11
+ # #hash, and #inspect
12
+ #
13
+ # @param [Array<Symbol>] keys
14
+ #
15
+ # @return [undefined]
16
+ #
17
+ # @api private
18
+ #
19
+ # rubocop:disable Lint/MissingSuper
20
+ def initialize(*keys)
21
+ @keys = keys
22
+ define_methods
23
+ freeze
24
+ end
25
+ # rubocop:enable Lint/MissingSuper
26
+
27
+ private
28
+
29
+ def included(descendant)
30
+ descendant.include(Methods)
31
+ end
32
+
33
+ def define_methods
34
+ define_cmp_method
35
+ define_hash_method
36
+ define_inspect_method
37
+ end
38
+
39
+ def define_cmp_method
40
+ keys = @keys
41
+ define_method(:cmp?) do |comparator, other|
42
+ keys.all? do |key|
43
+ __send__(key).public_send(comparator, other.__send__(key))
44
+ end
45
+ end
46
+ private :cmp?
47
+ end
48
+
49
+ def define_hash_method
50
+ keys = @keys
51
+ define_method(:hash) do
52
+ keys.map(&public_method(:__send__)).push(self.class).hash
53
+ end
54
+ end
55
+
56
+ def define_inspect_method
57
+ keys = @keys
58
+ define_method(:inspect) do
59
+ klass = self.class
60
+ name = klass.name || klass.inspect
61
+ "#<#{name}#{keys.map { |key| " #{key}=#{__send__(key).inspect}" }.join}>"
62
+ end
63
+ end
64
+
65
+ # The comparison methods
66
+ module Methods
67
+ # Compare the object with other object for equality
68
+ #
69
+ # @example
70
+ # object.eql?(other) # => true or false
71
+ #
72
+ # @param [Object] other
73
+ # the other object to compare with
74
+ #
75
+ # @return [Boolean]
76
+ #
77
+ # @api public
78
+ def eql?(other)
79
+ instance_of?(other.class) && cmp?(__method__, other)
80
+ end
81
+
82
+ # Compare the object with other object for equivalency
83
+ #
84
+ # @example
85
+ # object == other # => true or false
86
+ #
87
+ # @param [Object] other
88
+ # the other object to compare with
89
+ #
90
+ # @return [Boolean]
91
+ #
92
+ # @api public
93
+ def ==(other)
94
+ instance_of?(other.class) && cmp?(__method__, other)
95
+ end
96
+ end # module Methods
97
+ end # class Equalizer
98
+ end # Unparser
@@ -19,7 +19,10 @@ module Unparser
19
19
  end
20
20
 
21
21
  def binary_syntax_allowed?
22
- selector_binary_operator? && arguments.one? && !n_splat?(arguments.first)
22
+ selector_binary_operator? \
23
+ && arguments.one? \
24
+ && !n_splat?(arguments.first) \
25
+ && !n_kwargs?(arguments.first)
23
26
  end
24
27
 
25
28
  def selector_unary_operator?
@@ -48,7 +51,7 @@ module Unparser
48
51
  memoize :assignment?
49
52
 
50
53
  def arguments
51
- children[2..-1]
54
+ children[2..]
52
55
  end
53
56
  memoize :arguments
54
57
 
@@ -6,7 +6,7 @@ module Unparser
6
6
 
7
7
  def self.included(descendant)
8
8
  descendant.class_eval do
9
- include Adamantium::Flat, Concord.new(:node)
9
+ include Adamantium, Concord.new(:node)
10
10
 
11
11
  extend DSL
12
12
  end
@@ -36,18 +36,20 @@ module Unparser
36
36
  args
37
37
  array
38
38
  array_pattern
39
- empty_else
40
39
  begin
41
40
  block
42
41
  cbase
43
42
  const
44
43
  dstr
44
+ empty_else
45
45
  ensure
46
46
  hash
47
47
  hash_pattern
48
48
  if
49
49
  in_pattern
50
50
  int
51
+ kwarg
52
+ kwargs
51
53
  kwsplat
52
54
  lambda
53
55
  match_rest
@@ -3,7 +3,7 @@
3
3
  module Unparser
4
4
  # Validation of unparser results
5
5
  class Validation
6
- include Adamantium::Flat, Anima.new(
6
+ include Adamantium, Anima.new(
7
7
  :generated_node,
8
8
  :generated_source,
9
9
  :identification,
@@ -64,7 +64,7 @@ module Unparser
64
64
 
65
65
  new(
66
66
  identification: '(string)',
67
- original_source: MPrelude::Either::Right.new(original_source),
67
+ original_source: Either::Right.new(original_source),
68
68
  original_node: original_node,
69
69
  generated_source: generated_source,
70
70
  generated_node: generated_node
@@ -86,7 +86,7 @@ module Unparser
86
86
  new(
87
87
  identification: '(string)',
88
88
  original_source: generated_source,
89
- original_node: MPrelude::Either::Right.new(original_node),
89
+ original_node: Either::Right.new(original_node),
90
90
  generated_source: generated_source,
91
91
  generated_node: generated_node
92
92
  )