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
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