Skip to main content

surql_parser/upstream/syn/parser/
idiom.rs

1use super::basic::NumberToken;
2use super::mac::{expected, unexpected};
3use super::{ParseResult, Parser};
4use crate::upstream::sql::field::Selector;
5use crate::upstream::sql::lookup::LookupKind;
6use crate::upstream::sql::part::{DestructurePart, Recurse, RecurseInstruction};
7use crate::upstream::sql::{Dir, Expr, Field, Fields, Idiom, Literal, Lookup, Param, Part};
8use crate::upstream::syn::error::bail;
9use crate::upstream::syn::lexer::compound::{self, Numeric};
10use crate::upstream::syn::token::{Span, TokenKind, t};
11use reblessive::Stk;
12impl Parser<'_> {
13	pub(super) fn peek_continues_idiom(&mut self) -> bool {
14		let peek = self.peek().kind;
15		if matches!(peek, t!("->") | t!("[") | t!(".") | t!("...")) {
16			return true;
17		}
18		peek == t!("<") && matches!(self.peek1().kind, t!("-") | t!("~") | t!("->"))
19	}
20	/// Parse fields of a selecting query: `foo, bar` in `SELECT foo, bar FROM
21	/// baz`.
22	///
23	/// # Parser State
24	/// Expects the next tokens to be of a field set.
25	pub async fn parse_fields(&mut self, stk: &mut Stk) -> ParseResult<Fields> {
26		if self.eat(t!("VALUE")) {
27			let expr = stk.run(|ctx| self.parse_expr_field(ctx)).await?;
28			let alias = if self.eat(t!("AS")) {
29				Some(self.parse_basic_idiom()?)
30			} else {
31				None
32			};
33			Ok(Fields::Value(Box::new(Selector { expr, alias })))
34		} else {
35			let mut fields = Vec::new();
36			loop {
37				let field = if self.eat(t!("*")) {
38					Field::All
39				} else {
40					let expr = stk.run(|ctx| self.parse_expr_field(ctx)).await?;
41					let alias = if self.eat(t!("AS")) {
42						Some(self.parse_plain_idiom(stk).await?)
43					} else {
44						None
45					};
46					Field::Single(Selector { expr, alias })
47				};
48				fields.push(field);
49				if !self.eat(t!(",")) {
50					break;
51				}
52			}
53			Ok(Fields::Select(fields))
54		}
55	}
56	/// Parses a list of idioms separated by a `,`
57	pub(super) async fn parse_idiom_list(&mut self, stk: &mut Stk) -> ParseResult<Vec<Idiom>> {
58		let mut res = vec![self.parse_plain_idiom(stk).await?];
59		while self.eat(t!(",")) {
60			res.push(self.parse_plain_idiom(stk).await?);
61		}
62		Ok(res)
63	}
64	/// Parses the remaining idiom parts after the start: Any part like `...`,
65	/// `.foo` and `->foo`
66	///
67	/// This function differes from [`Parser::parse_remaining_value_idiom`] in
68	/// how it handles graph parsing. Graphs inside a plain idioms will remain
69	/// a normal graph production.
70	pub(super) async fn parse_remaining_idiom(
71		&mut self,
72		stk: &mut Stk,
73		start: Vec<Part>,
74	) -> ParseResult<Idiom> {
75		let mut res = start;
76		loop {
77			match self.peek_kind() {
78				t!("...") => {
79					self.pop_peek();
80					res.push(Part::Flatten);
81				}
82				t!(".") => {
83					self.pop_peek();
84					res.push(self.parse_dot_part(stk).await?)
85				}
86				t!("[") => {
87					let span = self.pop_peek().span;
88					let part = self.parse_bracket_part(stk, span).await?;
89					res.push(part)
90				}
91				t!("->") => {
92					self.pop_peek();
93					let lookup = stk
94						.run(|stk| self.parse_lookup(stk, LookupKind::Graph(Dir::Out)))
95						.await?;
96					res.push(Part::Graph(lookup))
97				}
98				t!("<") => {
99					if let Some(peek) = self.peek_whitespace1() {
100						if peek.kind == t!("~") {
101							self.pop_peek();
102							self.pop_peek();
103							let lookup = stk
104								.run(|stk| self.parse_lookup(stk, LookupKind::Reference))
105								.await?;
106							res.push(Part::Graph(lookup))
107						} else if peek.kind == t!("-") {
108							self.pop_peek();
109							self.pop_peek();
110							let lookup = stk
111								.run(|stk| self.parse_lookup(stk, LookupKind::Graph(Dir::In)))
112								.await?;
113							res.push(Part::Graph(lookup))
114						} else if peek.kind == t!("->") {
115							self.pop_peek();
116							self.pop_peek();
117							let lookup = stk
118								.run(|stk| self.parse_lookup(stk, LookupKind::Graph(Dir::Both)))
119								.await?;
120							res.push(Part::Graph(lookup))
121						} else {
122							break;
123						}
124					} else {
125						break;
126					}
127				}
128				_ => break,
129			}
130		}
131		Ok(Idiom(res))
132	}
133	/// Parses the remaining idiom parts after the start: Any part like `...`,
134	/// `.foo` and `->foo`
135	///
136	///
137	/// This function differes from [`Parser::parse_remaining_value_idiom`] in
138	/// how it handles graph parsing. When parsing a idiom like production
139	/// which can be a value, the initial start value might need to be changed
140	/// to a Edge depending on what is parsed next.
141	pub(super) async fn parse_remaining_value_idiom(
142		&mut self,
143		stk: &mut Stk,
144		start: Vec<Part>,
145	) -> ParseResult<Expr> {
146		let mut res = start;
147		loop {
148			match self.peek_kind() {
149				t!("...") => {
150					self.pop_peek();
151					res.push(Part::Flatten);
152				}
153				t!(".") => {
154					self.pop_peek();
155					res.push(self.parse_dot_part(stk).await?)
156				}
157				t!("[") => {
158					let span = self.pop_peek().span;
159					let part = self.parse_bracket_part(stk, span).await?;
160					res.push(part)
161				}
162				t!("->") => {
163					self.pop_peek();
164					let x = self.parse_lookup(stk, LookupKind::Graph(Dir::Out)).await?;
165					res.push(Part::Graph(x))
166				}
167				t!("<") => {
168					if let Some(peek) = self.peek_whitespace1() {
169						if peek.kind == t!("~") {
170							self.pop_peek();
171							self.pop_peek();
172							let lookup = self.parse_lookup(stk, LookupKind::Reference).await?;
173							res.push(Part::Graph(lookup))
174						} else if peek.kind == t!("-") {
175							self.pop_peek();
176							self.pop_peek();
177							let lookup = self.parse_lookup(stk, LookupKind::Graph(Dir::In)).await?;
178							res.push(Part::Graph(lookup))
179						} else if peek.kind == t!("->") {
180							self.pop_peek();
181							self.pop_peek();
182							let lookup =
183								self.parse_lookup(stk, LookupKind::Graph(Dir::Both)).await?;
184							res.push(Part::Graph(lookup))
185						} else {
186							break;
187						}
188					} else {
189						break;
190					}
191				}
192				_ => break,
193			}
194		}
195		Ok(Expr::Idiom(Idiom(res)))
196	}
197	/// Parse a idiom which can only start with a graph or an identifier.
198	/// Other expressions are not allowed as start of this idiom
199	pub async fn parse_plain_idiom(&mut self, stk: &mut Stk) -> ParseResult<Idiom> {
200		let start = match self.peek_kind() {
201			t!("->") => {
202				self.pop_peek();
203				let lookup = stk
204					.run(|ctx| self.parse_lookup(ctx, LookupKind::Graph(Dir::Out)))
205					.await?;
206				Part::Graph(lookup)
207			}
208			t!("<") => {
209				let t = self.pop_peek();
210				let lookup = if self.eat_whitespace(t!("~")) {
211					stk.run(|ctx| self.parse_lookup(ctx, LookupKind::Reference))
212						.await?
213				} else if self.eat_whitespace(t!("-")) {
214					stk.run(|ctx| self.parse_lookup(ctx, LookupKind::Graph(Dir::In)))
215						.await?
216				} else if self.eat_whitespace(t!("->")) {
217					stk.run(|ctx| self.parse_lookup(ctx, LookupKind::Graph(Dir::Both)))
218						.await?
219				} else {
220					unexpected!(self, t, "either `<-` `<->` or `->`")
221				};
222				Part::Graph(lookup)
223			}
224			_ => Part::Field(self.parse_ident()?),
225		};
226		let start = vec![start];
227		self.parse_remaining_idiom(stk, start).await
228	}
229	/// Parse the part after the `.` in a idiom
230	pub(super) async fn parse_dot_part(&mut self, stk: &mut Stk) -> ParseResult<Part> {
231		let res = match self.peek_kind() {
232			t!("?") => {
233				self.pop_peek();
234				Part::Optional
235			}
236			t!("*") => {
237				self.pop_peek();
238				Part::All
239			}
240			t!("@") => {
241				self.pop_peek();
242				Part::RepeatRecurse
243			}
244			t!("{") => {
245				self.pop_peek();
246				stk.run(|ctx| self.parse_curly_part(ctx)).await?
247			}
248			_ => {
249				let ident = self.parse_ident()?;
250				if self.eat(t!("(")) {
251					self.parse_function_part(stk, ident).await?
252				} else {
253					Part::Field(ident)
254				}
255			}
256		};
257		Ok(res)
258	}
259	/// Parse the part after the `.` in a idiom
260	pub(super) fn parse_basic_dot_part(&mut self) -> ParseResult<Part> {
261		let res = match self.peek_kind() {
262			t!("*") => {
263				self.pop_peek();
264				Part::All
265			}
266			_ => {
267				let ident = self.parse_ident()?;
268				Part::Field(ident)
269			}
270		};
271		Ok(res)
272	}
273	pub(super) async fn parse_function_part(
274		&mut self,
275		stk: &mut Stk,
276		name: String,
277	) -> ParseResult<Part> {
278		let args = self.parse_function_args(stk).await?;
279		Ok(Part::Method(name, args))
280	}
281	/// Parse the part after the `.{` in an idiom
282	pub(super) async fn parse_curly_part(&mut self, stk: &mut Stk) -> ParseResult<Part> {
283		match self.peek_kind() {
284			t!("*") | t!("..") | TokenKind::Digits => self.parse_recurse_part(stk).await,
285			_ => self.parse_destructure_part(stk).await,
286		}
287	}
288	/// Parse a destructure part, expects `.{` to already be parsed
289	pub(super) async fn parse_destructure_part(&mut self, stk: &mut Stk) -> ParseResult<Part> {
290		let start = self.last_span();
291		let mut destructured: Vec<DestructurePart> = Vec::new();
292		loop {
293			if self.eat(t!("}")) {
294				break;
295			}
296			let field = self.parse_ident()?;
297			let part = match self.peek_kind() {
298				t!(":") => {
299					self.pop_peek();
300					let idiom = match self.parse_expr_field(stk).await? {
301						Expr::Idiom(x) => x,
302						v => Idiom(vec![Part::Start(v)]),
303					};
304					DestructurePart::Aliased(field, idiom)
305				}
306				t!(".") => {
307					self.pop_peek();
308					let found = self.peek_kind();
309					match self.parse_dot_part(stk).await? {
310						Part::All => DestructurePart::All(field),
311						Part::Destructure(v) => DestructurePart::Destructure(field, v),
312						_ => {
313							bail!(
314								"Unexpected token `{}` expected a `*` or a destructuring",
315								found, @ self.last_span()
316							);
317						}
318					}
319				}
320				_ => DestructurePart::Field(field),
321			};
322			destructured.push(part);
323			if !self.eat(t!(",")) {
324				self.expect_closing_delimiter(t!("}"), start)?;
325				break;
326			}
327		}
328		Ok(Part::Destructure(destructured))
329	}
330	/// Parse the inner part of a recurse, expects a valid recurse value in the
331	/// current position
332	pub(super) fn parse_recurse_inner(&mut self) -> ParseResult<Recurse> {
333		let min = if matches!(self.peek().kind, TokenKind::Digits) {
334			Some(self.next_token_value::<u32>()?)
335		} else {
336			None
337		};
338		match (self.eat_whitespace(t!("..")), min) {
339			(true, _) => {}
340			(false, Some(v)) => {
341				return Ok(Recurse::Fixed(v));
342			}
343			_ => {
344				let found = self.next().kind;
345				bail!(
346					"Unexpected token `{}` expected an integer or ..", found, @ self
347					.last_span()
348				);
349			}
350		}
351		let max = if let Some(TokenKind::Digits) = self.peek_whitespace().map(|x| x.kind) {
352			Some(self.next_token_value::<u32>()?)
353		} else {
354			None
355		};
356		Ok(Recurse::Range(min, max))
357	}
358	/// Parse a recursion instruction following the inner recurse part, if any
359	pub(super) async fn parse_recurse_instruction(
360		&mut self,
361		stk: &mut Stk,
362	) -> ParseResult<Option<RecurseInstruction>> {
363		let instruction = if self.eat(t!("+")) {
364			let kind = self.parse_ident()?;
365			if kind.eq_ignore_ascii_case("path") {
366				let mut inclusive = false;
367				loop {
368					if self.eat(t!("+")) {
369						let kind = self.parse_ident()?;
370						if kind.eq_ignore_ascii_case("inclusive") {
371							inclusive = true
372						} else {
373							bail!(
374								"Unexpected option `{}` expected `inclusive`", kind, @ self
375								.last_span()
376							);
377						}
378					} else {
379						break;
380					};
381				}
382				Some(RecurseInstruction::Path { inclusive })
383			} else if kind.eq_ignore_ascii_case("collect") {
384				let mut inclusive = false;
385				loop {
386					if self.eat(t!("+")) {
387						let kind = self.parse_ident()?;
388						if kind.eq_ignore_ascii_case("inclusive") {
389							inclusive = true
390						} else {
391							bail!(
392								"Unexpected option `{}` expected `inclusive`", kind, @ self
393								.last_span()
394							);
395						}
396					} else {
397						break;
398					};
399				}
400				Some(RecurseInstruction::Collect { inclusive })
401			} else if kind.eq_ignore_ascii_case("shortest") {
402				expected!(self, t!("="));
403				let token = self.peek();
404				let expects = match token.kind {
405					TokenKind::Parameter => Expr::Param(self.next_token_value::<Param>()?),
406					x if Parser::kind_is_identifier(x) => {
407						Expr::Literal(Literal::RecordId(self.parse_record_id(stk).await?))
408					}
409					_ => {
410						unexpected!(self, token, "a param or record-id");
411					}
412				};
413				let mut inclusive = false;
414				loop {
415					if self.eat(t!("+")) {
416						let kind = self.parse_ident()?;
417						if kind.eq_ignore_ascii_case("inclusive") {
418							inclusive = true
419						} else {
420							bail!(
421								"Unexpected option `{}` expected `inclusive`", kind, @ self
422								.last_span()
423							);
424						}
425					} else {
426						break;
427					};
428				}
429				Some(RecurseInstruction::Shortest { expects, inclusive })
430			} else {
431				bail!(
432					"Unexpected instruction `{}` expected `path`, `collect`, or `shortest`",
433					kind, @ self.last_span()
434				);
435			}
436		} else {
437			None
438		};
439		Ok(instruction)
440	}
441	/// Parse a recurse part, expects `.{` to already be parsed
442	pub(super) async fn parse_recurse_part(&mut self, stk: &mut Stk) -> ParseResult<Part> {
443		let start = self.last_span();
444		let recurse = self.parse_recurse_inner()?;
445		let instruction = self.parse_recurse_instruction(stk).await?;
446		self.expect_closing_delimiter(t!("}"), start)?;
447		let nest = if self.eat(t!("(")) {
448			let start = self.last_span();
449			let idiom = self.parse_remaining_idiom(stk, vec![]).await?;
450			self.expect_closing_delimiter(t!(")"), start)?;
451			Some(idiom)
452		} else {
453			None
454		};
455		Ok(Part::Recurse(recurse, nest, instruction))
456	}
457	/// Parse the part after the `[` in a idiom
458	pub(super) async fn parse_bracket_part(
459		&mut self,
460		stk: &mut Stk,
461		start: Span,
462	) -> ParseResult<Part> {
463		let peek = self.peek();
464		let res = match peek.kind {
465			t!("*") => {
466				self.pop_peek();
467				Part::All
468			}
469			t!("$") => {
470				self.pop_peek();
471				Part::Last
472			}
473			t!("?") | t!("WHERE") => {
474				self.pop_peek();
475				let value = stk.run(|ctx| self.parse_expr_field(ctx)).await?;
476				Part::Where(value)
477			}
478			_ => {
479				let value = stk.run(|ctx| self.parse_expr_inherit(ctx)).await?;
480				Part::Value(value)
481			}
482		};
483		self.expect_closing_delimiter(t!("]"), start)?;
484		Ok(res)
485	}
486	/// Parse a basic idiom.
487	///
488	/// Basic idioms differ from normal idioms in that they are more
489	/// restrictive. Flatten, graphs, conditions and indexing by param is not
490	/// allowed.
491	pub(super) fn parse_basic_idiom(&mut self) -> ParseResult<Idiom> {
492		let start = self.parse_ident()?;
493		let mut parts = vec![Part::Field(start)];
494		loop {
495			let token = self.peek();
496			let part = match token.kind {
497				t!(".") => {
498					self.pop_peek();
499					self.parse_basic_dot_part()?
500				}
501				t!("[") => {
502					self.pop_peek();
503					let peek = self.peek();
504					let res = match peek.kind {
505						t!("*") => {
506							self.pop_peek();
507							Part::All
508						}
509						t!("$") => {
510							self.pop_peek();
511							Part::Last
512						}
513						TokenKind::Digits | t!("+") => {
514							let number = self.next_token_value::<NumberToken>()?;
515							let expr = match number {
516								NumberToken::Float(x) => Expr::Literal(Literal::Float(x)),
517								NumberToken::Integer(x) => {
518									Expr::Literal(Literal::Integer(x.into_int(self.recent_span())?))
519								}
520								NumberToken::Decimal(x) => Expr::Literal(Literal::Decimal(x)),
521							};
522							Part::Value(expr)
523						}
524						t!("-") => {
525							if let Some(peek_digit) = self.peek_whitespace1()
526								&& let TokenKind::Digits = peek_digit.kind
527							{
528								let span = self.recent_span().covers(peek_digit.span);
529								bail!(
530									"Unexpected token `-` expected $, *, or a number", @ span =>
531									"an index in a basic idiom can't be negative"
532								);
533							}
534							unexpected!(self, peek, "$, * or a number");
535						}
536						_ => unexpected!(self, peek, "$, * or a number"),
537					};
538					self.expect_closing_delimiter(t!("]"), token.span)?;
539					res
540				}
541				_ => break,
542			};
543			parts.push(part);
544		}
545		Ok(Idiom(parts))
546	}
547	/// Parse a local idiom.
548	///
549	/// Basic idioms differ from local idioms in that they are more restrictive.
550	/// Only field, all and number indexing is allowed. Flatten is also allowed
551	/// but only at the end.
552	pub(super) fn parse_local_idiom(&mut self) -> ParseResult<Idiom> {
553		let start = self.parse_ident()?;
554		let mut parts = vec![Part::Field(start)];
555		loop {
556			let token = self.peek();
557			let part = match token.kind {
558				t!(".") => {
559					self.pop_peek();
560					self.parse_basic_dot_part()?
561				}
562				t!("[") => {
563					self.pop_peek();
564					let token = self.peek();
565					let res = match token.kind {
566						t!("*") => {
567							self.pop_peek();
568							Part::All
569						}
570						TokenKind::Digits | t!("+") => {
571							let next = self.next();
572							let number = self.lex_compound(next, compound::numeric)?;
573							let number = match number.value {
574								Numeric::Duration(_) => {
575									bail!(
576										"Unexpected token `duration` expected a number", @ number
577										.span
578									);
579								}
580								Numeric::Integer(x) => {
581									Expr::Literal(Literal::Integer(x.into_int(number.span)?))
582								}
583								Numeric::Float(x) => Expr::Literal(Literal::Float(x)),
584								Numeric::Decimal(x) => Expr::Literal(Literal::Decimal(x)),
585							};
586							Part::Value(number)
587						}
588						t!("-") => {
589							if let Some(peek_digit) = self.peek_whitespace1()
590								&& let TokenKind::Digits = peek_digit.kind
591							{
592								let span = self.recent_span().covers(peek_digit.span);
593								bail!(
594									"Unexpected token `-` expected $, *, or a number", @ span =>
595									"index in a local idiom can't be negative"
596								);
597							}
598							unexpected!(self, token, "$, * or a number");
599						}
600						_ => unexpected!(self, token, "$, * or a number"),
601					};
602					self.expect_closing_delimiter(t!("]"), token.span)?;
603					res
604				}
605				_ => break,
606			};
607			parts.push(part);
608		}
609		if self.eat(t!("...")) {
610			let token = self.peek();
611			if let t!(".") | t!("[") = token.kind {
612				bail!(
613					"Unexpected token `...` expected a local idiom to end.", @ token.span
614					=> "Flattening can only be done at the end of a local idiom"
615				)
616			}
617			parts.push(Part::Flatten);
618		}
619		Ok(Idiom(parts))
620	}
621	/// Parses a list of what values seperated by comma's
622	///
623	/// # Parser state
624	/// Expects to be at the start of a what list.
625	pub(super) async fn parse_what_list(&mut self, stk: &mut Stk) -> ParseResult<Vec<Expr>> {
626		let mut res = vec![stk.run(|ctx| self.parse_expr_table(ctx)).await?];
627		while self.eat(t!(",")) {
628			res.push(stk.run(|ctx| self.parse_expr_table(ctx)).await?)
629		}
630		Ok(res)
631	}
632	/// Parses a graph value
633	///
634	/// # Parser state
635	/// Expects to just have eaten a direction (e.g. <-, <->, or ->) and be at
636	/// the field like part of the graph
637	pub(super) async fn parse_lookup(
638		&mut self,
639		stk: &mut Stk,
640		lookup_kind: LookupKind,
641	) -> ParseResult<Lookup> {
642		let token = self.peek();
643		match token.kind {
644			t!("?") => {
645				self.pop_peek();
646				Ok(Lookup {
647					kind: lookup_kind,
648					..Default::default()
649				})
650			}
651			t!("(") => {
652				let span = self.pop_peek().span;
653				let expr = if self.eat(t!("SELECT")) {
654					let before = self.peek().span;
655					let expr = self.parse_fields(stk).await?;
656					let fields_span = before.covers(self.last_span());
657					expected!(self, t!("FROM"));
658					Some((expr, fields_span))
659				} else {
660					None
661				};
662				let token = self.peek();
663				let what = match token.kind {
664					t!("?") => {
665						self.pop_peek();
666						Vec::new()
667					}
668					x if Self::kind_is_identifier(x) => {
669						let subject = self.parse_lookup_subject(stk, true).await?;
670						let mut subjects = vec![subject];
671						while self.eat(t!(",")) {
672							subjects.push(self.parse_lookup_subject(stk, true).await?);
673						}
674						subjects
675					}
676					_ => unexpected!(self, token, "`?`, an identifier or a range"),
677				};
678				let cond = self.try_parse_condition(stk).await?;
679				let (split, group, order) = if let Some((ref expr, fields_span)) = expr {
680					let split_before = self.peek().span;
681					let split = self.try_parse_split(expr, fields_span)?;
682					let split_span = split
683						.as_ref()
684						.map(|_| split_before.covers(self.last_span()));
685					let group = self.try_parse_group(expr, fields_span, split_span)?;
686					let order = self.try_parse_orders(expr, fields_span)?;
687					(split, group, order)
688				} else {
689					(None, None, None)
690				};
691				let (limit, start) = if let t!("START") = self.peek_kind() {
692					let start = self.try_parse_start(stk).await?;
693					let limit = self.try_parse_limit(stk).await?;
694					(limit, start)
695				} else {
696					let limit = self.try_parse_limit(stk).await?;
697					let start = self.try_parse_start(stk).await?;
698					(limit, start)
699				};
700				let alias = if self.eat(t!("AS")) {
701					Some(self.parse_plain_idiom(stk).await?)
702				} else {
703					None
704				};
705				self.expect_closing_delimiter(t!(")"), span)?;
706				Ok(Lookup {
707					kind: lookup_kind,
708					what,
709					cond,
710					alias,
711					expr: expr.map(|(x, _)| x),
712					split,
713					group,
714					order,
715					limit,
716					start,
717				})
718			}
719			x if Self::kind_is_identifier(x) => {
720				let subject = self.parse_lookup_subject(stk, false).await?;
721				Ok(Lookup {
722					kind: lookup_kind,
723					what: vec![subject],
724					..Default::default()
725				})
726			}
727			_ => unexpected!(self, token, "`?`, `(` or an identifier"),
728		}
729	}
730}