rubybreaker 0.0.1
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/AUTHORS +7 -0
- data/LICENSE +26 -0
- data/README.md +403 -0
- data/Rakefile +90 -0
- data/TODO +30 -0
- data/bin/gen_stub_rubylib +64 -0
- data/bin/rubybreaker +67 -0
- data/lib/rubybreaker/context.rb +122 -0
- data/lib/rubybreaker/debug.rb +48 -0
- data/lib/rubybreaker/error.rb +59 -0
- data/lib/rubybreaker/rubylib/core.rb +2316 -0
- data/lib/rubybreaker/rubylib.rb +3 -0
- data/lib/rubybreaker/runtime/inspector.rb +57 -0
- data/lib/rubybreaker/runtime/monitor.rb +235 -0
- data/lib/rubybreaker/runtime/object_wrapper.rb +77 -0
- data/lib/rubybreaker/runtime/overrides.rb +42 -0
- data/lib/rubybreaker/runtime/pluggable.rb +57 -0
- data/lib/rubybreaker/runtime/type_placeholder.rb +27 -0
- data/lib/rubybreaker/runtime/type_system.rb +228 -0
- data/lib/rubybreaker/runtime/typesig_parser.rb +45 -0
- data/lib/rubybreaker/runtime.rb +103 -0
- data/lib/rubybreaker/test/testcase.rb +39 -0
- data/lib/rubybreaker/test.rb +1 -0
- data/lib/rubybreaker/type/type.rb +241 -0
- data/lib/rubybreaker/type/type_comparer.rb +143 -0
- data/lib/rubybreaker/type/type_grammar.treetop +285 -0
- data/lib/rubybreaker/type/type_unparser.rb +142 -0
- data/lib/rubybreaker/type.rb +2 -0
- data/lib/rubybreaker/typing/rubytype.rb +47 -0
- data/lib/rubybreaker/typing/subtyping.rb +480 -0
- data/lib/rubybreaker/typing.rb +3 -0
- data/lib/rubybreaker/util.rb +31 -0
- data/lib/rubybreaker.rb +193 -0
- data/test/integrated/tc_method_missing.rb +30 -0
- data/test/integrated/tc_simple1.rb +77 -0
- data/test/runtime/tc_obj_wrapper.rb +73 -0
- data/test/runtime/tc_typesig_parser.rb +33 -0
- data/test/ts_integrated.rb +4 -0
- data/test/ts_runtime.rb +5 -0
- data/test/ts_type.rb +5 -0
- data/test/ts_typing.rb +4 -0
- data/test/type/tc_comparer.rb +211 -0
- data/test/type/tc_parser.rb +219 -0
- data/test/type/tc_unparser.rb +276 -0
- data/test/typing/tc_rubytype.rb +63 -0
- data/test/typing/tc_typing.rb +219 -0
- data/webpage/footer.html +5 -0
- data/webpage/generated_toc.js +319 -0
- data/webpage/header.html +14 -0
- data/webpage/images/logo.png +0 -0
- data/webpage/index.html +439 -0
- data/webpage/rubybreaker.css +53 -0
- metadata +119 -0
@@ -0,0 +1,143 @@
|
|
1
|
+
#--
|
2
|
+
# This file defines (almost) syntactic equivalences between types.
|
3
|
+
|
4
|
+
require_relative "../type"
|
5
|
+
|
6
|
+
module RubyBreaker
|
7
|
+
|
8
|
+
# This module compares two RubyBreaker-defined types for the syntactic
|
9
|
+
# equivalence.
|
10
|
+
module TypeComparer
|
11
|
+
|
12
|
+
include TypeDefs
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# This method checks if two types are syntactically equivalent. The
|
17
|
+
# order of method names do not matter.
|
18
|
+
def self.duck_compare(lhs,rhs)
|
19
|
+
is_equal = false
|
20
|
+
if lhs.kind_of?(DuckType) && rhs.kind_of?(DuckType) &&
|
21
|
+
lhs.meth_names.size == rhs.meth_names.size
|
22
|
+
is_equal = true
|
23
|
+
lhs.meth_names.each {|mname|
|
24
|
+
if !rhs.meth_names.include?(mname)
|
25
|
+
is_equal = false
|
26
|
+
break
|
27
|
+
end
|
28
|
+
}
|
29
|
+
end
|
30
|
+
return is_equal
|
31
|
+
end
|
32
|
+
|
33
|
+
# This method compares two proc types.
|
34
|
+
def self.proc_compare(lhs, rhs)
|
35
|
+
is_equal = false
|
36
|
+
if lhs.arg_types.size == rhs.arg_types.size
|
37
|
+
is_equal = true
|
38
|
+
# check arguments first
|
39
|
+
lhs.arg_types.each_with_index do |atype,i|
|
40
|
+
if !self.compare(atype,rhs.arg_types[i])
|
41
|
+
is_equal = false
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
45
|
+
# check block types
|
46
|
+
if lhs.blk_type && rhs.blk_type
|
47
|
+
is_equal = is_equal && self.proc_compare(lhs.blk_type, rhs.blk_type)
|
48
|
+
elsif lhs.blk_type || rhs.blk_type
|
49
|
+
is_equal = false
|
50
|
+
end
|
51
|
+
is_equal = is_equal && self.compare(lhs.ret_type, rhs.ret_type)
|
52
|
+
end
|
53
|
+
return is_equal
|
54
|
+
end
|
55
|
+
|
56
|
+
# This method determines if the type exists in the given type list.
|
57
|
+
def self.type_in_types?(t, types)
|
58
|
+
exist = false
|
59
|
+
types.each do |t2|
|
60
|
+
if self.compare(t, t2)
|
61
|
+
exist = true
|
62
|
+
break
|
63
|
+
end
|
64
|
+
end
|
65
|
+
return exist
|
66
|
+
end
|
67
|
+
|
68
|
+
# This method compares two OR or MethodListType. The order of inner
|
69
|
+
# types do not matter.
|
70
|
+
#
|
71
|
+
# XXX: Should the order not matter really?
|
72
|
+
def self.or_compare(lhs,rhs)
|
73
|
+
is_equal = false
|
74
|
+
if lhs.class == rhs.class && lhs.types.size == rhs.types.size
|
75
|
+
is_equal = true
|
76
|
+
lhs.types.each { |t|
|
77
|
+
if !self.type_in_types?(t, rhs.types)
|
78
|
+
is_equal = false
|
79
|
+
break
|
80
|
+
end
|
81
|
+
}
|
82
|
+
end
|
83
|
+
return is_equal
|
84
|
+
end
|
85
|
+
|
86
|
+
# This method compares a method list to another method list.
|
87
|
+
def self.meth_list_compare(lhs, rhs)
|
88
|
+
return self.or_compare(lhs, rhs)
|
89
|
+
end
|
90
|
+
|
91
|
+
public
|
92
|
+
|
93
|
+
# This equal method determines whether two types are *syntactically*
|
94
|
+
# equivalent. Note that this is NOT a type equivalent check.
|
95
|
+
def self.compare(lhs,rhs)
|
96
|
+
if lhs == rhs
|
97
|
+
is_equal = true
|
98
|
+
elsif lhs.class != rhs.class
|
99
|
+
is_equal = false
|
100
|
+
elsif lhs.instance_of?(NominalType)
|
101
|
+
is_equal = (lhs.mod == rhs.mod)
|
102
|
+
elsif lhs.instance_of?(SelfType)
|
103
|
+
is_equal = rhs.instance_of?(SelfType)
|
104
|
+
elsif lhs.instance_of?(DuckType)
|
105
|
+
is_equal = duck_compare(lhs,rhs)
|
106
|
+
elsif lhs.instance_of?(FusionType)
|
107
|
+
is_equal = self.compare(lhs.nom_type, rhs.nom_type)
|
108
|
+
if is_equal # do more testing
|
109
|
+
is_equal = duck_compare(lhs,rhs)
|
110
|
+
end
|
111
|
+
elsif lhs.instance_of?(MethodType)
|
112
|
+
is_equal = lhs.meth_name == rhs.meth_name
|
113
|
+
if is_equal # then do more testing
|
114
|
+
is_equal = self.proc_compare(lhs,rhs)
|
115
|
+
end
|
116
|
+
elsif lhs.instance_of?(BlockType)
|
117
|
+
is_equal = self.proc_compare(lhs,rhs)
|
118
|
+
elsif lhs.instance_of?(MethodListType)
|
119
|
+
is_equal = self.meth_list_compare(lhs,rhs)
|
120
|
+
elsif lhs.instance_of?(OrType)
|
121
|
+
is_equal = self.or_compare(lhs,rhs)
|
122
|
+
elsif lhs.instance_of?(VarLengthType)
|
123
|
+
is_equal = rhs.instance_of?(VarLengthType) &&
|
124
|
+
self.compare(lhs.type, rhs.type)
|
125
|
+
elsif lhs.instance_of?(OptionalType)
|
126
|
+
is_equal = rhs.instance_of?(OptionalType) &&
|
127
|
+
self.compare(lhs.type, rhs.type)
|
128
|
+
else
|
129
|
+
is_equal = lhs.class == rhs.class
|
130
|
+
end
|
131
|
+
return is_equal
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
class TypeDefs::Type
|
137
|
+
|
138
|
+
# This method compares this object to another object syntactically.
|
139
|
+
def eql?(other)
|
140
|
+
TypeComparer.compare(self, other)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,285 @@
|
|
1
|
+
#--
|
2
|
+
# This file contains the grammar of the RubyBreaker type annotation language.
|
3
|
+
# TODO: This file probably needs more documentation
|
4
|
+
|
5
|
+
require "treetop"
|
6
|
+
require "#{File.dirname(__FILE__)}/type"
|
7
|
+
require "#{File.dirname(__FILE__)}/type_unparser"
|
8
|
+
|
9
|
+
grammar TypeGrammar
|
10
|
+
include RubyBreaker
|
11
|
+
|
12
|
+
rule meth_type
|
13
|
+
space? meth_name space? '(' space? arg_types_opt:(arg_types)? space? ')'
|
14
|
+
space? blk_type_opt:(blk_type)? space? '->' space? ret_type space?
|
15
|
+
{
|
16
|
+
def value
|
17
|
+
mname = meth_name.text_value
|
18
|
+
args = arg_types_opt.empty? ? [] : arg_types_opt.value
|
19
|
+
blk = blk_type_opt.empty? ? nil : blk_type_opt.value
|
20
|
+
ret = ret_type.value
|
21
|
+
pos = RubyBreaker::Position.get()
|
22
|
+
pos.col = meth_name.interval.first
|
23
|
+
return RubyBreaker::MethodType.new(mname,args,blk,ret,pos)
|
24
|
+
end
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
rule arg_types
|
29
|
+
moretypes:( arg_type space? ',' space? )*
|
30
|
+
rest_arg_types
|
31
|
+
{
|
32
|
+
def value
|
33
|
+
types = []
|
34
|
+
moretypes.elements.each {|t| types << t.arg_type.value }
|
35
|
+
if rest_arg_types.value.kind_of?(Array)
|
36
|
+
types.concat(rest_arg_types.value)
|
37
|
+
else
|
38
|
+
types << rest_arg_types.value
|
39
|
+
end
|
40
|
+
return types
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
rule arg_type
|
46
|
+
or_type / or_parent_type / fusion_type / duck_type / simple_type
|
47
|
+
end
|
48
|
+
|
49
|
+
rule unambig_arg_type
|
50
|
+
or_parent_type / fusion_type / duck_type / simple_type
|
51
|
+
end
|
52
|
+
|
53
|
+
rule rest_arg_types
|
54
|
+
opt_varlen_types / opt_types / arg_type
|
55
|
+
end
|
56
|
+
|
57
|
+
rule opt_types
|
58
|
+
moretypes:( opt_type space? ',' space? )* opt_type
|
59
|
+
{
|
60
|
+
def value
|
61
|
+
types = []
|
62
|
+
moretypes.elements.each {|t| types << t.opt_type.value }
|
63
|
+
types << opt_type.value
|
64
|
+
return types
|
65
|
+
end
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
rule opt_varlen_types
|
70
|
+
moretypes:( opt_type space? ',' space? )* varlen_type
|
71
|
+
{
|
72
|
+
def value
|
73
|
+
types = []
|
74
|
+
moretypes.elements.each {|t| types << t.opt_type.value }
|
75
|
+
types << varlen_type.value
|
76
|
+
return types
|
77
|
+
end
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
rule opt_type
|
82
|
+
unambig_arg_type '?'
|
83
|
+
{
|
84
|
+
def value
|
85
|
+
pos = RubyBreaker::Position.get()
|
86
|
+
pos.col = interval.first
|
87
|
+
return RubyBreaker::OptionalType.new(unambig_arg_type.value, pos)
|
88
|
+
end
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
rule varlen_type
|
93
|
+
unambig_arg_type '*'
|
94
|
+
{
|
95
|
+
def value
|
96
|
+
pos = RubyBreaker::Position.get()
|
97
|
+
pos.col = interval.first
|
98
|
+
return RubyBreaker::VarLengthType.new(unambig_arg_type.value, pos)
|
99
|
+
end
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
rule or_parent_type
|
104
|
+
'(' or_type ')'
|
105
|
+
{
|
106
|
+
def value(); or_type.value; end
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
rule or_type
|
111
|
+
left:(fusion_type / duck_type / simple_type) space?
|
112
|
+
more:(space? '||' space? opt:(fusion_type / duck_type / simple_type) )+
|
113
|
+
{
|
114
|
+
def value
|
115
|
+
types = [left.value]
|
116
|
+
# let's flatten the types instead of nesting them
|
117
|
+
more.elements.each do |t|
|
118
|
+
types << t.opt.value
|
119
|
+
end
|
120
|
+
pos = RubyBreaker::Position.get()
|
121
|
+
pos.col = interval.first
|
122
|
+
return RubyBreaker::OrType.new(types, pos)
|
123
|
+
end
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
rule blk_type
|
128
|
+
blk_type_empty / blk_type_full
|
129
|
+
end
|
130
|
+
|
131
|
+
rule blk_type_empty
|
132
|
+
'{' space? '}'
|
133
|
+
{
|
134
|
+
def value
|
135
|
+
return nil
|
136
|
+
end
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
rule blk_type_full
|
141
|
+
'{' space? '|' space? arg_types_opt:(arg_types)? space? '|' space?
|
142
|
+
blk_type_opt:(blk_type)? space? '->' space? ret_type '}'
|
143
|
+
{
|
144
|
+
def value
|
145
|
+
args = arg_types_opt.empty? ? [] : arg_types_opt.value
|
146
|
+
blk = blk_type_opt.empty? ? nil : blk_type_opt.value
|
147
|
+
ret = ret_type.value
|
148
|
+
pos = RubyBreaker::Position.get()
|
149
|
+
pos.col = interval.first
|
150
|
+
return RubyBreaker::BlockType.new(args,blk,ret,pos)
|
151
|
+
end
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
rule fusion_type
|
156
|
+
simple_type '[' space? meth_name_list space? ']'
|
157
|
+
{
|
158
|
+
def value
|
159
|
+
pos = RubyBreaker::Position.get()
|
160
|
+
pos.col = interval.first
|
161
|
+
return RubyBreaker::FusionType.new(simple_type.value, meth_name_list.value,pos)
|
162
|
+
end
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
rule duck_type
|
167
|
+
'[' space? meth_name_list_opt:(meth_name_list)? space? ']'
|
168
|
+
{
|
169
|
+
def value
|
170
|
+
pos = RubyBreaker::Position.get()
|
171
|
+
pos.col = interval.first
|
172
|
+
if meth_name_list_opt.empty?
|
173
|
+
t = RubyBreaker::AnyType.new(pos)
|
174
|
+
else
|
175
|
+
mnames = meth_name_list_opt.value
|
176
|
+
t = RubyBreaker::DuckType.new(mnames,pos)
|
177
|
+
end
|
178
|
+
return t
|
179
|
+
end
|
180
|
+
}
|
181
|
+
end
|
182
|
+
|
183
|
+
rule meth_name_list
|
184
|
+
more_meth_names:(meth_name space? ',' space?)* meth_name
|
185
|
+
{
|
186
|
+
def value
|
187
|
+
mnames = []
|
188
|
+
more_meth_names.elements.each do |more_mname|
|
189
|
+
mnames << more_mname.meth_name.text_value
|
190
|
+
end
|
191
|
+
mnames << meth_name.text_value
|
192
|
+
return mnames
|
193
|
+
end
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
rule ret_type
|
198
|
+
more:(simple_type space? ',' space?)* simple_type
|
199
|
+
{
|
200
|
+
def value
|
201
|
+
pos = RubyBreaker::Position.get()
|
202
|
+
pos.col = interval.first
|
203
|
+
if more.empty?
|
204
|
+
return simple_type.value
|
205
|
+
else
|
206
|
+
types = []
|
207
|
+
more.elements.each {|e| types << e.simple_type.value }
|
208
|
+
types << simple_type.value
|
209
|
+
return types
|
210
|
+
end
|
211
|
+
end
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
215
|
+
rule simple_type
|
216
|
+
nil_type / any_type1 / self_type / nominal_type
|
217
|
+
end
|
218
|
+
|
219
|
+
rule self_type
|
220
|
+
'self' {
|
221
|
+
def value
|
222
|
+
pos = RubyBreaker::Position.get()
|
223
|
+
pos.col = interval.first
|
224
|
+
return RubyBreaker::SelfType.new(pos)
|
225
|
+
end
|
226
|
+
}
|
227
|
+
end
|
228
|
+
|
229
|
+
rule nominal_type
|
230
|
+
[a-z_]+ {
|
231
|
+
def value
|
232
|
+
pos = RubyBreaker::Position.get()
|
233
|
+
pos.col = interval.first
|
234
|
+
begin
|
235
|
+
mod_name = RubyBreaker::Utilities.camelize(text_value)
|
236
|
+
mod = eval mod_name
|
237
|
+
t = RubyBreaker::NominalType.new(mod,pos)
|
238
|
+
rescue
|
239
|
+
t = RubyBreaker::AnyType.new(pos)
|
240
|
+
end
|
241
|
+
return t # RubyBreaker::NominalType.new(text_value,pos)
|
242
|
+
end
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
rule nil_type
|
247
|
+
'nil' {
|
248
|
+
def value
|
249
|
+
pos = RubyBreaker::Position.get()
|
250
|
+
pos.col = interval.first
|
251
|
+
return RubyBreaker::NilType.new(pos)
|
252
|
+
end
|
253
|
+
}
|
254
|
+
end
|
255
|
+
|
256
|
+
rule any_type1
|
257
|
+
'?' {
|
258
|
+
def value
|
259
|
+
pos = RubyBreaker::Position.get()
|
260
|
+
pos.col = interval.first
|
261
|
+
return RubyBreaker::AnyType.new(pos)
|
262
|
+
end
|
263
|
+
}
|
264
|
+
end
|
265
|
+
|
266
|
+
rule meth_name
|
267
|
+
nominal_meth_name / sym_meth_name
|
268
|
+
end
|
269
|
+
|
270
|
+
rule sym_meth_name
|
271
|
+
# Be careful about the order. Remember, it finds a match first
|
272
|
+
'===' / '<=>' / '[]=' /
|
273
|
+
'==' / '!=' / '<<' / '>>' / '[]' / '**' / '<=' / '>=' / '-@' / '=~' /
|
274
|
+
'<' / '>' / '&' / '|' / '*' / '/' / '%' / '+' / '-' / '^'
|
275
|
+
end
|
276
|
+
|
277
|
+
rule nominal_meth_name
|
278
|
+
[a-z_] [a-zA-Z0-9_]* [!?=]?
|
279
|
+
end
|
280
|
+
|
281
|
+
rule space
|
282
|
+
[\s]+
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
#--
|
2
|
+
# This file contains the type unparser for the type structure used in
|
3
|
+
# RubyBreaker. No visitor pattern is used due to its "functional" element.
|
4
|
+
# Instead, it will rely on type matches and work each type in a conditional
|
5
|
+
# statement.
|
6
|
+
|
7
|
+
require_relative "type"
|
8
|
+
|
9
|
+
module RubyBreaker
|
10
|
+
|
11
|
+
# This module prints the RubyBreaker types in a user-friendly way.
|
12
|
+
module TypeUnparser
|
13
|
+
|
14
|
+
include TypeDefs
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# This method is used to determine if the inner type of +t+ should be
|
19
|
+
# wrapped around a parenthesis. This is for optional type and variable
|
20
|
+
# length type.
|
21
|
+
def self.peek_and_unparse_pp_inner_type(pp,t)
|
22
|
+
if t.type.kind_of?(OrType)
|
23
|
+
pp.text("(")
|
24
|
+
self.unparse_pp(pp,t.type)
|
25
|
+
pp.text(")")
|
26
|
+
else
|
27
|
+
self.unparse_pp(pp,t.type)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# This method unparses an object type (a duck type or fusion type)
|
32
|
+
def self.unparse_pp_object_type(pp,t)
|
33
|
+
pp.text("[#{t.meth_names.sort.join(", ")}]")
|
34
|
+
end
|
35
|
+
|
36
|
+
# This recursive method unparses a RubyBreaker type using the pretty
|
37
|
+
# print method.
|
38
|
+
def self.unparse_pp(pp,t)
|
39
|
+
if t.instance_of?(NominalType)
|
40
|
+
tname = Utilities.underscore(t.mod)
|
41
|
+
tokens = tname.split("/")
|
42
|
+
tname = tokens.last if tokens.size > 1
|
43
|
+
pp.text(tname)
|
44
|
+
elsif t.instance_of?(SelfType)
|
45
|
+
pp.text("self")
|
46
|
+
elsif t.instance_of?(DuckType)
|
47
|
+
unparse_pp_object_type(pp,t)
|
48
|
+
elsif t.instance_of?(FusionType)
|
49
|
+
unparse_pp(pp,t.nom_type)
|
50
|
+
unparse_pp_object_type(pp,t)
|
51
|
+
elsif t.instance_of?(MethodType)
|
52
|
+
pp.text("#{t.meth_name}(")
|
53
|
+
t.arg_types.each_with_index do |arg_type,i|
|
54
|
+
unparse_pp(pp,arg_type)
|
55
|
+
if i < t.arg_types.size - 1
|
56
|
+
pp.text(",")
|
57
|
+
pp.fill_breakable()
|
58
|
+
end
|
59
|
+
end
|
60
|
+
pp.text(")")
|
61
|
+
pp.fill_breakable()
|
62
|
+
if t.blk_type
|
63
|
+
pp.text("{")
|
64
|
+
unparse_pp(pp,t.blk_type)
|
65
|
+
pp.text("}")
|
66
|
+
pp.fill_breakable()
|
67
|
+
end
|
68
|
+
pp.text("->")
|
69
|
+
pp.fill_breakable()
|
70
|
+
unparse_pp(pp,t.ret_type)
|
71
|
+
elsif t.instance_of?(BlockType)
|
72
|
+
pp.text("|")
|
73
|
+
t.arg_types.each_with_index do |arg_type,i|
|
74
|
+
unparse_pp(pp,arg_type)
|
75
|
+
if i < t.arg_types.size - 1
|
76
|
+
pp.text(",")
|
77
|
+
pp.fill_breakable()
|
78
|
+
end
|
79
|
+
end
|
80
|
+
pp.text("|")
|
81
|
+
pp.fill_breakable()
|
82
|
+
if t.blk_type
|
83
|
+
pp.text("{")
|
84
|
+
unparse_pp(pp,t.blk_type)
|
85
|
+
pp.text("}")
|
86
|
+
pp.fill_breakable()
|
87
|
+
end
|
88
|
+
pp.text("->")
|
89
|
+
pp.fill_breakable()
|
90
|
+
unparse_pp(pp,t.ret_type)
|
91
|
+
elsif t.instance_of?(MethodListType)
|
92
|
+
t.types.each_with_index do |typ,i|
|
93
|
+
unparse_pp(pp,typ)
|
94
|
+
if i < t.types.size - 1
|
95
|
+
pp.fill_breakable()
|
96
|
+
end
|
97
|
+
end
|
98
|
+
elsif t.instance_of?(OrType)
|
99
|
+
t.types.each_with_index do |typ,i|
|
100
|
+
unparse_pp(pp,typ)
|
101
|
+
if i < t.types.size - 1
|
102
|
+
pp.text(" ||")
|
103
|
+
pp.fill_breakable()
|
104
|
+
end
|
105
|
+
end
|
106
|
+
elsif t.instance_of?(OptionalType)
|
107
|
+
peek_and_unparse_pp_inner_type(pp,t)
|
108
|
+
pp.text("?")
|
109
|
+
elsif t.instance_of?(VarLengthType)
|
110
|
+
peek_and_unparse_pp_inner_type(pp,t)
|
111
|
+
pp.text("*")
|
112
|
+
elsif t.instance_of?(NilType)
|
113
|
+
pp.text("nil")
|
114
|
+
elsif t.instance_of?(AnyType)
|
115
|
+
pp.text("?")
|
116
|
+
else
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
public
|
121
|
+
|
122
|
+
# This method is used to display any RubyBreaker type in a user-friendly
|
123
|
+
# way using the pretty print method.
|
124
|
+
def self.unparse(t)
|
125
|
+
str = ""
|
126
|
+
pp = PrettyPrint.new(str)
|
127
|
+
self.unparse_pp(pp,t)
|
128
|
+
pp.flush
|
129
|
+
return str.strip()
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class TypeDefs::Type
|
134
|
+
|
135
|
+
# This method unparses the type using the pretty print method.
|
136
|
+
def unparse()
|
137
|
+
TypeUnparser.unparse(self)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#--
|
2
|
+
# This file provides utility functions that deal with actual Ruby types
|
3
|
+
# directly. Subclass/submodule relationship is based on the inclusion of
|
4
|
+
# the class or the module...and not the true (theoretical) subtype
|
5
|
+
# relationship.
|
6
|
+
|
7
|
+
module RubyBreaker
|
8
|
+
|
9
|
+
module RubyTypeUtils
|
10
|
+
|
11
|
+
# Checks if lhs is a sub-module of rhs
|
12
|
+
def self.submodule_rel?(lhs,rhs)
|
13
|
+
# both lhs and rhs must be modules
|
14
|
+
if lhs == rhs
|
15
|
+
return true
|
16
|
+
else
|
17
|
+
lhs.included_modules.each {|m|
|
18
|
+
return true if self.submodule_rel?(m,rhs)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
|
24
|
+
# Checks if lhs is a subclass of rhs
|
25
|
+
def self.subclass_rel?(lhs, rhs)
|
26
|
+
return false unless lhs.kind_of?(Class)
|
27
|
+
if lhs == rhs
|
28
|
+
return true
|
29
|
+
elsif lhs == BasicObject
|
30
|
+
# lhs != rhs and no more to look upward, so quit
|
31
|
+
return false
|
32
|
+
elsif rhs.instance_of?(Module)
|
33
|
+
# lhs != rhs and rhs is a module, so lhs must have a parent module
|
34
|
+
# that is a subclass of rhs
|
35
|
+
lhs.included_modules.each {|m|
|
36
|
+
return true if self.submodule_rel?(m,rhs)
|
37
|
+
}
|
38
|
+
else
|
39
|
+
# then rhs is a class, so just go up as a class
|
40
|
+
return self.subclass_rel?(lhs.superclass, rhs)
|
41
|
+
end
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|