rubybreaker 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/AUTHORS +7 -0
  2. data/LICENSE +26 -0
  3. data/README.md +403 -0
  4. data/Rakefile +90 -0
  5. data/TODO +30 -0
  6. data/bin/gen_stub_rubylib +64 -0
  7. data/bin/rubybreaker +67 -0
  8. data/lib/rubybreaker/context.rb +122 -0
  9. data/lib/rubybreaker/debug.rb +48 -0
  10. data/lib/rubybreaker/error.rb +59 -0
  11. data/lib/rubybreaker/rubylib/core.rb +2316 -0
  12. data/lib/rubybreaker/rubylib.rb +3 -0
  13. data/lib/rubybreaker/runtime/inspector.rb +57 -0
  14. data/lib/rubybreaker/runtime/monitor.rb +235 -0
  15. data/lib/rubybreaker/runtime/object_wrapper.rb +77 -0
  16. data/lib/rubybreaker/runtime/overrides.rb +42 -0
  17. data/lib/rubybreaker/runtime/pluggable.rb +57 -0
  18. data/lib/rubybreaker/runtime/type_placeholder.rb +27 -0
  19. data/lib/rubybreaker/runtime/type_system.rb +228 -0
  20. data/lib/rubybreaker/runtime/typesig_parser.rb +45 -0
  21. data/lib/rubybreaker/runtime.rb +103 -0
  22. data/lib/rubybreaker/test/testcase.rb +39 -0
  23. data/lib/rubybreaker/test.rb +1 -0
  24. data/lib/rubybreaker/type/type.rb +241 -0
  25. data/lib/rubybreaker/type/type_comparer.rb +143 -0
  26. data/lib/rubybreaker/type/type_grammar.treetop +285 -0
  27. data/lib/rubybreaker/type/type_unparser.rb +142 -0
  28. data/lib/rubybreaker/type.rb +2 -0
  29. data/lib/rubybreaker/typing/rubytype.rb +47 -0
  30. data/lib/rubybreaker/typing/subtyping.rb +480 -0
  31. data/lib/rubybreaker/typing.rb +3 -0
  32. data/lib/rubybreaker/util.rb +31 -0
  33. data/lib/rubybreaker.rb +193 -0
  34. data/test/integrated/tc_method_missing.rb +30 -0
  35. data/test/integrated/tc_simple1.rb +77 -0
  36. data/test/runtime/tc_obj_wrapper.rb +73 -0
  37. data/test/runtime/tc_typesig_parser.rb +33 -0
  38. data/test/ts_integrated.rb +4 -0
  39. data/test/ts_runtime.rb +5 -0
  40. data/test/ts_type.rb +5 -0
  41. data/test/ts_typing.rb +4 -0
  42. data/test/type/tc_comparer.rb +211 -0
  43. data/test/type/tc_parser.rb +219 -0
  44. data/test/type/tc_unparser.rb +276 -0
  45. data/test/typing/tc_rubytype.rb +63 -0
  46. data/test/typing/tc_typing.rb +219 -0
  47. data/webpage/footer.html +5 -0
  48. data/webpage/generated_toc.js +319 -0
  49. data/webpage/header.html +14 -0
  50. data/webpage/images/logo.png +0 -0
  51. data/webpage/index.html +439 -0
  52. data/webpage/rubybreaker.css +53 -0
  53. 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,2 @@
1
+ require_relative "type/type"
2
+ require_relative "type/type_unparser"
@@ -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