finitio 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +40 -41
  5. data/lib/finitio/generation.rb +106 -0
  6. data/lib/finitio/generation/ad_type.rb +10 -0
  7. data/lib/finitio/generation/alias_type.rb +9 -0
  8. data/lib/finitio/generation/any_type.rb +11 -0
  9. data/lib/finitio/generation/builtin_type.rb +9 -0
  10. data/lib/finitio/generation/hash_based_type.rb +15 -0
  11. data/lib/finitio/generation/heuristic.rb +8 -0
  12. data/lib/finitio/generation/heuristic/constant.rb +30 -0
  13. data/lib/finitio/generation/heuristic/random.rb +52 -0
  14. data/lib/finitio/generation/rel_based_type.rb +13 -0
  15. data/lib/finitio/generation/seq_type.rb +13 -0
  16. data/lib/finitio/generation/set_type.rb +13 -0
  17. data/lib/finitio/generation/sub_type.rb +9 -0
  18. data/lib/finitio/generation/union_type.rb +10 -0
  19. data/lib/finitio/inference.rb +51 -0
  20. data/lib/finitio/support.rb +18 -0
  21. data/lib/finitio/support/attribute.rb +8 -0
  22. data/lib/finitio/support/compilation.rb +18 -18
  23. data/lib/finitio/support/contract.rb +8 -0
  24. data/lib/finitio/support/fetch_scope.rb +19 -0
  25. data/lib/finitio/support/heading.rb +36 -1
  26. data/lib/finitio/syntax.rb +1 -1
  27. data/lib/finitio/syntax/lexer.citrus +1 -1
  28. data/lib/finitio/syntax/type.rb +2 -0
  29. data/lib/finitio/syntax/type/high_order_type_instantiation.rb +29 -0
  30. data/lib/finitio/syntax/type/high_order_vars.rb +16 -0
  31. data/lib/finitio/syntax/type/type_def.rb +11 -1
  32. data/lib/finitio/syntax/types.citrus +14 -1
  33. data/lib/finitio/system.rb +11 -1
  34. data/lib/finitio/type.rb +19 -0
  35. data/lib/finitio/type/ad_type.rb +8 -0
  36. data/lib/finitio/type/alias_type.rb +8 -0
  37. data/lib/finitio/type/any_type.rb +12 -0
  38. data/lib/finitio/type/builtin_type.rb +4 -0
  39. data/lib/finitio/type/collection_type.rb +15 -0
  40. data/lib/finitio/type/heading_based_type.rb +17 -0
  41. data/lib/finitio/type/high_order_type.rb +39 -0
  42. data/lib/finitio/type/multi_relation_type.rb +4 -0
  43. data/lib/finitio/type/multi_tuple_type.rb +4 -0
  44. data/lib/finitio/type/proxy_type.rb +10 -20
  45. data/lib/finitio/type/relation_type.rb +4 -0
  46. data/lib/finitio/type/seq_type.rb +1 -1
  47. data/lib/finitio/type/struct_type.rb +8 -0
  48. data/lib/finitio/type/sub_type.rb +8 -0
  49. data/lib/finitio/type/tuple_type.rb +4 -0
  50. data/lib/finitio/type/union_type.rb +19 -0
  51. data/lib/finitio/version.rb +1 -1
  52. data/spec/generation/test_generation.rb +169 -0
  53. data/spec/heading/test_looks_similar.rb +45 -0
  54. data/spec/heading/test_suppremum.rb +56 -0
  55. data/spec/inference/test_inference.rb +42 -0
  56. data/spec/spec_helper.rb +31 -6
  57. data/spec/support/test_compare_attrs.rb +67 -0
  58. data/spec/syntax/test_compile.rb +57 -0
  59. data/spec/type/ad_type/test_initialize.rb +1 -8
  60. data/spec/type/relation_type/test_suppremum.rb +104 -0
  61. data/spec/type/seq_type/test_suppremum.rb +54 -0
  62. data/spec/type/set_type/test_suppremum.rb +54 -0
  63. data/spec/type/test_suppremum.rb +49 -0
  64. data/spec/type/test_unconstrained.rb +150 -0
  65. data/spec/type/tuple_type/test_suppremum.rb +119 -0
  66. data/spec/type/union_type/test_suppremum.rb +51 -0
  67. data/tasks/test.rake +1 -1
  68. metadata +183 -144
  69. data/spec/type/proxy_type/test_delegation.rb +0 -37
  70. data/spec/type/proxy_type/test_resolve.rb +0 -29
@@ -0,0 +1,9 @@
1
+ module Finitio
2
+ class SubType
3
+
4
+ def generate_data(generator, world = nil)
5
+ generator.call(super_type, world)
6
+ end
7
+
8
+ end # class SubType
9
+ end # module Finitio
@@ -0,0 +1,10 @@
1
+ module Finitio
2
+ class UnionType
3
+
4
+ def generate_data(generator, world = nil)
5
+ picked = generator.flip_one_out_of(candidates)
6
+ generator.call(picked, world)
7
+ end
8
+
9
+ end # class UnionType
10
+ end # module Finitio
@@ -0,0 +1,51 @@
1
+ module Finitio
2
+ class Inference
3
+
4
+ def initialize(system, options = {})
5
+ @system = system
6
+ @options = options
7
+ end
8
+ attr_reader :system
9
+
10
+ def call(input)
11
+ infer_type(input)
12
+ end
13
+
14
+ private
15
+
16
+ def infer_type(value)
17
+ case value
18
+ when Hash
19
+ attrs = value.map{|k,v|
20
+ Attribute.new(k.to_sym, infer_type(v))
21
+ }
22
+ heading = Heading.new(attrs)
23
+ TupleType.new(heading)
24
+ when Array
25
+ infered = value.inject(nil){|sup, value|
26
+ value_type = infer_type(value)
27
+ sup.nil? ? value_type : sup.suppremum(value_type)
28
+ }
29
+ SeqType.new(infered.nil? ? ANY_TYPE : infered)
30
+ else
31
+ found = self.system.types.values.find{|t|
32
+ try_dress(t, value)
33
+ }
34
+ found ? found : ANY_TYPE
35
+ end
36
+ end
37
+
38
+ def try_dress(t, value)
39
+ raise "Type expected, got #{t}" unless t.is_a?(Type)
40
+ t.dress(value)
41
+ true
42
+ rescue Finitio::Error => ex
43
+ #puts %Q{[#{ex.class}] #{ex.message}}
44
+ nil
45
+ rescue => ex
46
+ puts %Q{[#{ex.class}] #{ex.message}\n#{ex.backtrace.join("\n")}}
47
+ nil
48
+ end
49
+
50
+ end # class Inference
51
+ end # module Finitio
@@ -1,3 +1,20 @@
1
+ module Finitio
2
+ module Support
3
+
4
+ def compare_attrs(h1, h2, &bl)
5
+ mine, yours = if bl
6
+ [h1.map(&bl), h2.map(&bl)]
7
+ elsif h1.is_a?(Hash)
8
+ [h1.keys, h2.keys]
9
+ else
10
+ [h1, h2]
11
+ end
12
+ [ mine & yours, mine - yours, yours - mine ]
13
+ end
14
+ module_function :compare_attrs
15
+
16
+ end # module Support
17
+ end # module Finitio
1
18
  require_relative 'support/proc_with_code'
2
19
  require_relative 'support/metadata'
3
20
  require_relative 'support/attribute'
@@ -6,4 +23,5 @@ require_relative 'support/contract'
6
23
  require_relative 'support/heading'
7
24
  require_relative 'support/dress_helper'
8
25
  require_relative 'support/type_factory'
26
+ require_relative 'support/fetch_scope'
9
27
  require_relative 'support/compilation'
@@ -55,5 +55,13 @@ module Finitio
55
55
  name.hash ^ type.hash ^ required.hash
56
56
  end
57
57
 
58
+ def resolve_proxies(system)
59
+ Attribute.new(name, type.resolve_proxies(system), required, metadata)
60
+ end
61
+
62
+ def unconstrained
63
+ Attribute.new(name, type.unconstrained, required, metadata)
64
+ end
65
+
58
66
  end # class Attribute
59
67
  end # module Finitio
@@ -1,19 +1,19 @@
1
1
  module Finitio
2
2
  class Compilation
3
3
 
4
- def initialize(system = System.new, factory = TypeFactory.new, source = nil)
4
+ def initialize(system = System.new, factory = TypeFactory.new, scope = nil, source = nil)
5
5
  @system = system
6
6
  @factory = factory
7
- @proxies = []
7
+ @scope = scope || FetchScope.new(system, {})
8
8
  @source = source
9
9
  end
10
- attr_reader :system, :factory, :proxies, :source
10
+ attr_reader :system, :factory, :scope, :source
11
11
 
12
12
  def self.coerce(arg, source = nil)
13
13
  case arg
14
- when NilClass then new(System.new, TypeFactory.new, source)
15
- when System then new(arg, arg.factory, source)
16
- when TypeFactory then new(System.new, arg, source)
14
+ when NilClass then new(System.new, TypeFactory.new, nil, source)
15
+ when System then new(arg, arg.factory, nil, source)
16
+ when TypeFactory then new(System.new, arg, nil, source)
17
17
  else
18
18
  raise ArgumentError, "Unable to coerce `#{arg}`"
19
19
  end
@@ -48,11 +48,8 @@ module Finitio
48
48
  Pathname.new(file)
49
49
  end
50
50
 
51
- def resolve_proxies!
52
- proxies.each do |p|
53
- p.resolve(system)
54
- end
55
- self
51
+ def resolve_proxies
52
+ system.resolve_proxies
56
53
  end
57
54
 
58
55
  # Delegation to Factory
@@ -63,17 +60,10 @@ module Finitio
63
60
  }
64
61
  end
65
62
 
66
- def proxy(*args, &bl)
67
- proxy = factory.proxy(*args, &bl)
68
- proxies << proxy
69
- proxy
70
- end
71
-
72
63
  # Delegation to System
73
64
 
74
65
  [
75
66
  :add_type,
76
- :fetch,
77
67
  :main,
78
68
  ].each do |meth|
79
69
  define_method(meth) do |*args, &bl|
@@ -81,5 +71,15 @@ module Finitio
81
71
  end
82
72
  end
83
73
 
74
+ # Delegation to FetchScope
75
+
76
+ def fetch(type_name, &bl)
77
+ scope.fetch(type_name, &bl)
78
+ end
79
+
80
+ def with_scope(overrides)
81
+ Compilation.new(system, factory, scope.with(overrides), source)
82
+ end
83
+
84
84
  end # class Compilation
85
85
  end # module Finitio
@@ -44,5 +44,13 @@ module Finitio
44
44
  end
45
45
  alias :eql? :==
46
46
 
47
+ def resolve_proxies(system)
48
+ Contract.new(infotype.resolve_proxies(system), dresser, undresser, name, metadata)
49
+ end
50
+
51
+ def unconstrained
52
+ Contract.new(infotype.unconstrained, dresser, undresser, name, metadata)
53
+ end
54
+
47
55
  end # class Contract
48
56
  end # module Finitio
@@ -0,0 +1,19 @@
1
+ module Finitio
2
+ class FetchScope
3
+
4
+ def initialize(parent, overrides)
5
+ @parent, @overrides = parent, overrides
6
+ end
7
+
8
+ def fetch(name, &bl)
9
+ @overrides.fetch(name) do
10
+ @parent.fetch(name, &bl)
11
+ end
12
+ end
13
+
14
+ def with(overrides)
15
+ FetchScope.new(self, overrides)
16
+ end
17
+
18
+ end # class FetchScope
19
+ end # module Finitio
@@ -61,6 +61,32 @@ module Finitio
61
61
  name
62
62
  end
63
63
 
64
+ def looks_similar?(other)
65
+ return self if other == self
66
+ shared, mine, yours = Support.compare_attrs(attributes, other.attributes)
67
+ shared.length >= mine.length && shared.length >= yours.length
68
+ end
69
+
70
+ def suppremum(other)
71
+ raise ArgumentError unless other.is_a?(Heading)
72
+ return self if other == self
73
+ options = { allow_extra: allow_extra? || other.allow_extra? }
74
+ shared, mine, yours = Support.compare_attrs(attributes, other.attributes)
75
+ attributes = shared.map{|attr|
76
+ a1, o1 = self[attr], other[attr]
77
+ Attribute.new(attr, a1.type.suppremum(o1.type), a1.required && o1.required)
78
+ }
79
+ attributes += mine.map{|attrname|
80
+ attr = self[attrname]
81
+ Attribute.new(attr.name, attr.type, false)
82
+ }
83
+ attributes += yours.map{|attrname|
84
+ attr = other[attrname]
85
+ Attribute.new(attr.name, attr.type, false)
86
+ }
87
+ Heading.new(attributes, options)
88
+ end
89
+
64
90
  def ==(other)
65
91
  return nil unless other.is_a?(Heading)
66
92
  attributes == other.attributes && options == other.options
@@ -73,6 +99,15 @@ module Finitio
73
99
  attr_reader :attributes, :options
74
100
  protected :attributes, :options
75
101
 
102
+ def resolve_proxies(system)
103
+ as = attributes.map{|k,a| a.resolve_proxies(system) }
104
+ Heading.new(as, options)
105
+ end
106
+
107
+ def unconstrained
108
+ Heading.new(attributes.values.map{|a| a.unconstrained }, options)
109
+ end
110
+
76
111
  private
77
112
 
78
113
  def normalize_attributes(attrs)
@@ -83,7 +118,7 @@ module Finitio
83
118
  attributes = {}
84
119
  attrs.each do |attr|
85
120
  unless attr.is_a?(Attribute)
86
- raise ArgumentError, "Enumerable[Attribute] expected"
121
+ raise ArgumentError, "Enumerable[Attribute] expected, got a `#{attr.inspect}`"
87
122
  end
88
123
  if attributes[attr.name]
89
124
  raise ArgumentError, "Attribute names must be unique"
@@ -27,7 +27,7 @@ module Finitio
27
27
  def self.compile(source, cpl = nil)
28
28
  cpl = Compilation.coerce(cpl, source)
29
29
  parse(source, root: "system").compile(cpl)
30
- cpl.resolve_proxies!.system
30
+ cpl.resolve_proxies
31
31
  end
32
32
 
33
33
  def self.compile_type(source, cpl = nil)
@@ -25,7 +25,7 @@ grammar Finitio::Syntax::Lexer
25
25
  end
26
26
 
27
27
  rule type_name
28
- /[A-Z][a-zA-Z]+(\.[A-Z][a-zA-Z]+)*/
28
+ /[A-Z][a-zA-Z]*(\.[A-Z][a-zA-Z]+)*/
29
29
  end
30
30
 
31
31
  rule builtin_type_name
@@ -19,9 +19,11 @@ require_relative 'type/relation_type'
19
19
  require_relative 'type/union_type'
20
20
  require_relative 'type/type_ref'
21
21
  require_relative 'type/ad_type'
22
+ require_relative 'type/high_order_type_instantiation'
22
23
  require_relative 'type/contract'
23
24
  require_relative 'type/inline_pair'
24
25
  require_relative 'type/external_pair'
25
26
  require_relative 'type/lambda_expr'
26
27
  require_relative 'type/metadata'
27
28
  require_relative 'type/metadata_attr'
29
+ require_relative 'type/high_order_vars'
@@ -0,0 +1,29 @@
1
+ module Finitio
2
+ module Syntax
3
+ module HighOrderTypeInstantiation
4
+ include Node
5
+
6
+ capture :high
7
+ capture :vars
8
+
9
+ def compile(system)
10
+ target = system.fetch(high.to_s){
11
+ raise Error, "No such type `#{high.to_s}`"
12
+ }
13
+ raise "#{high} is not a high order type" unless target.is_a?(HighOrderType)
14
+
15
+ subs = vars.compile(system).map{|low|
16
+ system.fetch(low.to_s) {
17
+ raise Error, "No such type `#{low.to_s}`"
18
+ }
19
+ }
20
+ target.instantiate(system, subs)
21
+ end
22
+
23
+ def to_ast
24
+ [:high_order_type_instantiation, high.to_s, lows.to_s]
25
+ end
26
+
27
+ end # module HighOrderTypeInstantiation
28
+ end # module Syntax
29
+ end # module Finitio
@@ -0,0 +1,16 @@
1
+ module Finitio
2
+ module Syntax
3
+ module HighOrderVars
4
+ include Node
5
+
6
+ def compile(system)
7
+ captures[:type_name].map{|c| c.to_s }
8
+ end
9
+
10
+ def to_ast(var_name)
11
+ [ :high_order_vars, captures[:type_name].map{|c| c.to_s } ]
12
+ end
13
+
14
+ end # module HighOrderVars
15
+ end # module Syntax
16
+ end # module Finitio
@@ -5,9 +5,19 @@ module Finitio
5
5
 
6
6
  capture :type
7
7
  capture :type_name
8
+ capture :vars
8
9
 
9
10
  def compile(system)
10
- t = type.compile(system)
11
+ if vars
12
+ vs = vars.compile(system)
13
+ overrides = Hash[vs.map{|v|
14
+ [ v.to_s, ProxyType.new(v) ]
15
+ }]
16
+ t = type.compile(system.with_scope(overrides))
17
+ t = HighOrderType.new(vs, t)
18
+ else
19
+ t = type.compile(system)
20
+ end
11
21
  n = type_name && type_name.to_str
12
22
  m = metadata
13
23
  system.add_type(t, n, m)
@@ -6,10 +6,15 @@ grammar Finitio::Syntax::Types
6
6
  include Finitio::Syntax::Literals
7
7
 
8
8
  rule type_def
9
- (metadata? type_name equal type)
9
+ (metadata? type_name ('<' vars:high_order_vars '>')? equal type)
10
10
  <Finitio::Syntax::TypeDef>
11
11
  end
12
12
 
13
+ rule high_order_vars
14
+ (type_name (comma type_name)*)
15
+ <Finitio::Syntax::HighOrderVars>
16
+ end
17
+
13
18
  rule main_type
14
19
  (metadata? type)
15
20
  <Finitio::Syntax::MainType>
@@ -58,6 +63,7 @@ grammar Finitio::Syntax::Types
58
63
  rule rel_type
59
64
  relation_type
60
65
  | tuple_type
66
+ | high_order_type_instantiation
61
67
  | collection_type
62
68
  end
63
69
 
@@ -110,6 +116,13 @@ grammar Finitio::Syntax::Types
110
116
  <Finitio::Syntax::StructType>
111
117
  end
112
118
 
119
+ # high-order types
120
+
121
+ rule high_order_type_instantiation
122
+ (high:type_name '<' vars:high_order_vars '>')
123
+ <Finitio::Syntax::HighOrderTypeInstantiation>
124
+ end
125
+
113
126
  # terminal forms
114
127
 
115
128
  rule term_type
@@ -10,7 +10,7 @@ module Finitio
10
10
  end
11
11
 
12
12
  attr_reader :types, :imports
13
- private :types, :imports
13
+ private :imports
14
14
 
15
15
  def add_import(system)
16
16
  @imports << system
@@ -89,6 +89,16 @@ module Finitio
89
89
  Syntax.compile(source, self.dup)
90
90
  end
91
91
 
92
+ def resolve_proxies(recurse = true)
93
+ rebuilt = {}
94
+ scope = FetchScope.new(self, rebuilt)
95
+ types.each_with_object(rebuilt) do |(name,type),memo|
96
+ rebuilt[name] = type.resolve_proxies(scope)
97
+ end
98
+ resolved = System.new(rebuilt, imports)
99
+ recurse ? resolved.resolve_proxies(false) : resolved
100
+ end
101
+
92
102
  def inspect
93
103
  @types.each_pair.map{|k,v| "#{k} = #{v}" }.join("\n")
94
104
  end