gqlite 1.5.0 → 1.7.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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/ext/Cargo.toml +12 -6
  3. data/ext/db-index/Cargo.toml +29 -0
  4. data/ext/db-index/src/hnsw/error.rs +66 -0
  5. data/ext/db-index/src/hnsw/graph_store.rs +272 -0
  6. data/ext/db-index/src/hnsw/id.rs +36 -0
  7. data/ext/db-index/src/hnsw/implementation.rs +1517 -0
  8. data/ext/db-index/src/hnsw/kernels.rs +1228 -0
  9. data/ext/db-index/src/hnsw/metric.rs +244 -0
  10. data/ext/db-index/src/hnsw/scalar.rs +72 -0
  11. data/ext/db-index/src/hnsw/simple_store.rs +140 -0
  12. data/ext/db-index/src/hnsw/store.rs +472 -0
  13. data/ext/db-index/src/hnsw/vector.rs +105 -0
  14. data/ext/db-index/src/hnsw/vectors.rs +568 -0
  15. data/ext/db-index/src/hnsw.rs +42 -0
  16. data/ext/db-index/src/lib.rs +3 -0
  17. data/ext/gqlitedb/Cargo.toml +19 -8
  18. data/ext/gqlitedb/benches/common/pokec.rs +62 -2
  19. data/ext/gqlitedb/benches/pokec_divan.rs +66 -2
  20. data/ext/gqlitedb/benches/pokec_iai.rs +60 -3
  21. data/ext/gqlitedb/release.toml +2 -2
  22. data/ext/gqlitedb/src/aggregators/arithmetic.rs +3 -1
  23. data/ext/gqlitedb/src/aggregators/containers.rs +1 -1
  24. data/ext/gqlitedb/src/aggregators/stats.rs +21 -12
  25. data/ext/gqlitedb/src/capi.rs +20 -24
  26. data/ext/gqlitedb/src/compiler/expression_analyser.rs +28 -20
  27. data/ext/gqlitedb/src/compiler/variables_manager.rs +104 -38
  28. data/ext/gqlitedb/src/compiler.rs +506 -227
  29. data/ext/gqlitedb/src/connection.rs +151 -11
  30. data/ext/gqlitedb/src/consts.rs +1 -2
  31. data/ext/gqlitedb/src/error.rs +123 -61
  32. data/ext/gqlitedb/src/functions/common.rs +64 -0
  33. data/ext/gqlitedb/src/functions/containers.rs +2 -46
  34. data/ext/gqlitedb/src/functions/edge.rs +1 -1
  35. data/ext/gqlitedb/src/functions/math.rs +87 -15
  36. data/ext/gqlitedb/src/functions/node.rs +1 -1
  37. data/ext/gqlitedb/src/functions/path.rs +48 -11
  38. data/ext/gqlitedb/src/functions/scalar.rs +47 -4
  39. data/ext/gqlitedb/src/functions/string.rs +123 -1
  40. data/ext/gqlitedb/src/functions/value.rs +1 -1
  41. data/ext/gqlitedb/src/functions.rs +165 -28
  42. data/ext/gqlitedb/src/graph.rs +1 -9
  43. data/ext/gqlitedb/src/interpreter/evaluators.rs +968 -130
  44. data/ext/gqlitedb/src/interpreter/instructions.rs +39 -2
  45. data/ext/gqlitedb/src/lib.rs +5 -4
  46. data/ext/gqlitedb/src/parser/gql.pest +1 -1
  47. data/ext/gqlitedb/src/planner.rs +329 -0
  48. data/ext/gqlitedb/src/prelude.rs +3 -3
  49. data/ext/gqlitedb/src/store/pgrx.rs +1 -1
  50. data/ext/gqlitedb/src/store/postgres.rs +742 -16
  51. data/ext/gqlitedb/src/store/redb/hnsw_store.rs +702 -0
  52. data/ext/gqlitedb/src/store/redb/index.rs +274 -0
  53. data/ext/gqlitedb/src/store/redb.rs +1268 -113
  54. data/ext/gqlitedb/src/store/sqlbase/sqlmetadata.rs +28 -0
  55. data/ext/gqlitedb/src/store/sqlbase/sqlstore.rs +103 -0
  56. data/ext/gqlitedb/src/store/sqlbase.rs +146 -16
  57. data/ext/gqlitedb/src/store/sqlite.rs +576 -14
  58. data/ext/gqlitedb/src/store/vector_extract.rs +56 -0
  59. data/ext/gqlitedb/src/store.rs +140 -29
  60. data/ext/gqlitedb/src/tests/compiler.rs +207 -10
  61. data/ext/gqlitedb/src/tests/connection/postgres.rs +38 -3
  62. data/ext/gqlitedb/src/tests/connection/redb.rs +23 -0
  63. data/ext/gqlitedb/src/tests/connection/sqlite.rs +31 -0
  64. data/ext/gqlitedb/src/tests/connection.rs +61 -1
  65. data/ext/gqlitedb/src/tests/evaluators.rs +162 -7
  66. data/ext/gqlitedb/src/tests/parser.rs +54 -23
  67. data/ext/gqlitedb/src/tests/planner.rs +511 -0
  68. data/ext/gqlitedb/src/tests/store/postgres.rs +7 -0
  69. data/ext/gqlitedb/src/tests/store/redb.rs +8 -0
  70. data/ext/gqlitedb/src/tests/store/sqlite.rs +8 -0
  71. data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +182 -0
  72. data/ext/gqlitedb/src/tests/store/vector_index/redb.rs +386 -0
  73. data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +166 -0
  74. data/ext/gqlitedb/src/tests/store/vector_index.rs +2313 -0
  75. data/ext/gqlitedb/src/tests/store.rs +78 -14
  76. data/ext/gqlitedb/src/tests/templates/ast.rs +92 -16
  77. data/ext/gqlitedb/src/tests/templates/programs.rs +14 -7
  78. data/ext/gqlitedb/src/tests.rs +15 -9
  79. data/ext/gqlitedb/src/utils.rs +6 -1
  80. data/ext/gqlitedb/src/value/compare.rs +61 -3
  81. data/ext/gqlitedb/src/value.rs +136 -7
  82. data/ext/gqlitedb/templates/sql/postgres/metadata_delete.sql +1 -0
  83. data/ext/gqlitedb/templates/sql/postgres/node_select.sql +1 -1
  84. data/ext/gqlitedb/templates/sql/sqlite/metadata_delete.sql +1 -0
  85. data/ext/gqliterb/src/lib.rs +53 -8
  86. data/ext/gqlparser/Cargo.toml +25 -0
  87. data/ext/gqlparser/README.MD +9 -0
  88. data/ext/gqlparser/benches/pokec_divan.rs +34 -0
  89. data/ext/gqlparser/src/common.rs +69 -0
  90. data/ext/gqlparser/src/gqls/ast.rs +69 -0
  91. data/ext/gqlparser/src/gqls/constraint.rs +79 -0
  92. data/ext/gqlparser/src/gqls/error.rs +22 -0
  93. data/ext/gqlparser/src/gqls/parser.rs +813 -0
  94. data/ext/gqlparser/src/gqls/prelude.rs +8 -0
  95. data/ext/gqlparser/src/gqls/properties.rs +115 -0
  96. data/ext/gqlparser/src/gqls/resolve.rs +207 -0
  97. data/ext/gqlparser/src/gqls.rs +265 -0
  98. data/ext/gqlparser/src/lib.rs +7 -0
  99. data/ext/gqlparser/src/oc/ast.rs +680 -0
  100. data/ext/gqlparser/src/oc/error.rs +172 -0
  101. data/ext/gqlparser/src/oc/lexer.rs +429 -0
  102. data/ext/gqlparser/src/oc/parser/tests.rs +2284 -0
  103. data/ext/gqlparser/src/oc/parser.rs +2005 -0
  104. data/ext/gqlparser/src/oc.rs +33 -0
  105. data/ext/gqlparser/src/prelude.rs +3 -0
  106. data/ext/graphcore/Cargo.toml +1 -0
  107. data/ext/graphcore/src/error.rs +26 -0
  108. data/ext/graphcore/src/graph.rs +177 -51
  109. data/ext/graphcore/src/lib.rs +4 -2
  110. data/ext/graphcore/src/open_cypher.rs +12 -0
  111. data/ext/graphcore/src/table.rs +50 -2
  112. data/ext/graphcore/src/timestamp.rs +127 -104
  113. data/ext/graphcore/src/value/tensor.rs +739 -0
  114. data/ext/graphcore/src/value/value_map.rs +1 -1
  115. data/ext/graphcore/src/value.rs +343 -19
  116. metadata +93 -28
  117. data/ext/gqlitedb/src/parser/ast.rs +0 -604
  118. data/ext/gqlitedb/src/parser/parser_impl.rs +0 -1213
  119. data/ext/gqlitedb/src/parser.rs +0 -4
@@ -0,0 +1,2005 @@
1
+ use std::str::FromStr;
2
+
3
+ use nom::{
4
+ Compare, Parser,
5
+ branch::alt,
6
+ combinator::{map, map_res, opt, value},
7
+ error::{FromExternalError, ParseError},
8
+ multi::{fold_many0, many0, many1, separated_list0, separated_list1},
9
+ sequence::{delimited, pair, preceded, separated_pair},
10
+ };
11
+
12
+ use crate::{
13
+ common::map_res_failure,
14
+ oc::{
15
+ Error, ast,
16
+ error::{ErrorKind, parse_int_error_to_error_kind},
17
+ lexer::{SpannedToken, Token},
18
+ },
19
+ };
20
+
21
+ impl<'a> ParseError<Input<'a>> for Error
22
+ {
23
+ fn from_error_kind(input: Input<'a>, _: nom::error::ErrorKind) -> Self
24
+ {
25
+ Error::new(
26
+ super::error::ErrorKind::UnexpectedToken,
27
+ input.first().map_or(Default::default(), |t| t.span.clone()),
28
+ )
29
+ }
30
+
31
+ fn append(_: Input<'a>, _: nom::error::ErrorKind, other: Self) -> Self
32
+ {
33
+ other
34
+ }
35
+ fn or(self, other: Self) -> Self
36
+ {
37
+ if self.span().is_empty() || other.span().start < self.span().start
38
+ {
39
+ other
40
+ }
41
+ else if other.span().is_empty() || self.span().start < other.span().start
42
+ {
43
+ self
44
+ }
45
+ else
46
+ {
47
+ if let ErrorKind::ExpectedTokens(mut a) = other.kind
48
+ && let ErrorKind::ExpectedTokens(b) = self.kind
49
+ {
50
+ a.0.extend(b.0);
51
+ return Error::new(ErrorKind::ExpectedTokens(a), self.span);
52
+ }
53
+ Error::new(ErrorKind::UnexpectedToken, self.span)
54
+ }
55
+ }
56
+ }
57
+
58
+ impl<'a> FromExternalError<Input<'a>, ErrorKind> for Error
59
+ {
60
+ fn from_external_error(a: Input<'a>, _: nom::error::ErrorKind, e: ErrorKind) -> Self
61
+ {
62
+ Error::new(e, a.first().map_or(Default::default(), |t| t.span.clone()))
63
+ }
64
+ }
65
+
66
+ pub(crate) type OCPResult<I, O> = nom::IResult<I, O, Error>;
67
+
68
+ #[cfg(test)]
69
+ mod tests;
70
+
71
+ #[derive(Clone, Debug)]
72
+ pub(super) struct Input<'a>(pub(super) &'a [SpannedToken], pub(super) &'a Context<'a>);
73
+
74
+ pub(super) struct TokenIterator<'a>
75
+ {
76
+ tokens: &'a [SpannedToken],
77
+ position: usize,
78
+ }
79
+
80
+ impl<'a> Iterator for TokenIterator<'a>
81
+ {
82
+ type Item = Token;
83
+
84
+ fn next(&mut self) -> Option<Self::Item>
85
+ {
86
+ if self.position >= self.tokens.len()
87
+ {
88
+ None
89
+ }
90
+ else
91
+ {
92
+ let token = &self.tokens[self.position];
93
+ self.position += 1;
94
+ Some(token.value)
95
+ }
96
+ }
97
+ }
98
+
99
+ pub(super) struct IndexTokenIterator<'a>
100
+ {
101
+ tokens: &'a [SpannedToken],
102
+ position: usize,
103
+ }
104
+
105
+ impl<'a> Iterator for IndexTokenIterator<'a>
106
+ {
107
+ type Item = (usize, Token);
108
+
109
+ fn next(&mut self) -> Option<Self::Item>
110
+ {
111
+ if self.position >= self.tokens.len()
112
+ {
113
+ None
114
+ }
115
+ else
116
+ {
117
+ let index = self.position;
118
+ let token = &self.tokens[self.position];
119
+ self.position += 1;
120
+ Some((index, token.value))
121
+ }
122
+ }
123
+ }
124
+
125
+ impl<'a> nom::Input for Input<'a>
126
+ {
127
+ type Item = Token;
128
+ type Iter = TokenIterator<'a>;
129
+ type IterIndices = IndexTokenIterator<'a>;
130
+
131
+ fn input_len(&self) -> usize
132
+ {
133
+ self.len()
134
+ }
135
+
136
+ fn iter_elements(&self) -> Self::Iter
137
+ {
138
+ TokenIterator {
139
+ tokens: self.0,
140
+ position: 0,
141
+ }
142
+ }
143
+
144
+ fn iter_indices(&self) -> Self::IterIndices
145
+ {
146
+ IndexTokenIterator {
147
+ tokens: self.0,
148
+ position: 0,
149
+ }
150
+ }
151
+
152
+ fn position<P>(&self, predicate: P) -> Option<usize>
153
+ where
154
+ P: Fn(Self::Item) -> bool,
155
+ {
156
+ self.iter().position(|token| predicate(token.value))
157
+ }
158
+
159
+ fn slice_index(&self, count: usize) -> Result<usize, nom::Needed>
160
+ {
161
+ if count <= self.len()
162
+ {
163
+ Ok(count)
164
+ }
165
+ else
166
+ {
167
+ Err(nom::Needed::new(count - self.len()))
168
+ }
169
+ }
170
+ fn take(&self, index: usize) -> Self
171
+ {
172
+ Input(&self.0[..index], self.1)
173
+ }
174
+ fn take_from(&self, index: usize) -> Self
175
+ {
176
+ Input(&self.0[index..], self.1)
177
+ }
178
+ fn take_split(&self, index: usize) -> (Self, Self)
179
+ {
180
+ (
181
+ Input(&self.0[..index], self.1),
182
+ Input(&self.0[index..], self.1),
183
+ )
184
+ }
185
+ }
186
+
187
+ impl<'a> Compare<Token> for Input<'a>
188
+ {
189
+ fn compare(&self, t: Token) -> nom::CompareResult
190
+ {
191
+ if self.0.is_empty()
192
+ {
193
+ nom::CompareResult::Incomplete
194
+ }
195
+ else if t == self.0[0].value
196
+ {
197
+ nom::CompareResult::Ok
198
+ }
199
+ else
200
+ {
201
+ nom::CompareResult::Error
202
+ }
203
+ }
204
+ fn compare_no_case(&self, t: Token) -> nom::CompareResult
205
+ {
206
+ self.compare(t)
207
+ }
208
+ }
209
+
210
+ impl<'a> std::ops::Deref for Input<'a>
211
+ {
212
+ type Target = [SpannedToken];
213
+ fn deref(&self) -> &Self::Target
214
+ {
215
+ self.0
216
+ }
217
+ }
218
+
219
+ #[derive(Debug)]
220
+ pub(super) struct Context<'a>
221
+ {
222
+ identifiers: ast::VariableIdentifiers,
223
+ source: &'a str,
224
+ }
225
+
226
+ impl<'a> Context<'a>
227
+ {
228
+ pub(super) fn new(source: &'a str) -> Self
229
+ {
230
+ Self {
231
+ identifiers: Default::default(),
232
+ source,
233
+ }
234
+ }
235
+ pub(super) fn extract_text(&self, span: &std::ops::Range<usize>) -> &str
236
+ {
237
+ &self.source[span.clone()]
238
+ }
239
+ }
240
+
241
+ fn token(c: Token) -> impl FnMut(Input) -> OCPResult<Input, Token>
242
+ {
243
+ move |input: Input<'_>| match input.0
244
+ {
245
+ [SpannedToken { value, span: _ }, rest @ ..] if *value == c => Ok((Input(rest, input.1), c)),
246
+ _ => Err(nom::Err::Error(Error::new(
247
+ ErrorKind::ExpectedTokens(vec![c].into()),
248
+ input.first().map_or(Default::default(), |t| t.span.clone()),
249
+ ))),
250
+ }
251
+ }
252
+
253
+ fn spanned_token(c: Token) -> impl FnMut(Input) -> OCPResult<Input, SpannedToken>
254
+ {
255
+ move |input: Input<'_>| match input.0
256
+ {
257
+ [SpannedToken { value, span }, rest @ ..] if *value == c => Ok((
258
+ Input(rest, input.1),
259
+ SpannedToken {
260
+ value: *value,
261
+ span: span.clone(),
262
+ },
263
+ )),
264
+ _ => Err(nom::Err::Error(Error::new(
265
+ ErrorKind::ExpectedTokens(vec![c].into()),
266
+ input.first().map_or(Default::default(), |t| t.span.clone()),
267
+ ))),
268
+ }
269
+ }
270
+
271
+ pub fn to_string<'a, TOutput: Into<String>, E: ParseError<Input<'a>>, F>(
272
+ parser: F,
273
+ ) -> impl nom::Parser<Input<'a>, Output = String, Error = E>
274
+ where
275
+ F: Parser<Input<'a>, Output = TOutput, Error = E>,
276
+ {
277
+ parser.map(|x| x.into())
278
+ }
279
+
280
+ // Identifiers and names
281
+ fn ident(input: Input<'_>) -> OCPResult<Input<'_>, &str>
282
+ {
283
+ match input.0
284
+ {
285
+ [SpannedToken { value, span }, rest @ ..]
286
+ if *value == Token::Identifier || value.is_keyword() =>
287
+ {
288
+ Ok((Input(rest, input.1), input.1.extract_text(span)))
289
+ }
290
+ _ => Err(nom::Err::Error(Error::new(
291
+ ErrorKind::ExpectedTokens(vec![Token::Identifier].into()),
292
+ input.first().map_or(Default::default(), |t| t.span.clone()),
293
+ ))),
294
+ }
295
+ }
296
+
297
+ fn parameter_name(input: Input<'_>) -> OCPResult<Input<'_>, &str>
298
+ {
299
+ match input.0
300
+ {
301
+ [
302
+ SpannedToken {
303
+ value: Token::ParameterName,
304
+ span,
305
+ },
306
+ rest @ ..,
307
+ ] => Ok((Input(rest, input.1), input.1.extract_text(span))),
308
+ _ => Err(nom::Err::Error(Error::new(
309
+ ErrorKind::ExpectedTokens(vec![Token::ParameterName].into()),
310
+ input.first().map_or(Default::default(), |t| t.span.clone()),
311
+ ))),
312
+ }
313
+ }
314
+ // Literals
315
+
316
+ fn string_literal(input: Input<'_>) -> OCPResult<Input<'_>, &str>
317
+ {
318
+ match input.0
319
+ {
320
+ [
321
+ SpannedToken {
322
+ value: Token::String,
323
+ span,
324
+ },
325
+ rest @ ..,
326
+ ] =>
327
+ {
328
+ let lit = input.1.extract_text(span);
329
+
330
+ Ok((Input(rest, input.1), &lit[1..lit.len() - 1]))
331
+ }
332
+ _ => Err(nom::Err::Error(Error::new(
333
+ ErrorKind::ExpectedTokens(vec![Token::String].into()),
334
+ input.first().map_or(Default::default(), |t| t.span.clone()),
335
+ ))),
336
+ }
337
+ }
338
+ fn int(input: Input) -> OCPResult<Input, i64>
339
+ {
340
+ match input.0
341
+ {
342
+ [
343
+ SpannedToken {
344
+ value: Token::HexaDecimal,
345
+ span,
346
+ },
347
+ rest @ ..,
348
+ ] => Ok((Input(rest, input.1), {
349
+ let text = input.1.extract_text(span);
350
+ i64::from_str_radix(&text[2..], 16)
351
+ .map_err(|e| nom::Err::Failure(Error::from_parse_int_error(e, span.clone())))?
352
+ })),
353
+ [
354
+ SpannedToken {
355
+ value: Token::OctaDecimal,
356
+ span,
357
+ },
358
+ rest @ ..,
359
+ ] => Ok((Input(rest, input.1), {
360
+ let text = input.1.extract_text(span);
361
+ i64::from_str_radix(&text[2..], 8)
362
+ .map_err(|e| nom::Err::Failure(Error::from_parse_int_error(e, span.clone())))?
363
+ })),
364
+ [
365
+ SpannedToken {
366
+ value: Token::Integer,
367
+ span,
368
+ },
369
+ rest @ ..,
370
+ ] => Ok((Input(rest, input.1), {
371
+ let text = input.1.extract_text(span);
372
+ i64::from_str(text)
373
+ .map_err(|e| nom::Err::Failure(Error::from_parse_int_error(e, span.clone())))?
374
+ })),
375
+ _ => Err(nom::Err::Error(Error::new(
376
+ ErrorKind::ExpectedTokens(vec![Token::Integer].into()),
377
+ input.first().map_or(Default::default(), |t| t.span.clone()),
378
+ ))),
379
+ }
380
+ }
381
+ fn integer_dotdot(input: Input) -> OCPResult<Input, i64>
382
+ {
383
+ match input.0
384
+ {
385
+ [
386
+ SpannedToken {
387
+ value: Token::IntegerDotDot,
388
+ span,
389
+ },
390
+ rest @ ..,
391
+ ] => Ok((Input(rest, input.1), {
392
+ let text = input.1.extract_text(span);
393
+ i64::from_str(&text[..text.len() - 2])
394
+ .map_err(|e| nom::Err::Failure(Error::from_parse_int_error(e, span.clone())))?
395
+ })),
396
+ _ => Err(nom::Err::Error(Error::new(
397
+ ErrorKind::ExpectedTokens(vec![Token::Integer].into()),
398
+ input.first().map_or(Default::default(), |t| t.span.clone()),
399
+ ))),
400
+ }
401
+ }
402
+ fn num(input: Input) -> OCPResult<Input, f64>
403
+ {
404
+ match input.0
405
+ {
406
+ [
407
+ SpannedToken {
408
+ value: Token::Number,
409
+ span,
410
+ },
411
+ rest @ ..,
412
+ ] => Ok((Input(rest, input.1), {
413
+ let text = input.1.extract_text(span);
414
+ let v = f64::from_str(text).map_err(|_| {
415
+ nom::Err::Failure(Error::new(ErrorKind::MalformedFloatingPoint, span.clone()))
416
+ })?;
417
+ if v.is_finite()
418
+ {
419
+ v
420
+ }
421
+ else
422
+ {
423
+ return Err(nom::Err::Failure(Error::new(
424
+ ErrorKind::FloatingPointOverflow,
425
+ span.clone(),
426
+ )));
427
+ }
428
+ })),
429
+ _ => Err(nom::Err::Error(Error::new(
430
+ ErrorKind::ExpectedTokens(vec![Token::Number].into()),
431
+ input.first().map_or(Default::default(), |t| t.span.clone()),
432
+ ))),
433
+ }
434
+ }
435
+
436
+ fn function_call(input: Input) -> OCPResult<Input, ast::Expression>
437
+ {
438
+ map(
439
+ pair(
440
+ to_string(ident),
441
+ delimited(
442
+ token(Token::StartParenthesis),
443
+ separated_list0(token(Token::Comma), expression),
444
+ token(Token::EndParenthesis),
445
+ ),
446
+ ),
447
+ |(name, arguments)| ast::FunctionCall { name, arguments }.into(),
448
+ )
449
+ .parse(input)
450
+ }
451
+
452
+ fn primary(input: Input) -> OCPResult<Input, ast::Expression>
453
+ {
454
+ alt((
455
+ map(token(Token::Star), |_| {
456
+ ast::Value { value: 0.into() }.into()
457
+ }),
458
+ // Parenthesis
459
+ delimited(
460
+ token(Token::StartParenthesis),
461
+ expression,
462
+ token(Token::EndParenthesis),
463
+ ),
464
+ // function call,
465
+ function_call,
466
+ // Map
467
+ map_literal,
468
+ // Array
469
+ array_literal,
470
+ // Parameter
471
+ parameter,
472
+ // Number
473
+ map(num, |n| {
474
+ ast::Value {
475
+ value: graphcore::Value::Float(n),
476
+ }
477
+ .into()
478
+ }),
479
+ map(int, |i| {
480
+ ast::Value {
481
+ value: graphcore::Value::Integer(i),
482
+ }
483
+ .into()
484
+ }),
485
+ // String
486
+ map(string_literal, |s: &str| {
487
+ ast::Value {
488
+ value: graphcore::Value::String(s.to_string()),
489
+ }
490
+ .into()
491
+ }),
492
+ map(token(Token::Null), |_| {
493
+ ast::Value {
494
+ value: graphcore::Value::Null,
495
+ }
496
+ .into()
497
+ }),
498
+ map(token(Token::True), |_| {
499
+ ast::Value { value: true.into() }.into()
500
+ }),
501
+ map(token(Token::False), |_| {
502
+ ast::Value {
503
+ value: false.into(),
504
+ }
505
+ .into()
506
+ }),
507
+ // Variable
508
+ map(ident, |name: &str| {
509
+ ast::Variable {
510
+ identifier: input.1.identifiers.create_variable_from_name(name),
511
+ }
512
+ .into()
513
+ }),
514
+ ))
515
+ .parse(input)
516
+ }
517
+
518
+ fn member_access(input: Input) -> OCPResult<Input, ast::Expression>
519
+ {
520
+ map(
521
+ pair(
522
+ primary,
523
+ many0(preceded(
524
+ token(Token::Dot),
525
+ to_string(alt((ident, string_literal))),
526
+ )),
527
+ ),
528
+ |(left, path)| {
529
+ if path.is_empty()
530
+ {
531
+ left
532
+ }
533
+ else
534
+ {
535
+ ast::MemberAccess { left, path }.into()
536
+ }
537
+ },
538
+ )
539
+ .parse(input)
540
+ }
541
+ fn array_access(input: Input) -> OCPResult<Input, ast::Expression>
542
+ {
543
+ let (input, base) = member_access(input)?;
544
+
545
+ fold_many0(
546
+ alt((
547
+ map(
548
+ delimited(
549
+ token(Token::StartBracket),
550
+ (opt(expression), token(Token::DotDot), opt(expression)),
551
+ token(Token::EndBracket),
552
+ ),
553
+ |(start, _, end)| {
554
+ Box::new(move |left| ast::RangeAccess { left, start, end }.into())
555
+ as Box<dyn FnOnce(ast::Expression) -> ast::Expression>
556
+ },
557
+ ),
558
+ map(
559
+ delimited(
560
+ token(Token::StartBracket),
561
+ (integer_dotdot, opt(expression)),
562
+ token(Token::EndBracket),
563
+ ),
564
+ |(start, end)| {
565
+ Box::new(move |left| {
566
+ ast::RangeAccess {
567
+ left,
568
+ start: Some(
569
+ ast::Value {
570
+ value: start.into(),
571
+ }
572
+ .into(),
573
+ ),
574
+ end,
575
+ }
576
+ .into()
577
+ }) as Box<dyn FnOnce(ast::Expression) -> ast::Expression>
578
+ },
579
+ ),
580
+ map(
581
+ delimited(
582
+ token(Token::StartBracket),
583
+ expression,
584
+ token(Token::EndBracket),
585
+ ),
586
+ |index| {
587
+ Box::new(move |left| ast::IndexAccess { left, index }.into())
588
+ as Box<dyn FnOnce(ast::Expression) -> ast::Expression>
589
+ },
590
+ ),
591
+ )),
592
+ move || base.clone(),
593
+ |left, f| f(left),
594
+ )
595
+ .parse(input)
596
+ }
597
+
598
+ fn label_check(input: Input) -> OCPResult<Input, ast::Expression>
599
+ {
600
+ map(
601
+ (array_access, many0((token(Token::Colon), ident))),
602
+ |(expr, labels)| {
603
+ if labels.is_empty()
604
+ {
605
+ expr
606
+ }
607
+ else
608
+ {
609
+ ast::FunctionCall {
610
+ name: "has_labels".to_string(),
611
+ arguments: vec![expr]
612
+ .into_iter()
613
+ .chain(labels.into_iter().map(|(_, label)| {
614
+ ast::Value {
615
+ value: label.into(),
616
+ }
617
+ .into()
618
+ }))
619
+ .collect(),
620
+ }
621
+ .into()
622
+ }
623
+ },
624
+ )
625
+ .parse(input)
626
+ }
627
+
628
+ fn unary(input: Input) -> OCPResult<Input, ast::Expression>
629
+ {
630
+ alt((
631
+ map_res_failure(
632
+ preceded(token(Token::Minus), spanned_token(Token::Integer)),
633
+ |i| {
634
+ let span_with_minus = i.span.start - 1..i.span.end;
635
+ let text = input.1.extract_text(&span_with_minus);
636
+ Ok(
637
+ ast::Value {
638
+ value: i64::from_str(text)
639
+ .map_err(parse_int_error_to_error_kind)?
640
+ .into(),
641
+ }
642
+ .into(),
643
+ )
644
+ },
645
+ ),
646
+ map_res_failure(
647
+ preceded(token(Token::Minus), spanned_token(Token::OctaDecimal)),
648
+ |i| {
649
+ let text = input.1.extract_text(&i.span);
650
+ Ok(
651
+ ast::Value {
652
+ value: i64::from_str_radix(&format!("-{}", &text[2..]), 8)
653
+ .map_err(parse_int_error_to_error_kind)?
654
+ .into(),
655
+ }
656
+ .into(),
657
+ )
658
+ },
659
+ ),
660
+ map_res_failure(
661
+ preceded(token(Token::Minus), spanned_token(Token::HexaDecimal)),
662
+ |i| {
663
+ let text = input.1.extract_text(&i.span);
664
+ Ok(
665
+ ast::Value {
666
+ value: i64::from_str_radix(&format!("-{}", &text[2..]), 16)
667
+ .map_err(parse_int_error_to_error_kind)?
668
+ .into(),
669
+ }
670
+ .into(),
671
+ )
672
+ },
673
+ ),
674
+ map(preceded(token(Token::Not), unary), |e| {
675
+ ast::LogicalNegation { value: e }.into()
676
+ }),
677
+ map(preceded(token(Token::Minus), unary), |e| match e
678
+ {
679
+ ast::Expression::Value(ast::Value {
680
+ value: graphcore::Value::Integer(i),
681
+ }) => ast::Value {
682
+ value: graphcore::Value::Integer(-i),
683
+ }
684
+ .into(),
685
+ ast::Expression::Value(ast::Value {
686
+ value: graphcore::Value::Float(f),
687
+ }) => ast::Value {
688
+ value: graphcore::Value::Float(-f),
689
+ }
690
+ .into(),
691
+ _ => ast::Negation { value: e }.into(),
692
+ }),
693
+ preceded(token(Token::Plus), unary),
694
+ label_check,
695
+ ))
696
+ .parse(input)
697
+ }
698
+
699
+ fn exponent(input: Input) -> OCPResult<Input, ast::Expression>
700
+ {
701
+ map(
702
+ pair(unary, opt(preceded(token(Token::Caret), exponent))),
703
+ |(l, r)| {
704
+ if let Some(r) = r
705
+ {
706
+ ast::Exponent { left: l, right: r }.into()
707
+ }
708
+ else
709
+ {
710
+ l
711
+ }
712
+ },
713
+ )
714
+ .parse(input)
715
+ }
716
+
717
+ fn multiplicative(input: Input) -> OCPResult<Input, ast::Expression>
718
+ {
719
+ let (input, first) = exponent(input)?;
720
+ let (input, rest) = many0(pair(
721
+ alt((
722
+ token(Token::Star),
723
+ token(Token::Slash),
724
+ token(Token::Percent),
725
+ )),
726
+ exponent,
727
+ ))
728
+ .parse(input)?;
729
+
730
+ let expr = rest.into_iter().fold(first, |left, (op, right)| match op
731
+ {
732
+ Token::Star => ast::Multiplication { left, right }.into(),
733
+ Token::Slash => ast::Division { left, right }.into(),
734
+ Token::Percent => ast::Modulo { left, right }.into(),
735
+ _ => unreachable!(),
736
+ });
737
+
738
+ Ok((input, expr))
739
+ }
740
+
741
+ fn additive(input: Input) -> OCPResult<Input, ast::Expression>
742
+ {
743
+ let (input, first) = multiplicative(input)?;
744
+ let (input, rest) = many0(pair(
745
+ alt((token(Token::Plus), token(Token::Minus))),
746
+ multiplicative,
747
+ ))
748
+ .parse(input)?;
749
+
750
+ let expr = rest.into_iter().fold(first, |left, (op, right)| match op
751
+ {
752
+ Token::Plus => ast::Addition { left, right }.into(),
753
+ Token::Minus => ast::Subtraction { left, right }.into(),
754
+ _ => unreachable!(),
755
+ });
756
+
757
+ Ok((input, expr))
758
+ }
759
+
760
+ fn is_not_null(input: Input) -> OCPResult<Input, ast::Expression>
761
+ {
762
+ map(
763
+ (
764
+ additive,
765
+ opt(alt((
766
+ value(1, (token(Token::Is), token(Token::Not), token(Token::Null))),
767
+ value(0, (token(Token::Is), token(Token::Null))),
768
+ ))),
769
+ ),
770
+ |(expr, opt)| match opt
771
+ {
772
+ Some(1) => ast::IsNotNull { value: expr }.into(),
773
+ Some(0) => ast::IsNull { value: expr }.into(),
774
+ Some(_) => unreachable!(),
775
+ None => expr,
776
+ },
777
+ )
778
+ .parse(input)
779
+ }
780
+
781
+ fn relational(input: Input) -> OCPResult<Input, ast::Expression>
782
+ {
783
+ let (input, left) = is_not_null(input)?;
784
+
785
+ let ops = alt((
786
+ token(Token::In),
787
+ token(Token::Equal),
788
+ token(Token::Different),
789
+ token(Token::LessEqual),
790
+ token(Token::GreaterEqual),
791
+ token(Token::Less),
792
+ token(Token::Greater),
793
+ ));
794
+
795
+ let (input, opt) = opt(pair(ops, is_not_null)).parse(input)?;
796
+
797
+ if let Some((op, right)) = opt
798
+ {
799
+ let expr = match op
800
+ {
801
+ Token::In => ast::RelationalIn { left, right }.into(),
802
+ Token::Equal => ast::RelationalEqual { left, right }.into(),
803
+ Token::Different => ast::RelationalDifferent { left, right }.into(),
804
+ Token::Less => ast::RelationalInferior { left, right }.into(),
805
+ Token::Greater => ast::RelationalSuperior { left, right }.into(),
806
+ Token::LessEqual => ast::RelationalInferiorEqual { left, right }.into(),
807
+ Token::GreaterEqual => ast::RelationalSuperiorEqual { left, right }.into(),
808
+ _ => unreachable!(),
809
+ };
810
+ Ok((input, expr))
811
+ }
812
+ else
813
+ {
814
+ Ok((input, left))
815
+ }
816
+ }
817
+
818
+ fn logical_and(input: Input) -> OCPResult<Input, ast::Expression>
819
+ {
820
+ map(separated_list1(token(Token::And), relational), |mut v| {
821
+ let first = v.remove(0);
822
+ v.into_iter()
823
+ .fold(first, |l, r| ast::LogicalAnd { left: l, right: r }.into())
824
+ })
825
+ .parse(input)
826
+ }
827
+
828
+ fn logical_or(input: Input) -> OCPResult<Input, ast::Expression>
829
+ {
830
+ map(separated_list1(token(Token::Or), logical_and), |mut v| {
831
+ let first = v.remove(0);
832
+ v.into_iter()
833
+ .fold(first, |l, r| ast::LogicalOr { left: l, right: r }.into())
834
+ })
835
+ .parse(input)
836
+ }
837
+
838
+ fn logical_xor(input: Input) -> OCPResult<Input, ast::Expression>
839
+ {
840
+ map(separated_list1(token(Token::Xor), logical_or), |mut v| {
841
+ let first = v.remove(0);
842
+ v.into_iter()
843
+ .fold(first, |l, r| ast::LogicalXor { left: l, right: r }.into())
844
+ })
845
+ .parse(input)
846
+ }
847
+
848
+ // Expression parsing
849
+ fn expression(input: Input) -> OCPResult<Input, ast::Expression>
850
+ {
851
+ logical_xor(input)
852
+ }
853
+
854
+ fn labels_and(input: Input) -> OCPResult<Input, ast::LabelExpression>
855
+ {
856
+ map(
857
+ separated_list1(token(Token::Colon), ident),
858
+ |labels: Vec<&str>| {
859
+ if labels.len() == 1
860
+ {
861
+ ast::LabelExpression::String(labels[0].to_string())
862
+ }
863
+ else
864
+ {
865
+ ast::LabelExpression::And(
866
+ labels
867
+ .into_iter()
868
+ .map(|label| ast::LabelExpression::String(label.to_string()))
869
+ .collect(),
870
+ )
871
+ }
872
+ },
873
+ )
874
+ .parse(input)
875
+ }
876
+
877
+ // Pattern parsing
878
+ fn labels(input: Input) -> OCPResult<Input, ast::LabelExpression>
879
+ {
880
+ map(
881
+ preceded(
882
+ token(Token::Colon),
883
+ pair(
884
+ labels_and,
885
+ opt(preceded(
886
+ token(Token::Pipe),
887
+ separated_list1(
888
+ token(Token::Pipe),
889
+ preceded(opt(token(Token::Colon)), labels_and),
890
+ ),
891
+ )),
892
+ ),
893
+ ),
894
+ |(first_label, other_labels)| match other_labels
895
+ {
896
+ None => first_label,
897
+ Some(mut other_labels) =>
898
+ {
899
+ other_labels.insert(0, first_label);
900
+ ast::LabelExpression::Or(other_labels)
901
+ }
902
+ },
903
+ )
904
+ .parse(input)
905
+ }
906
+
907
+ fn map_pair(input: Input) -> OCPResult<Input, (String, ast::Expression)>
908
+ {
909
+ separated_pair(
910
+ to_string(alt((ident, string_literal))),
911
+ token(Token::Colon),
912
+ expression,
913
+ )
914
+ .parse(input)
915
+ }
916
+
917
+ fn map_literal(input: Input) -> OCPResult<Input, ast::Expression>
918
+ {
919
+ map(
920
+ delimited(
921
+ token(Token::StartBrace),
922
+ opt(separated_list0(token(Token::Comma), map_pair)),
923
+ token(Token::EndBrace),
924
+ ),
925
+ |pairs: Option<Vec<(String, ast::Expression)>>| {
926
+ ast::Expression::Map(ast::Map {
927
+ map: pairs.unwrap_or_default().into_iter().collect(),
928
+ })
929
+ },
930
+ )
931
+ .parse(input)
932
+ }
933
+
934
+ fn array_literal(input: Input) -> OCPResult<Input, ast::Expression>
935
+ {
936
+ map(
937
+ delimited(
938
+ token(Token::StartBracket),
939
+ opt(separated_list1(token(Token::Comma), expression)),
940
+ token(Token::EndBracket),
941
+ ),
942
+ |exprs: Option<Vec<ast::Expression>>| {
943
+ ast::Expression::Array(ast::Array {
944
+ array: exprs.unwrap_or_default(),
945
+ })
946
+ },
947
+ )
948
+ .parse(input)
949
+ }
950
+
951
+ fn node_pattern(input: Input) -> OCPResult<Input, ast::NodePattern>
952
+ {
953
+ delimited(
954
+ token(Token::StartParenthesis),
955
+ map(
956
+ (
957
+ // Optional variable
958
+ opt(ident),
959
+ // Optional labels (:Label1:Label2)
960
+ opt(labels),
961
+ // Optional properties map or parameter
962
+ opt(properties),
963
+ ),
964
+ |(ident, labels, properties)| ast::NodePattern {
965
+ variable: input
966
+ .1
967
+ .identifiers
968
+ .create_variable_from_name_optional(ident),
969
+ labels: labels.unwrap_or(ast::LabelExpression::None),
970
+ properties,
971
+ },
972
+ ),
973
+ token(Token::EndParenthesis),
974
+ )
975
+ .parse(input)
976
+ }
977
+
978
+ enum EdgeDirection
979
+ {
980
+ Left,
981
+ Right,
982
+ Undirected,
983
+ }
984
+
985
+ trait EdgeMode
986
+ {
987
+ const ALLOW_UNDIRECTED: bool;
988
+ const ALLOW_LABEL_ALTERNATIVE: bool;
989
+ }
990
+
991
+ struct AllowUndirected;
992
+ impl EdgeMode for AllowUndirected
993
+ {
994
+ const ALLOW_UNDIRECTED: bool = true;
995
+ const ALLOW_LABEL_ALTERNATIVE: bool = true;
996
+ }
997
+
998
+ struct DisallowUndirected;
999
+ impl EdgeMode for DisallowUndirected
1000
+ {
1001
+ const ALLOW_UNDIRECTED: bool = false;
1002
+ const ALLOW_LABEL_ALTERNATIVE: bool = false;
1003
+ }
1004
+
1005
+ struct EdgePattern
1006
+ {
1007
+ edge_direction: EdgeDirection,
1008
+ edge_variable: Option<ast::VariableIdentifier>,
1009
+ labels: ast::LabelExpression,
1010
+ properties: Option<ast::Expression>,
1011
+ recursive_range: Option<std::ops::Range<usize>>,
1012
+ destination_node: ast::NodePattern,
1013
+ }
1014
+
1015
+ fn edge_range(input: Input) -> OCPResult<Input, std::ops::Range<usize>>
1016
+ {
1017
+ map(
1018
+ (
1019
+ token(Token::Star),
1020
+ opt(alt((
1021
+ map(pair(integer_dotdot, opt(int)), |(min, max)| {
1022
+ (Some(min as usize), max.map(|x| x as usize))
1023
+ }),
1024
+ map(pair(token(Token::DotDot), opt(int)), |(_, max)| {
1025
+ (Some(0usize), max.map(|x| x as usize))
1026
+ }),
1027
+ map(int, |n| (Some(n as usize), Some(n as usize))),
1028
+ ))),
1029
+ ),
1030
+ |(_, range)| match range
1031
+ {
1032
+ Some((min, max)) => min.unwrap_or(0)..max.unwrap_or(usize::MAX),
1033
+ // Default for an unspecified range (i.e., `[*]`) is 1.., not 0..
1034
+ // so that `[*]` matches at least one relationship. This aligns
1035
+ // with openCypher TCK expectations for variable-length patterns.
1036
+ None => 1..usize::MAX,
1037
+ },
1038
+ )
1039
+ .parse(input)
1040
+ }
1041
+
1042
+ fn edge_pattern<T: EdgeMode>(input: Input) -> OCPResult<Input, EdgePattern>
1043
+ {
1044
+ map_res_failure(
1045
+ (
1046
+ alt((
1047
+ map(token(Token::RightArrow), |_| EdgeDirection::Right),
1048
+ map(token(Token::Minus), |_| EdgeDirection::Undirected),
1049
+ map(token(Token::LeftArrow), |_| EdgeDirection::Left),
1050
+ )),
1051
+ opt((
1052
+ token(Token::StartBracket),
1053
+ // Optional variable
1054
+ opt(ident),
1055
+ // Optional labels (:Label1:Label2)
1056
+ opt(labels),
1057
+ // Optional range, used for recursion
1058
+ opt(edge_range),
1059
+ // Optional properties map or parameter
1060
+ opt(properties),
1061
+ token(Token::EndBracket),
1062
+ )),
1063
+ alt((
1064
+ map(token(Token::RightArrow), |_| EdgeDirection::Right),
1065
+ map(token(Token::Minus), |_| EdgeDirection::Undirected),
1066
+ map(token(Token::LeftArrow), |_| EdgeDirection::Left),
1067
+ )),
1068
+ node_pattern, // Right node
1069
+ ),
1070
+ |(first_direction, edge_pattern, second_direction, destination_node)| {
1071
+ let (edge_variable, recursive_range, labels, properties) = match edge_pattern
1072
+ {
1073
+ Some((_, var, labels, recursive_range, properties, _)) =>
1074
+ {
1075
+ (var, recursive_range, labels, properties)
1076
+ }
1077
+ None => (None, None, None, None),
1078
+ };
1079
+ let labels = labels.unwrap_or(ast::LabelExpression::None);
1080
+ if !T::ALLOW_LABEL_ALTERNATIVE && !labels.is_all_inclusive()
1081
+ {
1082
+ return Err(ErrorKind::NoSingleRelationshipType);
1083
+ }
1084
+ let edge_direction = match (first_direction, second_direction)
1085
+ {
1086
+ (EdgeDirection::Right, EdgeDirection::Right)
1087
+ | (EdgeDirection::Right, EdgeDirection::Undirected)
1088
+ | (EdgeDirection::Undirected, EdgeDirection::Right) => EdgeDirection::Right,
1089
+ (EdgeDirection::Left, EdgeDirection::Left)
1090
+ | (EdgeDirection::Left, EdgeDirection::Undirected)
1091
+ | (EdgeDirection::Undirected, EdgeDirection::Left) => EdgeDirection::Left,
1092
+ (EdgeDirection::Undirected, EdgeDirection::Undirected)
1093
+ | (EdgeDirection::Left, EdgeDirection::Right) =>
1094
+ {
1095
+ if !T::ALLOW_UNDIRECTED
1096
+ {
1097
+ // Undirected edges are not allowed in this context, treat as Error
1098
+ return Err(ErrorKind::RequiresDirectedEdge);
1099
+ }
1100
+ EdgeDirection::Undirected
1101
+ }
1102
+ (EdgeDirection::Right, EdgeDirection::Left) =>
1103
+ {
1104
+ // Conflicting directions, treat as Error
1105
+ return Err(ErrorKind::ConflictingEdgeDirections);
1106
+ }
1107
+ };
1108
+ let edge_variable = edge_variable.map(|id| input.1.identifiers.create_variable_from_name(id));
1109
+ Ok(EdgePattern {
1110
+ edge_direction,
1111
+ edge_variable,
1112
+ labels,
1113
+ properties,
1114
+ destination_node,
1115
+ recursive_range,
1116
+ })
1117
+ },
1118
+ )
1119
+ .parse(input.clone())
1120
+ }
1121
+
1122
+ fn node_or_edges_patterns<T: EdgeMode>(input: Input) -> OCPResult<Input, Vec<ast::Pattern>>
1123
+ {
1124
+ let (input, node) = node_pattern(input)?;
1125
+
1126
+ let (input, edges) = many0(edge_pattern::<T>).parse(input)?;
1127
+
1128
+ if edges.is_empty()
1129
+ {
1130
+ Ok((input, vec![ast::Pattern::Node(node)]))
1131
+ }
1132
+ else
1133
+ {
1134
+ let mut current_node = node;
1135
+ let mut iterator = edges.into_iter().peekable();
1136
+ let mut patterns = Vec::new();
1137
+ while let Some(edge) = iterator.next()
1138
+ {
1139
+ let EdgePattern {
1140
+ edge_direction,
1141
+ edge_variable,
1142
+ labels,
1143
+ properties,
1144
+ mut destination_node,
1145
+ recursive_range,
1146
+ } = edge;
1147
+ if destination_node.variable.is_none() && iterator.peek().is_some()
1148
+ {
1149
+ destination_node.variable = Some(input.1.identifiers.create_anonymous_variable());
1150
+ }
1151
+ let source = current_node;
1152
+ current_node = destination_node.clone();
1153
+ let (source, directivity, destination) = match edge_direction
1154
+ {
1155
+ EdgeDirection::Right => (
1156
+ source,
1157
+ graphcore::EdgeDirectivity::Directed,
1158
+ destination_node,
1159
+ ),
1160
+ EdgeDirection::Left => (
1161
+ destination_node,
1162
+ graphcore::EdgeDirectivity::Directed,
1163
+ source,
1164
+ ),
1165
+ EdgeDirection::Undirected => (
1166
+ source,
1167
+ graphcore::EdgeDirectivity::Undirected,
1168
+ destination_node,
1169
+ ),
1170
+ };
1171
+ patterns.push(ast::Pattern::Edge(ast::EdgePattern {
1172
+ source,
1173
+ variable: edge_variable,
1174
+ directivity,
1175
+ labels,
1176
+ properties,
1177
+ destination,
1178
+ recursive_range,
1179
+ }));
1180
+ }
1181
+
1182
+ Ok((input, patterns))
1183
+ }
1184
+ }
1185
+
1186
+ fn patterns<T: EdgeMode>(input: Input) -> OCPResult<Input, Vec<ast::Pattern>>
1187
+ {
1188
+ let context = &input.1;
1189
+ alt((
1190
+ map_res(
1191
+ (ident, token(Token::Equal), node_or_edges_patterns::<T>),
1192
+ |(name, _, patterns)| {
1193
+ // Accept path assignment for one or more consecutive edges by combining them into a single EdgePattern.
1194
+ if patterns.is_empty()
1195
+ {
1196
+ return Err(ErrorKind::PathAcceptSingleEdge);
1197
+ }
1198
+ // Ensure all inner patterns are edges
1199
+ if !patterns.iter().all(|p| matches!(p, ast::Pattern::Edge(_)))
1200
+ {
1201
+ return Err(ErrorKind::PathAcceptSingleEdge);
1202
+ }
1203
+ // Consume the patterns and extract their EdgePattern values
1204
+ let mut edges: Vec<ast::EdgePattern> = patterns
1205
+ .into_iter()
1206
+ .map(|p| match p
1207
+ {
1208
+ ast::Pattern::Edge(e) => e,
1209
+ _ => unreachable!(),
1210
+ })
1211
+ .collect();
1212
+ // Combine edges into a single EdgePattern by taking the first source and last destination.
1213
+ // Preserve an inner edge variable if exactly one inner edge is named.
1214
+ let combined_variable = {
1215
+ let mut found: Option<ast::VariableIdentifier> = None;
1216
+ for e in edges.iter()
1217
+ {
1218
+ if let Some(var) = &e.variable
1219
+ {
1220
+ if found.is_some()
1221
+ {
1222
+ // multiple inner variables; do not attempt to preserve a single one
1223
+ found = None;
1224
+ break;
1225
+ }
1226
+ else
1227
+ {
1228
+ found = Some(var.clone());
1229
+ }
1230
+ }
1231
+ }
1232
+ found
1233
+ };
1234
+ let original_count = edges.len();
1235
+ let first_edge = edges.remove(0);
1236
+ let last_edge = edges.pop().unwrap_or_else(|| first_edge.clone());
1237
+ // For a single-edge path, preserve that edge's labels, properties, and recursive_range.
1238
+ // For multi-hop paths, compute the combined hop range (each fixed edge = 1 hop,
1239
+ // var-length edge contributes its own range). Use original_count (before removes)
1240
+ // to correctly distinguish single-edge (N=1) from two-edge (N=2) paths.
1241
+ let (combined_labels, combined_properties, combined_recursive_range) = if original_count
1242
+ == 1
1243
+ {
1244
+ // Single edge: preserve its constraints.
1245
+ (
1246
+ first_edge.labels.clone(),
1247
+ first_edge.properties.clone(),
1248
+ first_edge.recursive_range.clone(),
1249
+ )
1250
+ }
1251
+ else
1252
+ {
1253
+ // Multi-hop: accumulate min and max hops across all constituent edges.
1254
+ // Fixed edges contribute 1 hop each; var-length edges contribute their range.
1255
+ let hop_range = |e: &ast::EdgePattern| -> (usize, usize) {
1256
+ match &e.recursive_range
1257
+ {
1258
+ Some(r) => (r.start, r.end),
1259
+ None => (1, 1),
1260
+ }
1261
+ };
1262
+ let (mut min_total, mut max_total) = hop_range(&first_edge);
1263
+ for e in &edges
1264
+ {
1265
+ let (mn, mx) = hop_range(e);
1266
+ min_total += mn;
1267
+ max_total = max_total.saturating_add(mx);
1268
+ }
1269
+ let (mn, mx) = hop_range(&last_edge);
1270
+ min_total += mn;
1271
+ max_total = max_total.saturating_add(mx);
1272
+ (ast::LabelExpression::None, None, Some(min_total..max_total))
1273
+ };
1274
+ let combined_edge = ast::EdgePattern {
1275
+ variable: combined_variable,
1276
+ source: first_edge.source,
1277
+ destination: last_edge.destination,
1278
+ directivity: first_edge.directivity,
1279
+ labels: combined_labels,
1280
+ properties: combined_properties,
1281
+ recursive_range: combined_recursive_range,
1282
+ };
1283
+ Ok(vec![
1284
+ ast::PathPattern {
1285
+ variable: context.identifiers.create_variable_from_name(name),
1286
+ edge: combined_edge,
1287
+ }
1288
+ .into(),
1289
+ ])
1290
+ },
1291
+ ),
1292
+ node_or_edges_patterns::<T>,
1293
+ ))
1294
+ .parse(input)
1295
+ }
1296
+
1297
+ fn parameter(input: Input) -> OCPResult<Input, ast::Expression>
1298
+ {
1299
+ map(to_string(parameter_name), |name| {
1300
+ ast::Expression::Parameter(ast::Parameter { name })
1301
+ })
1302
+ .parse(input)
1303
+ }
1304
+
1305
+ fn properties(input: Input) -> OCPResult<Input, ast::Expression>
1306
+ {
1307
+ alt((map_literal, parameter)).parse(input)
1308
+ }
1309
+
1310
+ fn search_statement(input: Input) -> OCPResult<Input, ast::Search>
1311
+ {
1312
+ let context = &input.1;
1313
+ map(
1314
+ (
1315
+ token(Token::Search),
1316
+ ident,
1317
+ token(Token::In),
1318
+ token(Token::StartParenthesis),
1319
+ token(Token::Vector),
1320
+ token(Token::Index),
1321
+ ident,
1322
+ token(Token::For),
1323
+ expression,
1324
+ token(Token::Limit),
1325
+ int,
1326
+ token(Token::EndParenthesis),
1327
+ opt(preceded(pair(token(Token::Score), token(Token::As)), ident)),
1328
+ ),
1329
+ |(_, var_name, _, _, _, _, index_name, _, query_expr, _, limit_val, _, score_alias)| {
1330
+ ast::Search {
1331
+ variable: context.identifiers.create_variable_from_name(var_name),
1332
+ index: index_name.to_string(),
1333
+ query: query_expr,
1334
+ limit: limit_val as usize,
1335
+ score_alias: score_alias.map(|alias| context.identifiers.create_variable_from_name(alias)),
1336
+ }
1337
+ },
1338
+ )
1339
+ .parse(input)
1340
+ }
1341
+
1342
+ // Statement parsing
1343
+ fn match_statement(input: Input) -> OCPResult<Input, ast::Statement>
1344
+ {
1345
+ map_res(
1346
+ (
1347
+ opt(token(Token::Optional)),
1348
+ token(Token::Match),
1349
+ separated_list1(token(Token::Comma), patterns::<AllowUndirected>),
1350
+ opt(preceded(token(Token::Where), expression)),
1351
+ opt(search_statement),
1352
+ ),
1353
+ |(optional, _, patterns, where_expression, search)| {
1354
+ let patterns_vec: Vec<ast::Pattern> = patterns.into_iter().flatten().collect();
1355
+
1356
+ // Validate SEARCH clause if present
1357
+ if let Some(ref search) = search
1358
+ {
1359
+ // Collect all variables from patterns
1360
+ let pattern_vars: std::collections::HashSet<String> = patterns_vec
1361
+ .iter()
1362
+ .filter_map(|p| match p
1363
+ {
1364
+ ast::Pattern::Node(n) => n.variable.as_ref().map(|v| v.name().clone()),
1365
+ ast::Pattern::Edge(e) => e.variable.as_ref().map(|v| v.name().clone()),
1366
+ ast::Pattern::Path(p) => Some(p.variable.name().clone()),
1367
+ })
1368
+ .collect();
1369
+
1370
+ // Check if search variable exists in patterns
1371
+ if !pattern_vars.contains(search.variable.name())
1372
+ {
1373
+ return Err(ErrorKind::VariableNotFound);
1374
+ }
1375
+ }
1376
+
1377
+ Ok(ast::Statement::Match(ast::Match {
1378
+ patterns: patterns_vec,
1379
+ where_expression,
1380
+ optional: optional.is_some(),
1381
+ search,
1382
+ }))
1383
+ },
1384
+ )
1385
+ .parse(input)
1386
+ }
1387
+
1388
+ fn merge_statement(input: Input) -> OCPResult<Input, ast::Statement>
1389
+ {
1390
+ map(
1391
+ (
1392
+ token(Token::Merge),
1393
+ separated_list1(token(Token::Comma), patterns::<AllowUndirected>),
1394
+ many0(alt((
1395
+ map(
1396
+ preceded(
1397
+ (token(Token::On), token(Token::Create), token(Token::Set)),
1398
+ parse_set_actions,
1399
+ ),
1400
+ |actions| (true, actions),
1401
+ ),
1402
+ map(
1403
+ preceded(
1404
+ (token(Token::On), token(Token::Match), token(Token::Set)),
1405
+ parse_set_actions,
1406
+ ),
1407
+ |actions| (false, actions),
1408
+ ),
1409
+ ))),
1410
+ ),
1411
+ |(_, patterns, clauses)| {
1412
+ let patterns_vec: Vec<ast::Pattern> = patterns.into_iter().flatten().collect();
1413
+ let mut on_create = vec![];
1414
+ let mut on_match = vec![];
1415
+
1416
+ for (is_create, actions) in clauses
1417
+ {
1418
+ if is_create
1419
+ {
1420
+ on_create = actions;
1421
+ }
1422
+ else
1423
+ {
1424
+ on_match = actions;
1425
+ }
1426
+ }
1427
+
1428
+ ast::Merge {
1429
+ patterns: patterns_vec,
1430
+ on_create,
1431
+ on_match,
1432
+ }
1433
+ .into()
1434
+ },
1435
+ )
1436
+ .parse(input)
1437
+ }
1438
+
1439
+ fn create_vector_index_statement(input: Input) -> OCPResult<Input, ast::Statement>
1440
+ {
1441
+ map_res(
1442
+ (
1443
+ token(Token::Create),
1444
+ token(Token::Vector),
1445
+ token(Token::Index),
1446
+ ident,
1447
+ opt((token(Token::If), token(Token::Not), token(Token::Exists))),
1448
+ token(Token::On),
1449
+ preceded(token(Token::Colon), ident),
1450
+ delimited(
1451
+ token(Token::StartParenthesis),
1452
+ ident,
1453
+ token(Token::EndParenthesis),
1454
+ ),
1455
+ opt(preceded(
1456
+ token(Token::Options),
1457
+ delimited(
1458
+ token(Token::StartBrace),
1459
+ separated_list0(token(Token::Comma), map_pair),
1460
+ token(Token::EndBrace),
1461
+ ),
1462
+ )),
1463
+ ),
1464
+ |(_, _, _, index_name, if_not_exists, _, label, property, options)| {
1465
+ let mut dimension: Option<usize> = None;
1466
+ let mut metric: Option<ast::Metric> = None;
1467
+
1468
+ if let Some(opts) = options
1469
+ {
1470
+ for (key, value) in opts
1471
+ {
1472
+ match key.as_str()
1473
+ {
1474
+ "dimension" =>
1475
+ {
1476
+ if let ast::Expression::Value(v) = value
1477
+ {
1478
+ if let graphcore::Value::Integer(d) = v.value
1479
+ {
1480
+ dimension = Some(d as usize);
1481
+ }
1482
+ else
1483
+ {
1484
+ return Err(ErrorKind::UnexpectedToken);
1485
+ }
1486
+ }
1487
+ else
1488
+ {
1489
+ return Err(ErrorKind::UnexpectedToken);
1490
+ }
1491
+ }
1492
+ "metric" =>
1493
+ {
1494
+ if let ast::Expression::Value(v) = value
1495
+ {
1496
+ if let graphcore::Value::String(m) = v.value
1497
+ {
1498
+ metric = match m.as_str()
1499
+ {
1500
+ "cosine" => Some(ast::Metric::Cosine),
1501
+ "l2" => Some(ast::Metric::L2),
1502
+ "dot_product" | "dotproduct" => Some(ast::Metric::DotProduct),
1503
+ _ => return Err(ErrorKind::UnexpectedToken),
1504
+ };
1505
+ }
1506
+ else
1507
+ {
1508
+ return Err(ErrorKind::UnexpectedToken);
1509
+ }
1510
+ }
1511
+ else
1512
+ {
1513
+ return Err(ErrorKind::UnexpectedToken);
1514
+ }
1515
+ }
1516
+ _ =>
1517
+ {} // Ignore unknown options
1518
+ }
1519
+ }
1520
+ }
1521
+
1522
+ Ok(ast::Statement::CreateVectorIndex(ast::CreateVectorIndex {
1523
+ name: index_name.to_string(),
1524
+ label: label.to_string(),
1525
+ property: property.to_string(),
1526
+ dimension,
1527
+ metric,
1528
+ if_not_exists: if_not_exists.is_some(),
1529
+ }))
1530
+ },
1531
+ )
1532
+ .parse(input)
1533
+ }
1534
+
1535
+ fn create_statement(input: Input) -> OCPResult<Input, ast::Statement>
1536
+ {
1537
+ map(
1538
+ (
1539
+ token(Token::Create),
1540
+ separated_list1(token(Token::Comma), patterns::<DisallowUndirected>),
1541
+ ),
1542
+ |(_, patterns)| {
1543
+ ast::Statement::Create(ast::Create {
1544
+ patterns: patterns.into_iter().flatten().collect(),
1545
+ })
1546
+ },
1547
+ )
1548
+ .parse(input)
1549
+ }
1550
+
1551
+ fn expression_with_span(input: Input<'_>) -> OCPResult<Input<'_>, (ast::Expression, &str)>
1552
+ {
1553
+ let start = input.0.first().map_or(0, |x| x.span.start);
1554
+ let (input, expr) = expression(input)?;
1555
+
1556
+ let end = input
1557
+ .0
1558
+ .first()
1559
+ .map_or(input.1.source.len(), |x| x.span.start);
1560
+
1561
+ let text = &input.1.source[start..end];
1562
+
1563
+ Ok((input, (expr, text)))
1564
+ }
1565
+
1566
+ fn named_expression(input: Input) -> OCPResult<Input, ast::NamedExpression>
1567
+ {
1568
+ let (input, (expr, text)) = expression_with_span(input)?;
1569
+
1570
+ let (input, alias) = opt(preceded(token(Token::As), ident)).parse(input)?;
1571
+
1572
+ let identifier = match alias
1573
+ {
1574
+ Some(name) => input.1.identifiers.create_variable_from_name(name),
1575
+
1576
+ None => match &expr
1577
+ {
1578
+ ast::Expression::Variable(v) => v.identifier.clone(),
1579
+ _ => input.1.identifiers.create_variable_from_name(text.trim()),
1580
+ },
1581
+ };
1582
+
1583
+ Ok((
1584
+ input,
1585
+ ast::NamedExpression {
1586
+ identifier,
1587
+ expression: expr,
1588
+ },
1589
+ ))
1590
+ }
1591
+
1592
+ fn with_return_expression(input: Input) -> OCPResult<Input, (bool, Vec<ast::NamedExpression>)>
1593
+ {
1594
+ alt((
1595
+ value((true, Default::default()), token(Token::Star)),
1596
+ map(
1597
+ separated_list1(token(Token::Comma), named_expression),
1598
+ |exprs| (false, exprs),
1599
+ ),
1600
+ ))
1601
+ .parse(input)
1602
+ }
1603
+
1604
+ fn modifiers(input: Input) -> OCPResult<Input, ast::Modifiers>
1605
+ {
1606
+ fold_many0(
1607
+ alt((
1608
+ map(
1609
+ (
1610
+ token(Token::Order),
1611
+ token(Token::By),
1612
+ separated_list1(
1613
+ token(Token::Comma),
1614
+ (
1615
+ expression,
1616
+ opt(alt((token(Token::Asc), token(Token::Desc)))),
1617
+ ),
1618
+ ),
1619
+ ),
1620
+ |(_, _, orderings)| {
1621
+ Box::new(move |mut modifiers: ast::Modifiers| {
1622
+ modifiers.order_by = Some(ast::OrderBy {
1623
+ expressions: orderings
1624
+ .into_iter()
1625
+ .map(|(expr, order)| ast::OrderByExpression {
1626
+ expression: expr,
1627
+ asc: !matches!(order, Some(Token::Desc)),
1628
+ })
1629
+ .collect(),
1630
+ });
1631
+ modifiers
1632
+ }) as Box<dyn FnOnce(ast::Modifiers) -> ast::Modifiers>
1633
+ },
1634
+ ),
1635
+ map((token(Token::Limit), expression), |(_, expr)| {
1636
+ Box::new(move |mut modifiers: ast::Modifiers| {
1637
+ modifiers.limit = Some(expr);
1638
+ modifiers
1639
+ }) as Box<dyn FnOnce(ast::Modifiers) -> ast::Modifiers>
1640
+ }),
1641
+ map((token(Token::Skip), expression), |(_, expr)| {
1642
+ Box::new(move |mut modifiers: ast::Modifiers| {
1643
+ modifiers.skip = Some(expr);
1644
+ modifiers
1645
+ }) as Box<dyn FnOnce(ast::Modifiers) -> ast::Modifiers>
1646
+ }),
1647
+ )),
1648
+ ast::Modifiers::default,
1649
+ |modifiers, f| f(modifiers),
1650
+ )
1651
+ .parse(input)
1652
+ }
1653
+
1654
+ fn return_statement(input: Input) -> OCPResult<Input, ast::Statement>
1655
+ {
1656
+ map(
1657
+ (
1658
+ token(Token::Return),
1659
+ with_return_expression,
1660
+ modifiers,
1661
+ opt(preceded(token(Token::Where), expression)),
1662
+ ),
1663
+ |(_, (all, expressions), modifiers, where_expression)| {
1664
+ ast::Return {
1665
+ all,
1666
+ expressions,
1667
+ modifiers,
1668
+ where_expression,
1669
+ }
1670
+ .into()
1671
+ },
1672
+ )
1673
+ .parse(input)
1674
+ }
1675
+
1676
+ fn with_statement(input: Input) -> OCPResult<Input, ast::Statement>
1677
+ {
1678
+ map(
1679
+ (
1680
+ token(Token::With),
1681
+ with_return_expression,
1682
+ modifiers,
1683
+ opt(preceded(token(Token::Where), expression)),
1684
+ ),
1685
+ |(_, (all, expressions), modifiers, where_expression)| {
1686
+ ast::With {
1687
+ all,
1688
+ expressions,
1689
+ modifiers,
1690
+ where_expression,
1691
+ }
1692
+ .into()
1693
+ },
1694
+ )
1695
+ .parse(input)
1696
+ }
1697
+
1698
+ fn unwind_statement(input: Input) -> OCPResult<Input, ast::Statement>
1699
+ {
1700
+ map(
1701
+ (token(Token::Unwind), named_expression),
1702
+ |(_, expression)| {
1703
+ ast::Unwind {
1704
+ identifier: expression.identifier,
1705
+ expression: expression.expression,
1706
+ }
1707
+ .into()
1708
+ },
1709
+ )
1710
+ .parse(input)
1711
+ }
1712
+
1713
+ fn delete_statement(input: Input) -> OCPResult<Input, ast::Statement>
1714
+ {
1715
+ map(
1716
+ (
1717
+ opt(token(Token::Detach)),
1718
+ token(Token::Delete),
1719
+ separated_list1(token(Token::Comma), expression),
1720
+ ),
1721
+ |(detach, _, expressions)| {
1722
+ ast::Delete {
1723
+ detach: detach.is_some(),
1724
+ expressions,
1725
+ }
1726
+ .into()
1727
+ },
1728
+ )
1729
+ .parse(input)
1730
+ }
1731
+
1732
+ fn parse_set_actions(input: Input) -> OCPResult<Input, Vec<ast::OneUpdate>>
1733
+ {
1734
+ let context_0 = &input.1;
1735
+ separated_list1(
1736
+ token(Token::Comma),
1737
+ alt((
1738
+ map(
1739
+ (
1740
+ alt((
1741
+ ident,
1742
+ delimited(
1743
+ token(Token::StartParenthesis),
1744
+ ident,
1745
+ token(Token::EndParenthesis),
1746
+ ),
1747
+ )),
1748
+ many0(preceded(
1749
+ token(Token::Dot),
1750
+ to_string(alt((ident, string_literal))),
1751
+ )),
1752
+ token(Token::Equal),
1753
+ expression,
1754
+ ),
1755
+ |(ident, path, _, expression)| {
1756
+ ast::OneUpdate::SetProperty(ast::UpdateProperty {
1757
+ target: context_0.identifiers.create_variable_from_name(ident),
1758
+ path,
1759
+ expression,
1760
+ })
1761
+ },
1762
+ ),
1763
+ map(
1764
+ (
1765
+ alt((
1766
+ ident,
1767
+ delimited(
1768
+ token(Token::StartParenthesis),
1769
+ ident,
1770
+ token(Token::EndParenthesis),
1771
+ ),
1772
+ )),
1773
+ many0(preceded(
1774
+ token(Token::Dot),
1775
+ to_string(alt((ident, string_literal))),
1776
+ )),
1777
+ token(Token::PlusEqual),
1778
+ expression,
1779
+ ),
1780
+ |(ident, path, _, expression)| {
1781
+ ast::OneUpdate::AddProperty(ast::UpdateProperty {
1782
+ target: context_0.identifiers.create_variable_from_name(ident),
1783
+ path,
1784
+ expression,
1785
+ })
1786
+ },
1787
+ ),
1788
+ map(
1789
+ (
1790
+ ident,
1791
+ many1(preceded(token(Token::Colon), to_string(ident))),
1792
+ ),
1793
+ |(ident, labels)| {
1794
+ ast::OneUpdate::AddLabels(ast::AddRemoveLabels {
1795
+ target: context_0.identifiers.create_variable_from_name(ident),
1796
+ labels,
1797
+ })
1798
+ },
1799
+ ),
1800
+ )),
1801
+ )
1802
+ .parse(input)
1803
+ }
1804
+
1805
+ fn set_statement(input: Input) -> OCPResult<Input, ast::Statement>
1806
+ {
1807
+ map(preceded(token(Token::Set), parse_set_actions), |updates| {
1808
+ ast::Update { updates }.into()
1809
+ })
1810
+ .parse(input)
1811
+ }
1812
+
1813
+ fn remove_statement(input: Input) -> OCPResult<Input, ast::Statement>
1814
+ {
1815
+ let context_0 = &input.1;
1816
+ preceded(
1817
+ token(Token::Remove),
1818
+ map(
1819
+ separated_list1(
1820
+ token(Token::Comma),
1821
+ alt((
1822
+ map(
1823
+ (
1824
+ ident,
1825
+ many1(preceded(
1826
+ token(Token::Dot),
1827
+ to_string(alt((ident, string_literal))),
1828
+ )),
1829
+ ),
1830
+ |(ident, path)| {
1831
+ ast::OneUpdate::RemoveProperty(ast::RemoveProperty {
1832
+ target: context_0.identifiers.create_variable_from_name(ident),
1833
+ path,
1834
+ })
1835
+ },
1836
+ ),
1837
+ map(
1838
+ (
1839
+ ident,
1840
+ many1(preceded(token(Token::Colon), to_string(ident))),
1841
+ ),
1842
+ |(ident, labels)| {
1843
+ ast::OneUpdate::RemoveLabels(ast::AddRemoveLabels {
1844
+ target: context_0.identifiers.create_variable_from_name(ident),
1845
+ labels,
1846
+ })
1847
+ },
1848
+ ),
1849
+ )),
1850
+ ),
1851
+ |x| ast::Update { updates: x }.into(),
1852
+ ),
1853
+ )
1854
+ .parse(input)
1855
+ }
1856
+
1857
+ fn call_statement(input: Input) -> OCPResult<Input, ast::Statement>
1858
+ {
1859
+ map(
1860
+ (
1861
+ token(Token::Call),
1862
+ separated_list1(token(Token::Dot), ident),
1863
+ delimited(
1864
+ token(Token::StartParenthesis),
1865
+ separated_list0(token(Token::Comma), expression),
1866
+ token(Token::EndParenthesis),
1867
+ ),
1868
+ ),
1869
+ |(_, function_name, arguments)| {
1870
+ ast::Call {
1871
+ name: function_name
1872
+ .into_iter()
1873
+ .map(|s| s.to_string())
1874
+ .collect::<Vec<_>>()
1875
+ .join("."),
1876
+ arguments,
1877
+ }
1878
+ .into()
1879
+ },
1880
+ )
1881
+ .parse(input)
1882
+ }
1883
+
1884
+ fn use_statement(input: Input) -> OCPResult<Input, ast::Statement>
1885
+ {
1886
+ map(
1887
+ (
1888
+ token(Token::Use),
1889
+ alt((
1890
+ map(string_literal, |s| s.to_string()),
1891
+ map(ident, |s| s.to_string()),
1892
+ )),
1893
+ ),
1894
+ |(_, name)| ast::UseGraph { name }.into(),
1895
+ )
1896
+ .parse(input)
1897
+ }
1898
+
1899
+ fn create_graph_statement(input: Input) -> OCPResult<Input, ast::Statement>
1900
+ {
1901
+ map(
1902
+ (
1903
+ token(Token::Create),
1904
+ token(Token::Graph),
1905
+ opt((token(Token::If), token(Token::Not), token(Token::Exists))),
1906
+ alt((
1907
+ map(string_literal, |s| s.to_string()),
1908
+ map(ident, |s| s.to_string()),
1909
+ )),
1910
+ ),
1911
+ |(_, _, if_not_exists, name)| {
1912
+ ast::CreateGraph {
1913
+ name,
1914
+ if_not_exists: if_not_exists.is_some(),
1915
+ }
1916
+ .into()
1917
+ },
1918
+ )
1919
+ .parse(input)
1920
+ }
1921
+
1922
+ fn drop_graph_statement(input: Input) -> OCPResult<Input, ast::Statement>
1923
+ {
1924
+ map(
1925
+ (
1926
+ token(Token::Drop),
1927
+ token(Token::Graph),
1928
+ opt((token(Token::If), token(Token::Exists))),
1929
+ alt((
1930
+ map(string_literal, |s| s.to_string()),
1931
+ map(ident, |s| s.to_string()),
1932
+ )),
1933
+ ),
1934
+ |(_, _, if_exists, name)| {
1935
+ ast::DropGraph {
1936
+ name,
1937
+ if_exists: if_exists.is_some(),
1938
+ }
1939
+ .into()
1940
+ },
1941
+ )
1942
+ .parse(input)
1943
+ }
1944
+
1945
+ fn statement(input: Input) -> OCPResult<Input, ast::Statement>
1946
+ {
1947
+ alt((
1948
+ match_statement,
1949
+ merge_statement,
1950
+ create_vector_index_statement,
1951
+ create_statement,
1952
+ with_statement,
1953
+ return_statement,
1954
+ unwind_statement,
1955
+ delete_statement,
1956
+ set_statement,
1957
+ remove_statement,
1958
+ call_statement,
1959
+ use_statement,
1960
+ create_graph_statement,
1961
+ drop_graph_statement,
1962
+ ))
1963
+ .parse(input)
1964
+ }
1965
+
1966
+ fn query(input: Input) -> OCPResult<Input, ast::Query>
1967
+ {
1968
+ let (input, first) = many0(statement).parse(input)?;
1969
+ let (input, rest) = many0((
1970
+ token(Token::Union),
1971
+ opt(token(Token::All)),
1972
+ many0(statement),
1973
+ ))
1974
+ .parse(input)?;
1975
+
1976
+ if rest.is_empty()
1977
+ {
1978
+ Ok((input, ast::Query::Statements(first)))
1979
+ }
1980
+ else
1981
+ {
1982
+ // All separators must agree on ALL vs. not-ALL.
1983
+ let all = rest[0].1.is_some();
1984
+ let mut branches = vec![first];
1985
+
1986
+ for (_, all_token, stmts) in rest
1987
+ {
1988
+ if all_token.is_some() != all
1989
+ {
1990
+ return Err(nom::Err::Failure(Error::new(
1991
+ super::error::ErrorKind::MixedUnionOperators,
1992
+ input.first().map_or(Default::default(), |t| t.span.clone()),
1993
+ )));
1994
+ }
1995
+ branches.push(stmts);
1996
+ }
1997
+
1998
+ Ok((input, ast::Query::Union { branches, all }))
1999
+ }
2000
+ }
2001
+
2002
+ pub fn queries(input: Input) -> OCPResult<Input, ast::Queries>
2003
+ {
2004
+ separated_list1(token(Token::Semi), query).parse(input)
2005
+ }