use std::sync::Arc;
use std::{error::Error, ops::RangeInclusive};
use crate::{
err::{
Context, DiagnosticSink, LanguageArea, Level, Stage, TranslationError, TranslationErrors,
},
interner::Interner,
tree::ast::node::Spannable,
types::Type,
};
#[derive(Debug)]
pub struct ParseContext {
errors: Vec<Arc<TranslationError>>,
interner: Arc<Interner>,
}
impl ParseContext {
pub fn new() -> Self {
Self {
errors: Vec::new(),
interner: Arc::new(Interner::new()),
}
}
pub fn with_interner(interner: Arc<Interner>) -> Self {
Self {
errors: Vec::new(),
interner,
}
}
pub fn intern(&self, s: &str) -> Arc<str> {
self.interner.intern(s)
}
pub fn intern_type(&self, t: Type) -> Arc<Type> {
self.interner.intern_type(t)
}
pub fn error(&mut self, message: impl Into<String>) -> ErrorBuilder<'_, Self> {
ErrorBuilder::new(message.into(), self)
}
pub fn add_error(&mut self, error: Arc<TranslationError>) {
self.errors.push(error);
}
pub fn take_errors(self) -> TranslationErrors {
let unwrapped: Vec<TranslationError> =
self.errors.into_iter().map(|rc| (*rc).clone()).collect();
TranslationErrors(unwrapped)
}
}
impl Default for ParseContext {
fn default() -> Self {
Self::new()
}
}
impl DiagnosticSink for ParseContext {
fn add_error(&mut self, error: Arc<TranslationError>) {
self.add_error(error);
}
}
pub trait FromCst<T>: Sized {
fn from_cst_with_context(cst: T, ctx: &mut ParseContext) -> Self;
}
pub trait TryFromCst<T>: Sized {
fn try_from_cst_with_context(
cst: T,
ctx: &mut ParseContext,
) -> Result<Self, Arc<TranslationError>>;
}
pub struct ErrorBuilder<'ctx, C: DiagnosticSink> {
message: String,
span: Option<RangeInclusive<usize>>,
source_desc: Option<String>,
area: Option<LanguageArea>,
stage: Stage,
level: Level,
supporting: Vec<Context>,
ctx: &'ctx mut C,
}
impl<'ctx, C: DiagnosticSink> ErrorBuilder<'ctx, C> {
pub(crate) fn new(message: String, ctx: &'ctx mut C) -> Self {
Self {
message,
span: None,
source_desc: None,
area: None,
stage: Stage::Translation,
level: Level::Error,
supporting: Vec::new(),
ctx,
}
}
pub fn at(mut self, spannable: &impl Spannable) -> Self {
self.span = spannable.span();
self
}
pub fn with_source<E>(mut self, source: E) -> Self
where
E: Error + Send + Sync + 'static,
{
self.source_desc = Some(source.to_string());
self
}
pub fn with_note(mut self, note: impl Into<String>) -> Self {
self.source_desc = Some(note.into());
self
}
pub fn with_source_boxed(mut self, source: Box<dyn Error + Send + Sync + 'static>) -> Self {
self.source_desc = Some(source.to_string());
self
}
pub fn with_level(mut self, level: Level) -> Self {
self.level = level;
self
}
pub fn with_area(mut self, area: LanguageArea) -> Self {
self.area = Some(area);
self
}
pub fn with_stage(mut self, stage: Stage) -> Self {
self.stage = stage;
self
}
pub fn add_context(mut self, span: RangeInclusive<usize>, message: &str) -> Self {
self.supporting.push(Context::new(span, message));
self
}
pub fn emit(self) -> Arc<TranslationError> {
let error = TranslationError {
primary: Context::new(self.span.unwrap_or(0..=0), &self.message),
source_desc: self.source_desc,
area: self.area,
stage: self.stage,
level: self.level,
supporting: if self.supporting.is_empty() {
None
} else {
Some(self.supporting)
},
};
let rc_error = Arc::new(error);
self.ctx.add_error(rc_error.clone());
rc_error
}
}