Consumes kind or emits diagnostic; for missing closers anchors at opener and drops cascade. */
(&mut self, kind: TokenType)
| 392 | |
| 393 | /* Consumes kind or emits diagnostic; for missing closers anchors at opener and drops cascade. */ |
| 394 | pub(super) fn eat(&mut self, kind: TokenType) { |
| 395 | if matches!(self.peek(), Some(k) if k == kind) { |
| 396 | self.advance(); |
| 397 | return; |
| 398 | } |
| 399 | if matches!(kind, TokenType::Rpar | TokenType::Rsqb | TokenType::Rbrace) { |
| 400 | let peeked = self.tokens.peek().map(|t| t.kind); |
| 401 | // Wrong closer: bail silently; advance() will report the mismatch. |
| 402 | if matches!(peeked, Some(TokenType::Rpar | TokenType::Rsqb | TokenType::Rbrace)) && peeked != Some(kind) |
| 403 | { |
| 404 | return; |
| 405 | } |
| 406 | // Missing closer: anchor diagnostic at the opener, not at the next-line token. |
| 407 | let want_open = match kind { |
| 408 | TokenType::Rpar => TokenType::Lpar, |
| 409 | TokenType::Rsqb => TokenType::Lsqb, |
| 410 | _ => TokenType::Lbrace, |
| 411 | }; |
| 412 | if let Some(&(opener_kind, start, end, errors_at_open)) = self.bracket_stack.last() |
| 413 | && opener_kind == want_open |
| 414 | { |
| 415 | self.errors.truncate(errors_at_open); |
| 416 | self.errors.push(Diagnostic { |
| 417 | start, end, |
| 418 | msg: s!(str open_str(opener_kind), " was never closed"), |
| 419 | }); |
| 420 | self.bracket_stack.pop(); |
| 421 | return; |
| 422 | } |
| 423 | } |
| 424 | let label: alloc::string::String = match self.tokens.peek() { |
| 425 | Some(t) if t.kind == TokenType::Endmarker => "EOF".to_string(), |
| 426 | Some(t) if t.kind == TokenType::Newline || t.kind == TokenType::Nl => "newline".to_string(), |
| 427 | Some(t) if t.kind == TokenType::Indent => "indent".to_string(), |
| 428 | Some(t) if t.kind == TokenType::Dedent => "dedent".to_string(), |
| 429 | Some(t) if t.start == t.end => t.kind.as_str().to_string(), |
| 430 | Some(t) => { |
| 431 | let mut s = alloc::string::String::with_capacity(t.end - t.start + 2); |
| 432 | s.push('\''); s.push_str(&self.source[t.start..t.end]); s.push('\''); s |
| 433 | } |
| 434 | None => "EOF".to_string(), |
| 435 | }; |
| 436 | let msg = s!("expected ", str kind.as_str(), ", got ", str &label); |
| 437 | // Token on later line: anchor at end-of-prev-line (common case: missing `:` in header). |
| 438 | if let Some(t) = self.tokens.peek() |
| 439 | && t.line > self.last_line |
| 440 | && self.last_end > 0 |
| 441 | { |
| 442 | let p = self.last_end; |
| 443 | self.error_at(p, p, &msg); |
| 444 | } else { |
| 445 | self.error(&msg); |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | pub(super) fn eat_if(&mut self, kind: TokenType) -> bool { |
| 450 | if matches!(self.peek(), Some(k) if k == kind) { |
no test coverage detected