// XIR element parser generator errors // // Copyright (C) 2014-2023 Ryan Specialty, LLC. // // This file is part of TAME. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! Parsing errors from the parser generator. use core::fmt::Debug; use std::{ error::Error, fmt::{Display, Formatter}, marker::PhantomData, }; use crate::{ diagnose::{Annotate, AnnotatedSpan, Diagnostic}, fmt::{DisplayFn, DisplayWrapper, TtQuote}, span::Span, xir::{ attr::Attr, flat::{RefinedText, XirfToken}, EleSpan, OpenSpan, QName, }, }; use super::{AttrParseState, Nt, SumNt}; #[derive(Debug, PartialEq)] pub enum NtError { /// An element was expected, /// but the name of the element was unexpected. UnexpectedEle(QName, Span), /// Unexpected input while expecting an end tag for this /// element. /// /// The span corresponds to the opening tag. CloseExpected(QName, OpenSpan, XirfToken), Attrs(AttrParseError, PhantomData), } impl From> for NtError where NT: Nt, A: AttrParseState, { fn from(e: AttrParseError) -> Self { Self::Attrs(e, PhantomData::default()) } } impl Error for NtError { fn source(&self) -> Option<&(dyn Error + 'static)> { // TODO None } } impl Display for NtError { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { use crate::xir::fmt::{TtCloseXmlEle, TtOpenXmlEle}; match self { Self::UnexpectedEle(name, _) => write!( f, "unexpected {unexpected} (expecting {expected})", unexpected = TtOpenXmlEle::wrap(name), expected = TtOpenXmlEle::wrap(NT::matcher()), ), Self::CloseExpected(qname, _, tok) => write!( f, "expected {}, but found {}", TtCloseXmlEle::wrap(qname), TtQuote::wrap(tok) ), Self::Attrs(e, _) => Display::fmt(e, f), } } } impl Diagnostic for NtError { fn describe(&self) -> Vec { use crate::{parse::Token, xir::fmt::TtCloseXmlEle}; match self { Self::UnexpectedEle(_, ospan) => ospan .error(format!( "expected {ele_name} here", ele_name = TtQuote::wrap(NT::matcher()) )) .into(), Self::CloseExpected(qname, ospan, tok) => vec![ ospan.span().note("element starts here"), tok.span() .error(format!("expected {}", TtCloseXmlEle::wrap(qname),)), ], Self::Attrs(e, _) => e.describe(), } } } /// Error during parsing of a sum nonterminal. #[derive(Debug, PartialEq)] pub enum SumNtError { UnexpectedEle(QName, Span, PhantomData), } impl Error for SumNtError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None } } impl Display for SumNtError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use crate::xir::fmt::TtOpenXmlEle; match self { Self::UnexpectedEle(qname, _, _) => { write!(f, "unexpected {}", TtOpenXmlEle::wrap(qname)) } } } } impl Diagnostic for SumNtError { fn describe(&self) -> Vec { // Note that we should place expected values in the help // footnote rather than the span label because it can // get rather long. // Maybe in the future the diagnostic renderer can be // smart about that based on the terminal width and // automatically move into the footer. match self { Self::UnexpectedEle(qname, span, _) => span .error(format!( "element {name} cannot appear here", name = TtQuote::wrap(qname), )) .with_help(format!( "expecting {}", DisplayFn(NT::fmt_matches_top) )) .into(), } } } pub type ElementQName = QName; /// Error while parsing element attributes. #[derive(Debug, PartialEq)] pub enum AttrParseError { /// An attribute was encountered that was not expected by this parser. /// /// Parsing may recover by simply ignoring this attribute. UnexpectedAttr(Attr, ElementQName), /// An error occurred while parsing an attribute value into the /// declared type. InvalidValue(S::ValueError, ElementQName), } impl Display for AttrParseError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Self::UnexpectedAttr(attr, ele_name) => { write!( f, "unexpected attribute `{attr}` for \ element `{ele_name}`" ) } Self::InvalidValue(ev, ele_name) => { Display::fmt(ev, f)?; write!(f, " for element {}", TtQuote::wrap(ele_name)) } } } } impl Error for AttrParseError { fn source(&self) -> Option<&(dyn Error + 'static)> { None } } impl Diagnostic for AttrParseError { fn describe(&self) -> Vec { match self { // TODO: help stating attributes that can appear instead Self::UnexpectedAttr(attr @ Attr(.., aspan), ele_name) => aspan .key_span() .error(format!("element `{ele_name}` cannot contain `{attr}`")) .into(), Self::InvalidValue(ev, _) => ev.describe(), } } }