aql 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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