finitio 0.7.0.pre.rc3 → 0.9.1

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 (104) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +34 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +41 -41
  5. data/README.md +88 -12
  6. data/lib/finitio.rb +37 -5
  7. data/lib/finitio/generation.rb +106 -0
  8. data/lib/finitio/generation/ad_type.rb +10 -0
  9. data/lib/finitio/generation/alias_type.rb +9 -0
  10. data/lib/finitio/generation/any_type.rb +11 -0
  11. data/lib/finitio/generation/builtin_type.rb +9 -0
  12. data/lib/finitio/generation/hash_based_type.rb +15 -0
  13. data/lib/finitio/generation/heuristic.rb +8 -0
  14. data/lib/finitio/generation/heuristic/constant.rb +30 -0
  15. data/lib/finitio/generation/heuristic/random.rb +52 -0
  16. data/lib/finitio/generation/rel_based_type.rb +13 -0
  17. data/lib/finitio/generation/seq_type.rb +13 -0
  18. data/lib/finitio/generation/set_type.rb +13 -0
  19. data/lib/finitio/generation/sub_type.rb +9 -0
  20. data/lib/finitio/generation/union_type.rb +10 -0
  21. data/lib/finitio/inference.rb +51 -0
  22. data/lib/finitio/json_schema.rb +16 -0
  23. data/lib/finitio/json_schema/ad_type.rb +11 -0
  24. data/lib/finitio/json_schema/alias_type.rb +9 -0
  25. data/lib/finitio/json_schema/any_type.rb +9 -0
  26. data/lib/finitio/json_schema/builtin_type.rb +27 -0
  27. data/lib/finitio/json_schema/hash_based_type.rb +25 -0
  28. data/lib/finitio/json_schema/rel_based_type.rb +13 -0
  29. data/lib/finitio/json_schema/seq_type.rb +12 -0
  30. data/lib/finitio/json_schema/set_type.rb +13 -0
  31. data/lib/finitio/json_schema/struct_type.rb +12 -0
  32. data/lib/finitio/json_schema/sub_type.rb +10 -0
  33. data/lib/finitio/json_schema/union_type.rb +11 -0
  34. data/lib/finitio/support.rb +18 -0
  35. data/lib/finitio/support/attribute.rb +8 -0
  36. data/lib/finitio/support/compilation.rb +18 -18
  37. data/lib/finitio/support/contract.rb +10 -2
  38. data/lib/finitio/support/fetch_scope.rb +19 -0
  39. data/lib/finitio/support/heading.rb +42 -1
  40. data/lib/finitio/syntax.rb +1 -1
  41. data/lib/finitio/syntax/import.rb +1 -1
  42. data/lib/finitio/syntax/lexer.citrus +1 -1
  43. data/lib/finitio/syntax/type.rb +2 -0
  44. data/lib/finitio/syntax/type/high_order_type_instantiation.rb +29 -0
  45. data/lib/finitio/syntax/type/high_order_vars.rb +16 -0
  46. data/lib/finitio/syntax/type/type_def.rb +11 -1
  47. data/lib/finitio/syntax/types.citrus +14 -1
  48. data/lib/finitio/system.rb +20 -4
  49. data/lib/finitio/type.rb +19 -0
  50. data/lib/finitio/type/ad_type.rb +9 -1
  51. data/lib/finitio/type/alias_type.rb +8 -0
  52. data/lib/finitio/type/any_type.rb +12 -0
  53. data/lib/finitio/type/builtin_type.rb +4 -0
  54. data/lib/finitio/type/collection_type.rb +15 -0
  55. data/lib/finitio/type/heading_based_type.rb +17 -0
  56. data/lib/finitio/type/high_order_type.rb +39 -0
  57. data/lib/finitio/type/multi_relation_type.rb +4 -0
  58. data/lib/finitio/type/multi_tuple_type.rb +4 -0
  59. data/lib/finitio/type/proxy_type.rb +10 -20
  60. data/lib/finitio/type/relation_type.rb +4 -0
  61. data/lib/finitio/type/seq_type.rb +1 -1
  62. data/lib/finitio/type/struct_type.rb +8 -0
  63. data/lib/finitio/type/sub_type.rb +8 -0
  64. data/lib/finitio/type/tuple_type.rb +4 -0
  65. data/lib/finitio/type/union_type.rb +19 -0
  66. data/lib/finitio/version.rb +2 -2
  67. data/spec/finitio/test_stdlib_memoization.rb +22 -0
  68. data/spec/finitio/test_system.rb +0 -11
  69. data/spec/generation/test_generation.rb +169 -0
  70. data/spec/heading/test_looks_similar.rb +45 -0
  71. data/spec/heading/test_suppremum.rb +56 -0
  72. data/spec/inference/test_inference.rb +42 -0
  73. data/spec/json_schema/test_ad_type.rb +20 -0
  74. data/spec/json_schema/test_alias_type.rb +15 -0
  75. data/spec/json_schema/test_any_type.rb +11 -0
  76. data/spec/json_schema/test_builtin_type.rb +51 -0
  77. data/spec/json_schema/test_multi_relation_type.rb +58 -0
  78. data/spec/json_schema/test_multi_tuple_type.rb +50 -0
  79. data/spec/json_schema/test_relation_type.rb +30 -0
  80. data/spec/json_schema/test_seq_type.rb +18 -0
  81. data/spec/json_schema/test_set_type.rb +19 -0
  82. data/spec/json_schema/test_struct_type.rb +18 -0
  83. data/spec/json_schema/test_sub_type.rb +17 -0
  84. data/spec/json_schema/test_tuple_type.rb +26 -0
  85. data/spec/json_schema/test_union_type.rb +17 -0
  86. data/spec/regression/test_heading_extra_are_proxy_resolved.rb +41 -0
  87. data/spec/spec_helper.rb +32 -6
  88. data/spec/support/test_compare_attrs.rb +67 -0
  89. data/spec/syntax/test_compile.rb +57 -0
  90. data/spec/system/fixtures/system.fio +2 -0
  91. data/spec/{finitio → system/fixtures}/with-duplicates.fio +2 -1
  92. data/spec/system/test_check_and_warn.rb +55 -0
  93. data/spec/type/ad_type/test_initialize.rb +1 -8
  94. data/spec/type/relation_type/test_suppremum.rb +104 -0
  95. data/spec/type/seq_type/test_suppremum.rb +54 -0
  96. data/spec/type/set_type/test_suppremum.rb +54 -0
  97. data/spec/type/test_suppremum.rb +49 -0
  98. data/spec/type/test_unconstrained.rb +150 -0
  99. data/spec/type/tuple_type/test_suppremum.rb +119 -0
  100. data/spec/type/union_type/test_suppremum.rb +51 -0
  101. data/tasks/test.rake +1 -1
  102. metadata +99 -15
  103. data/spec/type/proxy_type/test_delegation.rb +0 -37
  104. data/spec/type/proxy_type/test_resolve.rb +0 -29
@@ -0,0 +1,11 @@
1
+ module Finitio
2
+ class UnionType
3
+
4
+ def to_json_schema(*args, &bl)
5
+ {
6
+ anyOf: candidates.map{|c| c.to_json_schema(*args, &bl) }
7
+ }
8
+ end
9
+
10
+ end # class UnionType
11
+ 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
@@ -30,7 +30,7 @@ module Finitio
30
30
  end
31
31
 
32
32
  def hash
33
- [infotype, dresser, undresser].hash
33
+ infotype.hash ^ dresser.hash ^ undresser.hash
34
34
  end
35
35
 
36
36
  def ==(other)
@@ -38,11 +38,19 @@ module Finitio
38
38
  other.is_a?(Contract) &&
39
39
  name == other.name &&
40
40
  infotype == other.infotype &&
41
- dresser == other.dresser &&
41
+ dresser == other.dresser &&
42
42
  undresser == other.undresser
43
43
  )
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,21 @@ 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|
104
+ a.resolve_proxies(system)
105
+ }
106
+ opts = options.dup
107
+ if options[:allow_extra] && options[:allow_extra].is_a?(Type)
108
+ opts[:allow_extra] = opts[:allow_extra].resolve_proxies(system)
109
+ end
110
+ Heading.new(as, opts)
111
+ end
112
+
113
+ def unconstrained
114
+ Heading.new(attributes.values.map{|a| a.unconstrained }, options)
115
+ end
116
+
76
117
  private
77
118
 
78
119
  def normalize_attributes(attrs)
@@ -83,7 +124,7 @@ module Finitio
83
124
  attributes = {}
84
125
  attrs.each do |attr|
85
126
  unless attr.is_a?(Attribute)
86
- raise ArgumentError, "Enumerable[Attribute] expected"
127
+ raise ArgumentError, "Enumerable[Attribute] expected, got a `#{attr.inspect}`"
87
128
  end
88
129
  if attributes[attr.name]
89
130
  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)
@@ -7,7 +7,7 @@ module Finitio
7
7
 
8
8
  def compile(system)
9
9
  file = system.resolve_url(import_url)
10
- imported = Finitio.system(file)
10
+ imported = Finitio.send(:_system, file)
11
11
  system.add_import(imported)
12
12
  system
13
13
  end
@@ -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
@@ -97,15 +107,21 @@ module Finitio
97
107
  System.new(@types.dup, @imports.dup)
98
108
  end
99
109
 
100
- def check_and_warn(io = STDERR)
110
+ def check_and_warn(logger = nil)
111
+ logger ||= begin
112
+ require 'logger'
113
+ Logger.new(STDERR)
114
+ end
101
115
  each_type do |t|
102
116
  next unless t.named?
103
117
  each_import do |i|
104
118
  next unless found = i.get_type(t.name)
105
119
  if found == t
106
- STDERR.puts "WARN: duplicate type def `#{t.name}`"
120
+ logger.info "Duplicate type def `#{t.name}`"
121
+ break
107
122
  else
108
- STDERR.puts "NOTICE: Type erasure `#{t.name}`"
123
+ logger.warn "Type erasure `#{t.name}`"
124
+ break
109
125
  end
110
126
  end
111
127
  end
@@ -54,6 +54,20 @@ module Finitio
54
54
  raise NotImplementedError, "Missing #{self.class.name}#dress"
55
55
  end
56
56
 
57
+ def suppremum(other)
58
+ return self if other == self
59
+ other._suppremum(self)
60
+ end
61
+
62
+ def _suppremum(other)
63
+ UnionType.new([other, self])
64
+ end
65
+ protected :_suppremum
66
+
67
+ def unconstrained
68
+ self
69
+ end
70
+
57
71
  def to_s
58
72
  name.to_s
59
73
  end
@@ -64,6 +78,10 @@ module Finitio
64
78
  }
65
79
  end
66
80
 
81
+ def resolve_proxies(system)
82
+ raise NotImplementedError, "resolve_proxies must be overriden"
83
+ end
84
+
67
85
  protected
68
86
 
69
87
  def set_equal?(s1, s2)
@@ -94,3 +112,4 @@ require_relative 'type/multi_tuple_type'
94
112
  require_relative 'type/relation_type'
95
113
  require_relative 'type/multi_relation_type'
96
114
  require_relative 'type/ad_type'
115
+ require_relative 'type/high_order_type'