qrb 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/CHANGELOG.md +7 -0
  2. data/Gemfile.lock +1 -1
  3. data/README.md +30 -1
  4. data/lib/qrb.rb +6 -0
  5. data/lib/qrb/data_type.rb +7 -1
  6. data/lib/qrb/support/dress_helper.rb +1 -1
  7. data/lib/qrb/support/type_factory.rb +6 -0
  8. data/lib/qrb/syntax.rb +7 -0
  9. data/lib/qrb/syntax/ad_type.rb +7 -0
  10. data/lib/qrb/syntax/any_type.rb +16 -0
  11. data/lib/qrb/syntax/attribute.rb +4 -0
  12. data/lib/qrb/syntax/builtin_type.rb +4 -0
  13. data/lib/qrb/syntax/constraint_def.rb +6 -0
  14. data/lib/qrb/syntax/constraints.rb +4 -0
  15. data/lib/qrb/syntax/contract.rb +21 -9
  16. data/lib/qrb/syntax/definitions.rb +4 -0
  17. data/lib/qrb/syntax/external_pair.rb +17 -0
  18. data/lib/qrb/syntax/heading.rb +4 -0
  19. data/lib/qrb/syntax/inline_pair.rb +16 -0
  20. data/lib/qrb/syntax/lambda_expr.rb +4 -0
  21. data/lib/qrb/syntax/named_constraint.rb +6 -0
  22. data/lib/qrb/syntax/q.citrus +29 -4
  23. data/lib/qrb/syntax/q.sexp +114 -0
  24. data/lib/qrb/syntax/relation_type.rb +4 -0
  25. data/lib/qrb/syntax/seq_type.rb +4 -0
  26. data/lib/qrb/syntax/set_type.rb +4 -0
  27. data/lib/qrb/syntax/sub_type.rb +4 -0
  28. data/lib/qrb/syntax/system.rb +6 -0
  29. data/lib/qrb/syntax/tuple_type.rb +4 -0
  30. data/lib/qrb/syntax/type_def.rb +4 -0
  31. data/lib/qrb/syntax/type_ref.rb +4 -0
  32. data/lib/qrb/syntax/union_type.rb +4 -0
  33. data/lib/qrb/syntax/unnamed_constraint.rb +6 -0
  34. data/lib/qrb/type.rb +1 -0
  35. data/lib/qrb/type/ad_type.rb +8 -7
  36. data/lib/qrb/type/any_type.rb +47 -0
  37. data/lib/qrb/version.rb +1 -1
  38. data/spec/spec_helper.rb +11 -0
  39. data/spec/unit/qrb/test_ast.rb +43 -0
  40. data/spec/unit/syntax/nodes/test_ad_type.rb +72 -0
  41. data/spec/unit/syntax/nodes/test_any_type.rb +30 -0
  42. data/spec/unit/syntax/nodes/test_attribute.rb +23 -10
  43. data/spec/unit/syntax/nodes/test_builtin_type.rb +25 -13
  44. data/spec/unit/syntax/nodes/test_constraint_def.rb +14 -0
  45. data/spec/unit/syntax/nodes/test_constraints.rb +35 -0
  46. data/spec/unit/syntax/nodes/test_contract.rb +76 -4
  47. data/spec/unit/syntax/nodes/test_heading.rb +37 -20
  48. data/spec/unit/syntax/nodes/test_named_constraint.rb +12 -0
  49. data/spec/unit/syntax/nodes/test_relation_type.rb +38 -20
  50. data/spec/unit/syntax/nodes/test_seq_type.rb +23 -9
  51. data/spec/unit/syntax/nodes/test_set_type.rb +23 -9
  52. data/spec/unit/syntax/nodes/test_sub_type.rb +29 -0
  53. data/spec/unit/syntax/nodes/test_system.rb +48 -0
  54. data/spec/unit/syntax/nodes/test_tuple_type.rb +38 -20
  55. data/spec/unit/syntax/nodes/test_type_def.rb +33 -0
  56. data/spec/unit/syntax/nodes/test_type_ref.rb +37 -0
  57. data/spec/unit/syntax/nodes/test_union_type.rb +23 -8
  58. data/spec/unit/syntax/nodes/test_unnamed_constraint.rb +12 -0
  59. data/spec/unit/type/ad_type/test_default_name.rb +2 -2
  60. data/spec/unit/type/ad_type/test_dress.rb +3 -3
  61. data/spec/unit/type/ad_type/test_initialize.rb +2 -2
  62. data/spec/unit/type/any_type/test_default_name.rb +12 -0
  63. data/spec/unit/type/any_type/test_dress.rb +22 -0
  64. data/spec/unit/type/any_type/test_equality.rb +26 -0
  65. data/spec/unit/type/any_type/test_include.rb +22 -0
  66. data/spec/unit/type/any_type/test_initialize.rb +10 -0
  67. data/spec/unit/type/any_type/test_name.rb +24 -0
  68. data/spec/unit/type_factory/dsl/test_adt.rb +2 -2
  69. data/spec/unit/type_factory/dsl/test_any.rb +28 -0
  70. metadata +33 -4
@@ -0,0 +1,114 @@
1
+ ### system
2
+
3
+ system:
4
+ - [ type_def* type ]
5
+
6
+ type_def:
7
+ - [ type_name type ]
8
+
9
+ ### types
10
+
11
+ type:
12
+ - any_type
13
+ - builtin_type
14
+ - sub_type
15
+ - union_type
16
+ - set_type
17
+ - seq_type
18
+ - tuple_type
19
+ - relation_type
20
+ - ad_type
21
+ - type_ref
22
+
23
+ any_type:
24
+ - [ ]
25
+
26
+ builtin_type:
27
+ - [ ruby_module_name ]
28
+
29
+ sub_type:
30
+ - [ type, constraint+ ]
31
+
32
+ union_type:
33
+ - [ type+ ]
34
+
35
+ set_type:
36
+ - [ type ]
37
+
38
+ seq_type:
39
+ - [ type ]
40
+
41
+ tuple_type:
42
+ - [ heading ]
43
+
44
+ relation_type:
45
+ - [ heading ]
46
+
47
+ ad_type:
48
+ - [ ruby_module_name_or_nil, contract+ ]
49
+
50
+ type_ref:
51
+ - [ type_name ]
52
+
53
+ ### heading
54
+
55
+ heading:
56
+ [ attribute+ ]
57
+
58
+ attribute:
59
+ [ attribute_name, type ]
60
+
61
+ ### constraints
62
+
63
+ constraint:
64
+ - [ constraint_name, fn ]
65
+
66
+ ### contracts
67
+
68
+ contract:
69
+ - [ contract_name, type, pair? ]
70
+
71
+ pair:
72
+ - external_pair
73
+ - inline_pair
74
+
75
+ inline_pair:
76
+ - [ fn, fn ]
77
+
78
+ external_pair:
79
+ - [ ruby_module_name ]
80
+
81
+ ### functions/expressions
82
+
83
+ fn:
84
+ - [ parameters, source ]
85
+
86
+ parameters:
87
+ - [ parameter_name+ ]
88
+
89
+ source:
90
+ - [ String ]
91
+
92
+ ### names
93
+
94
+ attribute_name:
95
+ !ruby/regexp /^[a-z][a-zA-Z0-9_]*$/
96
+
97
+ ruby_module_name:
98
+ !ruby/regexp /^[a-zA-Z0-9:]+$/
99
+
100
+ ruby_module_name_or_nil:
101
+ - ruby_module_name
102
+ - ~
103
+
104
+ contract_name:
105
+ !ruby/regexp /^[a-z][a-z0-9]*$/
106
+
107
+ constraint_name:
108
+ !ruby/regexp /^[a-z][a-zA-Z_]*$/
109
+
110
+ parameter_name:
111
+ !ruby/regexp /^[a-z]+$/
112
+
113
+ type_name:
114
+ !ruby/regexp /^[A-Z][a-zA-Z]+$/
@@ -6,6 +6,10 @@ module Qrb
6
6
  factory.relation(heading.compile(factory))
7
7
  end
8
8
 
9
+ def to_ast
10
+ [:relation_type, heading.to_ast]
11
+ end
12
+
9
13
  end # module RelationType
10
14
  end # module Syntax
11
15
  end # module Qrb
@@ -7,6 +7,10 @@ module Qrb
7
7
  factory.seq(elm_type)
8
8
  end
9
9
 
10
+ def to_ast
11
+ [:seq_type, type.to_ast]
12
+ end
13
+
10
14
  end # module SeqType
11
15
  end # module Syntax
12
16
  end # module Qrb
@@ -7,6 +7,10 @@ module Qrb
7
7
  factory.set(elm_type)
8
8
  end
9
9
 
10
+ def to_ast
11
+ [:set_type, type.to_ast]
12
+ end
13
+
10
14
  end # module SetType
11
15
  end # module Syntax
12
16
  end # module Qrb
@@ -8,6 +8,10 @@ module Qrb
8
8
  factory.subtype(s, c)
9
9
  end
10
10
 
11
+ def to_ast
12
+ [:sub_type, rel_type.to_ast] + constraint_def.to_ast
13
+ end
14
+
11
15
  end # module SubType
12
16
  end # module Syntax
13
17
  end # module Qrb
@@ -10,6 +10,12 @@ module Qrb
10
10
  system
11
11
  end
12
12
 
13
+ def to_ast
14
+ ast = [ :system ] + definitions.to_ast
15
+ ast << type.to_ast if type
16
+ ast
17
+ end
18
+
13
19
  end # module System
14
20
  end # module Syntax
15
21
  end # module Qrb
@@ -6,6 +6,10 @@ module Qrb
6
6
  factory.tuple(heading.compile(factory))
7
7
  end
8
8
 
9
+ def to_ast
10
+ [:tuple_type, heading.to_ast]
11
+ end
12
+
9
13
  end # module TupleType
10
14
  end # module Syntax
11
15
  end # module Qrb
@@ -9,6 +9,10 @@ module Qrb
9
9
  t
10
10
  end
11
11
 
12
+ def to_ast
13
+ [:type_def, type_name.to_s, type.to_ast]
14
+ end
15
+
12
16
  end # module TypeDef
13
17
  end # module Syntax
14
18
  end # module Qrb
@@ -8,6 +8,10 @@ module Qrb
8
8
  end
9
9
  end
10
10
 
11
+ def to_ast
12
+ [:type_ref, type_name.to_s]
13
+ end
14
+
11
15
  end # module TypeRef
12
16
  end # module Syntax
13
17
  end # module Qrb
@@ -7,6 +7,10 @@ module Qrb
7
7
  factory.union(cds)
8
8
  end
9
9
 
10
+ def to_ast
11
+ captures[:sub_type].map(&:to_ast).unshift(:union_type)
12
+ end
13
+
10
14
  end # module UnionType
11
15
  end # module Syntax
12
16
  end # module Qrb
@@ -6,6 +6,12 @@ module Qrb
6
6
  { predicate: expression.compile(var_name) }
7
7
  end
8
8
 
9
+ def to_ast(var_name)
10
+ [ :constraint,
11
+ "default",
12
+ [:fn, [:parameters, var_name], [:source, expression.to_s.strip]] ]
13
+ end
14
+
9
15
  end # module UnnamedConstraint
10
16
  end # module Syntax
11
17
  end # module Qrb
data/lib/qrb/type.rb CHANGED
@@ -53,6 +53,7 @@ module Qrb
53
53
 
54
54
  end # class Type
55
55
  end # module Qrb
56
+ require_relative 'type/any_type'
56
57
  require_relative 'type/builtin_type'
57
58
  require_relative 'type/union_type'
58
59
  require_relative 'type/sub_type'
@@ -56,7 +56,8 @@ module Qrb
56
56
  raise ArgumentError, "Hash expected, got `#{contracts}`"
57
57
  end
58
58
  invalid = contracts.values.reject{|v|
59
- v.is_a?(Array) and v.size == 2 and v.first.is_a?(Type) and v.last.respond_to?(:call)
59
+ v.is_a?(Array) and v.size == 3 and v[0].is_a?(Type) and \
60
+ v[1].respond_to?(:call) and v[2].respond_to?(:call)
60
61
  }
61
62
  unless invalid.empty?
62
63
  raise ArgumentError, "Invalid contracts `#{invalid}`"
@@ -86,20 +87,20 @@ module Qrb
86
87
 
87
88
  # Try each contract in turn. Do nothing on TypeError as
88
89
  # the next candidate could be the good one! Return the
89
- # first successfully uped.
90
- contracts.each_pair do |name, (infotype,upper)|
90
+ # first successfully dressed.
91
+ contracts.each_pair do |name, (infotype, dresser, _)|
91
92
 
92
93
  # First make the dress transformation on the information type
93
- success, uped = handler.just_try do
94
+ success, dressed = handler.just_try do
94
95
  infotype.dress(value, handler)
95
96
  end
96
97
  next unless success
97
98
 
98
99
  # Seems nice, just try to get one stage higher now
99
- success, uped = handler.just_try(StandardError) do
100
- upper.call(uped)
100
+ success, dressed = handler.just_try(StandardError) do
101
+ dresser.call(dressed)
101
102
  end
102
- return uped if success
103
+ return dressed if success
103
104
 
104
105
  end
105
106
 
@@ -0,0 +1,47 @@
1
+ module Qrb
2
+ #
3
+ # An AnyType generator allows capuring the set of every ruby citizen as a Q
4
+ # type.
5
+ #
6
+ # Any := .
7
+ #
8
+ # Object is used as concrete representation of the information type as the
9
+ # Ruby `Object` class already captures everything.
10
+ #
11
+ # R(.) = Object
12
+ #
13
+ # Accordingly, the `dress` transformation function has the signature below.
14
+ # Note that dress always succeeds and returns its first argument.
15
+ #
16
+ # dress :: Alpha -> Object throws TypeError
17
+ # dress :: Object -> Object throws TypeError
18
+ #
19
+ class AnyType < Type
20
+
21
+ def initialize(name = nil)
22
+ super(name)
23
+ end
24
+
25
+ def default_name
26
+ "Any"
27
+ end
28
+
29
+ def include?(value)
30
+ true
31
+ end
32
+
33
+ def dress(value, handler = nil)
34
+ value
35
+ end
36
+
37
+ def ==(other)
38
+ other.is_a?(AnyType)
39
+ end
40
+ alias :eql? :==
41
+
42
+ def hash
43
+ self.class.hash ^ 37
44
+ end
45
+
46
+ end # class AnyType
47
+ end # module Qrb
data/lib/qrb/version.rb CHANGED
@@ -2,7 +2,7 @@ module Qrb
2
2
  module Version
3
3
 
4
4
  MAJOR = 0
5
- MINOR = 2
5
+ MINOR = 3
6
6
  TINY = 0
7
7
 
8
8
  def self.to_s
data/spec/spec_helper.rb CHANGED
@@ -35,6 +35,13 @@ class Color
35
35
 
36
36
  end
37
37
 
38
+ module ExternalContract
39
+ def self.dress(x)
40
+ end
41
+ def self.undress(y)
42
+ end
43
+ end
44
+
38
45
  module SpecHelpers
39
46
 
40
47
  def intType
@@ -57,6 +64,10 @@ module SpecHelpers
57
64
  Qrb::TypeFactory.new
58
65
  end
59
66
 
67
+ def system
68
+ Qrb::System.new
69
+ end
70
+
60
71
  def blueviolet
61
72
  Color.new(138, 43, 226)
62
73
  end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ describe Qrb, "ast" do
3
+
4
+ subject{
5
+ Qrb.ast <<-EOF
6
+ Posint = .Fixnum( i | i>=0 )
7
+ Point = { x: Posint, y: Posint }
8
+ {{ p: Point }}
9
+ EOF
10
+ }
11
+
12
+ let(:expected){
13
+ [ :system,
14
+ [ :type_def,
15
+ "Posint",
16
+ [ :sub_type,
17
+ [:builtin_type, "Fixnum"],
18
+ [ :constraint,
19
+ "default",
20
+ [:fn, [:parameters, "i"], [:source, "i>=0"] ]
21
+ ]
22
+ ]
23
+ ],
24
+ [ :type_def,
25
+ "Point",
26
+ [ :tuple_type,
27
+ [ :heading,
28
+ [:attribute, "x", [:type_ref, "Posint"]],
29
+ [:attribute, "y", [:type_ref, "Posint"]]
30
+ ]
31
+ ]
32
+ ],
33
+ [ :relation_type,
34
+ [ :heading,
35
+ [:attribute, "p", [:type_ref, "Point"]]
36
+ ]
37
+ ]
38
+ ]
39
+ }
40
+
41
+ it{ should eq(expected) }
42
+
43
+ end
@@ -10,6 +10,10 @@ module Qrb
10
10
  subject.compile(type_factory)
11
11
  }
12
12
 
13
+ let(:ast){
14
+ subject.to_ast
15
+ }
16
+
13
17
  context 'One contract' do
14
18
  let(:input){ '.Color <rgb> {r: .Integer, g: .Integer, b: .Integer}' }
15
19
 
@@ -22,6 +26,23 @@ module Qrb
22
26
  it 'should behave as expected' do
23
27
  compiled.dress(r: 138, g: 43, b: 226).should eq(blueviolet)
24
28
  end
29
+
30
+ it 'has expected AST' do
31
+ ast.should eq([
32
+ :ad_type,
33
+ "Color",
34
+ [:contract,
35
+ "rgb",
36
+ [:tuple_type,
37
+ [:heading,
38
+ [:attribute, "r", [:builtin_type, "Integer"]],
39
+ [:attribute, "g", [:builtin_type, "Integer"]],
40
+ [:attribute, "b", [:builtin_type, "Integer"]]
41
+ ]
42
+ ]
43
+ ]
44
+ ])
45
+ end
25
46
  end
26
47
 
27
48
  context 'Two contracts' do
@@ -41,6 +62,27 @@ module Qrb
41
62
  it 'should behave as expected' do
42
63
  compiled.dress("#8A2BE2").should eq(blueviolet)
43
64
  end
65
+
66
+ it 'has expected AST' do
67
+ ast.should eq([
68
+ :ad_type,
69
+ "Color",
70
+ [:contract,
71
+ "rgb",
72
+ [:tuple_type,
73
+ [:heading,
74
+ [:attribute, "r", [:builtin_type, "Integer"]],
75
+ [:attribute, "g", [:builtin_type, "Integer"]],
76
+ [:attribute, "b", [:builtin_type, "Integer"]]
77
+ ]
78
+ ]
79
+ ],
80
+ [:contract,
81
+ "hex",
82
+ [:builtin_type, "String"]
83
+ ]
84
+ ])
85
+ end
44
86
  end
45
87
 
46
88
  context 'No ruby class' do
@@ -58,6 +100,21 @@ module Qrb
58
100
  compiled.dress("foo")
59
101
  }.should raise_error(TypeError)
60
102
  end
103
+
104
+ it 'has expected AST' do
105
+ ast.should eq([
106
+ :ad_type,
107
+ nil,
108
+ [:contract,
109
+ "as",
110
+ [:tuple_type,
111
+ [:heading,
112
+ [:attribute, "r", [:builtin_type, "Integer"]],
113
+ ]
114
+ ]
115
+ ]
116
+ ])
117
+ end
61
118
  end
62
119
 
63
120
  context 'Duplicate contract name' do
@@ -88,6 +145,21 @@ module Qrb
88
145
  err.should be_a(TypeError)
89
146
  err.message.should eq("Invalid value `foo` for DateTime")
90
147
  end
148
+
149
+ it 'has expected AST' do
150
+ ast.should eq([
151
+ :ad_type,
152
+ "DateTime",
153
+ [:contract,
154
+ "iso",
155
+ [:builtin_type, "String"],
156
+ [:inline_pair,
157
+ [:fn, [:parameters, "s"], [:source, "DateTime.parse(s)"]],
158
+ [:fn, [:parameters, "d"], [:source, "d.to_s"]],
159
+ ]
160
+ ]
161
+ ])
162
+ end
91
163
  end
92
164
 
93
165
  end