rbs-dynamic 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.
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbs"
4
+ # require_relative "./../refine/signature_merger.rb"
5
+
6
+ module RBS module Dynamic module Builder
7
+ class Types
8
+ BOOL = RBS::Types::Bases::Bool.new(location: nil)
9
+ VOID = RBS::Types::Bases::Void.new(location: nil)
10
+ ANY = RBS::Types::Bases::Any.new(location: nil)
11
+
12
+ module ToRBSType
13
+ refine Object do
14
+ def to_rbs_type
15
+ { type: self, args: [] }
16
+ end
17
+ end
18
+
19
+ refine Hash do
20
+ def to_rbs_type
21
+ self
22
+ end
23
+ end
24
+ end
25
+ using ToRBSType
26
+
27
+ module RBSTypeMerger
28
+ refine Object do
29
+ def merge_type(*)
30
+ nil
31
+ end
32
+ end
33
+
34
+ refine Hash do
35
+ def merge_type(other)
36
+ return unless Hash === other
37
+ return unless self[:type] == other[:type]
38
+ return unless self[:args].count == other[:args].count
39
+ { type: self[:type], value: [self[:value], other[:value]].flatten.compact.uniq, args: self[:args].zip(other[:args]).map(&:flatten).map(&:uniq) }
40
+ end
41
+ end
42
+
43
+ refine Array do
44
+ def push_merge_type(other)
45
+ each.with_index { |type, i|
46
+ merged = type.merge_type(other)
47
+ if merged
48
+ self[i] = merged
49
+ return self
50
+ end
51
+ }
52
+ push(other)
53
+ end
54
+
55
+ def zip_type
56
+ inject([]) { |result, type|
57
+ result.push_merge_type(type)
58
+ }
59
+ end
60
+ end
61
+ end
62
+ using RBSTypeMerger
63
+
64
+ using Module.new {
65
+ refine Object do
66
+ def type(*)
67
+ RBS::Types::Literal.new(
68
+ literal: self,
69
+ location: nil
70
+ )
71
+ end
72
+ end
73
+
74
+ refine Class do
75
+ def type(*args)
76
+ RBS::Types::ClassInstance.new(
77
+ args: args.map { Array === _1 && _1.empty? ? ANY : Types.new(_1).build },
78
+ location: nil,
79
+ name: name
80
+ )
81
+ end
82
+ end
83
+
84
+ refine NilClass do
85
+ def type(*)
86
+ RBS::Types::Bases::Nil.new(location: nil)
87
+ end
88
+ end
89
+
90
+ refine Hash do
91
+ def type
92
+ self[:type].type(*self[:args])
93
+ end
94
+ end
95
+
96
+ refine RBS::Types::Bases::Base do
97
+ def |(other)
98
+ RBS::Types::Union.new(
99
+ types: [self, other],
100
+ location: nil
101
+ )
102
+ end
103
+
104
+ def type
105
+ self
106
+ end
107
+ end
108
+
109
+ refine RBS::Types::Literal do
110
+ def |(other)
111
+ RBS::Types::Union.new(
112
+ types: [self, other],
113
+ location: nil
114
+ )
115
+ end
116
+ end
117
+
118
+ refine RBS::Types::Application do
119
+ def type(*)
120
+ self
121
+ end
122
+
123
+ def |(other)
124
+ RBS::Types::Union.new(
125
+ types: [self, other],
126
+ location: nil
127
+ )
128
+ end
129
+ end
130
+
131
+ refine RBS::Types::Union do
132
+ def |(other)
133
+ RBS::Types::Union.new(
134
+ types: (types + [other]),
135
+ location: nil
136
+ )
137
+ end
138
+ end
139
+
140
+ refine Array do
141
+ def union_type
142
+ map { _1.type }.inject { _1 | _2 }
143
+ end
144
+ end
145
+ }
146
+
147
+ attr_reader :types
148
+
149
+ def initialize(type)
150
+ @types = case type
151
+ when nil
152
+ [nil]
153
+ when Hash
154
+ [type]
155
+ else
156
+ Array(type)
157
+ end.map(&:to_rbs_type).uniq.zip_type
158
+ end
159
+
160
+ def build
161
+ if optional?
162
+ RBS::Types::Optional.new(
163
+ type: (target_types - [NIL_RBS_TYPE, NILCLASS_RBS_TYPE]).union_type,
164
+ location: nil
165
+ )
166
+ else
167
+ target_types.union_type
168
+ end
169
+ end
170
+
171
+ NILCLASS_RBS_TYPE = NilClass.to_rbs_type
172
+ NIL_RBS_TYPE = nil.to_rbs_type
173
+
174
+ TRUECLASS_RBS_TYPE = TrueClass.to_rbs_type
175
+ TRUE_RBS_TYPE = true.to_rbs_type
176
+
177
+ FALSECLASS_RBS_TYPE = FalseClass.to_rbs_type
178
+ FALSE_RBS_TYPE = false.to_rbs_type
179
+
180
+ def optional?
181
+ !!types.group_by { _1 == NIL_RBS_TYPE || _1 == NILCLASS_RBS_TYPE }.values.then { |opt, other| opt&.size&.>=(1) && other&.size&.>=(1) }
182
+ end
183
+
184
+ def in_bool?
185
+ 0 < types.count { _1 == TRUE_RBS_TYPE || _1 == TRUECLASS_RBS_TYPE } && 0 < types.count { _1 == FALSE_RBS_TYPE || _1 == FALSECLASS_RBS_TYPE }
186
+ end
187
+
188
+ def target_types
189
+ (types + types.flat_map { Array(_1[:value]).map { |value| { type: value, args: [] } } })
190
+ .tap { break _1 - [NIL_RBS_TYPE, NILCLASS_RBS_TYPE] if optional? }
191
+ .tap { break _1 - [TRUE_RBS_TYPE, TRUECLASS_RBS_TYPE, FALSE_RBS_TYPE, FALSECLASS_RBS_TYPE] + [BOOL] if in_bool? }
192
+ end
193
+ end
194
+ end end end
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbs"
4
+ require_relative "./builder/types.rb"
5
+ require_relative "./builder/methods.rb"
6
+
7
+ module RBS module Dynamic module Builder
8
+ class Base
9
+ attr_reader :interface_members
10
+
11
+ def initialize
12
+ @inclued_modules = []
13
+ @prepended_modules = []
14
+ @extended_modules = []
15
+ @constant_variables = Hash.new { |h, name| h[name] = [] }
16
+ @instance_variables = Hash.new { |h, name| h[name] = [] }
17
+ @methods = Hash.new { |h, name| h[name] = Methods.new(name, kind: :instance) }
18
+ @singleton_methods = Hash.new { |h, name| h[name] = Methods.new(name, kind: :singleton) }
19
+ @other_members = []
20
+ @interface_members = []
21
+ end
22
+
23
+ def add_inclued_module(mod)
24
+ @inclued_modules << mod
25
+ end
26
+
27
+ def add_prepended_module(mod)
28
+ @prepended_modules << mod
29
+ end
30
+
31
+ def add_extended_module(mod)
32
+ @extended_modules << mod
33
+ end
34
+
35
+ def add_constant_variable(name, type)
36
+ @constant_variables[name] << type
37
+ end
38
+
39
+ def add_instance_variable(name, type)
40
+ @instance_variables[name] << type
41
+ end
42
+
43
+ def add_singleton_method(name, sig)
44
+ @singleton_methods[name] << sig
45
+ end
46
+
47
+ def add_method(name, sig = {})
48
+ @methods[name] << sig
49
+ end
50
+
51
+ def add_member(member)
52
+ @other_members << member
53
+ end
54
+
55
+ def add_interface_members(member)
56
+ @interface_members << member
57
+ end
58
+
59
+ def build_members
60
+ [
61
+ *build_inclued_modules,
62
+ *build_prepended_modules,
63
+ *build_extended_modules,
64
+ *build_constant_variables,
65
+ *build_singleton_methods,
66
+ *build_methods,
67
+ *build_instance_variables,
68
+ *@interface_members.map(&:build),
69
+ *@other_members
70
+ ]
71
+ end
72
+
73
+ def build_inclued_modules
74
+ @inclued_modules.map { |mod|
75
+ RBS::AST::Members::Include.new(
76
+ name: mod.name,
77
+ args: [],
78
+ annotations: [],
79
+ location: nil,
80
+ comment: nil
81
+ )
82
+ }
83
+ end
84
+
85
+ def build_prepended_modules
86
+ @prepended_modules.map { |mod|
87
+ RBS::AST::Members::Prepend.new(
88
+ name: mod.name,
89
+ args: [],
90
+ annotations: [],
91
+ location: nil,
92
+ comment: nil
93
+ )
94
+ }
95
+ end
96
+
97
+ def build_extended_modules
98
+ @extended_modules.map { |mod|
99
+ RBS::AST::Members::Extend.new(
100
+ name: mod.name,
101
+ args: [],
102
+ annotations: [],
103
+ location: nil,
104
+ comment: nil
105
+ )
106
+ }
107
+ end
108
+
109
+ def build_constant_variables
110
+ @constant_variables.map { |name, types|
111
+ RBS::AST::Members::InstanceVariable.new(
112
+ name: name,
113
+ type: Types.new(types.flatten).build,
114
+ location: nil,
115
+ comment: nil
116
+ )
117
+ }
118
+ end
119
+
120
+ def build_instance_variables
121
+ @instance_variables.map { |name, types|
122
+ RBS::AST::Declarations::Constant.new(
123
+ name: name,
124
+ type: Types.new(types.flatten).build,
125
+ location: nil,
126
+ comment: nil
127
+ )
128
+ }
129
+ end
130
+
131
+ def build_singleton_methods
132
+ @singleton_methods.values.map(&:build)
133
+ end
134
+
135
+ def build_methods
136
+ @methods.values.map(&:build)
137
+ end
138
+ end
139
+ private_constant :Base
140
+
141
+ class Module < Base
142
+ attr_reader :module
143
+
144
+ def initialize(module_)
145
+ super()
146
+ @module = module_
147
+ end
148
+
149
+ def build
150
+ RBS::AST::Declarations::Module.new(
151
+ name: self.module.name,
152
+ type_params: [],
153
+ self_types: [],
154
+ members: build_members,
155
+ location: nil,
156
+ annotations: [],
157
+ comment: nil
158
+ )
159
+ end
160
+ end
161
+
162
+ class Class < Base
163
+ attr_reader :klass
164
+
165
+ def initialize(klass)
166
+ super()
167
+ @klass = klass
168
+ end
169
+
170
+ def build
171
+ RBS::AST::Declarations::Class.new(
172
+ name: klass.name,
173
+ type_params: [],
174
+ super_class: build_super_class,
175
+ members: build_members,
176
+ location: nil,
177
+ annotations: [],
178
+ comment: nil
179
+ )
180
+ end
181
+
182
+ def build_super_class
183
+ ::RBS::AST::Declarations::Class::Super.new(
184
+ name: klass.superclass.name,
185
+ args: [],
186
+ location: nil
187
+ ) unless klass.superclass&.name.nil? || Object == klass.superclass
188
+ end
189
+ end
190
+
191
+ class Interface < Base
192
+ attr_reader :name
193
+ attr_reader :methods
194
+
195
+ def initialize(name)
196
+ super()
197
+ @name = name
198
+ end
199
+
200
+ def build
201
+ RBS::AST::Declarations::Interface.new(
202
+ name: @name,
203
+ type_params: [],
204
+ members: build_members,
205
+ annotations: [],
206
+ location: nil,
207
+ comment: nil
208
+ )
209
+ end
210
+
211
+ def eql?(other)
212
+ self.methods.map { |_, method| [method.name, method.kind, method.sigs] } ==
213
+ other.methods.map { |_, method| [method.name, method.kind, method.sigs] }
214
+ end
215
+ end
216
+ end end end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ require "thor"
3
+ require_relative "../dynamic.rb"
4
+
5
+ module RBS module Dynamic
6
+ class CLI < Thor
7
+ desc "version", "show version"
8
+ def version
9
+ puts "rbs dynamic version #{RBS::Dynamic::VERSION}"
10
+ end
11
+
12
+ desc "trace [filename]", ""
13
+ option :"root-path", type: :string, default: Dir.pwd, desc: "Rooting path. Default: current dir"
14
+ option :"target-filepath-pattern", type: :string, default: ".*", desc: "Target filepath pattern. e.g. hoge\\|foo\\|bar. Default '.*'"
15
+ option :"ignore-filepath-pattern", type: :string, default: nil, desc: "Ignore filepath pattern. Priority over `target-filepath-pattern`. e.g. hoge\\|foo\\|bar. Default ''"
16
+ option :"target-classname-pattern", type: :string, default: ".*", desc: "Target class name pattern. e.g. RBS::Dynamic. Default '.*'"
17
+ option :"ignore-classname-pattern", type: :string, default: nil, desc: "Ignore class name pattern. Priority over `target-classname-pattern`. e.g. PP\\|PrettyPrint. Default ''"
18
+ option :"ignore-class_members", type: :array, enum: %w(inclued_modules prepended_modules extended_modules constant_variables instance_variables singleton_methods methods)
19
+ option :"method-defined-calsses", type: :array, enum: %w(defined_class receiver_class), desc: "Which class defines method type. Default: defined_class and receiver_class"
20
+ option :"show-method-location", type: :boolean, default: false, desc: "Show source_location and called_location in method comments. Default: no"
21
+ option :"use-literal_type", type: :boolean, default: false, desc: "Integer and Symbol as literal types. e.g func(:hoge, 42). Default: no"
22
+ option :"with-literal_type", type: :boolean, default: false, desc: "Integer and Symbol with literal types. e.g func(Symbol | :hoge | :foo). Default: no"
23
+ option :"use-interface_method_argument", type: :boolean, default: false, desc: "Define method arguments in interface. Default: no"
24
+ option :"stdout", type: :boolean, default: false, desc: "stdout at runtime. Default: no"
25
+ option :"trace-c_api-method", type: :boolean, default: false, desc: "Trace C API method. Default: no"
26
+ def trace(filename)
27
+ decls = RBS::Dynamic.trace(**options) {
28
+ load filename
29
+ }.values
30
+ stdout = StringIO.new
31
+ writer = RBS::Writer.new(out: stdout)
32
+ writer.write(decls)
33
+ puts "# RBS dynamic trace #{RBS::Dynamic::VERSION}"
34
+ puts
35
+ puts stdout.string
36
+ end
37
+ end
38
+ end end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RBS module Dynamic
4
+ class Config
5
+ attr_reader :options
6
+
7
+ def initialize(options)
8
+ @options = options
9
+ end
10
+
11
+ def except_build_members
12
+ ignore_class_members
13
+ end
14
+
15
+ def root_path
16
+ options["root-path"] || options[:root_path] || Dir.pwd
17
+ end
18
+
19
+ def target_filepath_pattern
20
+ options["target-filepath-pattern"] || options[:target_filepath_pattern] || /.*/
21
+ end
22
+
23
+ def ignore_filepath_pattern
24
+ options["ignore-filepath-pattern"] || options[:ignore_filepath_pattern]
25
+ end
26
+
27
+ def target_classname_pattern
28
+ Regexp.new(options["target-classname-pattern"] || options[:target_classname_pattern] || /.*/)
29
+ end
30
+
31
+ def ignore_classname_pattern
32
+ (options["ignore-classname-pattern"] || options[:ignore_classname_pattern])&.then { Regexp.new(_1) }
33
+ end
34
+
35
+ def ignore_class_members
36
+ (options["ignore-class_members"] || options[:ignore_class_members] || []).map(&:to_sym)
37
+ end
38
+
39
+ def method_defined_calssses
40
+ (options["method-defined-calsses"] || options[:method_defined_calssses] || %i(defined_class receiver_class)).map(&:to_sym)
41
+ end
42
+
43
+ def show_method_location?
44
+ options["show-method-location"] || options[:show_method_location] || false
45
+ end
46
+
47
+ def use_literal_type?
48
+ options["use-literal_type"] || options[:use_literal_type] || false
49
+ end
50
+
51
+ def with_literal_type?
52
+ options["with-literal_type"] || options[:with_literal_type] || false
53
+ end
54
+
55
+ def use_interface_method_argument?
56
+ options["use-interface_method_argument"] || options[:use_interface_method_argument] || false
57
+ end
58
+
59
+ def trace_c_api_method?
60
+ options["trace-c_api-method"] || options[:trace_c_api_method] || false
61
+ end
62
+
63
+ def stdout?
64
+ (options["stdout"] || options[:stdout]).then { _1.nil? ? true : _1 }
65
+ end
66
+ end
67
+ end end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbs"
4
+ require_relative "../refine/trace_point.rb"
5
+ require 'pathname'
6
+
7
+ module RBS module Dynamic module Converter
8
+ module CalledMethodToMethodSigunature
9
+ using RBS::Dynamic::Refine::TracePoint::ToRBSType
10
+
11
+ using Module.new {
12
+ refine String do
13
+ def relative_path(base_directory)
14
+ delete_prefix(base_directory)
15
+ end
16
+ end
17
+
18
+ refine Hash do
19
+ def except(*keys)
20
+ slice(*self.keys - keys)
21
+ end unless method_defined?(:except)
22
+
23
+ def value_to_type
24
+ merge(type: self[:value] || self[:type]).except(:value)
25
+ end
26
+
27
+ def type_to_rbs_type(use_literal_type: false, with_literal_type: false)
28
+ if use_literal_type
29
+ merge(type: self[:rbs_type].value_to_type).except(:op, :rbs_type, :value_object_id)
30
+ elsif with_literal_type
31
+ merge(type: self[:rbs_type]).except(:op, :rbs_type, :value_object_id)
32
+ else
33
+ merge(type: self[:rbs_type].except_value).except(:op, :rbs_type, :value_object_id)
34
+ end
35
+ end
36
+ end
37
+ }
38
+
39
+ refine Array do
40
+ def type_to_rbs_type(use_literal_type: false, with_literal_type: false)
41
+ map { _1.type_to_rbs_type(use_literal_type: use_literal_type, with_literal_type: with_literal_type) }
42
+ end
43
+ end
44
+
45
+ refine Hash do
46
+ def method_sigunature(root_path: nil, include_location: false, use_literal_type: false, with_literal_type: false)
47
+ opt = { use_literal_type: use_literal_type, with_literal_type: with_literal_type }
48
+ visibility = self[:visibility] if self[:visibility] != :public
49
+
50
+ req_opt = self[:arguments].take_while { _1[:op] != :rest }.to_a
51
+ rest, *other = self[:arguments].drop_while { _1[:op] != :rest }.to_a
52
+ req_opt ||= []
53
+ rest ||= []
54
+ {
55
+ required_positionals: req_opt.select { _1[:op] == :req }.type_to_rbs_type(**opt),
56
+ optional_positionals: req_opt.select { _1[:op] == :opt }.type_to_rbs_type(**opt),
57
+ rest_positionals: self[:arguments].select { _1[:op] == :rest }.type_to_rbs_type(**opt),
58
+ trailing_positionals: other.select { _1[:op] == :req }.type_to_rbs_type(**opt),
59
+ required_keywords: self[:arguments].select { _1[:op] == :keyreq }.type_to_rbs_type(**opt),
60
+ optional_keywords: self[:arguments].select { _1[:op] == :key }.type_to_rbs_type(**opt),
61
+ rest_keywords: self[:arguments].select { _1[:op] == :keyrest }.type_to_rbs_type(**opt),
62
+ block: self[:block].map { _1.method_sigunature(include_location: false, **opt) },
63
+ source_location: ("#{self[:path].relative_path(root_path)}:#{self[:lineno]}" if include_location),
64
+ reference_location: ("#{self[:called_path].relative_path(root_path)}:#{self[:called_lineno]}" if include_location)
65
+ }.merge(
66
+ return_type: (if use_literal_type
67
+ self[:return_value_rbs_type].value_to_type
68
+ elsif with_literal_type
69
+ self[:return_value_rbs_type]
70
+ else
71
+ self[:return_value_rbs_type].except_value
72
+ end),
73
+ visibility: visibility
74
+ )
75
+ end
76
+ end
77
+ end
78
+ end end end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require_relative "./called_method_to_sigunature.rb"
5
+
6
+ module RBS module Dynamic module Converter
7
+ module CalledMethodToWithInterface
8
+ using CalledMethodToMethodSigunature
9
+ refine Hash do
10
+ def to_with_interface(defined_interfaces: [])
11
+ self.dup.then { |called_method|
12
+ interfaces = called_method[:arguments].each.with_index(1).inject([]) { |interfaces, (argument, i)|
13
+ called_methods = called_method[:called_methods].select { _1[:receiver_object_id] == argument[:value_object_id] }
14
+ next interfaces if called_methods.empty?
15
+
16
+ interface = RBS::Dynamic::Builder::Interface.new("dummy_name")
17
+ sub_interfaces = called_methods.map { |called_method|
18
+ called_method, sub_interfaces = called_method.to_with_interface(defined_interfaces: defined_interfaces + interfaces)
19
+ # Non support nexted defined interface in RBS
20
+ # sub_interfaces.map { interface.add_member(_1.build) }
21
+ interface.add_method(called_method[:method_id], **called_method.method_sigunature)
22
+ sub_interfaces
23
+ }.flatten
24
+ interfaces += sub_interfaces
25
+
26
+ unless interface_ = (defined_interfaces + interfaces).find { _1.eql? interface }
27
+ interface_name = "_Interface_have__#{called_methods.map { _1[:method_id] }.uniq.join("__")}__#{(defined_interfaces + interfaces).count + 1}"
28
+ # except #+ #== #hoge? methods
29
+ .gsub(/[^[:alnum:]_]/, "")
30
+ interface.instance_exec { @name = interface_name }
31
+ interfaces << interface
32
+ end
33
+
34
+ argument[:rbs_type][:type] = RBS::Types::Interface.new(name: (interface_ || interface).name, args: [], location: nil)
35
+
36
+ interfaces
37
+ }
38
+
39
+ [called_method, interfaces]
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end end end