qrb 0.2.0 → 0.3.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. 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,30 @@
1
+ require 'spec_helper'
2
+ module Qrb
3
+ describe Syntax, "any_type" do
4
+
5
+ subject{
6
+ Syntax.parse(source, root: "any_type")
7
+ }
8
+
9
+ let(:source){ "." }
10
+
11
+ describe "compilation result" do
12
+ let(:compiled){
13
+ subject.compile(type_factory)
14
+ }
15
+
16
+ it 'compiles to an AnyType' do
17
+ compiled.should be_a(AnyType)
18
+ end
19
+ end
20
+
21
+ describe "AST" do
22
+ let(:ast){
23
+ subject.to_ast
24
+ }
25
+
26
+ it{ ast.should eq([:any_type]) }
27
+ end
28
+
29
+ end
30
+ end
@@ -6,20 +6,33 @@ module Qrb
6
6
  Syntax.parse(input, root: "attribute")
7
7
  }
8
8
 
9
- let(:compiled){
10
- subject.compile(type_factory)
11
- }
9
+ describe 'compilation result' do
10
+ let(:compiled){
11
+ subject.compile(type_factory)
12
+ }
12
13
 
13
- context 'a: .Integer' do
14
- let(:input){ 'a: .Integer' }
14
+ context 'a: .Integer' do
15
+ let(:input){ 'a: .Integer' }
15
16
 
16
- it 'compiles to an Attribute' do
17
- compiled.should be_a(Attribute)
18
- compiled.name.should eq(:a)
19
- compiled.type.should be_a(BuiltinType)
20
- compiled.type.ruby_type.should be(Integer)
17
+ it 'compiles to an Attribute' do
18
+ compiled.should be_a(Attribute)
19
+ compiled.name.should eq(:a)
20
+ compiled.type.should be_a(BuiltinType)
21
+ compiled.type.ruby_type.should be(Integer)
22
+ end
21
23
  end
22
24
  end
23
25
 
26
+ describe 'AST' do
27
+ let(:ast){
28
+ subject.to_ast
29
+ }
30
+
31
+ let(:input){ 'a: .Integer' }
32
+
33
+ it{ ast.should eq([:attribute, "a", [:builtin_type, "Integer"]]) }
34
+ end
35
+
36
+
24
37
  end
25
38
  end
@@ -6,26 +6,38 @@ module Qrb
6
6
  Syntax.parse(source, root: "builtin_type")
7
7
  }
8
8
 
9
- let(:compiled){
10
- subject.compile(type_factory)
11
- }
9
+ describe "compilation result" do
10
+ let(:compiled){
11
+ subject.compile(type_factory)
12
+ }
13
+
14
+ context 'when an unqualified class name' do
15
+ let(:source){ ".Integer" }
16
+
17
+ it 'compiles to a BuiltinType' do
18
+ compiled.should be_a(BuiltinType)
19
+ compiled.ruby_type.should be(Integer)
20
+ end
21
+ end
12
22
 
13
- context 'when an unqualified class name' do
14
- let(:source){ ".Integer" }
23
+ context 'when a qualified class name' do
24
+ let(:source){ ".Qrb::Type" }
15
25
 
16
- it 'compiles to a BuiltinType' do
17
- compiled.should be_a(BuiltinType)
18
- compiled.ruby_type.should be(Integer)
26
+ it 'compiles to a BuiltinType' do
27
+ compiled.should be_a(BuiltinType)
28
+ compiled.ruby_type.should be(::Qrb::Type)
29
+ end
19
30
  end
20
31
  end
21
32
 
22
- context 'when a qualified class name' do
33
+ describe "AST" do
23
34
  let(:source){ ".Qrb::Type" }
24
35
 
25
- it 'compiles to a BuiltinType' do
26
- compiled.should be_a(BuiltinType)
27
- compiled.ruby_type.should be(::Qrb::Type)
28
- end
36
+ let(:ast){
37
+ subject.to_ast
38
+ }
39
+
40
+ it{ ast.should eq([:builtin_type, "Qrb::Type"]) }
29
41
  end
30
42
 
31
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 '(i | i >= 0)' do
14
18
  let(:input){ '(i | i >= 0)' }
15
19
 
@@ -21,6 +25,16 @@ module Qrb
21
25
  compiled[:predicate].call(12).should be_true
22
26
  compiled[:predicate].call(-12).should be_false
23
27
  end
28
+
29
+ it 'has the expected AST' do
30
+ ast.should eq([
31
+ [
32
+ :constraint,
33
+ "default",
34
+ [:fn, [:parameters, "i"], [:source, "i >= 0"]]
35
+ ]
36
+ ])
37
+ end
24
38
  end
25
39
 
26
40
  end
@@ -10,6 +10,10 @@ module Qrb
10
10
  subject.compile("a")
11
11
  }
12
12
 
13
+ let(:ast){
14
+ subject.to_ast('a')
15
+ }
16
+
13
17
  context 'a >= 10' do
14
18
  let(:input){ 'a >= 10' }
15
19
 
@@ -17,6 +21,14 @@ module Qrb
17
21
  compiled.should be_a(Hash)
18
22
  compiled.keys.should eq([:predicate])
19
23
  end
24
+
25
+ it 'has the expected AST' do
26
+ ast.should eq([
27
+ :constraint,
28
+ "default",
29
+ [:fn, [:parameters, "a"], [:source, "a >= 10"]]
30
+ ])
31
+ end
20
32
  end
21
33
 
22
34
  context 'foo: a >= 10' do
@@ -26,6 +38,14 @@ module Qrb
26
38
  compiled.should be_a(Hash)
27
39
  compiled.keys.should eq([:foo])
28
40
  end
41
+
42
+ it 'has the expected AST' do
43
+ ast.should eq([[
44
+ :constraint,
45
+ "foo",
46
+ [:fn, [:parameters, "a"], [:source, "a >= 10"]]
47
+ ]])
48
+ end
29
49
  end
30
50
 
31
51
  context 'foo: a >= 10, bar: a <= 255' do
@@ -35,6 +55,21 @@ module Qrb
35
55
  compiled.should be_a(Hash)
36
56
  compiled.keys.should eq([:foo, :bar])
37
57
  end
58
+
59
+ it 'has the expected AST' do
60
+ ast.should eq([
61
+ [
62
+ :constraint,
63
+ "foo",
64
+ [:fn, [:parameters, "a"], [:source, "a >= 10"]]
65
+ ],
66
+ [
67
+ :constraint,
68
+ "bar",
69
+ [:fn, [:parameters, "a"], [:source, "a <= 255"]]
70
+ ]
71
+ ])
72
+ end
38
73
  end
39
74
 
40
75
  context 'foo: a >= 10, foo: a <= 255' do
@@ -10,6 +10,10 @@ module Qrb
10
10
  compiled.values.first
11
11
  }
12
12
 
13
+ let(:ast){
14
+ subject.to_ast
15
+ }
16
+
13
17
  context 'No converter and a class' do
14
18
  let(:input){ '<rgb> {r: .Integer, g: .Integer, b: .Integer}' }
15
19
 
@@ -21,8 +25,23 @@ module Qrb
21
25
  compiled.should be_a(Hash)
22
26
  compiled.keys.should eq([:rgb])
23
27
  contract.should be_a(Array)
24
- contract.first.should be_a(TupleType)
25
- contract.last.should be_a(Method)
28
+ contract[0].should be_a(TupleType)
29
+ contract[1].should be_a(Method)
30
+ contract[2].should be_a(Proc)
31
+ end
32
+
33
+ it 'has expected AST' do
34
+ ast.should eq([
35
+ :contract,
36
+ "rgb",
37
+ [:tuple_type,
38
+ [:heading,
39
+ [:attribute, "r", [:builtin_type, "Integer"]],
40
+ [:attribute, "g", [:builtin_type, "Integer"]],
41
+ [:attribute, "b", [:builtin_type, "Integer"]]
42
+ ]
43
+ ]
44
+ ])
26
45
  end
27
46
  end
28
47
 
@@ -37,8 +56,23 @@ module Qrb
37
56
  compiled.should be_a(Hash)
38
57
  compiled.keys.should eq([:rgb])
39
58
  contract.should be_a(Array)
40
- contract.first.should be_a(TupleType)
41
- contract.last.should be(Qrb::IDENTITY)
59
+ contract[0].should be_a(TupleType)
60
+ contract[1].should be(Qrb::IDENTITY)
61
+ contract[2].should be(Qrb::IDENTITY)
62
+ end
63
+
64
+ it 'has expected AST' do
65
+ ast.should eq([
66
+ :contract,
67
+ "rgb",
68
+ [:tuple_type,
69
+ [:heading,
70
+ [:attribute, "r", [:builtin_type, "Integer"]],
71
+ [:attribute, "g", [:builtin_type, "Integer"]],
72
+ [:attribute, "b", [:builtin_type, "Integer"]]
73
+ ]
74
+ ]
75
+ ])
42
76
  end
43
77
  end
44
78
 
@@ -56,6 +90,44 @@ module Qrb
56
90
  contract.first.should be_a(BuiltinType)
57
91
  contract.last.should be_a(Proc)
58
92
  end
93
+
94
+ it 'has expected AST' do
95
+ ast.should eq([
96
+ :contract,
97
+ "iso",
98
+ [:builtin_type, "String"],
99
+ [:inline_pair,
100
+ [:fn, [:parameters, "s"], [:source, "DateTime.parse(s)"]],
101
+ [:fn, [:parameters, "d"], [:source, "d.to_s"]]
102
+ ]
103
+ ])
104
+ end
105
+ end
106
+
107
+ context 'A contract with external dressers' do
108
+ let(:input){ '<iso> .String .ExternalContract' }
109
+
110
+ let(:compiled){
111
+ subject.compile(type_factory, nil)
112
+ }
113
+
114
+ it 'compiles to the expected Hash' do
115
+ compiled.should be_a(Hash)
116
+ compiled.keys.should eq([:iso])
117
+ contract.should be_a(Array)
118
+ contract[0].should be_a(BuiltinType)
119
+ contract[1].should be_a(Method)
120
+ contract[2].should be_a(Method)
121
+ end
122
+
123
+ it 'has expected AST' do
124
+ ast.should eq([
125
+ :contract,
126
+ "iso",
127
+ [:builtin_type, "String"],
128
+ [:external_pair, "ExternalContract"]
129
+ ])
130
+ end
59
131
  end
60
132
 
61
133
  end
@@ -6,36 +6,53 @@ module Qrb
6
6
  Syntax.parse(input, root: "heading")
7
7
  }
8
8
 
9
- let(:compiled){
10
- subject.compile(type_factory)
11
- }
9
+ describe "compilation result" do
10
+ let(:compiled){
11
+ subject.compile(type_factory)
12
+ }
13
+
14
+ context 'empty heading' do
15
+ let(:input){ ' ' }
16
+
17
+ it 'compiles to a Heading' do
18
+ compiled.should be_a(Heading)
19
+ compiled.to_name.should eq('')
20
+ end
21
+ end
12
22
 
13
- context 'empty heading' do
14
- let(:input){ ' ' }
23
+ context 'a: .Integer' do
24
+ let(:input){ 'a: .Integer' }
15
25
 
16
- it 'compiles to a Heading' do
17
- compiled.should be_a(Heading)
18
- compiled.to_name.should eq('')
26
+ it 'compiles to a Heading' do
27
+ compiled.should be_a(Heading)
28
+ compiled.to_name.should eq('a: Integer')
29
+ end
19
30
  end
20
- end
21
31
 
22
- context 'a: .Integer' do
23
- let(:input){ 'a: .Integer' }
32
+ context 'a: .Integer, b: .Float' do
33
+ let(:input){ 'a: .Integer, b: .Float' }
24
34
 
25
- it 'compiles to a Heading' do
26
- compiled.should be_a(Heading)
27
- compiled.to_name.should eq('a: Integer')
35
+ it 'compiles to a Heading' do
36
+ compiled.should be_a(Heading)
37
+ compiled.to_name.should eq('a: Integer, b: Float')
38
+ end
28
39
  end
29
40
  end
30
41
 
31
- context 'a: .Integer, b: .Float' do
42
+ describe "AST" do
32
43
  let(:input){ 'a: .Integer, b: .Float' }
33
44
 
34
- it 'compiles to a Heading' do
35
- compiled.should be_a(Heading)
36
- compiled.to_name.should eq('a: Integer, b: Float')
37
- end
45
+ let(:ast){
46
+ subject.to_ast
47
+ }
48
+
49
+ it{
50
+ ast.should eq([
51
+ :heading,
52
+ [ :attribute, "a", [:builtin_type, "Integer" ]],
53
+ [ :attribute, "b", [:builtin_type, "Float" ]]
54
+ ])
55
+ }
38
56
  end
39
-
40
57
  end
41
58
  end
@@ -10,6 +10,10 @@ module Qrb
10
10
  subject.compile("a")
11
11
  }
12
12
 
13
+ let(:ast){
14
+ subject.to_ast('a')
15
+ }
16
+
13
17
  context 'a >= 10' do
14
18
  let(:input){ 'foo: a >= 10' }
15
19
 
@@ -25,6 +29,14 @@ module Qrb
25
29
  compiled[:foo].call(12).should be_true
26
30
  compiled[:foo].call(9).should be_false
27
31
  end
32
+
33
+ it 'has the expected AST' do
34
+ ast.should eq([
35
+ :constraint,
36
+ "foo",
37
+ [:fn, [:parameters, "a"], [:source, "a >= 10"]]
38
+ ])
39
+ end
28
40
  end
29
41
 
30
42
  end
@@ -6,35 +6,53 @@ module Qrb
6
6
  Syntax.parse(input, root: "relation_type")
7
7
  }
8
8
 
9
- let(:compiled){
10
- subject.compile(type_factory)
11
- }
9
+ describe "compilation result" do
10
+ let(:compiled){
11
+ subject.compile(type_factory)
12
+ }
13
+
14
+ context 'empty heading' do
15
+ let(:input){ '{{ }}' }
16
+
17
+ it 'compiles to a RelationType' do
18
+ compiled.should be_a(RelationType)
19
+ compiled.heading.should be_empty
20
+ end
21
+ end
12
22
 
13
- context 'empty heading' do
14
- let(:input){ '{{ }}' }
23
+ context '{{a: .Integer}}' do
24
+ let(:input){ '{{a: .Integer}}' }
15
25
 
16
- it 'compiles to a RelationType' do
17
- compiled.should be_a(RelationType)
18
- compiled.heading.should be_empty
26
+ it 'compiles to a RelationType' do
27
+ compiled.should be_a(RelationType)
28
+ compiled.heading.size.should eq(1)
29
+ end
19
30
  end
20
- end
21
31
 
22
- context '{{a: .Integer}}' do
23
- let(:input){ '{{a: .Integer}}' }
32
+ context '{{a: .Integer, b: .Float}}' do
33
+ let(:input){ '{{a: .Integer, b: .Float}}' }
24
34
 
25
- it 'compiles to a RelationType' do
26
- compiled.should be_a(RelationType)
27
- compiled.heading.size.should eq(1)
35
+ it 'compiles to a RelationType' do
36
+ compiled.should be_a(RelationType)
37
+ compiled.heading.size.should eq(2)
38
+ end
28
39
  end
29
40
  end
30
41
 
31
- context '{{a: .Integer, b: .Float}}' do
32
- let(:input){ '{{a: .Integer, b: .Float}}' }
42
+ describe "AST" do
43
+ let(:input){ '{{a: .Integer}}' }
33
44
 
34
- it 'compiles to a RelationType' do
35
- compiled.should be_a(RelationType)
36
- compiled.heading.size.should eq(2)
37
- end
45
+ let(:ast){ subject.to_ast }
46
+
47
+ it{
48
+ ast.should eq([
49
+ :relation_type,
50
+ [
51
+ :heading,
52
+ [ :attribute, "a", [:builtin_type, "Integer" ]]
53
+ ]
54
+ ])
55
+ }
38
56
  end
39
57
 
40
58
  end