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.
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