use reblessive::Stk;
use crate::gql::ast::{
EdgeDirection, EdgePattern, ElementPredicate, Ident, LabelExpr, NodePattern, PathMode,
PathPattern, PathPatternPrefix, PathSearchKind, PathStep, Quantifier, QuantifierKind,
};
use crate::gql::parser::mac::{enter_object_recursion, expected, unexpected};
use crate::gql::parser::{ParseResult, Parser};
use crate::gql::token::{Keyword, NumberKind, TokenKind, t};
use crate::syn::error::bail;
impl Parser<'_> {
pub(super) async fn parse_path_pattern(&mut self, stk: &mut Stk) -> ParseResult<PathPattern> {
let path_var = if Self::token_can_be_ident(self.peek_kind()) && self.peek1().kind == t!("=")
{
let ident = self.parse_ident()?;
self.pop_peek();
Some(ident)
} else {
None
};
let prefix = self.parse_path_pattern_prefix()?;
let token = self.peek();
match token.kind {
kind if edge_pattern_starts(kind) => {
bail!(
"Unexpected edge pattern, path patterns must start with a node pattern",
@token.span
);
}
kind if simplified_path_starts(kind) => {
bail!(
"Simplified path pattern expressions (`-/ … /->`) are not supported yet",
@token.span
);
}
_ => {}
}
let start = self.parse_node_pattern(stk).await?;
let mut steps = Vec::new();
loop {
let token = self.peek();
match token.kind {
t!("*") | t!("+") | t!("?") | t!("{") => {
bail!(
"Quantifiers may only follow an edge pattern",
@token.span
);
}
t!("(") => {
bail!(
"Unexpected node pattern, expected an edge pattern between node patterns",
@token.span
);
}
kind if simplified_path_starts(kind) => {
bail!(
"Simplified path pattern expressions (`-/ … /->`) are not supported yet",
@token.span
);
}
_ => {}
}
let Some(edge) = self.parse_edge_pattern(stk).await? else {
break;
};
let token = self.peek();
if token.kind != t!("(") {
unexpected!(self, token, "a node pattern after this edge pattern");
}
let node = self.parse_node_pattern(stk).await?;
steps.push(PathStep {
edge,
node,
});
}
Ok(PathPattern {
path_var,
prefix,
start,
steps,
})
}
fn parse_path_pattern_prefix(&mut self) -> ParseResult<Option<PathPatternPrefix>> {
let first = self.peek();
let start = first.span;
let kind = match first.kind {
t!("WALK") | t!("TRAIL") | t!("SIMPLE") | t!("ACYCLIC") => {
let mode = self.parse_optional_path_mode()?;
self.eat_path_or_paths();
return Ok(Some(PathPatternPrefix {
kind: PathSearchKind::All,
mode,
span: start.covers(self.last_span()),
}));
}
t!("ALL") => {
self.pop_peek();
if self.eat(t!("SHORTEST")) {
PathSearchKind::AllShortest
} else {
PathSearchKind::All
}
}
t!("ANY") => {
self.pop_peek();
if self.eat(t!("SHORTEST")) {
PathSearchKind::AnyShortest
} else {
PathSearchKind::Any {
count: self.parse_optional_path_count()?,
}
}
}
t!("SHORTEST") => {
self.pop_peek();
let count = self.parse_optional_path_count()?;
let mode = self.parse_optional_path_mode()?;
self.eat_path_or_paths();
let kind = if self.eat(t!("GROUP")) || self.eat(t!("GROUPS")) {
PathSearchKind::ShortestGroups {
count,
}
} else {
match count {
Some(count) => PathSearchKind::ShortestCounted {
count,
},
None => bail!(
"`SHORTEST` requires a path count (`SHORTEST k`) or `GROUP`",
@start.covers(self.last_span())
),
}
};
return Ok(Some(PathPatternPrefix {
kind,
mode,
span: start.covers(self.last_span()),
}));
}
_ => return Ok(None),
};
let mode = self.parse_optional_path_mode()?;
self.eat_path_or_paths();
Ok(Some(PathPatternPrefix {
kind,
mode,
span: start.covers(self.last_span()),
}))
}
fn parse_optional_path_mode(&mut self) -> ParseResult<Option<PathMode>> {
let mode = match self.peek_kind() {
t!("WALK") => PathMode::Walk,
t!("TRAIL") => PathMode::Trail,
t!("SIMPLE") => PathMode::Simple,
t!("ACYCLIC") => PathMode::Acyclic,
_ => return Ok(None),
};
self.pop_peek();
Ok(Some(mode))
}
fn eat_path_or_paths(&mut self) -> bool {
self.eat(t!("PATH")) || self.eat(t!("PATHS"))
}
fn parse_optional_path_count(&mut self) -> ParseResult<Option<u32>> {
let token = self.peek();
let TokenKind::Number {
kind:
kind @ (NumberKind::Integer | NumberKind::Hex | NumberKind::Octal | NumberKind::Binary),
suffix: None,
} = token.kind
else {
return Ok(None);
};
self.pop_peek();
let count = self.parse_u32_token(token, kind)?;
if count == 0 {
bail!("A path search count must be a positive integer", @token.span);
}
Ok(Some(count))
}
async fn parse_node_pattern(&mut self, stk: &mut Stk) -> ParseResult<NodePattern> {
let token = self.peek();
if token.kind != t!("(") {
unexpected!(self, token, "a node pattern");
}
let open = self.pop_peek().span;
let next = self.peek();
if next.kind == t!("(")
|| edge_pattern_starts(next.kind)
|| simplified_path_starts(next.kind)
{
bail!(
"Parenthesized path pattern expressions are not supported yet",
@next.span,
@open => "expected this to start a node pattern"
);
}
let (var, label, predicate) = self.parse_element_filler(stk).await?;
if self.peek_kind() == t!("=") {
bail!(
"Parenthesized path pattern expressions (subpath variables) are not supported yet",
@self.peek().span
);
}
self.expect_closing_delimiter(t!(")"), open)?;
Ok(NodePattern {
var,
label,
predicate,
span: open.covers(self.last_span()),
})
}
async fn parse_edge_pattern(&mut self, stk: &mut Stk) -> ParseResult<Option<EdgePattern>> {
let token = self.peek();
let abbreviated = match token.kind {
t!("<-") => Some(EdgeDirection::Left),
t!("~") => Some(EdgeDirection::Undirected),
t!("->") => Some(EdgeDirection::Right),
t!("<~") => Some(EdgeDirection::LeftOrUndirected),
t!("~>") => Some(EdgeDirection::UndirectedOrRight),
t!("<->") => Some(EdgeDirection::LeftOrRight),
t!("-") => Some(EdgeDirection::Any),
_ => None,
};
if let Some(direction) = abbreviated {
self.pop_peek();
let quantifier = self.parse_quantifier()?;
return Ok(Some(EdgePattern {
var: None,
label: None,
direction,
predicate: None,
quantifier,
span: token.span.covers(self.last_span()),
}));
}
let direction = match token.kind {
t!("-[") => {
self.pop_peek();
let filler = self.parse_element_filler(stk).await?;
let close = self.next();
let direction = match close.kind {
t!("]->") => EdgeDirection::Right,
t!("]-") => EdgeDirection::Any,
_ => unexpected!(self, close, "`]->` or `]-`"),
};
(filler, direction)
}
t!("<-[") => {
self.pop_peek();
let filler = self.parse_element_filler(stk).await?;
let close = self.next();
let direction = match close.kind {
t!("]-") => EdgeDirection::Left,
t!("]->") => EdgeDirection::LeftOrRight,
_ => unexpected!(self, close, "`]-` or `]->`"),
};
(filler, direction)
}
t!("~[") => {
self.pop_peek();
let filler = self.parse_element_filler(stk).await?;
let close = self.next();
let direction = match close.kind {
t!("]~") => EdgeDirection::Undirected,
t!("]~>") => EdgeDirection::UndirectedOrRight,
_ => unexpected!(self, close, "`]~` or `]~>`"),
};
(filler, direction)
}
t!("<~[") => {
self.pop_peek();
let filler = self.parse_element_filler(stk).await?;
let close = self.next();
let direction = match close.kind {
t!("]~") => EdgeDirection::LeftOrUndirected,
_ => unexpected!(self, close, "`]~`"),
};
(filler, direction)
}
_ => return Ok(None),
};
let ((var, label, predicate), direction) = direction;
let quantifier = self.parse_quantifier()?;
Ok(Some(EdgePattern {
var,
label,
direction,
predicate,
quantifier,
span: token.span.covers(self.last_span()),
}))
}
async fn parse_element_filler(
&mut self,
stk: &mut Stk,
) -> ParseResult<(Option<Ident>, Option<LabelExpr>, Option<ElementPredicate>)> {
let token = self.peek();
let var = match token.kind {
kind if Self::token_can_be_ident(kind) => Some(self.parse_ident()?),
TokenKind::Keyword(keyword) if !matches!(keyword, Keyword::Is | Keyword::Where) => {
bail!(
"`{}` is a reserved word and cannot be used as a variable name",
self.span_str(token.span),
@token.span => "use a `\"…\"` or `` `…` `` delimited identifier instead"
);
}
_ => None,
};
let label = if self.eat(t!(":")) || self.eat(t!("IS")) {
Some(self.parse_label_expr(stk).await?)
} else {
None
};
let predicate = match self.peek_kind() {
t!("WHERE") => {
self.pop_peek();
let expr = stk.run(|stk| self.parse_expr(stk)).await?;
Some(ElementPredicate::Where(expr))
}
t!("{") => {
let open = self.pop_peek().span;
let mut props = Vec::new();
loop {
let key = self.parse_ident()?;
expected!(self, t!(":"));
let value = stk.run(|stk| self.parse_expr(stk)).await?;
props.push((key, value));
if !self.eat(t!(",")) {
break;
}
}
self.expect_closing_delimiter(t!("}"), open)?;
Some(ElementPredicate::Props(props))
}
_ => None,
};
let token = self.peek();
if predicate.is_some() && matches!(token.kind, t!("WHERE") | t!("{")) {
bail!(
"An element pattern may have either a WHERE clause or a property map, not both",
@token.span
);
}
Ok((var, label, predicate))
}
fn parse_quantifier(&mut self) -> ParseResult<Option<Quantifier>> {
let token = self.peek();
let kind = match token.kind {
t!("*") => {
self.pop_peek();
QuantifierKind::Star
}
t!("+") => {
self.pop_peek();
QuantifierKind::Plus
}
t!("?") => {
self.pop_peek();
QuantifierKind::Question
}
t!("{") => {
let open = self.pop_peek().span;
let lower = match self.peek_kind() {
t!(",") => None,
TokenKind::Number {
..
} => Some(self.parse_quantifier_bound()?),
_ => {
let token = self.peek();
unexpected!(self, token, "an unsigned integer or `,`");
}
};
let kind = if self.eat(t!(",")) {
let upper = if self.peek_kind() == t!("}") {
None
} else {
Some(self.parse_quantifier_bound()?)
};
QuantifierKind::Range(lower, upper)
} else {
match lower {
Some(n) => QuantifierKind::Fixed(n),
None => {
let token = self.peek();
unexpected!(self, token, "an unsigned integer or `,`");
}
}
};
self.expect_closing_delimiter(t!("}"), open)?;
kind
}
_ => return Ok(None),
};
Ok(Some(Quantifier {
kind,
span: token.span.covers(self.last_span()),
}))
}
fn parse_quantifier_bound(&mut self) -> ParseResult<u32> {
let token = self.next();
match token.kind {
TokenKind::Number {
kind:
kind @ (NumberKind::Integer
| NumberKind::Hex
| NumberKind::Octal
| NumberKind::Binary),
suffix: None,
} => self.parse_u32_token(token, kind),
_ => unexpected!(self, token, "an unsigned integer"),
}
}
async fn parse_label_expr(&mut self, stk: &mut Stk) -> ParseResult<LabelExpr> {
let mut left = self.parse_label_conjunction(stk).await?;
while self.eat(t!("|")) {
let right = self.parse_label_conjunction(stk).await?;
let span = left.span().covers(right.span());
left = LabelExpr::Disjunction(Box::new(left), Box::new(right), span);
}
Ok(left)
}
async fn parse_label_conjunction(&mut self, stk: &mut Stk) -> ParseResult<LabelExpr> {
let mut left = stk.run(|stk| self.parse_label_negation(stk)).await?;
while self.eat(t!("&")) {
let right = stk.run(|stk| self.parse_label_negation(stk)).await?;
let span = left.span().covers(right.span());
left = LabelExpr::Conjunction(Box::new(left), Box::new(right), span);
}
Ok(left)
}
async fn parse_label_negation(&mut self, stk: &mut Stk) -> ParseResult<LabelExpr> {
let token = self.peek();
if token.kind == t!("!") {
self.pop_peek();
let inner = stk.run(|stk| self.parse_label_negation(stk)).await?;
let span = token.span.covers(inner.span());
return Ok(LabelExpr::Negation(Box::new(inner), span));
}
self.parse_label_primary(stk).await
}
async fn parse_label_primary(&mut self, stk: &mut Stk) -> ParseResult<LabelExpr> {
let token = self.peek();
match token.kind {
t!("%") => {
self.pop_peek();
Ok(LabelExpr::Wildcard(token.span))
}
t!("(") => {
enter_object_recursion!(this = self => {
let open = this.pop_peek().span;
let inner = stk.run(|stk| this.parse_label_expr(stk)).await?;
this.expect_closing_delimiter(t!(")"), open)?;
Ok(inner)
})
}
kind if Self::token_can_be_ident(kind) => Ok(LabelExpr::Name(self.parse_ident()?)),
TokenKind::Keyword(_) => {
self.parse_ident().map(LabelExpr::Name)
}
_ => unexpected!(self, token, "a label expression"),
}
}
}
fn edge_pattern_starts(kind: TokenKind) -> bool {
matches!(
kind,
t!("-[")
| t!("<-[")
| t!("~[")
| t!("<~[")
| t!("<-")
| t!("~") | t!("->")
| t!("<~")
| t!("~>")
| t!("<->")
| t!("-")
)
}
fn simplified_path_starts(kind: TokenKind) -> bool {
matches!(kind, t!("-/") | t!("<-/") | t!("~/") | t!("<~/"))
}