rdl 1.0.0 → 1.0.1.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 55d12c6429608dd736e3eaff00d538a23f6cddab
4
- data.tar.gz: 809baeb4d773badba520c4ea8c02aec96abb54d3
3
+ metadata.gz: 8c8ba5d3f1fe3944a7eadae9366c10428470a623
4
+ data.tar.gz: d87c55ae1f8877a6cb14fafff20a38b3cc7f3137
5
5
  SHA512:
6
- metadata.gz: a9169c5a1bd28c8f2acb6efd1249ec208f1ecbe5da6bc899ec6a9709eccde9d6034feda29c8582340fccc063fb226e95826d7d6d1cd5f0882b15112a9f92364c
7
- data.tar.gz: 282bbeb9bf69c4e6d07fbf7acf5d715320dbbc9470f885f03e629e1485c74780171782deaa712859a9519caece26392b9784acebe67abb001924271f7ecf826a
6
+ metadata.gz: f31d49ff2763c501dff6093de526d6770624bae0f0e9d6c147903ade326fc8e8fecde916c7a7156f71ab218948a55334426779dfd9dc570126a2ad24a7fd7d1c
7
+ data.tar.gz: f81fbff3ac6b8348233c9a49471d99fcbd2885dbbf415a0454580109a2f24dd972944dc4f0a235ebee08b435334c25436b6932040baf3b86bd371256932aeb19
data/.travis.yml CHANGED
@@ -16,4 +16,5 @@ rvm:
16
16
  - 2.2.2
17
17
  - 2.2.3
18
18
  - 2.2.4
19
+ - 2.3.0
19
20
  sudo: false
data/README.md CHANGED
@@ -30,23 +30,28 @@ def m(x,y) ... end
30
30
 
31
31
  This indicates that `m` is that method that returns a `String` if given two `Fixnum` arguments. Again this contract is enforced at run-time: When `m` is called, RDL checks that `m` is given exactly two arguments and both are `Fixnums`, and that `m` returns an instance of `String`. RDL supports many more complex type annotations; see below for a complete discussion and examples. We should emphasize here that RDL types are enforced as contracts at method entry and exit. There is no static checking that the method body conforms to the types.
32
32
 
33
- RDL contracts and types are stored in memory at run time, so it's also possible for programs to query them. RDL includes lots of contracts and types for the core and standard libraries. Since those methods are generally trustworthy, RDL doesn't actually enforce the contracts (since that would add overhead), but they are available to search and query. For example:
33
+ RDL contracts and types are stored in memory at run time, so it's also possible for programs to query them. RDL includes lots of contracts and types for the core and standard libraries. Since those methods are generally trustworthy, RDL doesn't actually enforce the contracts (since that would add overhead), but they are available to search and query. RDL includes a small script `rdl_query` to look up type information from the command line. Note you might need to put the argument in quotes depending on your shell.
34
34
 
35
35
  ```
36
+ $ rdl_query String#include? # print type for instance method of another class
37
+ $ rdl_query Pathname.glob # print type for singleton method of a class
38
+ $ rdl_query Array # print types for all methods of a class
39
+ $ rdl_query "(Fixnum) -> Fixnum" # print all methods that take a Fixnum and return a Fixnum
40
+ $ rdl_query "(.) -> Fixnum" # print all methods that take a single arg of any type
41
+ $ rdl_query "(..., Fixnum, ...) -> ." # print all methods that take a Fixnum as some argument
42
+
43
+ ```
44
+
45
+ See below for more details of the query format. The `rdl_query` method performs the same function as long as the gem is loaded, so you can use this in `irb`.
46
+
47
+ ```
48
+ $ irb
36
49
  > require 'rdl'
37
50
  => true
38
51
  > require 'rdl_types'
39
52
  => true
40
53
 
41
- > rdl_query 'hash' # get type for instance method of current class
42
- Object#hash: () -> Fixnum
43
- => nil
44
- > rdl_query 'String#include?' # get type for instance method of another class
45
- String#include?: (String) -> FalseClass or TrueClass
46
- => nil
47
- > rdl_query 'Pathname.glob' # get type for singleton method of a class
48
- Pathname.glob: (String p1, ?String p2) -> Array<Pathname>
49
- => nil
54
+ > rdl_query '...' # as above
50
55
  ```
51
56
 
52
57
  Currently only type information is returned by `rdl_query` (and not other pre or postconditions).
@@ -55,7 +60,7 @@ Currently only type information is returned by `rdl_query` (and not other pre or
55
60
 
56
61
  ## Supported versions of Ruby
57
62
 
58
- RDL currently supports Ruby 2.2. It may or may not work with other versions.
63
+ RDL currently supports Ruby 2.x. It may or may not work with other versions.
59
64
 
60
65
  ## Installing RDL
61
66
 
@@ -67,7 +72,7 @@ Use `require 'rdl'` to load the RDL library. If you want to use the core and sta
67
72
 
68
73
  * 2.x
69
74
 
70
- (Currently all these are assume to have the same library type signatures, which may not be correct.)
75
+ (Currently all these are assumed to have the same library type signatures, which may not be correct.)
71
76
 
72
77
  If you're using Ruby on Rails, you can similarly `require 'rails_types'` to load in type annotations for the current `Rails::VERSION::STRING`. More specifically, add the following lines in `application.rb` after the `Bundler.require` call. (This placement is needed so the Rails version string is available and the Rails environment is loaded):
73
78
 
@@ -418,6 +423,58 @@ RDL also includes a few other useful methods:
418
423
 
419
424
  * `rdl_nowrap`, if called at the top-level of a class, tells RDL to record contracts and types for methods in that class but *not* enforce them. This is mostly used for the core and standard libraries, which have trustworthy behavior hence enforcing their types and contracts is not worth the overhead.
420
425
 
426
+ * `rdl_query` prints information about types; see below for details.
427
+
428
+ ## Queries
429
+
430
+ As discussed above, RDL includes a small script, `rdl_query`, to look up type information. (Currently it does not support other pre- and postconditions.) The script takes a single argument, which should be a string. Note that when using the shell script, you may need to use quotes depending on your shell. Currently several queries are supported:
431
+
432
+ * Instance methods can be looked up as `Class#method`.
433
+
434
+ ```
435
+ $ rdl_query String#include?
436
+ String#include?: (String) -> TrueClass or FalseClass
437
+ ```
438
+
439
+ * Singleton (class) methods can be looked up as `Class.method`.
440
+
441
+ ```
442
+ $ rdl_query Pathname.glob
443
+ Pathname.glob: (String p1, ?String p2) -> Array<Pathname>
444
+ ```
445
+
446
+ * All methods of a class can be listed by passing the class name `Class`.
447
+
448
+ ```
449
+ $ rdl_query Array
450
+ &: (Array<u>) -> Array<t>
451
+ *: (String) -> String
452
+ ... and a lot more
453
+ ```
454
+
455
+ * Methods can also be search for by their type signature:
456
+
457
+ ```
458
+ $ rdl_query "(Fixnum) -> Fixnum" # print all methods of type (Fixnum) -> Fixnum
459
+ BigDecimal.limit: (Fixnum) -> Fixnum
460
+ Dir#pos=: (Fixnum) -> Fixnum
461
+ ... and a lot more
462
+ ```
463
+
464
+ The type signature uses the standard RDL syntax, with two extensions: `.` can be used as a wildcard to match any type, and `...` can be used to match any sequence of arguments.
465
+
466
+ ```
467
+ $ rdl_query "(.) -> ." # methods that take one argument and return anything
468
+ $ rdl_query "(Fixnum, .) -> ." # methods that take two arguments, the first of which is a Fixnum
469
+ $ rdl_query "(Fixnum, ...) -> ." # methods whose first argument is a Fixnum
470
+ $ rdl_query "(..., Fixnum) -> ." # methods whose last argument is a Fixnum
471
+ $ rdl_query "(..., Fixnum, ...) -> ." # methods that take a Fixnum somewhere
472
+ $ rdl_query "(Fixnum or .) -> ." # methods that take a single argument that is a union containing a Fixnum
473
+ $ rdl_query "(.?) -> ." # methods that take one, optional argument
474
+ ```
475
+
476
+ Note that aside from `.` and `...`, the matching is exact. For example `(Fixnum) -> Fixnum` will not match a method of type `(Fixnum or String) -> Fixnum`.
477
+
421
478
  # Bibliography
422
479
 
423
480
  Here are some research papers we have written exploring contracts, types, and Ruby.
@@ -509,3 +566,9 @@ Copyright (c) 2014-2015, University of Maryland, College Park. All rights reserv
509
566
  * Better query facility (more kinds of searches). Contract queries?
510
567
 
511
568
  * Write documentation on: Raw Contracts and Types, RDL Configuration, Code Overview
569
+
570
+ * Structural type queries, allow name to be unknown; same with finite hash keys, same with generic base types?
571
+
572
+ * Allow ... in named args list in queries
573
+
574
+ * Queries, include more regexp operators aside from . and ...
data/bin/rdl_query ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/rdl.rb' # Fix for release!
4
+ require_relative '../lib/rdl_types.rb' # Fix for release!
5
+
6
+ if ARGV.length != 1 then
7
+ print <<EOF
8
+ Usage: rdl_query <query>
9
+
10
+ Valid queries:
11
+
12
+ Class#method - Display type of instance method
13
+ Class.method - Display type of class (singleton) method
14
+ (method type) - Display methods that have given type
15
+
16
+ Method type queries follow the usual syntax for RDL types,
17
+ but can also include `.' to match any type, and `...' to
18
+ match any sequence of types. See the README.md file for more
19
+ detail.
20
+ EOF
21
+ exit 0
22
+ end
23
+
24
+ rdl_query(ARGV[0])
data/lib/rdl.rb CHANGED
@@ -46,6 +46,7 @@ require_rel 'rdl/types/*.rb'
46
46
  require_rel 'rdl/contracts/*.rb'
47
47
  require_rel 'rdl/util.rb'
48
48
  require_rel 'rdl/wrap.rb'
49
+ require_rel 'rdl/query.rb'
49
50
  #require_rel 'rdl/stats.rb'
50
51
 
51
52
  $__rdl_parser = RDL::Type::Parser.new
@@ -53,4 +54,4 @@ $__rdl_parser = RDL::Type::Parser.new
53
54
  # Hash from special type names to their values
54
55
  $__rdl_special_types = {'%any' => RDL::Type::TopType.new,
55
56
  '%bool' => RDL::Type::UnionType.new(RDL::Type::NominalType.new(TrueClass),
56
- RDL::Type::NominalType.new(FalseClass)) }
57
+ RDL::Type::NominalType.new(FalseClass)) }
data/lib/rdl/query.rb ADDED
@@ -0,0 +1,96 @@
1
+ class RDL::Query
2
+
3
+ # Return a pair [name, array of the types] for the method specified by q. Valid queries are:
4
+ # Class#method - instance method
5
+ # Class.method - class method
6
+ # method - method of self's class
7
+ def self.method_query(q)
8
+ klass = nil
9
+ klass_pref = nil
10
+ meth = nil
11
+ if q =~ /(.+)#(.+)/
12
+ klass = $1
13
+ klass_pref = "#{klass}#"
14
+ meth = $2.to_sym
15
+ elsif q =~ /(.+)\.(.+)/
16
+ klass_pref = "#{$1}."
17
+ klass = RDL::Util.add_singleton_marker($1)
18
+ meth = $2.to_sym
19
+ else
20
+ klass = self.class.to_s
21
+ klass_pref = "#{klass}#"
22
+ meth = q.to_sym
23
+ end
24
+ name = "#{klass_pref}#{meth}"
25
+ if RDL::Wrap.has_contracts?(klass, meth, :type)
26
+ return [name, RDL::Wrap.get_contracts(klass, meth, :type)]
27
+ else
28
+ raise "No type for #{name}"
29
+ end
30
+ end
31
+
32
+ # Return an ordered list of all method types of a class. The query should be a class name.
33
+ def self.class_query(q)
34
+ klass = q.to_s
35
+ return [] unless $__rdl_contracts.has_key? klass
36
+ cls_meths = []
37
+ cls_klass = RDL::Util.add_singleton_marker(klass)
38
+ if $__rdl_contracts.has_key? cls_klass then
39
+ $__rdl_contracts[cls_klass].each { |meth, kinds|
40
+ if kinds.has_key? :type then
41
+ kinds[:type].each { |t| cls_meths << [meth.to_s, t] }
42
+ end
43
+ }
44
+ end
45
+ inst_meths = []
46
+ if $__rdl_contracts.has_key? klass then
47
+ $__rdl_contracts[klass].each { |meth, kinds|
48
+ if kinds.has_key? :type then
49
+ kinds[:type].each { |t| inst_meths << [meth.to_s, t] }
50
+ end
51
+ }
52
+ end
53
+ cls_meths.sort! { |p1,p2| p1[0] <=> p2[0] }
54
+ cls_meths.each { |m, t| m.insert(0, "self.") }
55
+ inst_meths.sort! { |p1,p2| p1[0] <=> p2[0] }
56
+ return cls_meths + inst_meths
57
+ end
58
+
59
+ # Return . The query should be a string containing a method type query.
60
+ def self.method_type_query(q)
61
+ q = $__rdl_parser.scan_str "#Q #{q}"
62
+ $__rdl_contracts.each { |klass, meths|
63
+ meths.each { |meth, kinds|
64
+ if kinds.has_key? :type then
65
+ kinds[:type].each { |t|
66
+ if q.match(t)
67
+ puts "#{RDL::Util.pretty_name(klass, meth)}: #{t}"
68
+ end
69
+ }
70
+ end
71
+ }
72
+ }
73
+ end
74
+
75
+ end
76
+
77
+ class Object
78
+
79
+ def rdl_query(q)
80
+ $__rdl_contract_switch.off {
81
+ if q =~ /^([A-Z]\w*(#|\.))?([a-z_]\w*(!|\?|=)?|!|~|\+|\*\*|-|\*|\/|%|<<|>>|&|\||\^|<|<=|=>|>|==|===|!=|=~|!~|<=>|\[\]|\[\]=)$/
82
+ name, typs = RDL::Query.method_query(q)
83
+ typs.each { |t|
84
+ puts "#{name}: #{t}"
85
+ }
86
+ elsif q =~ /^[A-Z]\w*$/
87
+ RDL::Query.class_query(q).each { |m, t| puts "#{m}: #{t}"}
88
+ elsif q =~ /(.*)/
89
+ RDL::Query.method_type_query(q)
90
+ else
91
+ raise "Don't know how to handle query"
92
+ end
93
+ }
94
+ end
95
+
96
+ end
@@ -10,7 +10,8 @@ module RDL::Type
10
10
  def initialize(name, type)
11
11
  @name = name
12
12
  @type = type
13
- raise RuntimeError, "Attempt to create vararg type with non-type" unless type.is_a? Type
13
+ raise RuntimeError, "Attempt to create annotated type with non-type" unless type.is_a? Type
14
+ raise RuntimeError, "Attempt to create doubly annotated type" if type.is_a? AnnotatedArgType
14
15
  super()
15
16
  end
16
17
 
@@ -26,6 +27,8 @@ module RDL::Type
26
27
  return (other.instance_of? AnnotatedArgType) && (other.name == @name) && (other.type == @type)
27
28
  end
28
29
 
30
+ # doesn't have a match method - queries shouldn't have annotations in them
31
+
29
32
  def hash # :nodoc:
30
33
  return (57 + @name.hash) * @type.hash
31
34
  end
@@ -0,0 +1,26 @@
1
+ require_relative 'type_query'
2
+
3
+ module RDL::Type
4
+ class DotsQuery < TypeQuery
5
+ @@cache = nil
6
+
7
+ class << self
8
+ alias :__new__ :new
9
+ end
10
+
11
+ def self.new
12
+ @@cache = DotsQuery.__new__ unless @@cache
13
+ return @@cache
14
+ end
15
+
16
+ def to_s
17
+ "..."
18
+ end
19
+
20
+ def ==(other)
21
+ return (other.instance_of? DotsQuery)
22
+ end
23
+
24
+ # doesn't have match method---taken care of at a higher level, in MethodType#match
25
+ end
26
+ end
@@ -20,6 +20,7 @@ module RDL::Type
20
20
  return (@@cache[elts] = t) # assignment evaluates to t
21
21
  end
22
22
 
23
+ # [+elts+] is a map from keys to types
23
24
  def initialize(elts)
24
25
  elts.each { |k, t|
25
26
  raise RuntimeError, "Got #{t.inspect} where Type expected" unless t.is_a? Type
@@ -41,20 +42,27 @@ module RDL::Type
41
42
  return (other.instance_of? FiniteHashType) && (other.elts == @elts)
42
43
  end
43
44
 
45
+ def match(other)
46
+ other = other.type if other.instance_of? AnnotatedArgType
47
+ return true if other.instance_of? WildQuery
48
+ return (@elts.length == other.elts.length &&
49
+ @elts.all? { |k, v| (other.elts.has_key? k) && (v.match(other.elts[k]))})
50
+ end
51
+
44
52
  def <=(other)
45
53
  return true if other.instance_of? TopType
46
54
  return self == other
47
55
  # Subtyping with Hash not allowed
48
56
  # All positions of HashTuple are invariant since tuples are mutable
49
57
  end
50
-
58
+
51
59
  def member?(obj, *args)
52
60
  t = RDL::Util.rdl_type obj
53
61
  return t <= self if t
54
62
  rest = @elts.clone # shallow copy
55
63
 
56
64
  return false unless obj.instance_of? Hash
57
-
65
+
58
66
  # Check that every mapping in obj exists in @map and matches the type
59
67
  obj.each_pair { |k, v|
60
68
  return false unless @elts.has_key?(k)
@@ -73,7 +81,7 @@ module RDL::Type
73
81
  def instantiate(inst)
74
82
  FiniteHashType.new(Hash[@elts.map { |k, t| [k, t.instantiate(inst)] }])
75
83
  end
76
-
84
+
77
85
  def hash
78
86
  h = 229 * @elts.hash
79
87
  end
@@ -41,6 +41,12 @@ module RDL::Type
41
41
  return (other.instance_of? GenericType) && (other.base == @base) && (other.params == @params)
42
42
  end
43
43
 
44
+ def match(other)
45
+ other = other.type if other.instance_of? AnnotatedArgType
46
+ return true if other.instance_of? WildQuery
47
+ return @params.length == other.params.length && @params.zip(other.params).all? { |t,o| t.match(o) }
48
+ end
49
+
44
50
  def <=(other)
45
51
  formals, variance, check = $__rdl_type_params[base.name]
46
52
  # do check here to avoid hiding errors if generic type written
@@ -79,7 +85,7 @@ module RDL::Type
79
85
  end
80
86
  return false
81
87
  end
82
-
88
+
83
89
  def member?(obj, *args)
84
90
  raise "No type parameters defined for #{base.name}" unless $__rdl_type_params[base.name]
85
91
  formals = $__rdl_type_params[base.name][0]
@@ -92,7 +98,7 @@ module RDL::Type
92
98
  def instantiate(inst)
93
99
  GenericType.new(base, *params.map { |t| t.instantiate(inst) })
94
100
  end
95
-
101
+
96
102
  def hash
97
103
  h = (61 + @base.hash) * @params.hash
98
104
  end
@@ -42,7 +42,7 @@ module RDL::Type
42
42
  def to_s # :nodoc:
43
43
  "(#{@types.map { |t| t.to_s }.join(' and ')})"
44
44
  end
45
-
45
+
46
46
  def eql?(other)
47
47
  self == other
48
48
  end
@@ -51,14 +51,21 @@ module RDL::Type
51
51
  return (other.instance_of? IntersectionType) && (other.types == @types)
52
52
  end
53
53
 
54
+ def match(other)
55
+ other = other.type if other.instance_of? AnnotatedArgType
56
+ return true if other.instance_of? WildQuery
57
+ return false if @types.length != other.types.length
58
+ @types.all? { |t| other.types.any? { |ot| t.match(ot) } }
59
+ end
60
+
54
61
  def member?(obj, *args)
55
62
  @types.all? { |t| t.member?(obj, *args) }
56
63
  end
57
-
64
+
58
65
  def instantiate(inst)
59
66
  return IntersectionType.new(*(@types.map { |t| t.instantiate(inst) }))
60
67
  end
61
-
68
+
62
69
  def hash # :nodoc:
63
70
  47 + @types.hash
64
71
  end
@@ -24,8 +24,11 @@ rule
24
24
  , { [:COMMA, text] }
25
25
  \? { [:QUERY, text] }
26
26
  \* { [:STAR, text] }
27
- \#\# { [:DOUBLE_HASH, text] }
27
+ \#T { [:HASH_TYPE, text] }
28
+ \#Q { [:HASH_QUERY, text] }
28
29
  \$\{ { [:CONST_BEGIN, text] }
30
+ \.\.\. { [:DOTS, text] }
31
+ \. { [:DOT, text] }
29
32
  {FLOAT} { [:FLOAT, text] } # Must go before FIXNUM
30
33
  {FIXNUM} { [:FIXNUM, text] }
31
34
  {ID} { [:ID, text] }