use crate::SyntaxKind;
use crate::parser::{Parser, syntax_codes as sc};
use super::expression;
pub(crate) fn pattern_list(p: &mut Parser<'_>) {
pattern(p);
while p.at(SyntaxKind::COMMA) {
p.bump(SyntaxKind::COMMA);
pattern(p);
}
}
pub(crate) fn pattern(p: &mut Parser<'_>) {
let m = p.start();
if is_path_binder(p) {
let bind = p.start();
name_binder(p);
p.bump(SyntaxKind::EQ);
path_pattern(p);
bind.complete(p, SyntaxKind::NAMED_PATTERN_PART);
} else {
path_pattern(p);
}
m.complete(p, SyntaxKind::PATTERN);
}
fn is_path_binder(p: &mut Parser<'_>) -> bool {
if !(p.current() == SyntaxKind::IDENT || p.current() == SyntaxKind::QUOTED_IDENT) {
return false;
}
p.nth(1) == SyntaxKind::EQ
}
fn path_pattern(p: &mut Parser<'_>) {
if p.at(SyntaxKind::SHORTESTPATH_KW) || p.at(SyntaxKind::ALLSHORTESTPATHS_KW) {
shortest_path_pattern(p);
return;
}
let m = p.start();
if !p.at(SyntaxKind::L_PAREN) {
p.error_code(
sc::EXPECTED_LPAREN_NODE,
"expected '(' to start a node pattern",
);
m.complete(p, SyntaxKind::PATTERN_PART);
return;
}
node_pattern(p);
while at_rel_start(p) {
rel_pattern(p);
if p.at(SyntaxKind::L_PAREN) {
node_pattern(p);
} else {
p.error_code(
sc::EXPECTED_NODE_AFTER_REL,
"expected node pattern after relationship",
);
break;
}
}
m.complete(p, SyntaxKind::PATTERN_PART);
}
fn shortest_path_pattern(p: &mut Parser<'_>) {
debug_assert!(p.at(SyntaxKind::SHORTESTPATH_KW) || p.at(SyntaxKind::ALLSHORTESTPATHS_KW));
let m = p.start();
p.bump_any();
if !p.eat(SyntaxKind::L_PAREN) {
p.error_code(
sc::EXPECTED_LPAREN_NODE,
"expected '(' after `shortestPath` / `allShortestPaths`",
);
}
if p.at(SyntaxKind::L_PAREN) {
path_pattern(p);
} else {
p.error_code(
sc::EXPECTED_LPAREN_NODE,
"expected '(' to start a node pattern",
);
}
if !p.eat(SyntaxKind::R_PAREN) {
p.error_code(
sc::EXPECTED_RPAREN_SHORTEST_PATH,
"expected ')' to close `shortestPath` / `allShortestPaths`",
);
}
m.complete(p, SyntaxKind::SHORTEST_PATH_PATTERN);
}
fn at_rel_start(p: &mut Parser<'_>) -> bool {
matches!(p.current(), SyntaxKind::MINUS | SyntaxKind::ARROW_L)
}
fn node_pattern(p: &mut Parser<'_>) {
debug_assert!(p.at(SyntaxKind::L_PAREN));
let m = p.start();
p.bump(SyntaxKind::L_PAREN);
if p.at(SyntaxKind::IDENT) || p.at(SyntaxKind::QUOTED_IDENT) {
name_binder(p);
}
if p.at(SyntaxKind::COLON) {
label_expr(p);
}
if p.at(SyntaxKind::L_BRACE) {
property_map(p);
}
if !p.eat(SyntaxKind::R_PAREN) {
p.error_code(
sc::EXPECTED_RPAREN_NODE,
"expected ')' to close node pattern",
);
}
m.complete(p, SyntaxKind::NODE_PATTERN);
}
fn rel_pattern(p: &mut Parser<'_>) {
debug_assert!(at_rel_start(p));
let m = p.start();
let left_arrow = if p.at(SyntaxKind::ARROW_L) {
p.bump(SyntaxKind::ARROW_L);
true
} else {
if !p.eat(SyntaxKind::MINUS) {
p.error_code(
sc::EXPECTED_DASH_REL_START,
"expected '-' at relationship start",
);
}
false
};
if p.at(SyntaxKind::L_BRACK) {
rel_detail(p);
}
if left_arrow {
if !p.eat(SyntaxKind::MINUS) {
p.error_code(
sc::EXPECTED_DASH_REL_CLOSE,
"expected '-' to close relationship",
);
}
} else if !(p.eat(SyntaxKind::ARROW_R) || p.eat(SyntaxKind::MINUS)) {
p.error_code(
sc::EXPECTED_DASH_OR_ARROW,
"expected '-' or '->' to close relationship",
);
}
m.complete(p, SyntaxKind::REL_PATTERN);
}
fn rel_detail(p: &mut Parser<'_>) {
debug_assert!(p.at(SyntaxKind::L_BRACK));
let m = p.start();
p.bump(SyntaxKind::L_BRACK);
if p.at(SyntaxKind::IDENT) || p.at(SyntaxKind::QUOTED_IDENT) {
name_binder(p);
}
if p.at(SyntaxKind::COLON) {
rel_type_expr(p);
}
if p.at(SyntaxKind::STAR) {
rel_length(p);
}
if p.at(SyntaxKind::L_BRACE) {
property_map(p);
}
if !p.eat(SyntaxKind::R_BRACK) {
p.error_code(
sc::EXPECTED_RBRACK_REL,
"expected ']' to close relationship detail",
);
}
m.complete(p, SyntaxKind::REL_DETAIL);
}
fn eat_label_or_type_name(p: &mut Parser<'_>) -> bool {
if p.eat(SyntaxKind::IDENT) || p.eat(SyntaxKind::QUOTED_IDENT) {
return true;
}
if p.current().is_keyword() {
p.bump_any();
return true;
}
false
}
fn label_expr(p: &mut Parser<'_>) {
debug_assert!(p.at(SyntaxKind::COLON));
let m = p.start();
while p.at(SyntaxKind::COLON) {
p.bump(SyntaxKind::COLON);
if !eat_label_or_type_name(p) {
p.error_code(sc::EXPECTED_LABEL, "expected label after ':'");
break;
}
}
m.complete(p, SyntaxKind::LABEL_EXPR);
}
fn rel_length(p: &mut Parser<'_>) {
debug_assert!(p.at(SyntaxKind::STAR));
let m = p.start();
p.bump(SyntaxKind::STAR);
if p.at(SyntaxKind::INT_LITERAL) {
p.bump(SyntaxKind::INT_LITERAL);
if p.at(SyntaxKind::DOT_DOT) {
p.bump(SyntaxKind::DOT_DOT);
p.eat(SyntaxKind::INT_LITERAL);
}
} else if p.at(SyntaxKind::DOT_DOT) {
p.bump(SyntaxKind::DOT_DOT);
p.eat(SyntaxKind::INT_LITERAL);
}
m.complete(p, SyntaxKind::REL_LENGTH);
}
fn rel_type_expr(p: &mut Parser<'_>) {
debug_assert!(p.at(SyntaxKind::COLON));
let m = p.start();
p.bump(SyntaxKind::COLON);
if !eat_label_or_type_name(p) {
p.error_code(
sc::EXPECTED_REL_TYPE,
"expected relationship type after ':'",
);
}
m.complete(p, SyntaxKind::REL_TYPE_EXPR);
}
fn property_map(p: &mut Parser<'_>) {
debug_assert!(p.at(SyntaxKind::L_BRACE));
let m = p.start();
p.bump(SyntaxKind::L_BRACE);
if !p.at(SyntaxKind::R_BRACE) {
property_kv(p);
while p.at(SyntaxKind::COMMA) {
p.bump(SyntaxKind::COMMA);
property_kv(p);
}
}
if !p.eat(SyntaxKind::R_BRACE) {
p.error_code(
sc::EXPECTED_RBRACE_PROP,
"expected '}' to close property map",
);
}
m.complete(p, SyntaxKind::PROPERTY_MAP);
}
fn property_kv(p: &mut Parser<'_>) {
if !(p.eat(SyntaxKind::IDENT) || p.eat(SyntaxKind::QUOTED_IDENT)) {
p.error_code(sc::EXPECTED_PROP_KEY, "expected property key");
}
if !p.eat(SyntaxKind::COLON) {
p.error_code(sc::EXPECTED_COLON_PROP, "expected ':' in property entry");
}
if expression::expr(p).is_none() {
p.error_code(
sc::EXPECTED_PROP_VALUE,
"expected expression for property value",
);
}
}
fn name_binder(p: &mut Parser<'_>) {
let m = p.start();
if !(p.eat(SyntaxKind::IDENT) || p.eat(SyntaxKind::QUOTED_IDENT)) {
p.error_code(sc::EXPECTED_IDENT, "expected identifier");
}
m.complete(p, SyntaxKind::NAME);
}