zoisite 0.1.0

Zoisite is a programming language designed specifically for competitive programming.
Documentation
use drop_bomb::DropBomb;
use rowan::TextRange;

use crate::diagnostic::{Diagnostic, DiagnosticKind};
use crate::event;
use crate::event::Event;
use crate::grammar::root;
use crate::language::SyntaxNode;
use crate::syntax_kind::SyntaxKind;
use crate::token::Token;
use crate::token_set::TokenSet;

pub struct Parser<'a> {
    tokens: Vec<Token<'a>>,
    cursor: usize,
    events: Vec<Event>,
}

impl<'a> Parser<'a> {
    pub fn new(tokens: Vec<Token<'a>>) -> Self {
        Self {
            tokens,
            cursor: 0,
            events: Vec::new(),
        }
    }
    pub fn parse(mut self) -> (SyntaxNode, Vec<Diagnostic>) {
        root(&mut self);
        event::process(&mut self.events, self.tokens)
    }
    pub fn current(&self) -> SyntaxKind {
        self.nth(0)
    }
    pub fn nth(&self, n: usize) -> SyntaxKind {
        let mut temp_cursor = self.cursor;
        for _ in 0..n {
            if self.tokens[temp_cursor].kind.is_eof() {
                break;
            }
            temp_cursor += 1;
            while self.tokens[temp_cursor].kind.is_trivia() {
                temp_cursor += 1;
            }
        }
        self.tokens[temp_cursor].kind
    }
    fn current_range(&self) -> TextRange {
        self.tokens[self.cursor].range
    }
    pub fn at(&self, kind: SyntaxKind) -> bool {
        self.nth_at(0, kind)
    }
    pub fn at_set(&self, set: &TokenSet) -> bool {
        set.contains(self.current())
    }
    pub fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {
        self.nth(n) == kind
    }
    pub fn eat(&mut self, kind: SyntaxKind) -> bool {
        if self.at(kind) {
            self.bump();
            true
        } else {
            false
        }
    }
    pub fn assert(&mut self, kind: SyntaxKind) {
        assert!(self.at(kind));
        self.bump();
    }
    pub fn bump(&mut self) {
        if self.current() != SyntaxKind::Eof {
            self.consume_token();
            self.eat_trivia();
        }
    }
    pub fn expect(&mut self, kind: SyntaxKind) -> bool {
        if self.eat(kind) {
            true
        } else {
            self.error(&[kind]);
            false
        }
    }
    pub fn error(&mut self, expected: &[SyntaxKind]) {
        self.events.push(Event::Error(Diagnostic::new(DiagnosticKind::UnexpectedToken {
            expected: expected.to_vec(),
            actual: self.current(),
        }, self.current_range())));
    }
    pub fn error_and_recover(&mut self, expected: &[SyntaxKind], recovery: &TokenSet) {
        if self.at_set(recovery) {
            self.error(expected);
            return;
        }
        let m = self.start();
        self.error(expected);
        self.bump();
        m.complete(self, SyntaxKind::Error);
    }
    pub fn eat_trivia(&mut self) {
        while self.current().is_trivia() {
            self.consume_token();
        }
    }
    pub fn start(&mut self) -> Marker {
        let pos = self.events.len();
        self.events.push(Event::Placeholder);
        Marker {
            pos,
            bomb: DropBomb::new("Marker must be either completed or abandoned")
        }
    }
    fn consume_token(&mut self) {
        self.events.push(Event::Token(self.current()));
        self.cursor += 1;
    }
}

pub struct Marker {
    pos: usize,
    bomb: DropBomb,
}

impl Marker {
    pub fn complete(mut self, p: &mut Parser<'_>, kind: SyntaxKind) -> CompletedMarker {
        self.bomb.defuse();
        let event = &mut p.events[self.pos];
        *event = Event::StartNode(kind, None);
        p.events.push(Event::FinishNode);
        CompletedMarker {
            pos: self.pos
        }
    }
    pub fn abandon(mut self) {
        self.bomb.defuse();
    }
}

pub struct CompletedMarker {
    pos: usize,
}

impl CompletedMarker {
    pub fn precede(self, p: &mut Parser<'_>) -> Marker {
        let m = p.start();
        match p.events[self.pos] {
            Event::StartNode(_, ref mut forward_parent) => {
                *forward_parent = Some(m.pos);
            },
            _ => unreachable!(),
        }
        m
    }
}