use crate::diagnostics::error::ErrorContext;
#[cfg(feature = "miette")]
use crate::diagnostics::grammar_names::GrammarNames;
use crate::types::RuleId;
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
#[cfg(feature = "std")]
use std::string::String;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BadGraphKind {
RuleEntryOutOfRange { rule: RuleId },
SyncRuleEntryOutOfRange { rule: RuleId },
TokenStackUnderflow,
RecoveryResumeWithoutRecoverFrame,
ReturnWithRecoverFrameOnStack,
UnexpectedFrameDuringReturn,
ReservedFrame,
BacktrackStackUnderflow,
PartialCommitWithoutBacktrack,
PopFlagsStackUnderflow,
SnapshotMarkOutOfRange { mark: u32 },
PosOutOfRange { len: usize },
LiteralSmallLenOutOfRange { len: u8, bytes_len: u8 },
InvalidTreeEvents,
}
impl core::fmt::Display for BadGraphKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::RuleEntryOutOfRange { rule } => {
write!(f, "rule entry missing for rule id {rule}")
}
Self::SyncRuleEntryOutOfRange { rule } => {
write!(f, "sync rule entry missing for rule id {rule}")
}
Self::TokenStackUnderflow => {
write!(f, "`TokenEnd` without matching `TokenBegin`")
}
Self::RecoveryResumeWithoutRecoverFrame => {
write!(f, "`RecoveryResume` without active recover frame")
}
Self::ReturnWithRecoverFrameOnStack => {
write!(f, "`Return` encountered recovery frame on stack")
}
Self::UnexpectedFrameDuringReturn => {
write!(f, "unexpected frame while unwinding `Return`")
}
Self::ReservedFrame => write!(f, "reserved frame in stack"),
Self::BacktrackStackUnderflow => {
write!(f, "expected `Backtrack` frame but stack underflowed")
}
Self::PartialCommitWithoutBacktrack => {
write!(f, "`PartialCommit` with no `Backtrack` frame")
}
Self::PopFlagsStackUnderflow => {
write!(f, "`PopFlags` without matching `PushFlags` frame")
}
Self::SnapshotMarkOutOfRange { mark } => {
write!(f, "snapshot mark out of range: {mark}")
}
Self::PosOutOfRange { len } => {
write!(f, "byte length out of range for Pos: {len}")
}
Self::LiteralSmallLenOutOfRange { len, bytes_len } => {
write!(
f,
"`LiteralSmall` len out of range (len={len}, bytes_len={bytes_len})"
)
}
Self::InvalidTreeEvents => {
write!(f, "invalid tree events in parse output")
}
}
}
}
#[derive(Clone, Debug)]
pub enum ParseError {
NoMatch(crate::diagnostics::error::Diagnostic),
BadGraph(BadGraphKind),
UnknownRuleName(String),
}
impl core::fmt::Display for ParseError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::NoMatch(d) => write!(f, "{d}"),
Self::BadGraph(k) => write!(f, "malformed parse graph: {k}"),
Self::UnknownRuleName(name) => write!(f, "unknown rule name: {name}"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseError {}
#[derive(Debug)]
pub struct RecoverMultiResult {
pub partial: crate::parse::engine::ParseOutput,
pub errors: Vec<ParseError>,
}
#[cfg(feature = "miette")]
impl ParseError {
pub fn to_miette_report(
&self,
source: impl Into<String>,
name: impl Into<String>,
literals: Option<&crate::parse::insn::LiteralTable<'_>>,
names: Option<&dyn GrammarNames>,
) -> Option<miette::Report> {
match self {
Self::NoMatch(diag) => {
let m = diag.into_miette(source, name, literals, names);
Some(miette::Report::new(m))
}
Self::BadGraph(kind) => {
let name_str: String = name.into();
let src_str: String = source.into();
Some(
miette::Report::msg(format!("malformed parse graph: {kind}"))
.with_source_code(miette::NamedSource::new(name_str, src_str)),
)
}
Self::UnknownRuleName(rule_name) => {
let name_str: String = name.into();
let src_str: String = source.into();
Some(
miette::Report::msg(format!("unknown rule name: {rule_name}"))
.with_source_code(miette::NamedSource::new(name_str, src_str)),
)
}
}
}
}
#[inline]
pub(super) fn no_match(error_ctx: &ErrorContext) -> ParseError {
ParseError::NoMatch(error_ctx.to_diagnostic())
}
#[cfg(test)]
mod tests {
use super::BadGraphKind;
extern crate alloc;
use alloc::string::ToString;
#[test]
fn bad_graph_kind_display_is_actionable() {
let s = BadGraphKind::RuleEntryOutOfRange { rule: 3 }.to_string();
assert!(s.contains('3'), "{s}");
let t = BadGraphKind::TokenStackUnderflow.to_string();
assert!(!t.is_empty(), "{t}");
}
}