use reblessive::Stk;
use crate::gql::ast::{
DeleteStatement, DetachMode, GqlExpr, Ident, InsertEdge, InsertEdgeDir, InsertNode, InsertPath,
InsertStatement, RemoveItem, RemoveStatement, SetItem, SetStatement,
};
use crate::gql::parser::mac::{expected, unexpected};
use crate::gql::parser::{ParseResult, Parser};
use crate::gql::token::{Keyword, Span, TokenKind, t};
use crate::syn::error::bail;
impl Parser<'_> {
pub(super) async fn parse_set_statement(
&mut self,
stk: &mut Stk,
start: Span,
) -> ParseResult<SetStatement> {
let mut items = Vec::new();
loop {
let item_start = self.peek().span;
let var = self.parse_ident()?;
let token = self.peek();
let item = match token.kind {
t!(".") => {
self.pop_peek();
let prop = self.parse_ident()?;
expected!(self, t!("="));
let value = stk.run(|stk| self.parse_expr(stk)).await?;
SetItem::Property {
var,
prop,
value,
span: item_start.covers(self.last_span()),
}
}
t!("=") => {
self.pop_peek();
let props = self.parse_property_map(stk).await?;
SetItem::AllProperties {
var,
props,
span: item_start.covers(self.last_span()),
}
}
t!(":") | t!("IS") => {
self.pop_peek();
let label = self.parse_ident()?;
SetItem::Label {
var,
label,
span: item_start.covers(self.last_span()),
}
}
_ => unexpected!(self, token, "`.`, `=`, `:` or `IS`"),
};
items.push(item);
if !self.eat(t!(",")) {
break;
}
}
Ok(SetStatement {
items,
span: start.covers(self.last_span()),
})
}
pub(super) fn parse_remove_statement(&mut self, start: Span) -> ParseResult<RemoveStatement> {
let mut items = Vec::new();
loop {
let item_start = self.peek().span;
let var = self.parse_ident()?;
let token = self.peek();
let item = match token.kind {
t!(".") => {
self.pop_peek();
let prop = self.parse_ident()?;
RemoveItem::Property {
var,
prop,
span: item_start.covers(self.last_span()),
}
}
t!(":") | t!("IS") => {
self.pop_peek();
let label = self.parse_ident()?;
RemoveItem::Label {
var,
label,
span: item_start.covers(self.last_span()),
}
}
_ => unexpected!(self, token, "`.`, `:` or `IS`"),
};
items.push(item);
if !self.eat(t!(",")) {
break;
}
}
Ok(RemoveStatement {
items,
span: start.covers(self.last_span()),
})
}
pub(super) async fn parse_delete_statement(
&mut self,
stk: &mut Stk,
detach: DetachMode,
start: Span,
) -> ParseResult<DeleteStatement> {
let mut items = Vec::new();
loop {
items.push(stk.run(|stk| self.parse_expr(stk)).await?);
if !self.eat(t!(",")) {
break;
}
}
Ok(DeleteStatement {
detach,
items,
span: start.covers(self.last_span()),
})
}
pub(super) async fn parse_insert_statement(
&mut self,
stk: &mut Stk,
start: Span,
) -> ParseResult<InsertStatement> {
let mut paths = Vec::new();
loop {
paths.push(self.parse_insert_path(stk).await?);
if !self.eat(t!(",")) {
break;
}
}
Ok(InsertStatement {
paths,
span: start.covers(self.last_span()),
})
}
async fn parse_insert_path(&mut self, stk: &mut Stk) -> ParseResult<InsertPath> {
let start = self.peek().span;
let first = self.parse_insert_node(stk).await?;
let mut steps = Vec::new();
while let Some(edge) = self.parse_insert_edge(stk).await? {
let node = self.parse_insert_node(stk).await?;
steps.push((edge, node));
}
Ok(InsertPath {
start: first,
steps,
span: start.covers(self.last_span()),
})
}
async fn parse_insert_node(&mut self, stk: &mut Stk) -> ParseResult<InsertNode> {
let open = expected!(self, t!("(")).span;
let (var, label, props) = self.parse_insert_filler(stk).await?;
self.expect_closing_delimiter(t!(")"), open)?;
Ok(InsertNode {
var,
label,
props,
span: open.covers(self.last_span()),
})
}
async fn parse_insert_edge(&mut self, stk: &mut Stk) -> ParseResult<Option<InsertEdge>> {
let token = self.peek();
let direction = match token.kind {
t!("-[") => InsertEdgeDir::Right,
t!("<-[") => InsertEdgeDir::Left,
t!("~[") | t!("<~[") => {
bail!("Undirected INSERT edges are not supported", @token.span);
}
_ => return Ok(None),
};
let open = token.span;
self.pop_peek();
let (var, label, props) = self.parse_insert_filler(stk).await?;
let close = self.next();
match (direction, close.kind) {
(InsertEdgeDir::Right, t!("]->")) | (InsertEdgeDir::Left, t!("]-")) => {}
_ => {
bail!(
"Only directed INSERT edges (`-[…]->` or `<-[…]-`) are supported",
@close.span
);
}
}
Ok(Some(InsertEdge {
var,
label,
direction,
props,
span: open.covers(self.last_span()),
}))
}
async fn parse_insert_filler(
&mut self,
stk: &mut Stk,
) -> ParseResult<(Option<Ident>, Option<Ident>, Vec<(Ident, GqlExpr)>)> {
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) => {
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_ident()?)
} else {
None
};
let props = if self.peek_kind() == t!("{") {
self.parse_property_map(stk).await?
} else {
Vec::new()
};
Ok((var, label, props))
}
async fn parse_property_map(&mut self, stk: &mut Stk) -> ParseResult<Vec<(Ident, GqlExpr)>> {
let open = expected!(self, t!("{")).span;
let mut props = Vec::new();
if self.peek_kind() != t!("}") {
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)?;
Ok(props)
}
}