raap 0.1.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.
data/lib/raap/type.rb ADDED
@@ -0,0 +1,310 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RaaP
4
+ # Type.new("Integer").pick(size: 10) #=> 2
5
+ # Type.new("Symbol").pick(size: 6) #=> :abcdef
6
+ # Type.new("Array[Integer]").pick(size: 3) #=> [1, 2, 3]
7
+ # Type.new("Array[Integer]") { sized { |size| Array.new(size + 1) { integer.pick(size: size) } } }
8
+ class Type
9
+ module Arithmetic
10
+ def self.float
11
+ positive_float.then { |x| [x, -x].sample or raise }
12
+ end
13
+
14
+ def self.positive_float
15
+ x = Random.rand
16
+ x / Math.sqrt(1 - x * x)
17
+ end
18
+ end
19
+
20
+ GENERATORS = {}
21
+ SIMPLE_SOURCE = ('a'..'z').to_a << '_'
22
+ RECURSION = Hash.new { |h, k| h[k] = { count: 0, logged: false } }
23
+
24
+ # Type.register "::Integer::positive" { sized { |size| size } }
25
+ def self.register(type_name, &block)
26
+ GENERATORS[type_name] = block
27
+ end
28
+
29
+ # Special class case
30
+ register("::Array") do
31
+ t = type.args[0] || 'untyped'
32
+ array(Type.new(t, range: range))
33
+ end
34
+ register("::Binding") { sized { binding } }
35
+ register("::Complex") { complex }
36
+ register("::Data") { sized { Data.define } }
37
+ register("::Encoding") { encoding }
38
+ register("::FalseClass") { sized { false } }
39
+ register("::Float") { float }
40
+ register("::Hash") do
41
+ sized do |size|
42
+ Array.new(integer.pick(size: size).abs).to_h do
43
+ k = type.args[0] || 'untyped'
44
+ v = type.args[1] || 'untyped'
45
+ [Type.new(k).pick(size: size), Type.new(v).pick(size: size)]
46
+ end
47
+ end
48
+ end
49
+ register("::Integer") { integer }
50
+ register("::IO") { sized { $stdout } }
51
+ register("::Method") { sized { temp_method_object } }
52
+ register("::NilClass") { sized { nil } }
53
+ register("::Proc") { sized { Proc.new {} } }
54
+ register("::Rational") { rational }
55
+ register("::Regexp") { sized { |size| Regexp.new(string.pick(size: size)) } }
56
+ register("::String") { string }
57
+ register("::Struct") { sized { Struct.new(:foo, :bar).new } }
58
+ register("::Symbol") { symbol }
59
+ register("::Time") { sized { Time.now } }
60
+ register("::TrueClass") { sized { true } }
61
+ register("::UnboundMethod") { sized { temp_method_object.unbind } }
62
+
63
+ attr_reader :type
64
+ attr_reader :range
65
+
66
+ def initialize(type, range: nil..nil)
67
+ @type = parse(type)
68
+ @range = range
69
+ end
70
+
71
+ def sized(&block)
72
+ Sized.new(&block)
73
+ end
74
+
75
+ def pick(size: 10, eval: true)
76
+ symb = to_symbolic_call(size:)
77
+ eval ? SymbolicCaller.new(symb).eval : symb
78
+ end
79
+
80
+ def to_symbolic_call(size: 10)
81
+ raise TypeError, "size should be Integer" unless size.is_a?(Integer)
82
+ raise ArgumentError, "negative size" if size.negative?
83
+
84
+ case type
85
+ when ::RBS::Types::Tuple
86
+ type.types.map { |t| Type.new(t).pick(size:) }
87
+ when ::RBS::Types::Union
88
+ type.types.sample&.then { |t| Type.new(t).pick(size:) }
89
+ when ::RBS::Types::Intersection
90
+ Value::Intersection.new(type, size: size)
91
+ when ::RBS::Types::Optional
92
+ case Random.rand(2)
93
+ in 0 then Type.new(type.type).pick(size:)
94
+ in 1 then nil
95
+ end
96
+ when ::RBS::Types::Alias
97
+ case gen = GENERATORS[type.name.absolute!.to_s]
98
+ in Proc then instance_exec(&gen)
99
+ in nil then Type.new(RBS.builder.expand_alias2(type.name, type.args)).pick(size:)
100
+ end
101
+ when ::RBS::Types::Bases::Class
102
+ raise "cannot resolve `class` type"
103
+ when ::RBS::Types::Bases::Instance
104
+ raise "cannot resolve `instance` type"
105
+ when ::RBS::Types::Bases::Self
106
+ raise "cannot resolve `self` type"
107
+ when ::RBS::Types::Interface
108
+ Value::Interface.new(type, size: size)
109
+ when ::RBS::Types::Variable
110
+ Value::Variable.new(type)
111
+ when ::RBS::Types::ClassSingleton
112
+ Object.const_get(type.name.to_s)
113
+ when ::RBS::Types::ClassInstance
114
+ case gen = GENERATORS[type.name.absolute!.to_s]
115
+ when Proc then instance_exec(&gen).pick(size: size)
116
+ when nil then pick_from_initialize(type, size:)
117
+ end
118
+ when ::RBS::Types::Record
119
+ type.fields.transform_values { |t| Type.new(t).pick(size:) }
120
+ when ::RBS::Types::Proc
121
+ Proc.new { Type.new(type.type.return_type).pick(size:) }
122
+ when ::RBS::Types::Literal
123
+ type.literal
124
+ when ::RBS::Types::Bases::Bool
125
+ bool.pick(size: size)
126
+ when ::RBS::Types::Bases::Void
127
+ Value::Void.new
128
+ when ::RBS::Types::Bases::Any
129
+ untyped.pick(size: size)
130
+ when ::RBS::Types::Bases::Nil
131
+ nil
132
+ when ::RBS::Types::Bases::Top
133
+ Value::Top.new
134
+ when ::RBS::Types::Bases::Bottom
135
+ Value::Bottom.new
136
+ else
137
+ raise "not implemented #{type.to_s}"
138
+ end
139
+ end
140
+
141
+ def pick_from_initialize(type, size:)
142
+ type_name = type.name.absolute!
143
+ const = Object.const_get(type_name.to_s)
144
+ definition = RBS.builder.build_singleton(type_name)
145
+ snew = definition.methods[:new]
146
+ if snew
147
+ # class
148
+ rbs_method_type = snew.method_types.sample or raise
149
+ type_params = definition.type_params_decl.concat(rbs_method_type.type_params.drop(definition.type_params_decl.length))
150
+ ts = TypeSubstitution.new(type_params, type.args)
151
+ maped_rbs_method_type = rbs_method_type
152
+ maped_rbs_method_type = ts.method_type_sub(rbs_method_type)
153
+ method_type = MethodType.new(maped_rbs_method_type)
154
+
155
+ begin
156
+ try(times: 5, size: size) do |size|
157
+ args, kwargs, block = method_type.pick_arguments(size: size, eval: false)
158
+ [:call, const, :new, args, kwargs, block]
159
+ end
160
+ rescue
161
+ $stderr.puts "Fail with `#{rbs_method_type}`"
162
+ raise
163
+ end
164
+ else
165
+ Value::Module.new(type)
166
+ end
167
+ end
168
+
169
+ private
170
+
171
+ def parse(type)
172
+ case type
173
+ when String
174
+ RBS.parse_type(type) or raise "cannot parse #{type.inspect}"
175
+ when ::RBS::TypeName
176
+ parse(type.to_s)
177
+ else
178
+ type
179
+ end
180
+ end
181
+
182
+ def try(times:, size:)
183
+ # @type var error: Exception?
184
+ ret = error = nil
185
+ times.times do
186
+ ret = yield size
187
+ if ret
188
+ error = nil
189
+ break
190
+ end
191
+ rescue => e
192
+ size += 1
193
+ error = e
194
+ next
195
+ end
196
+
197
+ if error
198
+ $stderr.puts
199
+ $stderr.puts "=== Catch error when generating type `#{type}`. Please check your RBS or RaaP bug. ==="
200
+ $stderr.puts "(#{error.class}) #{error.message}"
201
+ raise error
202
+ end
203
+
204
+ ret
205
+ end
206
+
207
+ def integer
208
+ sized { |size| float.pick(size: size).round }
209
+ end
210
+
211
+ def none_zero_integer
212
+ integer.such_that { |i| i != 0 }
213
+ end
214
+
215
+ def float
216
+ sized do |size|
217
+ case [@range.begin, @range.end]
218
+ in nil, nil
219
+ Arithmetic.float * size
220
+ in nil, high
221
+ high - Arithmetic.positive_float * size
222
+ in low, nil
223
+ low + Arithmetic.positive_float * size
224
+ in low, high
225
+ Random.rand(Range.new(low.to_f, high.to_f, @range.exclude_end?))
226
+ end.round(2)
227
+ end
228
+ end
229
+
230
+ def rational
231
+ sized do |size|
232
+ a = integer.pick(size: size)
233
+ b = none_zero_integer.pick(size: size)
234
+ [:call, Kernel, :Rational, [a, b], {}, nil]
235
+ end
236
+ end
237
+
238
+ def complex
239
+ sized do |size|
240
+ a = integer.pick(size: size)
241
+ b = none_zero_integer.pick(size: size)
242
+ [:call, Kernel, :Complex, [a, b], {}, nil]
243
+ end
244
+ end
245
+
246
+ def string
247
+ sized do |size|
248
+ if size == 0
249
+ +""
250
+ else
251
+ case [@range.begin, @range.end]
252
+ in nil, nil
253
+ size.times.map { SIMPLE_SOURCE.sample }.join
254
+ in nil, _
255
+ raise "Should set range.begin and range.end"
256
+ in _, nil
257
+ raise "Should set range.begin and range.end"
258
+ in s, e
259
+ a = (s..e).to_a
260
+ size.times.map { a.sample }.join
261
+ end
262
+ end
263
+ end
264
+ end
265
+
266
+ def symbol
267
+ sized do |size|
268
+ string.pick(size: size).to_sym
269
+ end
270
+ end
271
+
272
+ def array(type)
273
+ sized do |size|
274
+ Array.new(integer.pick(size: size).abs) do
275
+ type.pick(size: size)
276
+ end
277
+ end
278
+ end
279
+
280
+ def encoding
281
+ sized do
282
+ # @type block: RaaP::symbolic_call
283
+ e = Encoding.list.sample
284
+ [:call, Encoding, :find, [e.name], {} , nil]
285
+ end
286
+ end
287
+
288
+ def bool
289
+ sized { [true, false].sample }
290
+ end
291
+
292
+ def untyped
293
+ case Random.rand(5)
294
+ in 0 then integer
295
+ in 1 then float
296
+ in 2 then string
297
+ in 3 then symbol
298
+ in 4 then bool
299
+ in 5 then encoding
300
+ end
301
+ end
302
+
303
+ def temp_method_object
304
+ o = Object.new
305
+ m = 6.times.map { SIMPLE_SOURCE.sample }.join
306
+ o.define_singleton_method(m) { }
307
+ o.method(m)
308
+ end
309
+ end
310
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RaaP
4
+ class TypeSubstitution
5
+ def initialize(type_params, type_args)
6
+ @type_params = type_params
7
+ @type_args = type_args
8
+ end
9
+
10
+ def build
11
+ bound_map = @type_params.zip(@type_args).to_h do |(bound, arg)|
12
+ if arg
13
+ [bound.name, arg]
14
+ elsif bound.upper_bound
15
+ [bound.name, bound.upper_bound]
16
+ else
17
+ [bound.name, ::RBS::Types::Bases::Top.new(location: nil)]
18
+ end
19
+ end
20
+ ::RBS::Substitution.build(bound_map.keys, bound_map.values)
21
+ end
22
+
23
+ def method_type_sub(method_type, self_type: nil, instance_type: nil, class_type: nil)
24
+ sub = build
25
+ ::RBS::MethodType.new(
26
+ type_params: [],
27
+ type: method_type.type.sub(sub).then { |ty| sub(ty, self_type:, instance_type:, class_type:) },
28
+ block: method_type.block&.sub(sub)&.then { |bl| sub(bl, self_type:, instance_type:, class_type:) },
29
+ location: method_type.location
30
+ )
31
+ end
32
+
33
+ private
34
+
35
+ def sub(search, self_type:, instance_type:, class_type:)
36
+ if self_type.nil? && instance_type.nil? && class_type.nil?
37
+ return search
38
+ end
39
+ search.map_type do |ty|
40
+ case ty
41
+ when ::RBS::Types::Bases::Self
42
+ self_type || ty
43
+ when ::RBS::Types::Bases::Instance
44
+ instance_type || ty
45
+ when ::RBS::Types::Bases::Class
46
+ class_type || ty
47
+ else
48
+ sub(ty, self_type:, instance_type:, class_type:)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,8 @@
1
+ module RaaP
2
+ module Value
3
+ class Bottom < BasicObject
4
+ def inspect = "#<bot>"
5
+ def class = Bottom
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RaaP
4
+ module Value
5
+ class Interface < BasicObject
6
+ def initialize(type, size: 3, self_type: nil, instance_type: nil, class_type: nil)
7
+ @type = type.is_a?(::String) ? RBS.parse_type(type) : type
8
+ @size = size
9
+
10
+ definition = RBS.builder.build_interface(@type.name.absolute!)
11
+ definition.methods.each do |name, method|
12
+ method_type = method.method_types.sample or Kernel.raise
13
+ type_params = definition.type_params_decl.concat(method_type.type_params.drop(definition.type_params_decl.length))
14
+ ts = TypeSubstitution.new(type_params, @type.args)
15
+
16
+ subed_method_type = ts.method_type_sub(method_type, self_type:, instance_type:, class_type:)
17
+
18
+ BindCall.define_singleton_method(self, name) do |*, &b|
19
+ @fixed_return_value ||= Type.new(subed_method_type.type.return_type).pick(size: size)
20
+ if subed_method_type.block
21
+ @fixed_block_arguments ||= size.times.map do
22
+ fun_type = FunctionType.new(subed_method_type.block.type)
23
+ fun_type.pick_arguments(size: size)
24
+ end
25
+ else
26
+ @fixed_block_arguments = []
27
+ end
28
+ if b
29
+ unless subed_method_type.block
30
+ Kernel.raise "block of `#{@type.name}##{name}` was called. But block signature not defined."
31
+ end
32
+ @fixed_block_arguments.each do |a, kw|
33
+ b.call(*a, **kw)
34
+ end
35
+ end
36
+ @fixed_return_value
37
+ end
38
+ end
39
+ end
40
+
41
+ def class
42
+ Interface
43
+ end
44
+
45
+ def inspect
46
+ "#<interface @type=#{@type.to_s} @methods=#{RBS.builder.build_interface(@type.name.absolute!).methods.keys} @size=#{@size}>"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RaaP
4
+ module Value
5
+ class Intersection < BasicObject
6
+ def initialize(type, size:)
7
+ @type = type
8
+ @children = type.types.map { |t| Type.new(t).pick(size: size) }
9
+ @size = size
10
+ end
11
+
12
+ def inspect
13
+ "#<intersection @type=#{@type.to_s.inspect} @size=#{@size.inspect}>"
14
+ end
15
+
16
+ def class
17
+ Intersection
18
+ end
19
+
20
+ def method_missing(name, *args, **kwargs, &block)
21
+ @children.each do |child|
22
+ if BindCall.respond_to?(child, name)
23
+ return child.__send__(name, *args, **kwargs, &block)
24
+ end
25
+ end
26
+ end
27
+
28
+ def respond_to?(...)
29
+ @children.any? do |type|
30
+ BindCall.respond_to?(type, ...)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ module RaaP
2
+ module Value
3
+ # FIXME: consider self_types
4
+ class Module < BasicObject
5
+ attr_reader :type
6
+
7
+ def initialize(type)
8
+ @type = type
9
+ const = ::Object.const_get(type.name.absolute!.to_s)
10
+ BindCall.extend(self, const)
11
+ end
12
+
13
+ def inspect = "#<module #{@type}>"
14
+ def class = Value::Module
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ module RaaP
2
+ module Value
3
+ class Top < BasicObject
4
+ def inspect = "#<top>"
5
+ def class = Top
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ module RaaP
2
+ module Value
3
+ class Variable < BasicObject
4
+ attr_reader :type
5
+
6
+ def initialize(type)
7
+ @type = type
8
+ end
9
+
10
+ def inspect = "#<var #{type}>"
11
+ def class = Variable
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ module RaaP
2
+ module Value
3
+ class Void < BasicObject
4
+ def inspect = "#<void>"
5
+ def class = Void
6
+ end
7
+ end
8
+ end
data/lib/raap/value.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RaaP
4
+ module Value
5
+ autoload :Bottom, "raap/value/bottom"
6
+ autoload :Interface, "raap/value/interface"
7
+ autoload :Intersection, "raap/value/intersection"
8
+ autoload :Module, "raap/value/module"
9
+ autoload :Top, "raap/value/top"
10
+ autoload :Variable, "raap/value/variable"
11
+ autoload :Void, "raap/value/void"
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RaaP
4
+ VERSION = "0.1.0"
5
+ end
data/lib/raap.rb ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbs'
4
+ require 'rbs/cli'
5
+ require 'rbs/test'
6
+ require 'optparse'
7
+ require 'timeout'
8
+ require 'logger'
9
+
10
+ require_relative 'raap/version'
11
+ require_relative 'raap/value'
12
+
13
+ module RaaP
14
+ class << self
15
+ attr_accessor :logger
16
+ end
17
+
18
+ self.logger = ::Logger.new($stdout)
19
+ self.logger.level = ::Logger::WARN
20
+
21
+ autoload :BindCall, "raap/bind_call"
22
+ autoload :CLI, "raap/cli"
23
+ autoload :FunctionType, "raap/function_type"
24
+ autoload :MethodProperty, "raap/method_property"
25
+ autoload :MethodType, "raap/method_type"
26
+ autoload :MethodValue, "raap/method_value"
27
+ autoload :RBS, "raap/rbs"
28
+ autoload :Result, "raap/result"
29
+ autoload :Sized, "raap/sized"
30
+ autoload :SymbolicCaller, "raap/symbolic_caller"
31
+ autoload :Type, "raap/type"
32
+ autoload :TypeSubstitution, "raap/type_substitution"
33
+ autoload :VERSION, "raap/version"
34
+ end
@@ -0,0 +1,68 @@
1
+ ---
2
+ path: ".gem_rbs_collection"
3
+ gems:
4
+ - name: abbrev
5
+ version: '0'
6
+ source:
7
+ type: stdlib
8
+ - name: fileutils
9
+ version: '0'
10
+ source:
11
+ type: stdlib
12
+ - name: io-console
13
+ version: '0'
14
+ source:
15
+ type: stdlib
16
+ - name: json
17
+ version: '0'
18
+ source:
19
+ type: stdlib
20
+ - name: logger
21
+ version: '0'
22
+ source:
23
+ type: stdlib
24
+ - name: minitest
25
+ version: '0'
26
+ source:
27
+ type: stdlib
28
+ - name: monitor
29
+ version: '0'
30
+ source:
31
+ type: stdlib
32
+ - name: mutex_m
33
+ version: '0'
34
+ source:
35
+ type: stdlib
36
+ - name: optparse
37
+ version: '0'
38
+ source:
39
+ type: stdlib
40
+ - name: pathname
41
+ version: '0'
42
+ source:
43
+ type: stdlib
44
+ - name: rake
45
+ version: '13.0'
46
+ source:
47
+ type: git
48
+ name: ruby/gem_rbs_collection
49
+ revision: be76a75932e8ed6ee91a95ba936cf88b674bf044
50
+ remote: https://github.com/ruby/gem_rbs_collection.git
51
+ repo_dir: gems
52
+ - name: rbs
53
+ version: 3.4.4
54
+ source:
55
+ type: rubygems
56
+ - name: rdoc
57
+ version: '0'
58
+ source:
59
+ type: stdlib
60
+ - name: timeout
61
+ version: '0'
62
+ source:
63
+ type: stdlib
64
+ - name: tsort
65
+ version: '0'
66
+ source:
67
+ type: stdlib
68
+ gemfile_lock_path: Gemfile.lock
@@ -0,0 +1,20 @@
1
+ # Download sources
2
+ sources:
3
+ - type: git
4
+ name: ruby/gem_rbs_collection
5
+ remote: https://github.com/ruby/gem_rbs_collection.git
6
+ revision: main
7
+ repo_dir: gems
8
+
9
+ # You can specify local directories as sources also.
10
+ # - type: local
11
+ # path: path/to/your/local/repository
12
+
13
+ # A directory to install the downloaded RBSs
14
+ path: .gem_rbs_collection
15
+
16
+ gems:
17
+ - name: activesupport
18
+ ignore: true
19
+ - name: steep
20
+ ignore: true