finitio 0.7.0 → 0.8.0

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