raap 0.1.0

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