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