aql 0.0.1

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 (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +18 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.devtools +64 -0
  7. data/Guardfile +18 -0
  8. data/LICENSE +20 -0
  9. data/README.md +86 -0
  10. data/Rakefile +2 -0
  11. data/TODO +1 -0
  12. data/aql.gemspec +23 -0
  13. data/config/flay.yml +3 -0
  14. data/config/flog.yml +2 -0
  15. data/config/mutant.yml +3 -0
  16. data/config/roodi.yml +18 -0
  17. data/config/site.reek +100 -0
  18. data/config/yardstick.yml +2 -0
  19. data/examples/aql.rb +31 -0
  20. data/lib/aql.rb +62 -0
  21. data/lib/aql/buffer.rb +129 -0
  22. data/lib/aql/constants.rb +19 -0
  23. data/lib/aql/node.rb +45 -0
  24. data/lib/aql/node/attribute.rb +25 -0
  25. data/lib/aql/node/block.rb +28 -0
  26. data/lib/aql/node/call.rb +83 -0
  27. data/lib/aql/node/literal.rb +48 -0
  28. data/lib/aql/node/literal/composed.rb +10 -0
  29. data/lib/aql/node/literal/composed/document.rb +59 -0
  30. data/lib/aql/node/literal/composed/list.rb +42 -0
  31. data/lib/aql/node/literal/primitive.rb +10 -0
  32. data/lib/aql/node/literal/primitive/number.rb +28 -0
  33. data/lib/aql/node/literal/primitive/string.rb +27 -0
  34. data/lib/aql/node/literal/singleton.rb +51 -0
  35. data/lib/aql/node/name.rb +31 -0
  36. data/lib/aql/node/null.rb +21 -0
  37. data/lib/aql/node/operation.rb +20 -0
  38. data/lib/aql/node/operation/binary.rb +33 -0
  39. data/lib/aql/node/operation/for.rb +77 -0
  40. data/lib/aql/node/operation/limit.rb +42 -0
  41. data/lib/aql/node/operation/nary.rb +57 -0
  42. data/lib/aql/node/operation/unary.rb +75 -0
  43. data/lib/aql/node/operator.rb +20 -0
  44. data/lib/aql/node/operator/assignment.rb +28 -0
  45. data/lib/aql/node/operator/binary.rb +92 -0
  46. data/lib/aql/node/operator/nary.rb +48 -0
  47. data/lib/aql/node/operator/ternary.rb +30 -0
  48. data/lib/aql/node/operator/unary.rb +39 -0
  49. data/spec/shared/aql_behavior.rb +7 -0
  50. data/spec/spec_helper.rb +11 -0
  51. data/spec/support/aql_helper.rb +12 -0
  52. data/spec/unit/aql/buffer/append_spec.rb +59 -0
  53. data/spec/unit/aql/buffer/binary_spec.rb +22 -0
  54. data/spec/unit/aql/buffer/class_methods/utf8_encode_spec.rb +21 -0
  55. data/spec/unit/aql/buffer/content_spec.rb +24 -0
  56. data/spec/unit/aql/buffer/delimited_spec.rb +31 -0
  57. data/spec/unit/aql/buffer/parentheses_spec.rb +33 -0
  58. data/spec/unit/aql/buffer/wrap_delimited_spec.rb +42 -0
  59. data/spec/unit/aql/class_methods/literal_node_spec.rb +15 -0
  60. data/spec/unit/aql/class_methods/name_node_spec.rb +19 -0
  61. data/spec/unit/aql/node/attribute/aql_spec.rb +22 -0
  62. data/spec/unit/aql/node/block/aql_spec.rb +28 -0
  63. data/spec/unit/aql/node/call/aql_spec.rb +39 -0
  64. data/spec/unit/aql/node/literal/class_methods/build_spec.rb +139 -0
  65. data/spec/unit/aql/node/literal/class_methods/construct_spec.rb +16 -0
  66. data/spec/unit/aql/node/literal/class_methods/handle_spec.rb +22 -0
  67. data/spec/unit/aql/node/literal/composed/document/aql_spec.rb +32 -0
  68. data/spec/unit/aql/node/literal/composed/document/attribute/aql_spec.rb +15 -0
  69. data/spec/unit/aql/node/literal/composed/document/class_methods/construct_spec.rb +24 -0
  70. data/spec/unit/aql/node/literal/composed/list/aql_spec.rb +20 -0
  71. data/spec/unit/aql/node/literal/composed/list/class_methods/construct_spec.rb +37 -0
  72. data/spec/unit/aql/node/literal/primitive/number/aql_spec.rb +23 -0
  73. data/spec/unit/aql/node/literal/primitive/string/aql_spec.rb +21 -0
  74. data/spec/unit/aql/node/literal/singleton/aql_spec.rb +14 -0
  75. data/spec/unit/aql/node/literal/singleton/class_methods/construct_spec.rb +30 -0
  76. data/spec/unit/aql/node/name/aql_spec.rb +33 -0
  77. data/spec/unit/aql/node/null/aql_spec.rb +7 -0
  78. data/spec/unit/aql/node/operation/binary/let/aql_spec.rb +10 -0
  79. data/spec/unit/aql/node/operation/for/aql_spec.rb +18 -0
  80. data/spec/unit/aql/node/operation/keyword_spec.rb +16 -0
  81. data/spec/unit/aql/node/operation/limit/aql_spec.rb +19 -0
  82. data/spec/unit/aql/node/operation/nary/aql_spec.rb +16 -0
  83. data/spec/unit/aql/node/operation/nary/collect/into/aql_spec.rb +11 -0
  84. data/spec/unit/aql/node/operation/nary/sort/aql_spec.rb +22 -0
  85. data/spec/unit/aql/node/operation/unary/aql_spec.rb +12 -0
  86. data/spec/unit/aql/node/operation/unary/direction/aql_spec.rb +14 -0
  87. data/spec/unit/aql/node/operation/unary/filter/aql_spec.rb +20 -0
  88. data/spec/unit/aql/node/operation/unary/return/aql_spec.rb +17 -0
  89. data/spec/unit/aql/node/operator/assignment/aql_spec.rb +10 -0
  90. data/spec/unit/aql/node/operator/binary/aql_spec.rb +15 -0
  91. data/spec/unit/aql/node/operator/nary/aql_spec.rb +31 -0
  92. data/spec/unit/aql/node/operator/operator_spec.rb +18 -0
  93. data/spec/unit/aql/node/operator/ternary/aql_spec.rb +12 -0
  94. data/spec/unit/aql/node/operator/unary/aql_spec.rb +12 -0
  95. data/spec/unit/aql/node/visit_spec.rb +27 -0
  96. metadata +262 -0
@@ -0,0 +1,129 @@
1
+ module AQL
2
+ # The emit buffer
3
+ class Buffer
4
+ include Equalizer.new(:contents)
5
+
6
+ # Initialize object
7
+ #
8
+ # @return [undefined]
9
+ #
10
+ # @api private
11
+ #
12
+ def initialize
13
+ @buffer = []
14
+ end
15
+
16
+ # Emit wrapped delimited
17
+ #
18
+ # @param [String] open
19
+ # @param [Enumerable<Node>] nodes
20
+ # @param [String] close
21
+ #
22
+ # @return [self]
23
+ #
24
+ # @api private
25
+ #
26
+ def wrap_delimited(open, nodes, close)
27
+ parentheses(open, close) do
28
+ delimited(nodes)
29
+ end
30
+ end
31
+
32
+ # Emit block in parentheses
33
+ #
34
+ # @return [self]
35
+ #
36
+ # @api private
37
+ #
38
+ def parentheses(open = '(', close = ')')
39
+ append(open)
40
+ yield
41
+ append(close)
42
+ end
43
+
44
+ # Emit delimited nodes
45
+ #
46
+ # @param [Enumerable<Node>] nodes
47
+ #
48
+ # @return [self]
49
+ #
50
+ # @api private
51
+ #
52
+ def delimited(nodes, delimiter = ', ')
53
+ max = nodes.length - 1
54
+ nodes.each_with_index do |element, index|
55
+ element.visit(self)
56
+ append(delimiter) if index < max
57
+ end
58
+ self
59
+ end
60
+
61
+ # Emit binary
62
+ #
63
+ # @param [Node] left
64
+ # @param [Symbol] operator
65
+ # @param [Node] right
66
+ #
67
+ # @return [self]
68
+ #
69
+ # @api private
70
+ #
71
+ def binary(left, operator, right)
72
+ parentheses do
73
+ left.visit(self)
74
+ append(" #{operator} ")
75
+ right.visit(self)
76
+ end
77
+ end
78
+
79
+ # Append content to buffer
80
+ #
81
+ # @param [String] content
82
+ #
83
+ # @return [self]
84
+ #
85
+ # @api private
86
+ #
87
+ def append(content)
88
+ @buffer << self.class.utf8_encode(content)
89
+ self
90
+ end
91
+
92
+ # Return content
93
+ #
94
+ # @return [String]
95
+ #
96
+ # @api private
97
+ #
98
+ def content
99
+ @buffer.join.freeze
100
+ end
101
+
102
+ if defined?(Encoding)
103
+ # Encode string in UTF-8
104
+ #
105
+ # @param [String] string
106
+ #
107
+ # @return [String]
108
+ #
109
+ # @api private
110
+ #
111
+ def self.utf8_encode(string)
112
+ string.encode(Encoding::UTF_8)
113
+ end
114
+ else
115
+ # Dummy encode string for rubies that do not support encoding hell
116
+ #
117
+ # @param [String] string
118
+ #
119
+ # @return [String]
120
+ #
121
+ # @api private
122
+ #
123
+ def self.utf8_encode(string)
124
+ string
125
+ end
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,19 @@
1
+ module AQL
2
+ # The list of AQL keywords from http://www.arangodb.org/manuals/current/Aql.html#AqlKeywords
3
+ KEYWORDS = IceNine.deep_freeze(%w(
4
+ FOR
5
+ RETURN
6
+ FILTER
7
+ SORT
8
+ LIMIT
9
+ LET
10
+ COLLECT
11
+ ASC
12
+ DESC
13
+ IN
14
+ INTO
15
+ NULL
16
+ TRUE
17
+ FALSE
18
+ ).to_set)
19
+ end
@@ -0,0 +1,45 @@
1
+ module AQL
2
+ # Abstract base class for AQL nodes
3
+ class Node
4
+ include Adamantium, AbstractType
5
+
6
+ # Return source representation
7
+ #
8
+ # @return [String]
9
+ #
10
+ # @api private
11
+ #
12
+ def aql
13
+ emitter = Buffer.new
14
+ emit(emitter)
15
+ emitter.content
16
+ end
17
+ memoize :aql
18
+
19
+ # Visit node
20
+ #
21
+ # @param [Buffer] buffer
22
+ #
23
+ # @return [self]
24
+ #
25
+ # @api private
26
+ #
27
+ def visit(buffer)
28
+ emit(buffer)
29
+ self
30
+ end
31
+
32
+ private
33
+
34
+ # Emit AQL
35
+ #
36
+ # @param [Emitter] emitter
37
+ #
38
+ # @return [undefined]
39
+ #
40
+ # @api private
41
+ #
42
+ abstract_method :emit
43
+
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ module AQL
2
+ class Node
3
+ # Attribute node
4
+ class Attribute < self
5
+ include Concord.new(:target, :name)
6
+
7
+ private
8
+
9
+ # Emit attribute
10
+ #
11
+ # @param [Buffer] buffer
12
+ #
13
+ # @return [undefined]
14
+ #
15
+ # @api private
16
+ #
17
+ def emit(buffer)
18
+ target.visit(buffer)
19
+ buffer.append('.')
20
+ name.visit(buffer)
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ module AQL
2
+ class Node
3
+ # Block of AQL statements
4
+ class Block < self
5
+ include Concord.new(:elements)
6
+
7
+ private
8
+
9
+ # Emit contents
10
+ #
11
+ # @param [Buffer] buffer
12
+ #
13
+ # @return [undefined]
14
+ #
15
+ # @api private
16
+ #
17
+ def emit(buffer)
18
+ local = elements
19
+ max = local.length - 1
20
+ local.each_with_index do |element, index|
21
+ element.visit(buffer)
22
+ buffer.append(' ') if index < max
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,83 @@
1
+ module AQL
2
+ class Node
3
+ # AST node for function call
4
+ class Call < self
5
+ include Concord.new(:name, :arguments)
6
+
7
+ private
8
+
9
+ # Emit node
10
+ #
11
+ # @param [Buffer] buffer
12
+ #
13
+ # @return [undefined]
14
+ #
15
+ # @api private
16
+ #
17
+ def emit(buffer)
18
+ buffer.append(name)
19
+ buffer.parentheses do
20
+ emit_arguments_with_for_check(buffer)
21
+ end
22
+ end
23
+
24
+ # Emit arguments with for check
25
+ #
26
+ # Handles edge case described in https://github.com/triAGENS/ArangoDB/issues/399
27
+ #
28
+ # @param [Buffer] buffer
29
+ #
30
+ # @return [undefined]
31
+ #
32
+ # @api private
33
+ #
34
+ def emit_arguments_with_for_check(buffer)
35
+ if for_argument?
36
+ emit_arguments_with_extra_parentheses(buffer)
37
+ else
38
+ emit_arguments(buffer)
39
+ end
40
+ end
41
+
42
+ # Emit arguments with parentheses
43
+ #
44
+ # @param [Buffer] buffer
45
+ #
46
+ # @return [undefined]
47
+ #
48
+ # @api private
49
+ #
50
+ def emit_arguments_with_extra_parentheses(buffer)
51
+ buffer.parentheses { emit_arguments(buffer) }
52
+ end
53
+
54
+ # Test for `for` body
55
+ #
56
+ # @return [true]
57
+ # if an instance of Node::Operation::For is the only argument
58
+ #
59
+ # @return [false]
60
+ # otherwise
61
+ #
62
+ # @api private
63
+ #
64
+ def for_argument?
65
+ local = arguments
66
+ local.length == 1 and local.first.kind_of?(Operation::For)
67
+ end
68
+
69
+ # Emit arguments
70
+ #
71
+ # @param [Buffer] buffer
72
+ #
73
+ # @return [undefined]
74
+ #
75
+ # @api private
76
+ #
77
+ def emit_arguments(buffer)
78
+ buffer.delimited(arguments)
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,48 @@
1
+ module AQL
2
+ class Node
3
+ # Abstract base class for literal nodes
4
+ class Literal < self
5
+ REGISTRY = {}
6
+
7
+ # Register handler
8
+ #
9
+ # @param [Class] klass
10
+ #
11
+ # @return [undefined]
12
+ #
13
+ # @api private
14
+ #
15
+ def self.handle(klass)
16
+ REGISTRY[klass]=self
17
+ end
18
+ private_class_method :handle
19
+
20
+ # Build literal handler
21
+ #
22
+ # @param [Object] object
23
+ #
24
+ # @return [Node::Literal]
25
+ #
26
+ # @api private
27
+ #
28
+ def self.build(object)
29
+ klass = object.class
30
+ handler = REGISTRY.fetch(klass) do
31
+ raise "No support for literal #{klass}"
32
+ end
33
+ handler.construct(object)
34
+ end
35
+
36
+ # Construct object
37
+ #
38
+ # @return [Node::Literal]
39
+ #
40
+ # @api private
41
+ #
42
+ def self.construct(*args)
43
+ new(*args)
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,10 @@
1
+ module AQL
2
+ class Node
3
+ class Literal
4
+ # Base class for composed literals
5
+ class Composed < self
6
+ include Concord.new(:body)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,59 @@
1
+ module AQL
2
+ class Node
3
+ class Literal
4
+ class Composed
5
+ # Literal document
6
+ class Document < self
7
+ handle(Hash)
8
+
9
+ # Construct node from primitive
10
+ #
11
+ # @param [Hash] hash
12
+ #
13
+ # @return [Node::Literal::Document]
14
+ #
15
+ # @api private
16
+ #
17
+ def self.construct(hash)
18
+ attributes = hash.map do |key, value|
19
+ Attribute.new(Literal.build(key), Literal.build(value))
20
+ end
21
+ new(attributes)
22
+ end
23
+
24
+ # Literal document attribute
25
+ class Attribute < Literal
26
+ include Concord.new(:key, :value)
27
+
28
+ private
29
+
30
+ # Emit node
31
+ #
32
+ # @return [self]
33
+ #
34
+ # @api private
35
+ #
36
+ def emit(buffer)
37
+ key.visit(buffer)
38
+ buffer.append(': ')
39
+ value.visit(buffer)
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ # Emit node
46
+ #
47
+ # @return [undefined]
48
+ #
49
+ # @api private
50
+ #
51
+ def emit(buffer)
52
+ buffer.wrap_delimited('{', body, '}')
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end