finitio 0.7.0.pre.rc2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +29 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +40 -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 +19 -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 +23 -0
  38. data/lib/finitio/support/fetch_scope.rb +19 -0
  39. data/lib/finitio/support/heading.rb +36 -1
  40. data/lib/finitio/support/proc_with_code.rb +34 -0
  41. data/lib/finitio/syntax.rb +1 -1
  42. data/lib/finitio/syntax/import.rb +1 -1
  43. data/lib/finitio/syntax/lexer.citrus +1 -1
  44. data/lib/finitio/syntax/type.rb +2 -0
  45. data/lib/finitio/syntax/type/expression.rb +1 -2
  46. data/lib/finitio/syntax/type/high_order_type_instantiation.rb +29 -0
  47. data/lib/finitio/syntax/type/high_order_vars.rb +16 -0
  48. data/lib/finitio/syntax/type/type_def.rb +11 -1
  49. data/lib/finitio/syntax/types.citrus +14 -1
  50. data/lib/finitio/system.rb +20 -4
  51. data/lib/finitio/type.rb +19 -0
  52. data/lib/finitio/type/ad_type.rb +21 -0
  53. data/lib/finitio/type/alias_type.rb +8 -0
  54. data/lib/finitio/type/any_type.rb +12 -0
  55. data/lib/finitio/type/builtin_type.rb +4 -0
  56. data/lib/finitio/type/collection_type.rb +15 -0
  57. data/lib/finitio/type/heading_based_type.rb +17 -0
  58. data/lib/finitio/type/high_order_type.rb +39 -0
  59. data/lib/finitio/type/multi_relation_type.rb +4 -0
  60. data/lib/finitio/type/multi_tuple_type.rb +4 -0
  61. data/lib/finitio/type/proxy_type.rb +10 -20
  62. data/lib/finitio/type/relation_type.rb +4 -0
  63. data/lib/finitio/type/seq_type.rb +1 -1
  64. data/lib/finitio/type/struct_type.rb +8 -0
  65. data/lib/finitio/type/sub_type.rb +8 -0
  66. data/lib/finitio/type/tuple_type.rb +4 -0
  67. data/lib/finitio/type/union_type.rb +19 -0
  68. data/lib/finitio/version.rb +2 -2
  69. data/spec/finitio/test_stdlib_memoization.rb +22 -0
  70. data/spec/finitio/test_system.rb +0 -8
  71. data/spec/generation/test_generation.rb +169 -0
  72. data/spec/heading/test_looks_similar.rb +45 -0
  73. data/spec/heading/test_suppremum.rb +56 -0
  74. data/spec/inference/test_inference.rb +42 -0
  75. data/spec/json_schema/test_ad_type.rb +20 -0
  76. data/spec/json_schema/test_alias_type.rb +15 -0
  77. data/spec/json_schema/test_any_type.rb +11 -0
  78. data/spec/json_schema/test_builtin_type.rb +51 -0
  79. data/spec/json_schema/test_multi_relation_type.rb +58 -0
  80. data/spec/json_schema/test_multi_tuple_type.rb +50 -0
  81. data/spec/json_schema/test_relation_type.rb +30 -0
  82. data/spec/json_schema/test_seq_type.rb +18 -0
  83. data/spec/json_schema/test_set_type.rb +19 -0
  84. data/spec/json_schema/test_struct_type.rb +18 -0
  85. data/spec/json_schema/test_sub_type.rb +17 -0
  86. data/spec/json_schema/test_tuple_type.rb +26 -0
  87. data/spec/json_schema/test_union_type.rb +17 -0
  88. data/spec/spec_helper.rb +32 -6
  89. data/spec/support/test_compare_attrs.rb +67 -0
  90. data/spec/support/test_proc_with_code.rb +27 -0
  91. data/spec/syntax/nodes/test_ad_type.rb +6 -0
  92. data/spec/syntax/nodes/test_contract.rb +5 -0
  93. data/spec/syntax/nodes/test_expression.rb +5 -0
  94. data/spec/syntax/nodes/test_sub_type.rb +5 -0
  95. data/spec/syntax/test_compile.rb +57 -0
  96. data/spec/system/fixtures/system.fio +2 -0
  97. data/spec/system/fixtures/with-duplicates.fio +6 -0
  98. data/spec/system/test_check_and_warn.rb +55 -0
  99. data/spec/type/ad_type/test_initialize.rb +1 -8
  100. data/spec/type/relation_type/test_suppremum.rb +104 -0
  101. data/spec/type/seq_type/test_suppremum.rb +54 -0
  102. data/spec/type/set_type/test_suppremum.rb +54 -0
  103. data/spec/type/test_suppremum.rb +49 -0
  104. data/spec/type/test_unconstrained.rb +150 -0
  105. data/spec/type/tuple_type/test_suppremum.rb +119 -0
  106. data/spec/type/union_type/test_suppremum.rb +51 -0
  107. data/tasks/test.rake +1 -1
  108. metadata +230 -145
  109. data/spec/finitio/with-duplicates.fio +0 -3
  110. data/spec/type/proxy_type/test_delegation.rb +0 -37
  111. 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,21 @@
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
18
+ require_relative 'support/proc_with_code'
1
19
  require_relative 'support/metadata'
2
20
  require_relative 'support/attribute'
3
21
  require_relative 'support/constraint'
@@ -5,4 +23,5 @@ require_relative 'support/contract'
5
23
  require_relative 'support/heading'
6
24
  require_relative 'support/dress_helper'
7
25
  require_relative 'support/type_factory'
26
+ require_relative 'support/fetch_scope'
8
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
@@ -29,5 +29,28 @@ module Finitio
29
29
  @undresser = clazz.instance_method(:"to_#{name}")
30
30
  end
31
31
 
32
+ def hash
33
+ infotype.hash ^ dresser.hash ^ undresser.hash
34
+ end
35
+
36
+ def ==(other)
37
+ super || (
38
+ other.is_a?(Contract) &&
39
+ name == other.name &&
40
+ infotype == other.infotype &&
41
+ dresser == other.dresser &&
42
+ undresser == other.undresser
43
+ )
44
+ end
45
+ alias :eql? :==
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
+
32
55
  end # class Contract
33
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"
@@ -0,0 +1,34 @@
1
+ module Finitio
2
+ class ProcWithCode < Proc
3
+
4
+ def self.new(*args, &bl)
5
+ return Proc.new(*args, &bl) unless args.size == 2 && bl.nil?
6
+
7
+ p = Kernel.eval("->(#{args.first}){ #{args.last} }")
8
+ p.instance_variable_set(:@_finitio_src, args)
9
+
10
+ def p.finitio_src
11
+ @_finitio_src
12
+ end
13
+
14
+ def p.hash
15
+ @_finitio_src.hash
16
+ end
17
+
18
+ def p.==(other)
19
+ super || (
20
+ other.is_a?(Proc) &&
21
+ other.respond_to?(:finitio_src) &&
22
+ other.finitio_src == self.finitio_src
23
+ )
24
+ end
25
+
26
+ def p.eql?(other)
27
+ self.==(other)
28
+ end
29
+
30
+ p
31
+ end
32
+
33
+ end # module ProcWithCode
34
+ end # module Finitio
@@ -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'
@@ -4,8 +4,7 @@ module Finitio
4
4
  include Node
5
5
 
6
6
  def compile(var_name)
7
- expr = "->(#{var_name}){ #{to_str} }"
8
- ::Kernel.eval(expr)
7
+ ProcWithCode.new(var_name, to_str)
9
8
  end
10
9
 
11
10
  end # module Expression
@@ -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