surrealdb_core/syn/parser/
idiom.rs

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