use crate::SyntaxKind;
use crate::parser::{CompletedMarker, Marker, Parser, TokenSet, syntax_codes as sc};
use super::pattern;
pub(crate) fn expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
expr_bp(p, 0)
}
const MAX_EXPR_DEPTH: u32 = 256;
fn expr_bp(p: &mut Parser<'_>, min_bp: u8) -> Option<CompletedMarker> {
expr_bp_depth(p, min_bp, 0)
}
fn expr_bp_depth(p: &mut Parser<'_>, min_bp: u8, depth: u32) -> Option<CompletedMarker> {
if depth > MAX_EXPR_DEPTH {
p.error_code(
sc::EXPR_NESTING_LIMIT,
"expression nesting exceeds parser limit",
);
return None;
}
let mut lhs = if let Some(prefix_bp) = prefix_bp(p.current()) {
let m = p.start();
let op_kind = p.current();
p.bump_any();
if expr_bp_depth(p, prefix_bp, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_UNARY_OPERAND,
format!("expected operand after unary {op_kind:?}"),
);
}
m.complete(p, SyntaxKind::UNARY_EXPR)
} else {
atom(p, depth)?
};
loop {
if let Some(postfix) = postfix_op(p) {
if postfix.bp < min_bp {
break;
}
lhs = apply_postfix(p, lhs, postfix, depth);
continue;
}
if p.at(SyntaxKind::IS_KW) {
let null_check_bp = 10;
if null_check_bp < min_bp {
break;
}
let m = lhs.precede(p);
p.bump(SyntaxKind::IS_KW);
p.eat(SyntaxKind::NOT_KW);
if !p.eat(SyntaxKind::NULL_KW) {
p.error_code(sc::EXPECTED_NULL_AFTER_IS, "expected NULL after IS");
}
lhs = m.complete(p, SyntaxKind::IS_NULL_EXPR);
continue;
}
if let Some(op) = infix_op(p) {
if op.left_bp < min_bp {
break;
}
let m = lhs.precede(p);
consume_infix_op(p, op.kind);
if expr_bp_depth(p, op.right_bp, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_BINOP_RHS,
"expected right-hand side of binary expression",
);
}
lhs = m.complete(p, op.node);
continue;
}
break;
}
Some(lhs)
}
fn atom(p: &mut Parser<'_>, depth: u32) -> Option<CompletedMarker> {
let kind = p.current();
Some(match kind {
SyntaxKind::INT_LITERAL | SyntaxKind::FLOAT_LITERAL | SyntaxKind::STRING_LITERAL => {
literal_atom(p, SyntaxKind::LITERAL_EXPR)
}
SyntaxKind::TRUE_KW | SyntaxKind::FALSE_KW => {
literal_keyword_atom(p, SyntaxKind::LITERAL_EXPR)
}
SyntaxKind::NULL_KW => literal_keyword_atom(p, SyntaxKind::LITERAL_EXPR),
SyntaxKind::PARAM => {
let m = p.start();
p.bump(SyntaxKind::PARAM);
m.complete(p, SyntaxKind::PARAM_EXPR)
}
SyntaxKind::IDENT | SyntaxKind::QUOTED_IDENT => {
let m = p.start();
p.bump_any();
m.complete(p, SyntaxKind::VAR_EXPR)
}
SyntaxKind::EXISTS_KW => {
if p.nth(1) == SyntaxKind::L_BRACE {
exists_block_deferred(p)
} else if p.nth(1) == SyntaxKind::L_PAREN && p.nth(2) == SyntaxKind::L_PAREN {
exists_pattern_predicate(p)
} else {
let m = p.start();
p.bump_any();
m.complete(p, SyntaxKind::VAR_EXPR)
}
}
SyntaxKind::COUNT_KW => {
let m = p.start();
p.bump_any();
m.complete(p, SyntaxKind::VAR_EXPR)
}
SyntaxKind::ANY_KW | SyntaxKind::ALL_KW | SyntaxKind::NONE_KW | SyntaxKind::SINGLE_KW
if p.nth(1) == SyntaxKind::L_PAREN =>
{
list_predicate(p, depth)
}
SyntaxKind::L_PAREN => {
if at_bare_pattern_predicate(p) {
bare_pattern_predicate(p)
} else {
paren_expr(p, depth)
}
}
SyntaxKind::L_BRACK => list_literal(p, depth),
SyntaxKind::L_BRACE => map_literal(p, depth),
SyntaxKind::CASE_KW => case_expr(p, depth),
_ => return None,
})
}
fn list_literal(p: &mut Parser<'_>, depth: u32) -> CompletedMarker {
debug_assert!(p.at(SyntaxKind::L_BRACK));
if matches!(p.nth(1), SyntaxKind::IDENT | SyntaxKind::QUOTED_IDENT)
&& p.nth(2) == SyntaxKind::IN_KW
{
return list_comprehension(p, depth);
}
let m = p.start();
p.bump(SyntaxKind::L_BRACK);
if !p.at(SyntaxKind::R_BRACK) {
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_LIST_ELEM,
"expected expression in list literal",
);
}
while p.at(SyntaxKind::COMMA) {
p.bump(SyntaxKind::COMMA);
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_LIST_ELEM,
"expected expression in list literal",
);
break;
}
}
}
if !p.eat(SyntaxKind::R_BRACK) {
p.error_code(
sc::EXPECTED_RBRACK_LIST,
"expected ']' to close list literal",
);
}
m.complete(p, SyntaxKind::LIST_LITERAL)
}
fn list_comprehension(p: &mut Parser<'_>, depth: u32) -> CompletedMarker {
debug_assert!(p.at(SyntaxKind::L_BRACK));
let m = p.start();
p.bump(SyntaxKind::L_BRACK);
{
let name = p.start();
if !(p.eat(SyntaxKind::IDENT) || p.eat(SyntaxKind::QUOTED_IDENT)) {
p.error_code(
sc::EXPECTED_IDENT,
"expected iteration variable in list comprehension",
);
}
name.complete(p, SyntaxKind::NAME);
}
if !p.eat(SyntaxKind::IN_KW) {
p.error_code(
sc::EXPECTED_IN_LIST_COMP,
"expected `IN` in list comprehension",
);
}
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_LIST_ELEM,
"expected expression for list-comprehension source",
);
}
if p.at(SyntaxKind::WHERE_KW) {
p.bump(SyntaxKind::WHERE_KW);
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_WHERE_EXPR,
"expected predicate expression after `WHERE` in list comprehension",
);
}
}
if p.at(SyntaxKind::PIPE) {
p.bump(SyntaxKind::PIPE);
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_BINOP_RHS,
"expected projection expression after `|` in list comprehension",
);
}
}
if !p.eat(SyntaxKind::R_BRACK) {
p.error_code(
sc::EXPECTED_PIPE_OR_RBRACK_LIST_COMP,
"expected `|` or `]` in list comprehension",
);
}
m.complete(p, SyntaxKind::LIST_COMPREHENSION)
}
fn map_literal(p: &mut Parser<'_>, depth: u32) -> CompletedMarker {
debug_assert!(p.at(SyntaxKind::L_BRACE));
let m = p.start();
p.bump(SyntaxKind::L_BRACE);
if !p.at(SyntaxKind::R_BRACE) {
map_entry(p, depth);
while p.at(SyntaxKind::COMMA) {
p.bump(SyntaxKind::COMMA);
if p.at(SyntaxKind::R_BRACE) {
break;
}
map_entry(p, depth);
}
}
if !p.eat(SyntaxKind::R_BRACE) {
p.error_code(sc::EXPECTED_RBRACE_MAP, "expected '}' to close map literal");
}
m.complete(p, SyntaxKind::MAP_LITERAL)
}
fn map_entry(p: &mut Parser<'_>, depth: u32) {
if !(p.eat(SyntaxKind::IDENT) || p.eat(SyntaxKind::QUOTED_IDENT)) {
p.error_code(sc::EXPECTED_MAP_KEY, "expected key in map literal");
}
if !p.eat(SyntaxKind::COLON) {
p.error_code(sc::EXPECTED_COLON_MAP, "expected ':' in map entry");
}
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(sc::EXPECTED_MAP_VALUE, "expected expression for map value");
}
}
fn literal_atom(p: &mut Parser<'_>, node: SyntaxKind) -> CompletedMarker {
let m = p.start();
p.bump_any();
m.complete(p, node)
}
fn literal_keyword_atom(p: &mut Parser<'_>, node: SyntaxKind) -> CompletedMarker {
let m = p.start();
p.bump_any();
m.complete(p, node)
}
fn list_predicate(p: &mut Parser<'_>, depth: u32) -> CompletedMarker {
debug_assert!(matches!(
p.current(),
SyntaxKind::ANY_KW | SyntaxKind::ALL_KW | SyntaxKind::NONE_KW | SyntaxKind::SINGLE_KW
));
let m = p.start();
p.bump_any();
if !p.eat(SyntaxKind::L_PAREN) {
p.error_code(
sc::EXPECTED_LPAREN_LIST_PREDICATE,
"expected `(` after ANY/ALL/NONE/SINGLE",
);
}
let name_marker = p.start();
if !(p.eat(SyntaxKind::IDENT) || p.eat(SyntaxKind::QUOTED_IDENT)) {
p.error_code(
sc::EXPECTED_IDENT,
"expected binder identifier in list predicate",
);
}
name_marker.complete(p, SyntaxKind::NAME);
if !p.eat(SyntaxKind::IN_KW) {
p.error_code(sc::EXPECTED_IN_LIST_PREDICATE, "expected `IN`");
}
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_BINOP_RHS,
"expected iterable expression in list predicate",
);
}
if p.eat(SyntaxKind::WHERE_KW) && expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_WHERE_EXPR,
"expected predicate expression after WHERE in list predicate",
);
}
if !p.eat(SyntaxKind::R_PAREN) {
p.error_code(
sc::EXPECTED_RPAREN_LIST_PREDICATE,
"expected `)` to close list predicate",
);
}
m.complete(p, SyntaxKind::LIST_PREDICATE_EXPR)
}
fn case_expr(p: &mut Parser<'_>, depth: u32) -> CompletedMarker {
debug_assert!(p.at(SyntaxKind::CASE_KW));
let m = p.start();
p.bump(SyntaxKind::CASE_KW);
if !matches!(
p.current(),
SyntaxKind::WHEN_KW | SyntaxKind::ELSE_KW | SyntaxKind::END_KW
) && expr_bp_depth(p, 0, depth + 1).is_none()
{
p.error_code(
sc::EXPECTED_BINOP_RHS,
"expected expression after `CASE` (simple-when scrutinee)",
);
}
while p.at(SyntaxKind::WHEN_KW) {
let arm = p.start();
p.bump(SyntaxKind::WHEN_KW);
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_BINOP_RHS,
"expected expression after `WHEN` in CASE arm",
);
}
if !p.eat(SyntaxKind::THEN_KW) {
p.error_code(sc::EXPECTED_THEN_CASE, "expected `THEN` in CASE arm");
}
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_BINOP_RHS,
"expected expression after `THEN` in CASE arm",
);
}
arm.complete(p, SyntaxKind::CASE_WHEN_ARM);
}
if p.at(SyntaxKind::ELSE_KW) {
let else_arm = p.start();
p.bump(SyntaxKind::ELSE_KW);
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_BINOP_RHS,
"expected expression after `ELSE` in CASE",
);
}
else_arm.complete(p, SyntaxKind::CASE_ELSE_ARM);
}
if !p.eat(SyntaxKind::END_KW) {
p.error_code(
sc::EXPECTED_END_CASE,
"expected `END` to close CASE expression",
);
}
m.complete(p, SyntaxKind::CASE_EXPR)
}
fn exists_pattern_predicate(p: &mut Parser<'_>) -> CompletedMarker {
debug_assert!(p.at(SyntaxKind::EXISTS_KW));
let m = p.start();
p.bump(SyntaxKind::EXISTS_KW);
p.bump(SyntaxKind::L_PAREN);
pattern::pattern(p);
if !p.eat(SyntaxKind::R_PAREN) {
p.error_code(
sc::EXPECTED_RPAREN_EXISTS,
"expected ')' to close EXISTS pattern predicate",
);
}
m.complete(p, SyntaxKind::PATTERN_PREDICATE)
}
fn exists_block_deferred(p: &mut Parser<'_>) -> CompletedMarker {
debug_assert!(p.at(SyntaxKind::EXISTS_KW));
debug_assert!(p.nth(1) == SyntaxKind::L_BRACE);
let m = p.start();
p.error_code(
sc::EXISTS_BLOCK_DEFERRED,
"EXISTS { ... } block subqueries are deferred per spec §19 / §20 D1",
);
p.bump(SyntaxKind::EXISTS_KW);
p.bump(SyntaxKind::L_BRACE);
let mut depth: u32 = 1;
while depth > 0 && !p.at(SyntaxKind::EOF) {
match p.current() {
SyntaxKind::L_BRACE => {
depth += 1;
p.bump(SyntaxKind::L_BRACE);
}
SyntaxKind::R_BRACE => {
depth -= 1;
p.bump(SyntaxKind::R_BRACE);
}
_ => p.bump_any(),
}
}
m.complete(p, SyntaxKind::ERROR)
}
fn at_bare_pattern_predicate(p: &mut Parser<'_>) -> bool {
debug_assert!(p.at(SyntaxKind::L_PAREN));
match p.nth(1) {
SyntaxKind::R_PAREN | SyntaxKind::COLON | SyntaxKind::L_BRACE => true,
SyntaxKind::IDENT | SyntaxKind::QUOTED_IDENT => matches!(
p.nth(2),
SyntaxKind::COLON
| SyntaxKind::L_BRACE
| SyntaxKind::R_PAREN
| SyntaxKind::COMMA
),
_ => false,
}
}
fn bare_pattern_predicate(p: &mut Parser<'_>) -> CompletedMarker {
debug_assert!(p.at(SyntaxKind::L_PAREN));
let m = p.start();
pattern::pattern(p);
m.complete(p, SyntaxKind::PATTERN_PREDICATE)
}
fn paren_expr(p: &mut Parser<'_>, depth: u32) -> CompletedMarker {
debug_assert!(p.at(SyntaxKind::L_PAREN));
let m = p.start();
p.bump(SyntaxKind::L_PAREN);
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_EXPR_IN_PARENS,
"expected expression inside parentheses",
);
}
if !p.eat(SyntaxKind::R_PAREN) {
p.error_code(sc::EXPECTED_RPAREN_EXPR, "expected ')' to close expression");
}
m.complete(p, SyntaxKind::PAREN_EXPR)
}
fn prefix_bp(kind: SyntaxKind) -> Option<u8> {
Some(match kind {
SyntaxKind::NOT_KW => 8,
SyntaxKind::MINUS | SyntaxKind::PLUS => 18,
_ => return None,
})
}
#[derive(Copy, Clone, Debug)]
struct InfixOp {
kind: InfixKind,
left_bp: u8,
right_bp: u8,
node: SyntaxKind,
}
#[derive(Copy, Clone, Debug)]
enum InfixKind {
Single(SyntaxKind),
StartsWith,
EndsWith,
}
fn infix_op(p: &mut Parser<'_>) -> Option<InfixOp> {
let c = p.current();
Some(match c {
SyntaxKind::OR_KW => InfixOp {
kind: InfixKind::Single(c),
left_bp: 2,
right_bp: 3,
node: SyntaxKind::BINARY_EXPR,
},
SyntaxKind::XOR_KW => InfixOp {
kind: InfixKind::Single(c),
left_bp: 4,
right_bp: 5,
node: SyntaxKind::BINARY_EXPR,
},
SyntaxKind::AND_KW => InfixOp {
kind: InfixKind::Single(c),
left_bp: 6,
right_bp: 7,
node: SyntaxKind::BINARY_EXPR,
},
SyntaxKind::EQ
| SyntaxKind::NEQ
| SyntaxKind::BANG_EQ
| SyntaxKind::LT
| SyntaxKind::LE
| SyntaxKind::GT
| SyntaxKind::GE => InfixOp {
kind: InfixKind::Single(c),
left_bp: 10,
right_bp: 11,
node: SyntaxKind::BINARY_EXPR,
},
SyntaxKind::REGEX_EQ => InfixOp {
kind: InfixKind::Single(c),
left_bp: 10,
right_bp: 11,
node: SyntaxKind::REGEX_MATCH_EXPR,
},
SyntaxKind::IN_KW => InfixOp {
kind: InfixKind::Single(c),
left_bp: 10,
right_bp: 11,
node: SyntaxKind::IN_EXPR,
},
SyntaxKind::STARTS_KW => InfixOp {
kind: InfixKind::StartsWith,
left_bp: 10,
right_bp: 11,
node: SyntaxKind::STRING_OP_EXPR,
},
SyntaxKind::ENDS_KW => InfixOp {
kind: InfixKind::EndsWith,
left_bp: 10,
right_bp: 11,
node: SyntaxKind::STRING_OP_EXPR,
},
SyntaxKind::CONTAINS_KW => InfixOp {
kind: InfixKind::Single(c),
left_bp: 10,
right_bp: 11,
node: SyntaxKind::STRING_OP_EXPR,
},
SyntaxKind::PLUS | SyntaxKind::MINUS => InfixOp {
kind: InfixKind::Single(c),
left_bp: 12,
right_bp: 13,
node: SyntaxKind::BINARY_EXPR,
},
SyntaxKind::STAR | SyntaxKind::SLASH | SyntaxKind::PERCENT => InfixOp {
kind: InfixKind::Single(c),
left_bp: 14,
right_bp: 15,
node: SyntaxKind::BINARY_EXPR,
},
SyntaxKind::CARET => InfixOp {
kind: InfixKind::Single(c),
left_bp: 16,
right_bp: 16,
node: SyntaxKind::BINARY_EXPR,
},
_ => return None,
})
}
fn consume_infix_op(p: &mut Parser<'_>, kind: InfixKind) {
match kind {
InfixKind::Single(tok) => p.bump(tok),
InfixKind::StartsWith => {
p.bump(SyntaxKind::STARTS_KW);
if !p.eat(SyntaxKind::WITH_KW) {
p.error_code(sc::EXPECTED_WITH_AFTER_STARTS, "expected WITH after STARTS");
}
}
InfixKind::EndsWith => {
p.bump(SyntaxKind::ENDS_KW);
if !p.eat(SyntaxKind::WITH_KW) {
p.error_code(sc::EXPECTED_WITH_AFTER_ENDS, "expected WITH after ENDS");
}
}
}
}
#[derive(Copy, Clone, Debug)]
struct PostfixOp {
bp: u8,
kind: PostfixKind,
}
#[derive(Copy, Clone, Debug)]
enum PostfixKind {
Dot,
Index,
Call,
MapProjection,
#[allow(dead_code)]
IsNull,
}
fn postfix_op(p: &mut Parser<'_>) -> Option<PostfixOp> {
let bp = 20; Some(match p.current() {
SyntaxKind::DOT => PostfixOp {
bp,
kind: PostfixKind::Dot,
},
SyntaxKind::L_BRACK => PostfixOp {
bp,
kind: PostfixKind::Index,
},
SyntaxKind::L_PAREN => PostfixOp {
bp,
kind: PostfixKind::Call,
},
SyntaxKind::L_BRACE => PostfixOp {
bp,
kind: PostfixKind::MapProjection,
},
_ => return None,
})
}
fn apply_postfix(
p: &mut Parser<'_>,
lhs: CompletedMarker,
op: PostfixOp,
depth: u32,
) -> CompletedMarker {
match op.kind {
PostfixKind::Dot => {
let m = lhs.precede(p);
p.bump(SyntaxKind::DOT);
if !(p.eat(SyntaxKind::IDENT) || p.eat(SyntaxKind::QUOTED_IDENT)) {
p.error_code(
sc::EXPECTED_PROP_KEY_AFTER_DOT,
"expected property key after '.'",
);
}
m.complete(p, SyntaxKind::PROP_ACCESS_EXPR)
}
PostfixKind::Index => index_or_slice_postfix(p, lhs, depth),
PostfixKind::Call => call_postfix(p, lhs, depth),
PostfixKind::MapProjection => map_projection_postfix(p, lhs, depth),
PostfixKind::IsNull => {
let m = lhs.precede(p);
m.complete(p, SyntaxKind::IS_NULL_EXPR)
}
}
}
fn call_postfix(p: &mut Parser<'_>, lhs: CompletedMarker, depth: u32) -> CompletedMarker {
let m = lhs.precede(p);
p.bump(SyntaxKind::L_PAREN);
p.eat(SyntaxKind::DISTINCT_KW);
if !p.at(SyntaxKind::R_PAREN) {
let args = p.start();
call_arg(p, depth);
while p.at(SyntaxKind::COMMA) {
p.bump(SyntaxKind::COMMA);
call_arg(p, depth);
}
args.complete(p, SyntaxKind::ARG_LIST);
}
if !p.eat(SyntaxKind::R_PAREN) {
p.error_code(
sc::EXPECTED_RPAREN_CALL,
"expected ')' to close function call",
);
}
m.complete(p, SyntaxKind::FUNCTION_CALL)
}
fn call_arg(p: &mut Parser<'_>, depth: u32) {
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(sc::EXPECTED_CALL_ARG, "expected function argument");
}
}
fn map_projection_postfix(p: &mut Parser<'_>, lhs: CompletedMarker, depth: u32) -> CompletedMarker {
debug_assert!(p.at(SyntaxKind::L_BRACE));
let m = lhs.precede(p);
p.bump(SyntaxKind::L_BRACE);
if !p.at(SyntaxKind::R_BRACE) {
map_projection_item(p, depth);
while p.at(SyntaxKind::COMMA) {
p.bump(SyntaxKind::COMMA);
if p.at(SyntaxKind::R_BRACE) {
break;
}
map_projection_item(p, depth);
}
}
if !p.eat(SyntaxKind::R_BRACE) {
p.error_code(
sc::EXPECTED_RBRACE_MAP_PROJECTION,
"expected '}' to close map projection",
);
}
m.complete(p, SyntaxKind::MAP_PROJECTION)
}
fn map_projection_item(p: &mut Parser<'_>, depth: u32) {
let m = p.start();
match p.current() {
SyntaxKind::DOT => {
p.bump(SyntaxKind::DOT);
if p.at(SyntaxKind::STAR) {
p.bump(SyntaxKind::STAR);
} else if !(p.eat(SyntaxKind::IDENT) || p.eat(SyntaxKind::QUOTED_IDENT)) {
p.error_code(
sc::EXPECTED_PROP_OR_STAR_AFTER_DOT_IN_PROJECTION,
"expected property name or '*' after '.' in map projection item",
);
}
}
SyntaxKind::STAR => {
p.bump(SyntaxKind::STAR);
}
SyntaxKind::IDENT | SyntaxKind::QUOTED_IDENT => {
p.bump_any();
if !p.eat(SyntaxKind::COLON) {
p.error_code(
sc::EXPECTED_COLON_MAP_PROJECTION,
"expected ':' in map projection literal item",
);
}
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(
sc::EXPECTED_VALUE_MAP_PROJECTION,
"expected expression for map projection value",
);
}
}
_ => {
p.error_code(
sc::EXPECTED_MAP_PROJECTION_ITEM,
"expected `.name`, `key: expr`, `.*`, or `*` in map projection",
);
if !p.at(SyntaxKind::R_BRACE) && !p.at(SyntaxKind::COMMA) {
p.bump_any();
}
}
}
m.complete(p, SyntaxKind::MAP_PROJECTION_ITEM);
}
fn index_or_slice_postfix(p: &mut Parser<'_>, lhs: CompletedMarker, depth: u32) -> CompletedMarker {
let m = lhs.precede(p);
p.bump(SyntaxKind::L_BRACK);
if p.at(SyntaxKind::DOT_DOT) {
p.bump(SyntaxKind::DOT_DOT);
if !p.at(SyntaxKind::R_BRACK) && expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(sc::EXPECTED_INDEX_EXPR, "expected slice end expression");
}
if !p.eat(SyntaxKind::R_BRACK) {
p.error_code(
sc::UNCLOSED_INDEX_BRACKET,
"expected ']' to close indexing bracket",
);
}
return m.complete(p, SyntaxKind::SLICE_EXPR);
}
if expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(sc::EXPECTED_INDEX_EXPR, "expected index expression");
}
if p.at(SyntaxKind::DOT_DOT) {
p.bump(SyntaxKind::DOT_DOT);
if !p.at(SyntaxKind::R_BRACK) && expr_bp_depth(p, 0, depth + 1).is_none() {
p.error_code(sc::EXPECTED_INDEX_EXPR, "expected slice end expression");
}
if !p.eat(SyntaxKind::R_BRACK) {
p.error_code(
sc::UNCLOSED_INDEX_BRACKET,
"expected ']' to close indexing bracket",
);
}
m.complete(p, SyntaxKind::SLICE_EXPR)
} else {
if !p.eat(SyntaxKind::R_BRACK) {
p.error_code(
sc::UNCLOSED_INDEX_BRACKET,
"expected ']' to close indexing bracket",
);
}
m.complete(p, SyntaxKind::INDEX_EXPR)
}
}
#[allow(dead_code)]
fn _recovery_anchor_placeholder(_: &mut Parser<'_>, _: TokenSet, _: Marker) {
}