rdl 1.0.0.rc1
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.
- checksums.yaml +7 -0
- data/lib/rails_types.rb +1 -0
- data/lib/rdl.rb +56 -0
- data/lib/rdl/config.rb +121 -0
- data/lib/rdl/contracts/and.rb +29 -0
- data/lib/rdl/contracts/contract.rb +7 -0
- data/lib/rdl/contracts/flat.rb +31 -0
- data/lib/rdl/contracts/or.rb +25 -0
- data/lib/rdl/contracts/proc.rb +24 -0
- data/lib/rdl/switch.rb +20 -0
- data/lib/rdl/types/annotated_arg.rb +41 -0
- data/lib/rdl/types/finitehash.rb +81 -0
- data/lib/rdl/types/generic.rb +100 -0
- data/lib/rdl/types/intersection.rb +66 -0
- data/lib/rdl/types/lexer.rex +39 -0
- data/lib/rdl/types/lexer.rex.rb +148 -0
- data/lib/rdl/types/method.rb +219 -0
- data/lib/rdl/types/nil.rb +50 -0
- data/lib/rdl/types/nominal.rb +80 -0
- data/lib/rdl/types/optional.rb +54 -0
- data/lib/rdl/types/parser.racc +150 -0
- data/lib/rdl/types/parser.tab.rb +654 -0
- data/lib/rdl/types/singleton.rb +62 -0
- data/lib/rdl/types/structural.rb +72 -0
- data/lib/rdl/types/top.rb +50 -0
- data/lib/rdl/types/tuple.rb +61 -0
- data/lib/rdl/types/type.rb +24 -0
- data/lib/rdl/types/type_inferencer.rb +73 -0
- data/lib/rdl/types/union.rb +74 -0
- data/lib/rdl/types/var.rb +51 -0
- data/lib/rdl/types/vararg.rb +54 -0
- data/lib/rdl/types/wild.rb +26 -0
- data/lib/rdl/util.rb +56 -0
- data/lib/rdl/wrap.rb +505 -0
- data/lib/rdl_types.rb +2 -0
- data/types/other/chronic.rb +5 -0
- data/types/other/paperclip_attachment.rb +7 -0
- data/types/other/securerandom.rb +4 -0
- data/types/rails-4.2.1/fixnum.rb +3 -0
- data/types/rails-4.2.1/string.rb +3 -0
- data/types/rails-tmp/action_dispatch.rb +406 -0
- data/types/rails-tmp/active_record.rb +406 -0
- data/types/rails-tmp/devise_contracts.rb +216 -0
- data/types/ruby-2.2.0/_aliases.rb +4 -0
- data/types/ruby-2.2.0/abbrev.rb +5 -0
- data/types/ruby-2.2.0/array.rb +137 -0
- data/types/ruby-2.2.0/base64.rb +10 -0
- data/types/ruby-2.2.0/basic_object.rb +13 -0
- data/types/ruby-2.2.0/benchmark.rb +11 -0
- data/types/ruby-2.2.0/bigdecimal.rb +15 -0
- data/types/ruby-2.2.0/bigmath.rb +12 -0
- data/types/ruby-2.2.0/class.rb +17 -0
- data/types/ruby-2.2.0/complex.rb +42 -0
- data/types/ruby-2.2.0/coverage.rb +6 -0
- data/types/ruby-2.2.0/csv.rb +5 -0
- data/types/ruby-2.2.0/date.rb +6 -0
- data/types/ruby-2.2.0/dir.rb +38 -0
- data/types/ruby-2.2.0/encoding.rb +23 -0
- data/types/ruby-2.2.0/enumerable.rb +98 -0
- data/types/ruby-2.2.0/enumerator.rb +26 -0
- data/types/ruby-2.2.0/exception.rb +15 -0
- data/types/ruby-2.2.0/file.rb +124 -0
- data/types/ruby-2.2.0/fileutils.rb +6 -0
- data/types/ruby-2.2.0/fixnum.rb +45 -0
- data/types/ruby-2.2.0/float.rb +54 -0
- data/types/ruby-2.2.0/gem.rb +245 -0
- data/types/ruby-2.2.0/hash.rb +72 -0
- data/types/ruby-2.2.0/integer.rb +31 -0
- data/types/ruby-2.2.0/io.rb +103 -0
- data/types/ruby-2.2.0/kernel.rb +89 -0
- data/types/ruby-2.2.0/marshal.rb +5 -0
- data/types/ruby-2.2.0/matchdata.rb +26 -0
- data/types/ruby-2.2.0/math.rb +53 -0
- data/types/ruby-2.2.0/numeric.rb +46 -0
- data/types/ruby-2.2.0/object.rb +73 -0
- data/types/ruby-2.2.0/pathname.rb +106 -0
- data/types/ruby-2.2.0/process.rb +127 -0
- data/types/ruby-2.2.0/random.rb +15 -0
- data/types/ruby-2.2.0/range.rb +38 -0
- data/types/ruby-2.2.0/rational.rb +31 -0
- data/types/ruby-2.2.0/regexp.rb +30 -0
- data/types/ruby-2.2.0/set.rb +58 -0
- data/types/ruby-2.2.0/string.rb +145 -0
- data/types/ruby-2.2.0/strscan.rb +7 -0
- data/types/ruby-2.2.0/symbol.rb +29 -0
- data/types/ruby-2.2.0/time.rb +68 -0
- data/types/ruby-2.2.0/uri.rb +18 -0
- data/types/ruby-2.2.0/yaml.rb +5 -0
- metadata +131 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
require_relative 'type'
|
|
2
|
+
|
|
3
|
+
module RDL::Type
|
|
4
|
+
# A type that is parameterized on one or more other types. The base type
|
|
5
|
+
# must be a NominalType, while the parameters should be strings or symbols
|
|
6
|
+
class GenericType < Type
|
|
7
|
+
attr_reader :base
|
|
8
|
+
attr_reader :params
|
|
9
|
+
|
|
10
|
+
@@cache = {}
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
alias :__new__ :new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.new(base, *params)
|
|
17
|
+
t = @@cache[[base, params]]
|
|
18
|
+
return t if t
|
|
19
|
+
raise RuntimeError, "Attempt to create generic type with non-type param" unless params.all? { |p| p.is_a? Type }
|
|
20
|
+
t = GenericType.__new__(base, params)
|
|
21
|
+
return (@@cache[[base, params]] = t) # assignment evaluates to t
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def initialize(base, params)
|
|
25
|
+
raise "base must be NominalType" unless base.instance_of? NominalType
|
|
26
|
+
|
|
27
|
+
@base = base
|
|
28
|
+
@params = params
|
|
29
|
+
super()
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_s
|
|
33
|
+
"#{@base}<#{@params.map { |t| t.to_s }.join(', ')}>"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def eql?(other)
|
|
37
|
+
self == other
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def ==(other) # :nodoc:
|
|
41
|
+
return (other.instance_of? GenericType) && (other.base == @base) && (other.params == @params)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def <=(other)
|
|
45
|
+
formals, variance, check = $__rdl_type_params[base.name]
|
|
46
|
+
# do check here to avoid hiding errors if generic type written
|
|
47
|
+
# with wrong number of parameters but never checked against
|
|
48
|
+
# instantiated instances
|
|
49
|
+
raise TypeError, "No type parameters defined for #{base.name}" unless formals
|
|
50
|
+
return true if other.instance_of? TopType
|
|
51
|
+
# return (@base <= other) if other.instance_of?(NominalType) # raw subtyping not allowed
|
|
52
|
+
if other.instance_of? GenericType
|
|
53
|
+
return false unless @base == other.base
|
|
54
|
+
return variance.zip(params, other.params).all? { |v, self_t, other_t|
|
|
55
|
+
case v
|
|
56
|
+
when :+
|
|
57
|
+
self_t <= other_t
|
|
58
|
+
when :-
|
|
59
|
+
other_t <= self_t
|
|
60
|
+
when :~
|
|
61
|
+
self_t == other_t
|
|
62
|
+
else
|
|
63
|
+
raise RuntimeError, "Unexpected variance #{v}" # shouldn't happen
|
|
64
|
+
end
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
if other.instance_of? StructuralType
|
|
68
|
+
# similar logic in NominalType
|
|
69
|
+
inst = Hash[*formals.zip(params).flatten]
|
|
70
|
+
k = base.klass
|
|
71
|
+
other.methods.each_pair { |m, t|
|
|
72
|
+
return false unless k.method_defined? m
|
|
73
|
+
if RDL::Wrap.has_contracts?(k, m, :type)
|
|
74
|
+
types = RDL::Wrap.get_contracts(k, m, :type)
|
|
75
|
+
return false unless types.all? { |t_self| t_self.instantiate(inst) <= t }
|
|
76
|
+
end
|
|
77
|
+
}
|
|
78
|
+
return true
|
|
79
|
+
end
|
|
80
|
+
return false
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def member?(obj, *args)
|
|
84
|
+
raise "No type parameters defined for #{base.name}" unless $__rdl_type_params[base.name]
|
|
85
|
+
formals = $__rdl_type_params[base.name][0]
|
|
86
|
+
t = RDL::Util.rdl_type obj
|
|
87
|
+
return t <= self if t
|
|
88
|
+
return false unless base.member?(obj, *args)
|
|
89
|
+
return true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def instantiate(inst)
|
|
93
|
+
GenericType.new(base, *params.map { |t| t.instantiate(inst) })
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def hash
|
|
97
|
+
h = (61 + @base.hash) * @params.hash
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require_relative 'type'
|
|
2
|
+
|
|
3
|
+
module RDL::Type
|
|
4
|
+
class IntersectionType < Type
|
|
5
|
+
attr_reader :types
|
|
6
|
+
|
|
7
|
+
@@cache = {}
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
alias :__new__ :new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.new(*types)
|
|
14
|
+
ts = []
|
|
15
|
+
types.each { |t|
|
|
16
|
+
if t.instance_of? NilType
|
|
17
|
+
next
|
|
18
|
+
elsif t.instance_of? IntersectionType
|
|
19
|
+
ts.concat t.types
|
|
20
|
+
else
|
|
21
|
+
raise RuntimeError, "Attempt to create intersection type with non-type" unless t.is_a? Type
|
|
22
|
+
ts << t
|
|
23
|
+
end
|
|
24
|
+
}
|
|
25
|
+
ts.sort! { |a,b| a.object_id <=> b.object_id }
|
|
26
|
+
ts.uniq!
|
|
27
|
+
|
|
28
|
+
return NilType.new if ts.size == 0
|
|
29
|
+
return ts[0] if ts.size == 1
|
|
30
|
+
|
|
31
|
+
t = @@cache[ts]
|
|
32
|
+
return t if t
|
|
33
|
+
t = IntersectionType.__new__(ts)
|
|
34
|
+
return (@@cache[ts] = t) # assignment evaluates to t
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def initialize(types)
|
|
38
|
+
@types = types
|
|
39
|
+
super()
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_s # :nodoc:
|
|
43
|
+
"(#{@types.map { |t| t.to_s }.join(' and ')})"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def eql?(other)
|
|
47
|
+
self == other
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def ==(other) # :nodoc:
|
|
51
|
+
return (other.instance_of? IntersectionType) && (other.types == @types)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def member?(obj, *args)
|
|
55
|
+
@types.all? { |t| t.member?(obj, *args) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def instantiate(inst)
|
|
59
|
+
return IntersectionType.new(*(@types.map { |t| t.instantiate(inst) }))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def hash # :nodoc:
|
|
63
|
+
47 + @types.hash
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module RDL::Type
|
|
2
|
+
class Parser
|
|
3
|
+
|
|
4
|
+
macro
|
|
5
|
+
ID (\w|\:\:)+
|
|
6
|
+
SYMBOL :\w+
|
|
7
|
+
SPECIAL_ID %\w+
|
|
8
|
+
FIXNUM -?(\d)+
|
|
9
|
+
FLOAT -?\d\.\d+
|
|
10
|
+
|
|
11
|
+
rule
|
|
12
|
+
\s # skip
|
|
13
|
+
or { [:OR, text] }
|
|
14
|
+
-> { [:RARROW, text] }
|
|
15
|
+
=> { [:RASSOC, text] }
|
|
16
|
+
\( { [:LPAREN, text] }
|
|
17
|
+
\) { [:RPAREN, text] }
|
|
18
|
+
\{ { [:LBRACE, text] }
|
|
19
|
+
\} { [:RBRACE, text] }
|
|
20
|
+
\[ { [:LBRACKET, text] }
|
|
21
|
+
\] { [:RBRACKET, text] }
|
|
22
|
+
< { [:LESS, text] }
|
|
23
|
+
> { [:GREATER, text] }
|
|
24
|
+
, { [:COMMA, text] }
|
|
25
|
+
\? { [:QUERY, text] }
|
|
26
|
+
\* { [:STAR, text] }
|
|
27
|
+
\#\# { [:DOUBLE_HASH, text] }
|
|
28
|
+
\$\{ { [:CONST_BEGIN, text] }
|
|
29
|
+
{FLOAT} { [:FLOAT, text] } # Must go before FIXNUM
|
|
30
|
+
{FIXNUM} { [:FIXNUM, text] }
|
|
31
|
+
{ID} { [:ID, text] }
|
|
32
|
+
{SYMBOL} { [:SYMBOL, text[1..-1]] }
|
|
33
|
+
\: { [:COLON, text] } # Must come after SYMBOL
|
|
34
|
+
{SPECIAL_ID} { [:SPECIAL_ID, text] }
|
|
35
|
+
'[^']*' { [:STRING, text.gsub("'", "")] }
|
|
36
|
+
"[^"]*" { [:STRING, text.gsub('"', "")] }
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# DO NOT MODIFY!!!!
|
|
3
|
+
# This file is automatically generated by rex 1.0.5
|
|
4
|
+
# from lexical definition file "lexer.rex".
|
|
5
|
+
#++
|
|
6
|
+
|
|
7
|
+
require 'racc/parser'
|
|
8
|
+
module RDL::Type
|
|
9
|
+
class Parser < Racc::Parser
|
|
10
|
+
require 'strscan'
|
|
11
|
+
|
|
12
|
+
class ScanError < StandardError ; end
|
|
13
|
+
|
|
14
|
+
attr_reader :lineno
|
|
15
|
+
attr_reader :filename
|
|
16
|
+
attr_accessor :state
|
|
17
|
+
|
|
18
|
+
def scan_setup(str)
|
|
19
|
+
@ss = StringScanner.new(str)
|
|
20
|
+
@lineno = 1
|
|
21
|
+
@state = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def action
|
|
25
|
+
yield
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def scan_str(str)
|
|
29
|
+
scan_setup(str)
|
|
30
|
+
do_parse
|
|
31
|
+
end
|
|
32
|
+
alias :scan :scan_str
|
|
33
|
+
|
|
34
|
+
def load_file( filename )
|
|
35
|
+
@filename = filename
|
|
36
|
+
open(filename, "r") do |f|
|
|
37
|
+
scan_setup(f.read)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def scan_file( filename )
|
|
42
|
+
load_file(filename)
|
|
43
|
+
do_parse
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def next_token
|
|
48
|
+
return if @ss.eos?
|
|
49
|
+
|
|
50
|
+
# skips empty actions
|
|
51
|
+
until token = _next_token or @ss.eos?; end
|
|
52
|
+
token
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def _next_token
|
|
56
|
+
text = @ss.peek(1)
|
|
57
|
+
@lineno += 1 if text == "\n"
|
|
58
|
+
token = case @state
|
|
59
|
+
when nil
|
|
60
|
+
case
|
|
61
|
+
when (text = @ss.scan(/\s/))
|
|
62
|
+
;
|
|
63
|
+
|
|
64
|
+
when (text = @ss.scan(/or/))
|
|
65
|
+
action { [:OR, text] }
|
|
66
|
+
|
|
67
|
+
when (text = @ss.scan(/->/))
|
|
68
|
+
action { [:RARROW, text] }
|
|
69
|
+
|
|
70
|
+
when (text = @ss.scan(/=>/))
|
|
71
|
+
action { [:RASSOC, text] }
|
|
72
|
+
|
|
73
|
+
when (text = @ss.scan(/\(/))
|
|
74
|
+
action { [:LPAREN, text] }
|
|
75
|
+
|
|
76
|
+
when (text = @ss.scan(/\)/))
|
|
77
|
+
action { [:RPAREN, text] }
|
|
78
|
+
|
|
79
|
+
when (text = @ss.scan(/\{/))
|
|
80
|
+
action { [:LBRACE, text] }
|
|
81
|
+
|
|
82
|
+
when (text = @ss.scan(/\}/))
|
|
83
|
+
action { [:RBRACE, text] }
|
|
84
|
+
|
|
85
|
+
when (text = @ss.scan(/\[/))
|
|
86
|
+
action { [:LBRACKET, text] }
|
|
87
|
+
|
|
88
|
+
when (text = @ss.scan(/\]/))
|
|
89
|
+
action { [:RBRACKET, text] }
|
|
90
|
+
|
|
91
|
+
when (text = @ss.scan(/</))
|
|
92
|
+
action { [:LESS, text] }
|
|
93
|
+
|
|
94
|
+
when (text = @ss.scan(/>/))
|
|
95
|
+
action { [:GREATER, text] }
|
|
96
|
+
|
|
97
|
+
when (text = @ss.scan(/,/))
|
|
98
|
+
action { [:COMMA, text] }
|
|
99
|
+
|
|
100
|
+
when (text = @ss.scan(/\?/))
|
|
101
|
+
action { [:QUERY, text] }
|
|
102
|
+
|
|
103
|
+
when (text = @ss.scan(/\*/))
|
|
104
|
+
action { [:STAR, text] }
|
|
105
|
+
|
|
106
|
+
when (text = @ss.scan(/\#\#/))
|
|
107
|
+
action { [:DOUBLE_HASH, text] }
|
|
108
|
+
|
|
109
|
+
when (text = @ss.scan(/\$\{/))
|
|
110
|
+
action { [:CONST_BEGIN, text] }
|
|
111
|
+
|
|
112
|
+
when (text = @ss.scan(/-?\d\.\d+/))
|
|
113
|
+
action { [:FLOAT, text] } # Must go before FIXNUM
|
|
114
|
+
|
|
115
|
+
when (text = @ss.scan(/-?(\d)+/))
|
|
116
|
+
action { [:FIXNUM, text] }
|
|
117
|
+
|
|
118
|
+
when (text = @ss.scan(/(\w|\:\:)+/))
|
|
119
|
+
action { [:ID, text] }
|
|
120
|
+
|
|
121
|
+
when (text = @ss.scan(/:\w+/))
|
|
122
|
+
action { [:SYMBOL, text[1..-1]] }
|
|
123
|
+
|
|
124
|
+
when (text = @ss.scan(/\:/))
|
|
125
|
+
action { [:COLON, text] } # Must come after SYMBOL
|
|
126
|
+
|
|
127
|
+
when (text = @ss.scan(/%\w+/))
|
|
128
|
+
action { [:SPECIAL_ID, text] }
|
|
129
|
+
|
|
130
|
+
when (text = @ss.scan(/'[^']*'/))
|
|
131
|
+
action { [:STRING, text.gsub("'", "")] }
|
|
132
|
+
|
|
133
|
+
when (text = @ss.scan(/"[^"]*"/))
|
|
134
|
+
action { [:STRING, text.gsub('"', "")] }
|
|
135
|
+
|
|
136
|
+
else
|
|
137
|
+
text = @ss.string[@ss.pos .. -1]
|
|
138
|
+
raise ScanError, "can not match: '" + text + "'"
|
|
139
|
+
end # if
|
|
140
|
+
|
|
141
|
+
else
|
|
142
|
+
raise ScanError, "undefined state: '" + state.to_s + "'"
|
|
143
|
+
end # case state
|
|
144
|
+
token
|
|
145
|
+
end # def _next_token
|
|
146
|
+
|
|
147
|
+
end # class
|
|
148
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
require_relative 'type'
|
|
2
|
+
|
|
3
|
+
module RDL::Type
|
|
4
|
+
|
|
5
|
+
# A type representing some method or block. MethodType has subcomponent
|
|
6
|
+
# types for arguments (zero or more), block (optional) and return value
|
|
7
|
+
# (exactly one).
|
|
8
|
+
class MethodType < Type
|
|
9
|
+
attr_reader :args
|
|
10
|
+
attr_reader :block
|
|
11
|
+
attr_reader :ret
|
|
12
|
+
|
|
13
|
+
@@contract_cache = {}
|
|
14
|
+
|
|
15
|
+
# Create a new MethodType
|
|
16
|
+
#
|
|
17
|
+
# [+args+] List of types of the arguments of the procedure (use [] for no args).
|
|
18
|
+
# [+block+] The type of the block passed to this method, if it takes one.
|
|
19
|
+
# [+ret+] The type that the procedure returns.
|
|
20
|
+
def initialize(args, block, ret)
|
|
21
|
+
# First check argument types have form (any number of required
|
|
22
|
+
# or optional args, at most one vararg, any number of named arguments)
|
|
23
|
+
state = :required
|
|
24
|
+
args.each { |arg|
|
|
25
|
+
arg = arg.type if arg.instance_of? RDL::Type::AnnotatedArgType
|
|
26
|
+
case arg
|
|
27
|
+
when OptionalType
|
|
28
|
+
raise "Optional arguments not allowed after varargs" if state == :vararg
|
|
29
|
+
raise "Optional arguments not allowed after named arguments" if state == :hash
|
|
30
|
+
state = :optional
|
|
31
|
+
when VarargType
|
|
32
|
+
raise "Multiple varargs not allowed" if state == :vararg
|
|
33
|
+
raise "Varargs not allowed after named arguments" if state == :hash
|
|
34
|
+
state = :vararg
|
|
35
|
+
when FiniteHashType
|
|
36
|
+
raise "Only one set of named arguments allowed" if state == :hash
|
|
37
|
+
state = :hash
|
|
38
|
+
else
|
|
39
|
+
raise "Attempt to create method type with non-type arg" unless arg.is_a? Type
|
|
40
|
+
raise "Required arguments not allowed after varargs" if state == :vararg
|
|
41
|
+
raise "Required arguments not allowed after named arguments" if state == :hash
|
|
42
|
+
end
|
|
43
|
+
}
|
|
44
|
+
@args = *args
|
|
45
|
+
|
|
46
|
+
raise "Block must be MethodType" unless (not block) or (block.instance_of? MethodType)
|
|
47
|
+
@block = block
|
|
48
|
+
|
|
49
|
+
raise "Attempt to create method type with non-type ret" unless ret.is_a? Type
|
|
50
|
+
@ret = ret
|
|
51
|
+
|
|
52
|
+
super()
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def le(other, h={})
|
|
56
|
+
raise RuntimeError, "should not be called"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# TODO: Check blk
|
|
60
|
+
def pre_cond?(inst, *args, &blk)
|
|
61
|
+
states = [[0, 0]] # [position in @arg, position in args]
|
|
62
|
+
until states.empty?
|
|
63
|
+
formal, actual = states.pop
|
|
64
|
+
return true if formal == @args.size && actual == args.size # Matched all actuals, no formals left over
|
|
65
|
+
next if formal >= @args.size # Too many actuals to match
|
|
66
|
+
t = @args[formal]
|
|
67
|
+
t = t.type if t.instance_of? AnnotatedArgType
|
|
68
|
+
case t
|
|
69
|
+
when OptionalType
|
|
70
|
+
t = t.type.instantiate(inst)
|
|
71
|
+
if actual == args.size
|
|
72
|
+
states << [formal+1, actual] # skip to allow extra formal optionals at end
|
|
73
|
+
elsif t.member?(args[actual], vars_wild: true)
|
|
74
|
+
states << [formal+1, actual+1] # match
|
|
75
|
+
states << [formal+1, actual] # skip
|
|
76
|
+
else
|
|
77
|
+
states << [formal+1, actual] # type doesn't match; must skip this formal
|
|
78
|
+
end
|
|
79
|
+
when VarargType
|
|
80
|
+
t = t.type.instantiate(inst)
|
|
81
|
+
if actual == args.size
|
|
82
|
+
states << [formal+1, actual] # skip to allow empty vararg at end
|
|
83
|
+
elsif t.member?(args[actual], vars_wild: true)
|
|
84
|
+
states << [formal, actual+1] # match, more varargs coming
|
|
85
|
+
states << [formal+1, actual+1] # match, no more varargs
|
|
86
|
+
# states << [formal+1, actual] # skip - can't happen, varargs have to be at end
|
|
87
|
+
else
|
|
88
|
+
states << [formal+1, actual] # skip
|
|
89
|
+
end
|
|
90
|
+
else
|
|
91
|
+
t = t.instantiate(inst)
|
|
92
|
+
the_actual = nil
|
|
93
|
+
if actual == args.size
|
|
94
|
+
next unless t.instance_of? FiniteHashType
|
|
95
|
+
if t.member?({}, vars_wild: true) # try matching against the empty hash
|
|
96
|
+
states << [formal+1, actual]
|
|
97
|
+
end
|
|
98
|
+
elsif t.member?(args[actual], vars_wild: true)
|
|
99
|
+
states << [formal+1, actual+1] # match
|
|
100
|
+
# no else case; if there is no match, this is a dead end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
return false
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def post_cond?(inst, ret, *args)
|
|
108
|
+
method_name = method_name ? method_name + ": " : ""
|
|
109
|
+
return @ret.instantiate(inst).member?(ret, vars_wild: true)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def to_contract(inst: nil)
|
|
113
|
+
c = @@contract_cache[self]
|
|
114
|
+
return c if c
|
|
115
|
+
|
|
116
|
+
# slf.ret, slf.args are the formals
|
|
117
|
+
# ret, args are the actuals
|
|
118
|
+
slf = self # Bind self so it's captured in a closure, since contracts are executed
|
|
119
|
+
# with self bound to the receiver method's self
|
|
120
|
+
prec = RDL::Contract::FlatContract.new { |*args, &blk|
|
|
121
|
+
raise TypeError, "Arguments #{args} do not match argument types #{slf}" unless slf.pre_cond?(inst, *args, &blk)
|
|
122
|
+
true
|
|
123
|
+
}
|
|
124
|
+
postc = RDL::Contract::FlatContract.new { |ret, *args|
|
|
125
|
+
raise TypeError, "Return #{ret} does not match return type #{slf}" unless slf.post_cond?(inst, ret, *args)
|
|
126
|
+
true
|
|
127
|
+
}
|
|
128
|
+
c = RDL::Contract::ProcContract.new(pre_cond: prec, post_cond: postc)
|
|
129
|
+
return (@@contract_cache[self] = c) # assignment evaluates to c
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# [+types+] is an array of method types. Checks that [+args+] and
|
|
133
|
+
# [+blk+] match at least one arm of the intersection type;
|
|
134
|
+
# otherwise raises exception. Returns array of method types that
|
|
135
|
+
# matched [+args+] and [+blk+]
|
|
136
|
+
def self.check_arg_types(method_name, types, inst, *args, &blk)
|
|
137
|
+
$__rdl_contract_switch.off {
|
|
138
|
+
matches = [] # types that matched args
|
|
139
|
+
types.each_with_index { |t, i| matches << i if t.pre_cond?(inst, *args, &blk) }
|
|
140
|
+
return matches if matches.size > 0
|
|
141
|
+
method_name = method_name ? method_name + ": " : ""
|
|
142
|
+
raise TypeError, <<RUBY
|
|
143
|
+
#{method_name}Argument type error.
|
|
144
|
+
Method type:
|
|
145
|
+
#{ types.map { |t| " " + t.to_s }.join("\n") }
|
|
146
|
+
Actual argument type#{args.size > 1 ? "s" : ""}:
|
|
147
|
+
(#{args.map { |arg| RDL::Util.rdl_type_or_class(arg) }.join(', ')}) #{if blk then blk.to_s end}
|
|
148
|
+
Actual argument values (one per line):
|
|
149
|
+
#{ args.map { |arg| " " + arg.inspect }.join("\n") }
|
|
150
|
+
RUBY
|
|
151
|
+
}
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.check_ret_types(method_name, types, inst, matches, ret, *args, &blk)
|
|
155
|
+
$__rdl_contract_switch.off {
|
|
156
|
+
matches.each { |i| return true if types[i].post_cond?(inst, ret, *args) }
|
|
157
|
+
method_name = method_name ? method_name + ": " : ""
|
|
158
|
+
raise TypeError, <<RUBY
|
|
159
|
+
#{method_name}Return type error. *'s indicate argument lists that matched.
|
|
160
|
+
Method type:
|
|
161
|
+
#{types.each_with_index.map { |t,i| " " + (matches.member?(i) ? "*" : " ") + t.to_s }.join("\n") }
|
|
162
|
+
Actual return type:
|
|
163
|
+
#{ RDL::Util.rdl_type_or_class(ret)}
|
|
164
|
+
Actual return value:
|
|
165
|
+
#{ ret.inspect }
|
|
166
|
+
RUBY
|
|
167
|
+
}
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def to_s # :nodoc:
|
|
171
|
+
if @block
|
|
172
|
+
return "(#{@args.map { |arg| arg.to_s }.join(', ')}) {#{@block.to_s}} -> #{@ret.to_s}"
|
|
173
|
+
elsif @args
|
|
174
|
+
return "(#{@args.map { |arg| arg.to_s }.join(', ')}) -> #{@ret.to_s}"
|
|
175
|
+
else
|
|
176
|
+
return "() -> #{@ret.to_s}"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def <=(other)
|
|
181
|
+
return false unless other.instance_of? MethodType # only comparable to method types
|
|
182
|
+
return false unless other.args.size == @args.size
|
|
183
|
+
return false unless @args.zip(other.args).all? { |left, right| right <= left } # contravariance
|
|
184
|
+
return false unless @ret <= other.ret # covariance
|
|
185
|
+
if @block && other.block
|
|
186
|
+
return (other.block <= @block) # contravariance
|
|
187
|
+
elsif @block.nil? && other.block.nil?
|
|
188
|
+
return true
|
|
189
|
+
else
|
|
190
|
+
return false # one has a block and the other doesn't
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def instantiate(inst)
|
|
195
|
+
return MethodType.new(@args.map { |arg| arg.instantiate(inst) },
|
|
196
|
+
@block ? @block.instantiate(inst) : nil,
|
|
197
|
+
@ret.instantiate(inst))
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def eql?(other)
|
|
201
|
+
self == other
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Return +true+ if +other+ is the same type
|
|
205
|
+
def ==(other)
|
|
206
|
+
return (other.instance_of? MethodType) &&
|
|
207
|
+
(other.args == @args) &&
|
|
208
|
+
(other.block == @block) &&
|
|
209
|
+
(other.ret == @ret)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def hash # :nodoc:
|
|
213
|
+
h = (37 + @ret.hash) * 41 + @args.hash
|
|
214
|
+
h = h * 31 + @block.hash if @block
|
|
215
|
+
return h
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|