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
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 0.3.0 / 2014-03-09
2
+
3
+ * Added AnyType abstraction, aka '.'
4
+ * Added support for external contracts in ADTs
5
+ * Added support for extracting an Abstract Syntax Tree from parsing result
6
+ * Allows camelCasing in constraint names
7
+
1
8
  # 0.2.0 / 2014-03-04
2
9
 
3
10
  * Fix dependencies in gemspec (judofyr)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- qrb (0.2.0)
4
+ qrb (0.3.0)
5
5
  citrus (~> 2.4)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -34,7 +34,7 @@ JSON
34
34
  puts schema.dress(data)
35
35
  ```
36
36
 
37
- ## About this Q binding
37
+ ## ADTs with internal contracts
38
38
 
39
39
  Qrb tries to provide an idiomatic binding for ruby developers. In particular,
40
40
  it uses a simple convention-over-configuration protocol for information
@@ -71,12 +71,41 @@ class Color
71
71
  end
72
72
  ```
73
73
 
74
+ ## ADTs with external contracts
75
+
76
+ When the scenario above is not possible or not wanted (would require core
77
+ extensions for instance), Qrb allows defining ADTs with external contracts.
78
+ The following ADT definition:
79
+
80
+ ```ruby
81
+ Color = .Color <rgb> {r: Byte, g: Byte, b: Byte} ColorContract
82
+ ```
83
+
84
+ expected the following ruby module:
85
+
86
+ ```ruby
87
+ module ColorContract
88
+
89
+ def self.dress(tuple)
90
+ Color.new(tuple[:r], tuple[:g], tuple[:b])
91
+ end
92
+
93
+ def self.undress(color)
94
+ { r: color.r, g: color.g, b: color.b }
95
+ end
96
+
97
+ end
98
+ ```
99
+
74
100
  ## About representations
75
101
 
76
102
  The `Rep` representation function mapping Q types to ruby classes is as
77
103
  follows:
78
104
 
79
105
  ```ruby
106
+ # Any type is represented by Ruby's Object class
107
+ Rep(.) = Object
108
+
80
109
  # Builtins are represented by the corresponding ruby class
81
110
  Rep(.Builtin) = Builtin
82
111
 
data/lib/qrb.rb CHANGED
@@ -10,6 +10,7 @@ module Qrb
10
10
  :attribute,
11
11
  :heading,
12
12
  :constraints,
13
+ :any,
13
14
  :builtin,
14
15
  :adt,
15
16
  :subtype,
@@ -43,6 +44,11 @@ module Qrb
43
44
  Syntax.compile(source)
44
45
  end
45
46
 
47
+ def ast(source)
48
+ require "qrb/syntax"
49
+ Syntax.ast(source)
50
+ end
51
+
46
52
  def system(identifier)
47
53
  f = File.expand_path("../qrb/#{identifier}.q", __FILE__)
48
54
  if File.exists?(f)
data/lib/qrb/data_type.rb CHANGED
@@ -6,7 +6,13 @@ module Qrb
6
6
  end
7
7
 
8
8
  def contract(name, infotype)
9
- ad_contracts[name] = [ Qrb.type(infotype) , method(name) ]
9
+ dresser = method(name)
10
+ undresser = instance_method(:"to_#{name}")
11
+ ad_contracts[name] = [
12
+ Qrb.type(infotype),
13
+ dresser,
14
+ ->(d){ undresser.bind(d).call }
15
+ ]
10
16
  end
11
17
 
12
18
  private
@@ -54,7 +54,7 @@ module Qrb
54
54
  private
55
55
 
56
56
  def value_to_s(value)
57
- return 'nil' if value.nil?
57
+ return 'null' if value.nil?
58
58
  s = value.to_s
59
59
  s = "#{s[0..25]}..." if s.size>25
60
60
  s
@@ -97,6 +97,12 @@ module Qrb
97
97
 
98
98
  ########################################################## Type generators
99
99
 
100
+ def any(name = nil)
101
+ name = name(name)
102
+
103
+ AnyType.new(name)
104
+ end
105
+
100
106
  def builtin(ruby_type, name = nil)
101
107
  ruby_type = ruby_type(ruby_type)
102
108
  name = name(name)
data/lib/qrb/syntax.rb CHANGED
@@ -6,6 +6,7 @@ require_relative 'syntax/type_def'
6
6
  require_relative 'syntax/expression'
7
7
  require_relative 'syntax/attribute'
8
8
  require_relative 'syntax/heading'
9
+ require_relative 'syntax/any_type'
9
10
  require_relative 'syntax/builtin_type'
10
11
  require_relative 'syntax/sub_type'
11
12
  require_relative 'syntax/constraint_def'
@@ -20,6 +21,8 @@ require_relative 'syntax/union_type'
20
21
  require_relative 'syntax/type_ref'
21
22
  require_relative 'syntax/ad_type'
22
23
  require_relative 'syntax/contract'
24
+ require_relative 'syntax/inline_pair'
25
+ require_relative 'syntax/external_pair'
23
26
  require_relative 'syntax/lambda_expr'
24
27
  module Qrb
25
28
  module Syntax
@@ -30,6 +33,10 @@ module Qrb
30
33
  Parser.parse(*args, &bl)
31
34
  end
32
35
 
36
+ def self.ast(source)
37
+ Parser.parse(source, root: "system").to_ast
38
+ end
39
+
33
40
  def self.compile(source, system = Qrb::System.new)
34
41
  Parser.parse(source, root: "system").compile(system)
35
42
  end
@@ -20,6 +20,13 @@ module Qrb
20
20
  contracts
21
21
  end
22
22
 
23
+ def to_ast
24
+ [
25
+ :ad_type,
26
+ builtin_type_name ? builtin_type_name.to_s : nil
27
+ ] + captures[:contract].map(&:to_ast)
28
+ end
29
+
23
30
  end # module AdType
24
31
  end # module Syntax
25
32
  end # module Qrb
@@ -0,0 +1,16 @@
1
+ module Qrb
2
+ module Syntax
3
+ module AnyType
4
+ include Support
5
+
6
+ def compile(factory)
7
+ factory.any
8
+ end
9
+
10
+ def to_ast
11
+ [:any_type]
12
+ end
13
+
14
+ end # module AnyType
15
+ end # module Syntax
16
+ end # module Qrb
@@ -6,6 +6,10 @@ module Qrb
6
6
  factory.attribute(attribute_name.to_sym, type.compile(factory))
7
7
  end
8
8
 
9
+ def to_ast
10
+ [:attribute, attribute_name.to_s, type.to_ast]
11
+ end
12
+
9
13
  end # module BuiltinType
10
14
  end # module Syntax
11
15
  end # module Qrb
@@ -8,6 +8,10 @@ module Qrb
8
8
  factory.builtin(clazz)
9
9
  end
10
10
 
11
+ def to_ast
12
+ [:builtin_type, builtin_type_name.to_s]
13
+ end
14
+
11
15
  end # module BuiltinType
12
16
  end # module Syntax
13
17
  end # module Qrb
@@ -6,6 +6,12 @@ module Qrb
6
6
  constraints.compile(var_name.to_s)
7
7
  end
8
8
 
9
+ def to_ast
10
+ ast = constraints.to_ast(var_name.to_s)
11
+ ast = [ast] if ast.first.is_a?(Symbol)
12
+ ast
13
+ end
14
+
9
15
  end # module ConstraintDef
10
16
  end # module Syntax
11
17
  end # module Qrb
@@ -13,6 +13,10 @@ module Qrb
13
13
  constraints
14
14
  end
15
15
 
16
+ def to_ast(var_name)
17
+ captures[:named_constraint].map{|c| c.to_ast(var_name) }
18
+ end
19
+
16
20
  end # module Constraints
17
21
  end # module Syntax
18
22
  end # module Qrb
@@ -3,23 +3,35 @@ module Qrb
3
3
  module Contract
4
4
 
5
5
  def compile(factory, clazz)
6
- contract = [
7
- type.compile(factory),
8
- compile_upper(factory, clazz)
9
- ]
6
+ contract = [ type.compile(factory) ] + compile_pair(factory, clazz)
10
7
  { contract_name.to_sym => contract }
11
8
  end
12
9
 
13
- def compile_upper(factory, clazz)
14
- if up
15
- up.compile(factory)
10
+ def compile_pair(factory, clazz)
11
+ if pair
12
+ pair.compile(factory)
16
13
  elsif clazz
17
- clazz.method(contract_name.to_sym)
14
+ dresser = clazz.method(contract_name.to_sym)
15
+ undresser = clazz.instance_method(:"to_#{contract_name}")
16
+ [
17
+ dresser,
18
+ ->(d){ undresser.bind(d).call }
19
+ ]
18
20
  else
19
- Qrb::IDENTITY
21
+ [ Qrb::IDENTITY, Qrb::IDENTITY ]
20
22
  end
21
23
  end
22
24
 
25
+ def to_ast
26
+ ast = [
27
+ :contract,
28
+ contract_name.to_s,
29
+ (type && type.to_ast)
30
+ ]
31
+ ast << pair.to_ast if pair
32
+ ast
33
+ end
34
+
23
35
  end # module Contract
24
36
  end # module Syntax
25
37
  end # module Qrb
@@ -9,6 +9,10 @@ module Qrb
9
9
  system
10
10
  end
11
11
 
12
+ def to_ast
13
+ captures[:type_def].map(&:to_ast)
14
+ end
15
+
12
16
  end # module Definitions
13
17
  end # module Syntax
14
18
  end # module Qrb
@@ -0,0 +1,17 @@
1
+ module Qrb
2
+ module Syntax
3
+ module ExternalPair
4
+ include Support
5
+
6
+ def compile(factory)
7
+ clazz = resolve_ruby_const(builtin_type_name.to_s)
8
+ [ clazz.method(:dress), clazz.method(:undress) ]
9
+ end
10
+
11
+ def to_ast
12
+ [ :external_pair, builtin_type_name.to_s ]
13
+ end
14
+
15
+ end # module ExternalPair
16
+ end # module Syntax
17
+ end # module Qrb
@@ -10,6 +10,10 @@ module Qrb
10
10
  Qrb::Heading.new(attributes(factory))
11
11
  end
12
12
 
13
+ def to_ast
14
+ captures[:attribute].map(&:to_ast).unshift(:heading)
15
+ end
16
+
13
17
  end # module Heading
14
18
  end # module Syntax
15
19
  end # module Qrb
@@ -0,0 +1,16 @@
1
+ module Qrb
2
+ module Syntax
3
+ module InlinePair
4
+ include Support
5
+
6
+ def compile(factory)
7
+ [ dress.compile(factory), undress.compile(factory) ]
8
+ end
9
+
10
+ def to_ast
11
+ [ :inline_pair, dress.to_ast, undress.to_ast ]
12
+ end
13
+
14
+ end # module InlinePair
15
+ end # module Syntax
16
+ end # module Qrb
@@ -6,6 +6,10 @@ module Qrb
6
6
  expression.compile(var_name)
7
7
  end
8
8
 
9
+ def to_ast
10
+ [:fn, [:parameters, var_name.to_s], [:source, expression.to_s.strip]]
11
+ end
12
+
9
13
  end # module LambdaExpr
10
14
  end # module Syntax
11
15
  end # module Qrb
@@ -6,6 +6,12 @@ module Qrb
6
6
  { constraint_name.to_sym => expression.compile(var_name) }
7
7
  end
8
8
 
9
+ def to_ast(var_name)
10
+ [ :constraint,
11
+ constraint_name.to_s,
12
+ [:fn, [:parameters, var_name], [:source, expression.to_s.strip]] ]
13
+ end
14
+
9
15
  end # module NamedConstraint
10
16
  end # module Syntax
11
17
  end # module Qrb
@@ -108,6 +108,7 @@ grammar Qrb::Syntax::Parser
108
108
  rule term_type
109
109
  ad_type
110
110
  | builtin_type
111
+ | any_type
111
112
  | type_ref
112
113
  end
113
114
 
@@ -119,10 +120,32 @@ grammar Qrb::Syntax::Parser
119
120
  end
120
121
 
121
122
  rule contract
122
- ('<' contract_name '>' spacing type (spacing ('\\' up:lambda_expr) spacing ('\\' down:lambda_expr))?)
123
+ ('<' contract_name '>' spacing type spacing pair?)
123
124
  <Qrb::Syntax::Contract>
124
125
  end
125
126
 
127
+ rule pair
128
+ inline_pair
129
+ | external_pair
130
+ end
131
+
132
+ rule inline_pair
133
+ ('\\' dress:lambda_expr spacing '\\' undress:lambda_expr)
134
+ <Qrb::Syntax::InlinePair>
135
+ end
136
+
137
+ rule external_pair
138
+ ('.' builtin_type_name)
139
+ <Qrb::Syntax::ExternalPair>
140
+ end
141
+
142
+ # any
143
+
144
+ rule any_type
145
+ '.'
146
+ <Qrb::Syntax::AnyType>
147
+ end
148
+
126
149
  # builtin and refs
127
150
 
128
151
  rule builtin_type
@@ -150,7 +173,7 @@ grammar Qrb::Syntax::Parser
150
173
  <Qrb::Syntax::Expression>
151
174
  end
152
175
 
153
- # lexer
176
+ # LEXER (names)
154
177
 
155
178
  rule var_name
156
179
  /[a-z]+/
@@ -161,7 +184,7 @@ grammar Qrb::Syntax::Parser
161
184
  end
162
185
 
163
186
  rule constraint_name
164
- /[a-z][a-z_]*/
187
+ /[a-z][a-zA-Z_]*/
165
188
  end
166
189
 
167
190
  rule attribute_name
@@ -173,9 +196,11 @@ grammar Qrb::Syntax::Parser
173
196
  end
174
197
 
175
198
  rule builtin_type_name
176
- /[a-zA-Z0-9:]/+
199
+ /[a-zA-Z0-9:]+/
177
200
  end
178
201
 
202
+ # LEXER (spacing & comments)
203
+
179
204
  rule spacing
180
205
  (spaces | comment)*
181
206
  end