unparser 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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