use crate::ast::*;
use dmc_diagnostic::Code;
use dmc_diagnostic::metadata::{Origin, SourceMeta};
use dmc_lexer::Lexer;
use dmc_lexer::token::{Token, TokenKind};
use duck_diagnostic::{Diagnostic, DiagnosticEngine, Span};
use std::sync::Arc;
pub struct Parser<'eng, 'tokens> {
pub tokens: Vec<Token<'tokens>>,
pub meta: Arc<SourceMeta>,
pub pos: usize,
pub diag_engine: &'eng mut DiagnosticEngine<Code>,
}
impl<'eng, 'tokens> Parser<'eng, 'tokens> {
pub fn new(
tokens: Vec<Token<'tokens>>,
meta: Arc<SourceMeta>,
diag_engine: &'eng mut DiagnosticEngine<Code>,
) -> Self {
Self { tokens, meta, pos: 0, diag_engine }
}
pub fn parse(&mut self) -> Document {
let span = self.tokens.first().map(|t| t.span.clone()).unwrap_or_else(default_span);
let mut children = Vec::new();
while !self.is_eof() {
let before = self.pos;
if let Some(node) = self.parse_block() {
children.push(node);
}
if self.pos == before {
self.advance();
}
}
Document { children, span }
}
pub(crate) fn emit_diagnostic(&mut self, diagnostic: Diagnostic<Code>) {
self.diag_engine.emit(diagnostic);
}
pub(crate) fn diag(&mut self, code: Code, message: impl Into<String>) {
let (line, column) = self.tokens.get(self.pos).map(|t| (t.span.line, t.span.column)).unwrap_or((0, 0));
let span = Span::from_zero_based(self.meta.path.clone(), line, column, 1);
self.emit_diagnostic(duck_diagnostic::diag!(code, span, message.into()));
}
pub(crate) fn warn(&mut self, code: Code, message: impl Into<String>) {
self.diag(code, message);
}
pub(crate) fn current_span(&self) -> Span {
self.tokens.get(self.pos).map(|t| t.span.clone()).unwrap_or_else(default_span)
}
pub(crate) fn peek(&'_ self) -> Option<&'_ Token<'_>> {
self.tokens.get(self.pos)
}
pub(crate) fn peek_kind(&self) -> Option<&TokenKind> {
self.tokens.get(self.pos).map(|t| &t.kind)
}
pub(crate) fn peek_raw(&self) -> Option<&'tokens str> {
self.tokens.get(self.pos).map(|t| t.raw)
}
pub(crate) fn advance(&'_ mut self) -> Option<&'_ Token<'_>> {
let t = self.tokens.get(self.pos);
if t.is_some() {
self.pos += 1;
}
t
}
pub(crate) fn is_eof(&self) -> bool {
matches!(self.peek_kind(), Some(TokenKind::Eof) | None)
}
}
pub fn parse(source: &str) -> Document {
let meta = Arc::from(SourceMeta { path: Arc::from("<inline>"), origin: Origin::Inline("<inline>") });
let mut lex_engine = DiagnosticEngine::new();
let mut lexer = Lexer::new(source, meta.clone(), &mut lex_engine);
let _ = lexer.scan_tokens();
let tokens = std::mem::take(&mut lexer.tokens);
drop(lexer);
let mut parse_engine = DiagnosticEngine::new();
let mut p = Parser::new(tokens, meta, &mut parse_engine);
p.parse()
}
pub fn parse_inline_str(s: &str) -> Vec<crate::ast::Node> {
let meta = Arc::from(SourceMeta { path: Arc::from("<inline>"), origin: Origin::Inline("<inline>") });
let mut lex_engine = DiagnosticEngine::new();
let mut lexer = Lexer::new(s, meta.clone(), &mut lex_engine);
let _ = lexer.scan_tokens();
let tokens = std::mem::take(&mut lexer.tokens);
drop(lexer);
let mut parse_engine = DiagnosticEngine::new();
let mut p = Parser::new(tokens, meta, &mut parse_engine);
p.collect_inline_until_break()
}