rdl 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|