unparser 0.5.3 → 0.6.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3bd3746bcbd6711c80f0251429c198b6c7e9128159467af9aec91c468976d6ba
4
- data.tar.gz: b7133c33285430ba21b15e531e2bb125d1ac6d725ccf38603985854a6fb2f75f
3
+ metadata.gz: 0a585e06cf743ca1901a601efa2fb7f178528307f89a6bea190df19014e3abf3
4
+ data.tar.gz: dc1980af7f00fa4b54da6fc91cc740857588bf5d1bf30f6c362be995a86a2d83
5
5
  SHA512:
6
- metadata.gz: f84831adbb37277301fdc9e5b1f36d26497fe39bae7df9dba921d6fef642c928e0357f6cd3392c6cff23855813ea40aca55585bebfc9afad80b4ea02bc9b18eb
7
- data.tar.gz: 0f305fdee5f1dbc1cfd704a4fca7981c8741fb6954a2fd61ff812895748443acb33354d70c432bb9be481717b797229c466b4363ebe9e09b135fbc88edf6001b
6
+ metadata.gz: da5524a1621bd1202f943c201b79b425ae4c203a9642a633b2a8a66fc85a0b8d3a250464c18432b791ce7e656c5b1a3940bc93d004f0bc18aa5413f931fc74bf
7
+ data.tar.gz: ab9cd266e002fcf85a12e1051a6b6a9fb9f47479ac2d7d883abcb99306d09ea6a196cb5d7de98a8c4f8a6635a78284e14e689cc2786f0a994eb504db0a0545ee
data/README.md CHANGED
@@ -168,6 +168,18 @@ People
168
168
 
169
169
  Various people contributed to this repository. See [Contributors](https://github.com/mbj/unparser/graphs/contributors).
170
170
 
171
+ Included Libraries
172
+ ------------------
173
+
174
+ For dependency reduction reasons unparser ships vendored (and reduced) versions of:
175
+
176
+ * [abstract_type](https://github.com/mbj/concord) -> Unparser::AbstractType
177
+ * [adamantium](https://github.com/dkubb/adamantium) -> Unparser::Adamantium
178
+ * [anima](https://github.com/mbj/concord) -> Unparser::Anima
179
+ * [concord](https://github.com/mbj/concord) -> Unparser::Concord
180
+ * [memoizable](https://github.com/dkubb/memoizable) -> Unparser::Adamantium
181
+ * [mprelude](https://github.com/dkubb/memoizable) -> Unparser::Either
182
+
171
183
  Contributing
172
184
  -------------
173
185
 
@@ -1,16 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'abstract_type'
4
- require 'anima'
5
- require 'concord'
6
3
  require 'diff/lcs'
7
4
  require 'diff/lcs/hunk'
8
- require 'mprelude'
9
5
  require 'optparse'
10
6
  require 'parser/current'
11
- require 'procto'
12
7
  require 'set'
13
8
 
9
+ require 'unparser/equalizer'
10
+ require 'unparser/adamantium'
11
+ require 'unparser/adamantium/method_builder'
12
+ require 'unparser/abstract_type'
13
+
14
+ require 'unparser/concord'
15
+ require 'unparser/either'
16
+ require 'unparser/anima'
17
+ require 'unparser/anima/attribute'
18
+ require 'unparser/anima/error'
19
+
14
20
  # Library namespace
15
21
  module Unparser
16
22
  # Unparser specific AST builder defaulting to modern AST format
@@ -27,7 +33,18 @@ module Unparser
27
33
  EMPTY_STRING = ''.freeze
28
34
  EMPTY_ARRAY = [].freeze
29
35
 
30
- private_constant(*constants(false))
36
+ private_constant(*constants(false) - %i[Adamantium AbstractType Anima Concord Either Equalizer Memoizable])
37
+
38
+ # Error raised when unparser encounters an invalid AST
39
+ class InvalidNodeError < RuntimeError
40
+ attr_reader :node
41
+
42
+ def initialize(message, node)
43
+ super(message)
44
+ @node = node
45
+ freeze
46
+ end
47
+ end
31
48
 
32
49
  # Unparse an AST (and, optionally, comments) into a string
33
50
  #
@@ -36,8 +53,10 @@ module Unparser
36
53
  #
37
54
  # @return [String]
38
55
  #
39
- # @api private
56
+ # @raise InvalidNodeError
57
+ # if the node passed is invalid
40
58
  #
59
+ # @api public
41
60
  def self.unparse(node, comment_array = [])
42
61
  return '' if node.nil?
43
62
 
@@ -61,9 +80,9 @@ module Unparser
61
80
  validation = Validation.from_string(generated)
62
81
 
63
82
  if validation.success?
64
- MPrelude::Either::Right.new(generated)
83
+ Either::Right.new(generated)
65
84
  else
66
- MPrelude::Either::Left.new(validation)
85
+ Either::Left.new(validation)
67
86
  end
68
87
  end
69
88
 
@@ -75,8 +94,7 @@ module Unparser
75
94
  #
76
95
  # @return [Either<Exception, String>]
77
96
  def self.unparse_either(node)
78
- MPrelude::Either
79
- .wrap_error(Exception) { unparse(node) }
97
+ Either.wrap_error(Exception) { unparse(node) }
80
98
  end
81
99
 
82
100
  # Parse string into AST
@@ -92,9 +110,9 @@ module Unparser
92
110
  #
93
111
  # @param [String] source
94
112
  #
95
- # @return [MPrelude::Either<Parser::SyntaxError, (Parser::ASTNode, nil)>]
113
+ # @return [Either<Parser::SyntaxError, (Parser::ASTNode, nil)>]
96
114
  def self.parse_either(source)
97
- MPrelude::Either.wrap_error(Parser::SyntaxError) do
115
+ Either.wrap_error(Parser::SyntaxError) do
98
116
  parser.parse(buffer(source))
99
117
  end
100
118
  end
@@ -117,21 +135,10 @@ module Unparser
117
135
  Parser::CurrentRuby.new(Builder.new).tap do |parser|
118
136
  parser.diagnostics.tap do |diagnostics|
119
137
  diagnostics.all_errors_are_fatal = true
120
- diagnostics.consumer = method(:consume_diagnostic)
121
138
  end
122
139
  end
123
140
  end
124
141
 
125
- # Consume diagnostic
126
- #
127
- # @param [Parser::Diagnostic] diagnostic
128
- #
129
- # @return [undefined]
130
- def self.consume_diagnostic(diagnostic)
131
- Kernel.warn(diagnostic.render)
132
- end
133
- private_class_method :consume_diagnostic
134
-
135
142
  # Construct a parser buffer from string
136
143
  #
137
144
  # @param [String] source
@@ -209,6 +216,9 @@ require 'unparser/emitter/undef'
209
216
  require 'unparser/emitter/variable'
210
217
  require 'unparser/emitter/xstr'
211
218
  require 'unparser/emitter/yield'
219
+ require 'unparser/emitter/kwargs'
220
+ require 'unparser/emitter/pair'
221
+ require 'unparser/emitter/match_pattern'
212
222
  require 'unparser/writer'
213
223
  require 'unparser/writer/binary'
214
224
  require 'unparser/writer/dynamic_string'
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ # Module to allow class and methods to be abstract
5
+ #
6
+ # Original code before vendoring and reduction from: https://github.com/dkubb/abstract_type.
7
+ module AbstractType
8
+
9
+ # Hook called when module is included
10
+ #
11
+ # @param [Module] descendant
12
+ # the module or class including AbstractType
13
+ #
14
+ # @return [undefined]
15
+ #
16
+ # @api private
17
+ def self.included(descendant)
18
+ super
19
+ create_new_method(descendant)
20
+ descendant.extend(AbstractMethodDeclarations)
21
+ end
22
+
23
+ private_class_method :included
24
+
25
+ # Define the new method on the abstract type
26
+ #
27
+ # Ensures that the instance cannot be of the abstract type
28
+ # and must be a descendant.
29
+ #
30
+ # @param [Class] abstract_class
31
+ #
32
+ # @return [undefined]
33
+ #
34
+ # @api private
35
+ def self.create_new_method(abstract_class)
36
+ abstract_class.define_singleton_method(:new) do |*args, &block|
37
+ if equal?(abstract_class)
38
+ fail NotImplementedError, "#{self} is an abstract type"
39
+ else
40
+ super(*args, &block)
41
+ end
42
+ end
43
+ end
44
+
45
+ private_class_method :create_new_method
46
+
47
+ module AbstractMethodDeclarations
48
+
49
+ # Create abstract instance methods
50
+ #
51
+ # @example
52
+ # class Foo
53
+ # include AbstractType
54
+ #
55
+ # # Create an abstract instance method
56
+ # abstract_method :some_method
57
+ # end
58
+ #
59
+ # @param [Array<#to_s>] names
60
+ #
61
+ # @return [self]
62
+ #
63
+ # @api public
64
+ def abstract_method(*names)
65
+ names.each(&method(:create_abstract_instance_method))
66
+ self
67
+ end
68
+
69
+ # Create abstract singleton methods
70
+ #
71
+ # @example
72
+ # class Foo
73
+ # include AbstractType
74
+ #
75
+ # # Create an abstract instance method
76
+ # abstract_singleton_method :some_method
77
+ # end
78
+ #
79
+ # @param [Array<#to_s>] names
80
+ #
81
+ # @return [self]
82
+ #
83
+ # @api private
84
+ def abstract_singleton_method(*names)
85
+ names.each(&method(:create_abstract_singleton_method))
86
+ self
87
+ end
88
+
89
+ private
90
+
91
+ # Create abstract singleton method
92
+ #
93
+ # @param [#to_s] name
94
+ # the name of the method to create
95
+ #
96
+ # @return [undefined]
97
+ #
98
+ # @api private
99
+ def create_abstract_singleton_method(name)
100
+ define_singleton_method(name) do |*|
101
+ fail NotImplementedError, "#{self}.#{name} is not implemented"
102
+ end
103
+ end
104
+
105
+ # Create abstract instance method
106
+ #
107
+ # @param [#to_s] name
108
+ # the name of the method to create
109
+ #
110
+ # @return [undefined]
111
+ #
112
+ # @api private
113
+ def create_abstract_instance_method(name)
114
+ define_method(name) do |*|
115
+ fail NotImplementedError, "#{self.class}##{name} is not implemented"
116
+ end
117
+ end
118
+
119
+ end # AbstractMethodDeclarations
120
+ end # AbstractType
121
+ end # Unparser
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ # Allows objects to be made immutable
5
+ #
6
+ # Original code before vendoring and reduction from: https://github.com/dkubb/adamantium.
7
+ module Adamantium
8
+ module InstanceMethods
9
+ # A noop #dup for immutable objects
10
+ #
11
+ # @return [self]
12
+ #
13
+ # @api public
14
+ def dup
15
+ self
16
+ end
17
+
18
+ # Freeze the object
19
+ #
20
+ # @return [Object]
21
+ #
22
+ # @api public
23
+ def freeze
24
+ memoized_method_cache
25
+ super()
26
+ end
27
+
28
+ private
29
+
30
+ def memoized_method_cache
31
+ @memoized_method_cache ||= Memory.new({})
32
+ end
33
+
34
+ end # InstanceMethods
35
+
36
+ # Storage for memoized methods
37
+ class Memory
38
+
39
+ # Initialize the memory storage for memoized methods
40
+ #
41
+ # @return [undefined]
42
+ #
43
+ # @api private
44
+ def initialize(values)
45
+ @values = values
46
+ @monitor = Monitor.new
47
+ freeze
48
+ end
49
+
50
+ # Fetch the value from memory, or evaluate if it does not exist
51
+ #
52
+ # @param [Symbol] name
53
+ #
54
+ # @yieldreturn [Object]
55
+ # the value to memoize
56
+ #
57
+ # @api public
58
+ def fetch(name)
59
+ @values.fetch(name) do # check for the key
60
+ @monitor.synchronize do # acquire a lock if the key is not found
61
+ @values.fetch(name) do # recheck under lock
62
+ @values[name] = yield # set the value
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end # Memory
68
+
69
+ # Methods mixed in to adamantium classes
70
+ module ClassMethods
71
+
72
+ # Instantiate a new frozen object
73
+ #
74
+ # @return [Object]
75
+ #
76
+ # @api public
77
+ def new(*)
78
+ super.freeze
79
+ end
80
+
81
+ end # ClassMethods
82
+
83
+ # Methods mixed in to adamantium modules
84
+ module ModuleMethods
85
+
86
+ # Memoize a list of methods
87
+ #
88
+ # @param [Array<#to_s>] methods
89
+ # a list of methods to memoize
90
+ #
91
+ # @return [self]
92
+ #
93
+ # @api public
94
+ def memoize(*methods)
95
+ methods.each(&method(:memoize_method))
96
+ self
97
+ end
98
+
99
+ # Test if method is memoized
100
+ #
101
+ # @param [Symbol] name
102
+ #
103
+ # @return [Bool]
104
+ def memoized?(method_name)
105
+ memoized_methods.key?(method_name)
106
+ end
107
+
108
+ # Return unmemoized instance method
109
+ #
110
+ # @param [Symbol] name
111
+ #
112
+ # @return [UnboundMethod]
113
+ # the memoized method
114
+ #
115
+ # @raise [NameError]
116
+ # raised if the method is unknown
117
+ #
118
+ # @api public
119
+ def unmemoized_instance_method(method_name)
120
+ memoized_methods.fetch(method_name) do
121
+ fail ArgumentError, "##{method_name} is not memoized"
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def memoize_method(method_name)
128
+ if memoized_methods.key?(method_name)
129
+ fail ArgumentError, "##{method_name} is already memoized"
130
+ end
131
+
132
+ memoized_methods[method_name] = MethodBuilder.new(self, method_name).call
133
+ end
134
+
135
+ def memoized_methods
136
+ @memoized_methods ||= {}
137
+ end
138
+
139
+ end # ModuleMethods
140
+
141
+ def self.included(descendant)
142
+ descendant.class_eval do
143
+ include InstanceMethods
144
+ extend ModuleMethods
145
+ extend ClassMethods if instance_of?(Class)
146
+ end
147
+ end
148
+ private_class_method :included
149
+ end # Adamantium
150
+ end # Unparser
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Adamantium
5
+ # Build the memoized method
6
+ class MethodBuilder
7
+
8
+ # Raised when the method arity is invalid
9
+ class InvalidArityError < ArgumentError
10
+
11
+ # Initialize an invalid arity exception
12
+ #
13
+ # @param [Module] descendant
14
+ # @param [Symbol] method
15
+ # @param [Integer] arity
16
+ #
17
+ # @api private
18
+ def initialize(descendant, method, arity)
19
+ super("Cannot memoize #{descendant}##{method}, its arity is #{arity}")
20
+ end
21
+
22
+ end # InvalidArityError
23
+
24
+ # Raised when a block is passed to a memoized method
25
+ class BlockNotAllowedError < ArgumentError
26
+
27
+ # Initialize a block not allowed exception
28
+ #
29
+ # @param [Module] descendant
30
+ # @param [Symbol] method
31
+ #
32
+ # @api private
33
+ def initialize(descendant, method)
34
+ super("Cannot pass a block to #{descendant}##{method}, it is memoized")
35
+ end
36
+
37
+ end # BlockNotAllowedError
38
+
39
+ # Initialize an object to build a memoized method
40
+ #
41
+ # @param [Module] descendant
42
+ # @param [Symbol] method_name
43
+ #
44
+ # @return [undefined]
45
+ #
46
+ # @api private
47
+ def initialize(descendant, method_name)
48
+ @descendant = descendant
49
+ @method_name = method_name
50
+ @original_visibility = visibility
51
+ @original_method = @descendant.instance_method(@method_name)
52
+ assert_arity(@original_method.arity)
53
+ end
54
+
55
+ # Build a new memoized method
56
+ #
57
+ # @example
58
+ # method_builder.call # => creates new method
59
+ #
60
+ # @return [UnboundMethod]
61
+ #
62
+ # @api public
63
+ def call
64
+ remove_original_method
65
+ create_memoized_method
66
+ set_method_visibility
67
+ @original_method
68
+ end
69
+
70
+ private
71
+
72
+ def assert_arity(arity)
73
+ if arity.nonzero?
74
+ fail InvalidArityError.new(@descendant, @method_name, arity)
75
+ end
76
+ end
77
+
78
+ def remove_original_method
79
+ name = @method_name
80
+ @descendant.module_eval { undef_method(name) }
81
+ end
82
+
83
+ def create_memoized_method
84
+ name = @method_name
85
+ method = @original_method
86
+ @descendant.module_eval do
87
+ define_method(name) do |&block|
88
+ fail BlockNotAllowedError.new(self.class, name) if block
89
+
90
+ memoized_method_cache.fetch(name) do
91
+ method.bind(self).call.freeze
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ def set_method_visibility
98
+ @descendant.__send__(@original_visibility, @method_name)
99
+ end
100
+
101
+ def visibility
102
+ if @descendant.private_method_defined?(@method_name) then :private
103
+ elsif @descendant.protected_method_defined?(@method_name) then :protected
104
+ else :public
105
+ end
106
+ end
107
+
108
+ end # MethodBuilder
109
+ end # Adamantium
110
+ end # Unparser