use super::error::{Diagnostic, SemanticDiagnostic};
use super::grammar_names::GrammarNames;
use crate::parse::insn::LiteralTable;
use std::fmt;
#[derive(Debug)]
pub struct MietteParseDiagnostic {
expected_msg: String,
source: miette::NamedSource<String>,
offset: usize,
hints: Vec<String>,
context: Vec<String>,
}
impl MietteParseDiagnostic {
pub fn new(
diagnostic: &Diagnostic,
source: impl Into<String>,
name: impl Into<String>,
literals: Option<&LiteralTable<'_>>,
names: Option<&dyn GrammarNames>,
) -> Self {
let expected_msg = {
let items: Vec<String> = diagnostic
.expected
.iter()
.map(|e| e.display(literals, names))
.collect();
let list = super::error::format_expected_list(&items);
if list == "nothing" {
"expected nothing".to_string()
} else {
format!("expected {list}")
}
};
let source_str = source.into();
let name_str = name.into();
let offset = diagnostic.furthest as usize;
let source = miette::NamedSource::new(name_str, source_str);
let hints: Vec<String> = diagnostic
.hints
.iter()
.map(|&id| {
let s = names.and_then(|n| n.resolve_symbol(id)).unwrap_or("?");
s.to_string()
})
.collect();
let context = diagnostic
.context_chain
.iter()
.map(|&id| {
let label = names.and_then(|n| n.expected_label(id)).unwrap_or("?");
format!(" while parsing: {label}")
})
.collect();
Self {
expected_msg,
source,
offset,
hints,
context,
}
}
}
impl std::error::Error for MietteParseDiagnostic {}
impl fmt::Display for MietteParseDiagnostic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "parse error: {}", self.expected_msg)
}
}
impl miette::Diagnostic for MietteParseDiagnostic {
fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
Some(Box::new("sipha::parse"))
}
fn severity(&self) -> Option<miette::Severity> {
Some(miette::Severity::Error)
}
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
Some(&self.source)
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
let offset = self.offset;
let msg = self.expected_msg.clone();
let span = miette::SourceSpan::from((offset, 1));
let label = miette::LabeledSpan::new_with_span(Some(msg), span);
Some(Box::new(std::iter::once(label)))
}
fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
if self.hints.is_empty() && self.context.is_empty() {
None
} else {
let mut parts: Vec<String> = Vec::new();
parts.extend(self.context.iter().cloned());
parts.extend(self.hints.iter().map(|h| format!(" hint: {h}")));
let s = parts.join("\n");
Some(Box::new(HintDisplay(s)))
}
}
}
struct HintDisplay(String);
impl fmt::Display for HintDisplay {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug)]
pub struct MietteSemanticDiagnostic {
message: String,
source: miette::NamedSource<String>,
span: miette::SourceSpan,
severity: miette::Severity,
code: Option<String>,
}
impl MietteSemanticDiagnostic {
pub fn new(
diagnostic: &SemanticDiagnostic,
source: impl Into<String>,
name: impl Into<String>,
) -> Self {
let source_str = source.into();
let name_str = name.into();
let start = diagnostic.span.start as usize;
let len = diagnostic.span.len().max(1);
let span = miette::SourceSpan::from((start, len));
let severity = match diagnostic.severity {
super::error::Severity::Error => miette::Severity::Error,
super::error::Severity::Warning | super::error::Severity::Deprecation => {
miette::Severity::Warning
}
super::error::Severity::Note => miette::Severity::Advice,
};
Self {
message: diagnostic.message.clone(),
source: miette::NamedSource::new(name_str, source_str),
span,
severity,
code: diagnostic.code.clone(),
}
}
}
impl std::error::Error for MietteSemanticDiagnostic {}
impl fmt::Display for MietteSemanticDiagnostic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl miette::Diagnostic for MietteSemanticDiagnostic {
fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
self.code
.as_ref()
.map(|c| Box::new(c.as_str()) as Box<dyn fmt::Display>)
}
fn severity(&self) -> Option<miette::Severity> {
Some(self.severity)
}
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
Some(&self.source)
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
let msg = self.message.clone();
let span = self.span;
let label = miette::LabeledSpan::new_with_span(Some(msg), span);
Some(Box::new(std::iter::once(label)))
}
}