mod inline_error;
#[cfg(feature = "annotate-snippets")]
mod render_annotate;
pub use inline_error::{
AnnotationKind, BuildInlineError, ClosureBuild, DiagnosticAnnotation, InlineError,
MatchDiagCtx, MissingSyntax, SnapshotFactory, UnwantedSyntax, ctx_factory,
};
pub(crate) mod error_handler;
pub enum ParserError {
FurthestFail(FurthestFailError),
Inline(InlineError),
}
impl ParserError {
#[inline]
pub fn span(&self) -> (usize, usize) {
match self {
ParserError::FurthestFail(error) => error.span,
ParserError::Inline(error) => error.reporting_span(),
}
}
#[cfg(feature = "annotate-snippets")]
pub fn eprint(&self, source_id: &str, source_text: &str) {
let mut out = String::new();
self.render_into(&mut out, source_id, source_text);
eprint!("{}", out);
}
#[cfg(feature = "annotate-snippets")]
pub fn write(&self, source_id: &str, source_text: &str, mut sink: impl std::io::Write) {
let mut out = String::new();
self.render_into(&mut out, source_id, source_text);
sink.write_all(out.as_bytes()).unwrap();
}
#[cfg(feature = "annotate-snippets")]
fn render_into(&self, out: &mut String, source_id: &str, source_text: &str) {
render_annotate::render_errors_slice_into(
std::slice::from_ref(self),
out,
source_id,
source_text,
);
}
#[cfg(feature = "annotate-snippets")]
pub fn eprint_many(errors: &[ParserError], source_id: &str, source_text: &str) {
if errors.is_empty() {
return;
}
let mut out = String::new();
render_annotate::render_errors_slice_into(errors, &mut out, source_id, source_text);
eprint!("{}", out);
}
#[cfg(feature = "annotate-snippets")]
pub fn write_many(
errors: &[ParserError],
source_id: &str,
source_text: &str,
mut sink: impl std::io::Write,
) {
if errors.is_empty() {
return;
}
let mut out = String::new();
render_annotate::render_errors_slice_into(errors, &mut out, source_id, source_text);
sink.write_all(out.as_bytes()).unwrap();
}
}
impl std::fmt::Display for ParserError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ParserError::FurthestFail(e) => e.fmt(f),
ParserError::Inline(e) => e.fmt(f),
}
}
}
pub struct FurthestFailError {
pub span: (usize, usize),
pub expected: Vec<String>,
pub annotations: Vec<DiagnosticAnnotation>,
pub notes: Vec<String>,
pub helps: Vec<String>,
}
impl FurthestFailError {
#[inline]
pub fn as_parser_error(self) -> ParserError {
ParserError::FurthestFail(self)
}
pub fn with_span(mut self, span: (usize, usize)) -> Self {
self.span = span;
self
}
pub fn set_span(&mut self, span: (usize, usize)) -> &mut Self {
self.span = span;
self
}
pub fn with_note(mut self, note: impl Into<String>) -> Self {
self.notes.push(note.into());
self
}
pub fn add_note(&mut self, note: impl Into<String>) -> &mut Self {
self.notes.push(note.into());
self
}
pub fn with_help(mut self, help: impl Into<String>) -> Self {
self.helps.push(help.into());
self
}
pub fn add_help(&mut self, help: impl Into<String>) -> &mut Self {
self.helps.push(help.into());
self
}
pub fn with_annotation(
mut self,
span: (usize, usize),
message: impl Into<String>,
kind: AnnotationKind,
) -> Self {
self.annotations.push(DiagnosticAnnotation {
span,
message: message.into(),
kind,
});
self
}
pub fn add_annotation(
&mut self,
span: (usize, usize),
message: impl Into<String>,
kind: AnnotationKind,
) -> &mut Self {
self.annotations.push(DiagnosticAnnotation {
span,
message: message.into(),
kind,
});
self
}
#[cfg(feature = "annotate-snippets")]
pub fn eprint(&self, source_id: &str, source_text: &str) {
ParserError::FurthestFail(self.clone()).eprint(source_id, source_text);
}
#[cfg(feature = "annotate-snippets")]
pub fn write(&self, source_id: &str, source_text: &str, sink: impl std::io::Write) {
ParserError::FurthestFail(self.clone()).write(source_id, source_text, sink);
}
#[cfg(feature = "annotate-snippets")]
pub(crate) fn main_message(&self, source_text: &str) -> String {
let found = source_text
.get(self.span.0..self.span.1)
.filter(|s| !s.is_empty())
.unwrap_or(if self.span.0 >= source_text.len() {
"end of input"
} else {
"unknown token"
});
match self.expected.len() {
0 => format!("unexpected '{}'", found),
1 => format!("expected {} but found '{}'", self.expected[0], found),
_ => format!(
"expected one of {} but found '{}'",
self.expected.join(", "),
found
),
}
}
}
impl Clone for FurthestFailError {
fn clone(&self) -> Self {
Self {
span: self.span,
expected: self.expected.clone(),
annotations: self.annotations.clone(),
notes: self.notes.clone(),
helps: self.helps.clone(),
}
}
}
impl From<InlineError> for ParserError {
fn from(value: InlineError) -> Self {
ParserError::Inline(value)
}
}
impl std::fmt::Display for FurthestFailError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let expected_msg = match self.expected.len() {
0 => "unexpected token".to_string(),
1 => format!("expected {}", self.expected[0]),
_ => {
let mut sorted = self.expected.clone();
sorted.sort();
format!("expected one of {}", sorted.join(", "))
}
};
write!(f, "{} at {}..{}", expected_msg, self.span.0, self.span.1)?;
for ann in &self.annotations {
write!(f, "\n {}..{}: {}", ann.span.0, ann.span.1, ann.message)?;
}
for note in &self.notes {
write!(f, "\nnote: {note}")?;
}
for help in &self.helps {
write!(f, "\nhelp: {help}")?;
}
Ok(())
}
}
impl std::fmt::Debug for FurthestFailError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FurthestFailError")
.field("span", &self.span)
.field("expected", &self.expected)
.field("annotations", &self.annotations)
.field("notes", &self.notes)
.field("helps", &self.helps)
.finish()
}
}
#[derive(Debug)]
pub enum MatcherRunError {
FurthestFail(FurthestFailError),
RetryRerunNeeded,
}
impl std::fmt::Display for MatcherRunError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MatcherRunError::FurthestFail(e) => write!(f, "{e}"),
MatcherRunError::RetryRerunNeeded => write!(f, "retry rerun needed"),
}
}
}
impl From<FurthestFailError> for MatcherRunError {
fn from(value: FurthestFailError) -> Self {
MatcherRunError::FurthestFail(value)
}
}
impl MatcherRunError {
#[inline]
pub fn into_furthest_fail_for_parser(self, span: (usize, usize)) -> FurthestFailError {
match self {
MatcherRunError::FurthestFail(f) => f,
MatcherRunError::RetryRerunNeeded => FurthestFailError {
span,
expected: Vec::new(),
annotations: Vec::new(),
notes: vec!["internal: unexpected nested retry marker".into()],
helps: Vec::new(),
},
}
}
}