surrealdb_core/syn/parser/
idiom.rs

1use reblessive::Stk;
2
3use crate::{
4	sql::{
5		graph::GraphSubjects,
6		part::{DestructurePart, Recurse, RecurseInstruction},
7		Dir, Edges, Field, Fields, Graph, Ident, Idiom, Param, Part, Table, Value,
8	},
9	syn::{
10		error::bail,
11		token::{t, Glued, Span, TokenKind},
12	},
13};
14
15use super::{
16	mac::{expected, parse_option, unexpected},
17	ParseResult, Parser,
18};
19
20impl Parser<'_> {
21	pub(super) fn peek_continues_idiom(&mut self) -> bool {
22		let peek = self.peek().kind;
23		if matches!(peek, t!("->") | t!("[") | t!(".") | t!("...") | t!("?")) {
24			return true;
25		}
26		peek == t!("<") && matches!(self.peek1().kind, t!("-") | t!("->"))
27	}
28
29	/// Parse fields of a selecting query: `foo, bar` in `SELECT foo, bar FROM baz`.
30	///
31	/// # Parser State
32	/// Expects the next tokens to be of a field set.
33	pub(super) async fn parse_fields(&mut self, ctx: &mut Stk) -> ParseResult<Fields> {
34		if self.eat(t!("VALUE")) {
35			let expr = ctx.run(|ctx| self.parse_value_field(ctx)).await?;
36			let alias = if self.eat(t!("AS")) {
37				Some(self.parse_plain_idiom(ctx).await?)
38			} else {
39				None
40			};
41			Ok(Fields(
42				vec![Field::Single {
43					expr,
44					alias,
45				}],
46				true,
47			))
48		} else {
49			let mut fields = Vec::new();
50			loop {
51				let field = if self.eat(t!("*")) {
52					Field::All
53				} else {
54					let expr = ctx.run(|ctx| self.parse_value_field(ctx)).await?;
55					let alias = if self.eat(t!("AS")) {
56						Some(self.parse_plain_idiom(ctx).await?)
57					} else {
58						None
59					};
60					Field::Single {
61						expr,
62						alias,
63					}
64				};
65				fields.push(field);
66				if !self.eat(t!(",")) {
67					break;
68				}
69			}
70			Ok(Fields(fields, false))
71		}
72	}
73
74	/// Parses a list of idioms separated by a `,`
75	pub(super) async fn parse_idiom_list(&mut self, ctx: &mut Stk) -> ParseResult<Vec<Idiom>> {
76		let mut res = vec![self.parse_plain_idiom(ctx).await?];
77		while self.eat(t!(",")) {
78			res.push(self.parse_plain_idiom(ctx).await?);
79		}
80		Ok(res)
81	}
82
83	/// Parses the remaining idiom parts after the start: Any part like `...`, `.foo` and `->foo`
84	///
85	/// This function differes from [`Parser::parse_remaining_value_idiom`] in how it handles graph
86	/// parsing. Graphs inside a plain idioms will remain a normal graph production.
87	pub(super) async fn parse_remaining_idiom(
88		&mut self,
89		stk: &mut Stk,
90		start: Vec<Part>,
91	) -> ParseResult<Idiom> {
92		let mut res = start;
93		loop {
94			match self.peek_kind() {
95				t!("?") => {
96					self.pop_peek();
97					res.push(Part::Optional);
98				}
99				t!("...") => {
100					self.pop_peek();
101					res.push(Part::Flatten);
102				}
103				t!(".") => {
104					self.pop_peek();
105					res.push(self.parse_dot_part(stk).await?)
106				}
107				t!("[") => {
108					let span = self.pop_peek().span;
109					let part = self.parse_bracket_part(stk, span).await?;
110					res.push(part)
111				}
112				t!("->") => {
113					self.pop_peek();
114					let graph = stk.run(|stk| self.parse_graph(stk, Dir::Out)).await?;
115					res.push(Part::Graph(graph))
116				}
117				t!("<") => {
118					let peek = self.peek_whitespace1();
119					if peek.kind == t!("-") {
120						self.pop_peek();
121						self.pop_peek();
122						let graph = stk.run(|stk| self.parse_graph(stk, Dir::In)).await?;
123						res.push(Part::Graph(graph))
124					} else if peek.kind == t!("->") {
125						self.pop_peek();
126						self.pop_peek();
127						let graph = stk.run(|stk| self.parse_graph(stk, Dir::Both)).await?;
128						res.push(Part::Graph(graph))
129					} else {
130						break;
131					}
132				}
133				t!("..") => {
134					bail!("Unexpected token `{}` expected and idiom",t!(".."),
135						@self.last_span() => "Did you maybe intent to use the flatten operator `...`");
136				}
137				_ => break,
138			}
139		}
140		Ok(Idiom(res))
141	}
142
143	/// Parses the remaining idiom parts after the start: Any part like `...`, `.foo` and `->foo`
144	///
145	///
146	/// This function differes from [`Parser::parse_remaining_value_idiom`] in how it handles graph
147	/// parsing. When parsing a idiom like production which can be a value, the initial start value
148	/// might need to be changed to a Edge depending on what is parsed next.
149	pub(super) async fn parse_remaining_value_idiom(
150		&mut self,
151		ctx: &mut Stk,
152		start: Vec<Part>,
153	) -> ParseResult<Value> {
154		let mut res = start;
155		loop {
156			match self.peek_kind() {
157				t!("?") => {
158					self.pop_peek();
159					res.push(Part::Optional);
160				}
161				t!("...") => {
162					self.pop_peek();
163					res.push(Part::Flatten);
164				}
165				t!(".") => {
166					self.pop_peek();
167					res.push(self.parse_dot_part(ctx).await?)
168				}
169				t!("[") => {
170					let span = self.pop_peek().span;
171					let part = self.parse_bracket_part(ctx, span).await?;
172					res.push(part)
173				}
174				t!("->") => {
175					self.pop_peek();
176					if let Some(x) = self.parse_graph_idiom(ctx, &mut res, Dir::Out).await? {
177						return Ok(x);
178					}
179				}
180				t!("<") => {
181					let peek = self.peek_whitespace1();
182					if peek.kind == t!("-") {
183						self.pop_peek();
184						self.pop_peek();
185
186						if let Some(x) = self.parse_graph_idiom(ctx, &mut res, Dir::In).await? {
187							return Ok(x);
188						}
189					} else if peek.kind == t!("->") {
190						self.pop_peek();
191						self.pop_peek();
192
193						if let Some(x) = self.parse_graph_idiom(ctx, &mut res, Dir::Both).await? {
194							return Ok(x);
195						}
196					} else {
197						break;
198					}
199				}
200				t!("..") => {
201					bail!("Unexpected token `{}` expected and idiom",t!(".."),
202						@self.last_span() => "Did you maybe intent to use the flatten operator `...`");
203				}
204				_ => break,
205			}
206		}
207		Ok(Value::Idiom(Idiom(res)))
208	}
209
210	/// Parse a graph idiom and possibly rewrite the starting value to be an edge whenever the
211	/// parsed production matches `Thing -> Ident`.
212	async fn parse_graph_idiom(
213		&mut self,
214		ctx: &mut Stk,
215		res: &mut Vec<Part>,
216		dir: Dir,
217	) -> ParseResult<Option<Value>> {
218		let graph = ctx.run(|ctx| self.parse_graph(ctx, dir)).await?;
219		// the production `Thing Graph` is reparsed as an edge if the graph does not contain an
220		// alias or a condition.
221		if res.len() == 1
222			&& graph.alias.is_none()
223			&& graph.cond.is_none()
224			&& graph.group.is_none()
225			&& graph.limit.is_none()
226			&& graph.order.is_none()
227			&& graph.split.is_none()
228			&& graph.start.is_none()
229			&& graph.expr.is_none()
230		{
231			match std::mem::replace(&mut res[0], Part::All) {
232				Part::Value(Value::Thing(t)) | Part::Start(Value::Thing(t)) => {
233					let edge = Edges {
234						dir: graph.dir,
235						from: t,
236						what: graph.what,
237					};
238					let value = Value::Edges(Box::new(edge));
239
240					if !self.peek_continues_idiom() {
241						return Ok(Some(value));
242					}
243					res[0] = Part::Start(value);
244					return Ok(None);
245				}
246				x => {
247					res[0] = x;
248				}
249			}
250		}
251		res.push(Part::Graph(graph));
252		Ok(None)
253	}
254
255	/// Parse a idiom which can only start with a graph or an identifier.
256	/// Other expressions are not allowed as start of this idiom
257	pub async fn parse_plain_idiom(&mut self, ctx: &mut Stk) -> ParseResult<Idiom> {
258		let start = match self.peek_kind() {
259			t!("->") => {
260				self.pop_peek();
261				let graph = ctx.run(|ctx| self.parse_graph(ctx, Dir::Out)).await?;
262				Part::Graph(graph)
263			}
264			t!("<") => {
265				let t = self.pop_peek();
266				let graph = if self.eat_whitespace(t!("-")) {
267					ctx.run(|ctx| self.parse_graph(ctx, Dir::In)).await?
268				} else if self.eat_whitespace(t!("->")) {
269					ctx.run(|ctx| self.parse_graph(ctx, Dir::Both)).await?
270				} else {
271					unexpected!(self, t, "either `<-` `<->` or `->`")
272				};
273				Part::Graph(graph)
274			}
275			_ => Part::Field(self.next_token_value()?),
276		};
277		let start = vec![start];
278		self.parse_remaining_idiom(ctx, start).await
279	}
280
281	/// Parse the part after the `.` in a idiom
282	pub(super) async fn parse_dot_part(&mut self, ctx: &mut Stk) -> ParseResult<Part> {
283		let res = match self.peek_kind() {
284			t!("*") => {
285				self.pop_peek();
286				Part::All
287			}
288			t!("@") => {
289				self.pop_peek();
290				Part::RepeatRecurse
291			}
292			t!("{") => {
293				self.pop_peek();
294				ctx.run(|ctx| self.parse_curly_part(ctx)).await?
295			}
296			_ => {
297				let ident: Ident = self.next_token_value()?;
298				if self.eat(t!("(")) {
299					self.parse_function_part(ctx, ident).await?
300				} else {
301					Part::Field(ident)
302				}
303			}
304		};
305		Ok(res)
306	}
307	pub(super) async fn parse_function_part(
308		&mut self,
309		ctx: &mut Stk,
310		name: Ident,
311	) -> ParseResult<Part> {
312		let args = self.parse_function_args(ctx).await?;
313		Ok(Part::Method(name.0, args))
314	}
315	/// Parse the part after the `.{` in an idiom
316	pub(super) async fn parse_curly_part(&mut self, ctx: &mut Stk) -> ParseResult<Part> {
317		match self.peek_kind() {
318			t!("*") | t!("..") | TokenKind::Digits => self.parse_recurse_part(ctx).await,
319			_ => self.parse_destructure_part(ctx).await,
320		}
321	}
322	/// Parse a destructure part, expects `.{` to already be parsed
323	pub(super) async fn parse_destructure_part(&mut self, ctx: &mut Stk) -> ParseResult<Part> {
324		let start = self.last_span();
325		let mut destructured: Vec<DestructurePart> = Vec::new();
326		loop {
327			if self.eat(t!("}")) {
328				// We've reached the end of the destructure
329				break;
330			}
331
332			let field: Ident = self.next_token_value()?;
333			let part = match self.peek_kind() {
334				t!(":") => {
335					self.pop_peek();
336					let idiom = match self.parse_value_inherit(ctx).await? {
337						Value::Idiom(x) => x,
338						v => Idiom(vec![Part::Start(v)]),
339					};
340					DestructurePart::Aliased(field, idiom)
341				}
342				t!(".") => {
343					self.pop_peek();
344					let found = self.peek_kind();
345					match self.parse_dot_part(ctx).await? {
346						Part::All => DestructurePart::All(field),
347						Part::Destructure(v) => DestructurePart::Destructure(field, v),
348						_ => {
349							bail!("Unexpected token `{}` expected a `*` or a destructuring", found, @self.last_span());
350						}
351					}
352				}
353				_ => DestructurePart::Field(field),
354			};
355
356			destructured.push(part);
357
358			if !self.eat(t!(",")) {
359				// We've reached the end of the destructure
360				self.expect_closing_delimiter(t!("}"), start)?;
361				break;
362			}
363		}
364
365		Ok(Part::Destructure(destructured))
366	}
367	/// Parse the inner part of a recurse, expects a valid recurse value in the current position
368	pub(super) fn parse_recurse_inner(&mut self) -> ParseResult<Recurse> {
369		let min = if matches!(self.peek().kind, TokenKind::Digits) {
370			Some(self.next_token_value::<u32>()?)
371		} else {
372			None
373		};
374
375		match (self.eat_whitespace(t!("..")), min) {
376			(true, _) => (),
377			(false, Some(v)) => {
378				return Ok(Recurse::Fixed(v));
379			}
380			_ => {
381				let found = self.next().kind;
382				bail!("Unexpected token `{}` expected an integer or ..", found, @self.last_span());
383			}
384		}
385
386		// parse ending id.
387		let max = if matches!(self.peek_whitespace().kind, TokenKind::Digits) {
388			Some(self.next_token_value::<u32>()?)
389		} else {
390			None
391		};
392
393		Ok(Recurse::Range(min, max))
394	}
395	/// Parse a recursion instruction following the inner recurse part, if any
396	pub(super) async fn parse_recurse_instruction(
397		&mut self,
398		ctx: &mut Stk,
399	) -> ParseResult<Option<RecurseInstruction>> {
400		let instruction = parse_option!(
401			self,
402			"instruction",
403			"path" => {
404				let mut inclusive = false;
405				loop {
406					parse_option!(
407						self,
408						"option",
409						"inclusive" => inclusive = true,
410						_ => break
411					);
412				};
413
414				Some(RecurseInstruction::Path { inclusive })
415			},
416			"collect" => {
417				let mut inclusive = false;
418				loop {
419					parse_option!(
420						self,
421						"option",
422						"inclusive" => inclusive = true,
423						_ => break
424					);
425				};
426
427				Some(RecurseInstruction::Collect { inclusive })
428			},
429			"shortest" => {
430				expected!(self, t!("="));
431				let token = self.peek();
432				let expects = match token.kind {
433					TokenKind::Parameter => {
434						Value::from(self.next_token_value::<Param>()?)
435					},
436					x if Parser::kind_is_identifier(x) => {
437						Value::from(self.parse_thing(ctx).await?)
438					}
439					_ => {
440						unexpected!(self, token, "a param or thing");
441					}
442				};
443				let mut inclusive = false;
444				loop {
445					parse_option!(
446						self,
447						"option",
448						"inclusive" => inclusive = true,
449						_ => break
450					);
451				};
452
453				Some(RecurseInstruction::Shortest { expects, inclusive })
454			},
455			_ => None
456		);
457
458		Ok(instruction)
459	}
460	/// Parse a recurse part, expects `.{` to already be parsed
461	pub(super) async fn parse_recurse_part(&mut self, ctx: &mut Stk) -> ParseResult<Part> {
462		let start = self.last_span();
463		let recurse = self.parse_recurse_inner()?;
464		let instruction = self.parse_recurse_instruction(ctx).await?;
465		self.expect_closing_delimiter(t!("}"), start)?;
466
467		let nest = if self.eat(t!("(")) {
468			let start = self.last_span();
469			let idiom = self.parse_remaining_idiom(ctx, vec![]).await?;
470			self.expect_closing_delimiter(t!(")"), start)?;
471			Some(idiom)
472		} else {
473			None
474		};
475
476		Ok(Part::Recurse(recurse, nest, instruction))
477	}
478	/// Parse the part after the `[` in a idiom
479	pub(super) async fn parse_bracket_part(
480		&mut self,
481		ctx: &mut Stk,
482		start: Span,
483	) -> ParseResult<Part> {
484		let peek = self.peek();
485		let res = match peek.kind {
486			t!("*") => {
487				self.pop_peek();
488				Part::All
489			}
490			t!("$") => {
491				self.pop_peek();
492				Part::Last
493			}
494			t!("?") | t!("WHERE") => {
495				self.pop_peek();
496				let value = ctx.run(|ctx| self.parse_value_field(ctx)).await?;
497				Part::Where(value)
498			}
499			_ => {
500				let value = ctx.run(|ctx| self.parse_value_inherit(ctx)).await?;
501				if let Value::Number(x) = value {
502					Part::Index(x)
503				} else {
504					Part::Value(value)
505				}
506			}
507		};
508		self.expect_closing_delimiter(t!("]"), start)?;
509		Ok(res)
510	}
511
512	/// Parse a basic idiom.
513	///
514	/// Basic idioms differ from normal idioms in that they are more restrictive.
515	/// Flatten, graphs, conditions and indexing by param is not allowed.
516	pub(super) async fn parse_basic_idiom(&mut self, ctx: &mut Stk) -> ParseResult<Idiom> {
517		let start = self.next_token_value::<Ident>()?;
518		let mut parts = vec![Part::Field(start)];
519		loop {
520			let token = self.peek();
521			let part = match token.kind {
522				t!(".") => {
523					self.pop_peek();
524					self.parse_dot_part(ctx).await?
525				}
526				t!("[") => {
527					self.pop_peek();
528					let peek = self.peek();
529					let res = match peek.kind {
530						t!("*") => {
531							self.pop_peek();
532							Part::All
533						}
534						t!("$") => {
535							self.pop_peek();
536							Part::Last
537						}
538						TokenKind::Digits | t!("+") | TokenKind::Glued(Glued::Number) => {
539							let number = self.next_token_value()?;
540							Part::Index(number)
541						}
542						t!("-") => {
543							let peek_digit = self.peek_whitespace1();
544							if let TokenKind::Digits = peek_digit.kind {
545								let span = self.recent_span().covers(peek_digit.span);
546								bail!("Unexpected token `-` expected $, *, or a number", @span => "an index can't be negative");
547							}
548							unexpected!(self, peek, "$, * or a number");
549						}
550						_ => unexpected!(self, peek, "$, * or a number"),
551					};
552					self.expect_closing_delimiter(t!("]"), token.span)?;
553					res
554				}
555				_ => break,
556			};
557			parts.push(part);
558		}
559		Ok(Idiom(parts))
560	}
561
562	/// Parse a local idiom.
563	///
564	/// Basic idioms differ from local idioms in that they are more restrictive.
565	/// Only field, all and number indexing is allowed. Flatten is also allowed but only at the
566	/// end.
567	pub(super) async fn parse_local_idiom(&mut self, ctx: &mut Stk) -> ParseResult<Idiom> {
568		let start = self.next_token_value()?;
569		let mut parts = vec![Part::Field(start)];
570		loop {
571			let token = self.peek();
572			let part = match token.kind {
573				t!(".") => {
574					self.pop_peek();
575					self.parse_dot_part(ctx).await?
576				}
577				t!("[") => {
578					self.pop_peek();
579					let token = self.peek();
580					let res = match token.kind {
581						t!("*") => {
582							self.pop_peek();
583							Part::All
584						}
585						TokenKind::Digits | t!("+") | TokenKind::Glued(Glued::Number) => {
586							let number = self.next_token_value()?;
587							Part::Index(number)
588						}
589						t!("-") => {
590							let peek_digit = self.peek_whitespace1();
591							if let TokenKind::Digits = peek_digit.kind {
592								let span = self.recent_span().covers(peek_digit.span);
593								bail!("Unexpected token `-` expected $, *, or a number", @span => "an index can't be negative");
594							}
595							unexpected!(self, token, "$, * or a number");
596						}
597						_ => unexpected!(self, token, "$, * or a number"),
598					};
599					self.expect_closing_delimiter(t!("]"), token.span)?;
600					res
601				}
602				_ => break,
603			};
604
605			parts.push(part);
606		}
607
608		if self.eat(t!("...")) {
609			let token = self.peek();
610			if let t!(".") | t!("[") = token.kind {
611				bail!("Unexpected token `...` expected a local idiom to end.",
612					@token.span => "Flattening can only be done at the end of a local idiom")
613			}
614			parts.push(Part::Flatten);
615		}
616
617		Ok(Idiom(parts))
618	}
619
620	/// Parses a list of what values seperated by comma's
621	///
622	/// # Parser state
623	/// Expects to be at the start of a what list.
624	pub(super) async fn parse_what_list(&mut self, ctx: &mut Stk) -> ParseResult<Vec<Value>> {
625		let mut res = vec![self.parse_what_value(ctx).await?];
626		while self.eat(t!(",")) {
627			res.push(self.parse_what_value(ctx).await?)
628		}
629		Ok(res)
630	}
631
632	/// Parses a single what value,
633	///
634	/// # Parser state
635	/// Expects to be at the start of a what value
636	pub(super) async fn parse_what_value(&mut self, ctx: &mut Stk) -> ParseResult<Value> {
637		let start = self.parse_what_primary(ctx).await?;
638		if start.can_start_idiom() && self.peek_continues_idiom() {
639			let start = match start {
640				Value::Table(Table(x)) => vec![Part::Field(Ident(x))],
641				Value::Idiom(Idiom(x)) => x,
642				x => vec![Part::Start(x)],
643			};
644
645			let idiom = self.parse_remaining_value_idiom(ctx, start).await?;
646			Ok(idiom)
647		} else {
648			Ok(start)
649		}
650	}
651
652	/// Parses a graph value
653	///
654	/// # Parser state
655	/// Expects to just have eaten a direction (e.g. <-, <->, or ->) and be at the field like part
656	/// of the graph
657	pub(super) async fn parse_graph(&mut self, ctx: &mut Stk, dir: Dir) -> ParseResult<Graph> {
658		let token = self.peek();
659		match token.kind {
660			t!("?") => {
661				self.pop_peek();
662				Ok(Graph {
663					dir,
664					..Default::default()
665				})
666			}
667			t!("(") => {
668				let span = self.pop_peek().span;
669				let expr = if self.eat(t!("SELECT")) {
670					let before = self.peek().span;
671					let expr = self.parse_fields(ctx).await?;
672					let fields_span = before.covers(self.last_span());
673					expected!(self, t!("FROM"));
674					Some((expr, fields_span))
675				} else {
676					None
677				};
678
679				let token = self.peek();
680				let what = match token.kind {
681					t!("?") => {
682						self.pop_peek();
683						GraphSubjects::default()
684					}
685					x if Self::kind_is_identifier(x) => {
686						let subject = self.parse_graph_subject(ctx).await?;
687						let mut subjects = GraphSubjects(vec![subject]);
688						while self.eat(t!(",")) {
689							subjects.0.push(self.parse_graph_subject(ctx).await?);
690						}
691						subjects
692					}
693					_ => unexpected!(self, token, "`?`, an identifier or a range"),
694				};
695
696				let cond = self.try_parse_condition(ctx).await?;
697				let (split, group, order) = if let Some((ref expr, fields_span)) = expr {
698					let split = self.try_parse_split(ctx, expr, fields_span).await?;
699					let group = self.try_parse_group(ctx, expr, fields_span).await?;
700					let order = self.try_parse_orders(ctx, expr, fields_span).await?;
701					(split, group, order)
702				} else {
703					(None, None, None)
704				};
705
706				let (limit, start) = if let t!("START") = self.peek_kind() {
707					let start = self.try_parse_start(ctx).await?;
708					let limit = self.try_parse_limit(ctx).await?;
709					(limit, start)
710				} else {
711					let limit = self.try_parse_limit(ctx).await?;
712					let start = self.try_parse_start(ctx).await?;
713					(limit, start)
714				};
715
716				let alias = if self.eat(t!("AS")) {
717					Some(self.parse_plain_idiom(ctx).await?)
718				} else {
719					None
720				};
721
722				self.expect_closing_delimiter(t!(")"), span)?;
723
724				Ok(Graph {
725					dir,
726					what,
727					cond,
728					alias,
729					expr: expr.map(|(x, _)| x),
730					split,
731					group,
732					order,
733					limit,
734					start,
735					..Default::default()
736				})
737			}
738			x if Self::kind_is_identifier(x) => {
739				// The following function should always succeed here,
740				// returning an error here would be a bug, so unwrap.
741				let subject = self.parse_graph_subject(ctx).await?;
742				Ok(Graph {
743					dir,
744					what: GraphSubjects(vec![subject]),
745					..Default::default()
746				})
747			}
748			_ => unexpected!(self, token, "`?`, `(` or an identifier"),
749		}
750	}
751}
752
753#[cfg(test)]
754mod tests {
755	use crate::sql::{Expression, Id, Number, Object, Operator, Param, Strand, Thing};
756	use crate::syn::Parse;
757
758	use super::*;
759
760	#[test]
761	fn graph_in() {
762		let sql = "<-likes";
763		let out = Value::parse(sql);
764		assert_eq!("<-likes", format!("{}", out));
765	}
766
767	#[test]
768	fn graph_out() {
769		let sql = "->likes";
770		let out = Value::parse(sql);
771		assert_eq!("->likes", format!("{}", out));
772	}
773
774	#[test]
775	fn graph_both() {
776		let sql = "<->likes";
777		let out = Value::parse(sql);
778		assert_eq!("<->likes", format!("{}", out));
779	}
780
781	#[test]
782	fn graph_multiple() {
783		let sql = "->(likes, follows)";
784		let out = Value::parse(sql);
785		assert_eq!("->(likes, follows)", format!("{}", out));
786	}
787
788	#[test]
789	fn graph_aliases() {
790		let sql = "->(likes, follows AS connections)";
791		let out = Value::parse(sql);
792		assert_eq!("->(likes, follows AS connections)", format!("{}", out));
793	}
794
795	#[test]
796	fn graph_conditions() {
797		let sql = "->(likes, follows WHERE influencer = true)";
798		let out = Value::parse(sql);
799		assert_eq!("->(likes, follows WHERE influencer = true)", format!("{}", out));
800	}
801
802	#[test]
803	fn graph_conditions_aliases() {
804		let sql = "->(likes, follows WHERE influencer = true AS connections)";
805		let out = Value::parse(sql);
806		assert_eq!("->(likes, follows WHERE influencer = true AS connections)", format!("{}", out));
807	}
808
809	#[test]
810	fn idiom_normal() {
811		let sql = "test";
812		let out = Value::parse(sql);
813		assert_eq!("test", format!("{}", out));
814		assert_eq!(out, Value::from(Idiom(vec![Part::from("test")])));
815	}
816
817	#[test]
818	fn idiom_quoted_backtick() {
819		let sql = "`test`";
820		let out = Value::parse(sql);
821		assert_eq!("test", format!("{}", out));
822		assert_eq!(out, Value::from(Idiom(vec![Part::from("test")])));
823	}
824
825	#[test]
826	fn idiom_quoted_brackets() {
827		let sql = "⟨test⟩";
828		let out = Value::parse(sql);
829		assert_eq!("test", format!("{}", out));
830		assert_eq!(out, Value::from(Idiom(vec![Part::from("test")])));
831	}
832
833	#[test]
834	fn idiom_nested() {
835		let sql = "test.temp";
836		let out = Value::parse(sql);
837		assert_eq!("test.temp", format!("{}", out));
838		assert_eq!(out, Value::from(Idiom(vec![Part::from("test"), Part::from("temp")])));
839	}
840
841	#[test]
842	fn idiom_nested_quoted() {
843		let sql = "test.`some key`";
844		let out = Value::parse(sql);
845		assert_eq!("test.`some key`", format!("{}", out));
846		assert_eq!(out, Value::from(Idiom(vec![Part::from("test"), Part::from("some key")])));
847	}
848
849	#[test]
850	fn idiom_nested_array_all() {
851		let sql = "test.temp[*]";
852		let out = Value::parse(sql);
853		assert_eq!("test.temp[*]", format!("{}", out));
854		assert_eq!(
855			out,
856			Value::from(Idiom(vec![Part::from("test"), Part::from("temp"), Part::All]))
857		);
858	}
859
860	#[test]
861	fn idiom_nested_array_last() {
862		let sql = "test.temp[$]";
863		let out = Value::parse(sql);
864		assert_eq!("test.temp[$]", format!("{}", out));
865		assert_eq!(
866			out,
867			Value::from(Idiom(vec![Part::from("test"), Part::from("temp"), Part::Last]))
868		);
869	}
870
871	#[test]
872	fn idiom_nested_array_value() {
873		let sql = "test.temp[*].text";
874		let out = Value::parse(sql);
875		assert_eq!("test.temp[*].text", format!("{}", out));
876		assert_eq!(
877			out,
878			Value::from(Idiom(vec![
879				Part::from("test"),
880				Part::from("temp"),
881				Part::All,
882				Part::from("text")
883			]))
884		);
885	}
886
887	#[test]
888	fn idiom_nested_array_question() {
889		let sql = "test.temp[? test = true].text";
890		let out = Value::parse(sql);
891		assert_eq!("test.temp[WHERE test = true].text", format!("{}", out));
892		assert_eq!(
893			out,
894			Value::from(Idiom(vec![
895				Part::from("test"),
896				Part::from("temp"),
897				Part::Where(Value::Expression(Box::new(Expression::Binary {
898					l: Value::Idiom(Idiom(vec![Part::Field(Ident("test".to_string()))])),
899					o: Operator::Equal,
900					r: Value::Bool(true)
901				}))),
902				Part::from("text")
903			]))
904		);
905	}
906
907	#[test]
908	fn idiom_nested_array_condition() {
909		let sql = "test.temp[WHERE test = true].text";
910		let out = Value::parse(sql);
911		assert_eq!("test.temp[WHERE test = true].text", format!("{}", out));
912		assert_eq!(
913			out,
914			Value::from(Idiom(vec![
915				Part::from("test"),
916				Part::from("temp"),
917				Part::Where(Value::Expression(Box::new(Expression::Binary {
918					l: Value::Idiom(Idiom(vec![Part::Field(Ident("test".to_string()))])),
919					o: Operator::Equal,
920					r: Value::Bool(true)
921				}))),
922				Part::from("text")
923			]))
924		);
925	}
926
927	#[test]
928	fn idiom_start_param_local_field() {
929		let sql = "$test.temporary[0].embedded…";
930		let out = Value::parse(sql);
931		assert_eq!("$test.temporary[0].embedded…", format!("{}", out));
932		assert_eq!(
933			out,
934			Value::from(Idiom(vec![
935				Part::Start(Param::from("test").into()),
936				Part::from("temporary"),
937				Part::Index(Number::Int(0)),
938				Part::from("embedded"),
939				Part::Flatten,
940			]))
941		);
942	}
943
944	#[test]
945	fn idiom_start_thing_remote_traversal() {
946		let sql = "person:test.friend->like->person";
947		let out = Value::parse(sql);
948		assert_eq!("person:test.friend->like->person", format!("{}", out));
949		assert_eq!(
950			out,
951			Value::from(Idiom(vec![
952				Part::Start(Thing::from(("person", "test")).into()),
953				Part::from("friend"),
954				Part::Graph(Graph {
955					dir: Dir::Out,
956					what: Table::from("like").into(),
957					..Default::default()
958				}),
959				Part::Graph(Graph {
960					dir: Dir::Out,
961					what: Table::from("person").into(),
962					..Default::default()
963				}),
964			]))
965		);
966	}
967
968	#[test]
969	fn part_all() {
970		let sql = "{}[*]";
971		let out = Value::parse(sql);
972		assert_eq!("{  }[*]", format!("{}", out));
973		assert_eq!(
974			out,
975			Value::from(Idiom(vec![Part::Start(Value::from(Object::default())), Part::All]))
976		);
977	}
978
979	#[test]
980	fn part_last() {
981		let sql = "{}[$]";
982		let out = Value::parse(sql);
983		assert_eq!("{  }[$]", format!("{}", out));
984		assert_eq!(
985			out,
986			Value::from(Idiom(vec![Part::Start(Value::from(Object::default())), Part::Last]))
987		);
988	}
989
990	#[test]
991	fn part_param() {
992		let sql = "{}[$param]";
993		let out = Value::parse(sql);
994		assert_eq!("{  }[$param]", format!("{}", out));
995		assert_eq!(
996			out,
997			Value::from(Idiom(vec![
998				Part::Start(Value::from(Object::default())),
999				Part::Value(Value::Param(Param::from("param")))
1000			]))
1001		);
1002	}
1003
1004	#[test]
1005	fn part_flatten() {
1006		let sql = "{}...";
1007		let out = Value::parse(sql);
1008		assert_eq!("{  }…", format!("{}", out));
1009		assert_eq!(
1010			out,
1011			Value::from(Idiom(vec![Part::Start(Value::from(Object::default())), Part::Flatten]))
1012		);
1013	}
1014
1015	#[test]
1016	fn part_flatten_ellipsis() {
1017		let sql = "{}…";
1018		let out = Value::parse(sql);
1019		assert_eq!("{  }…", format!("{}", out));
1020		assert_eq!(
1021			out,
1022			Value::from(Idiom(vec![Part::Start(Value::from(Object::default())), Part::Flatten]))
1023		);
1024	}
1025
1026	#[test]
1027	fn part_number() {
1028		let sql = "{}[0]";
1029		let out = Value::parse(sql);
1030		assert_eq!("{  }[0]", format!("{}", out));
1031		assert_eq!(
1032			out,
1033			Value::from(Idiom(vec![
1034				Part::Start(Value::from(Object::default())),
1035				Part::Index(Number::from(0))
1036			]))
1037		);
1038	}
1039
1040	#[test]
1041	fn part_expression_question() {
1042		let sql = "{}[?test = true]";
1043		let out = Value::parse(sql);
1044		assert_eq!("{  }[WHERE test = true]", format!("{}", out));
1045		assert_eq!(
1046			out,
1047			Value::from(Idiom(vec![
1048				Part::Start(Value::from(Object::default())),
1049				Part::Where(Value::Expression(Box::new(Expression::Binary {
1050					l: Value::Idiom(Idiom(vec![Part::Field(Ident("test".to_string()))])),
1051					o: Operator::Equal,
1052					r: Value::Bool(true)
1053				}))),
1054			]))
1055		);
1056	}
1057
1058	#[test]
1059	fn part_expression_condition() {
1060		let sql = "{}[WHERE test = true]";
1061		let out = Value::parse(sql);
1062		assert_eq!("{  }[WHERE test = true]", format!("{}", out));
1063		assert_eq!(
1064			out,
1065			Value::from(Idiom(vec![
1066				Part::Start(Value::from(Object::default())),
1067				Part::Where(Value::Expression(Box::new(Expression::Binary {
1068					l: Value::Idiom(Idiom(vec![Part::Field(Ident("test".to_string()))])),
1069					o: Operator::Equal,
1070					r: Value::Bool(true)
1071				}))),
1072			]))
1073		);
1074	}
1075
1076	#[test]
1077	fn idiom_thing_number() {
1078		let sql = "test:1.foo";
1079		let out = Value::parse(sql);
1080		assert_eq!(
1081			out,
1082			Value::from(Idiom(vec![
1083				Part::Start(Value::Thing(Thing {
1084					tb: "test".to_owned(),
1085					id: Id::from(1),
1086				})),
1087				Part::from("foo"),
1088			]))
1089		);
1090	}
1091
1092	#[test]
1093	fn idiom_thing_index() {
1094		let sql = "test:1['foo']";
1095		let out = Value::parse(sql);
1096		assert_eq!(
1097			out,
1098			Value::from(Idiom(vec![
1099				Part::Start(Value::Thing(Thing {
1100					tb: "test".to_owned(),
1101					id: Id::from(1),
1102				})),
1103				Part::Value(Value::Strand(Strand("foo".to_owned()))),
1104			]))
1105		);
1106	}
1107
1108	#[test]
1109	fn idiom_thing_all() {
1110		let sql = "test:1.*";
1111		let out = Value::parse(sql);
1112		assert_eq!(
1113			out,
1114			Value::from(Idiom(vec![
1115				Part::Start(Value::Thing(Thing {
1116					tb: "test".to_owned(),
1117					id: Id::from(1),
1118				})),
1119				Part::All
1120			]))
1121		);
1122	}
1123}