tame/tamer/src/obj/xmlo/error.rs

215 lines
7.3 KiB
Rust
Raw Normal View History

// XIR-based xmlo object errors
//
// Copyright (C) 2014-2022 Ryan Specialty Group, 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 <http://www.gnu.org/licenses/>.
//! Errors while processing `xmlo` object files.
use crate::diagnose::{Annotate, AnnotatedSpan, Diagnostic};
use crate::parse::Token;
use crate::span::Span;
use crate::sym::SymbolId;
use crate::xir::flat::Object as XirfToken;
use std::fmt::Display;
/// Error during `xmlo` processing.
///
/// Errors contain only owned values rather than references to original
/// data since they represent conditions requiring termination from
/// malformed compiler output,
/// and so should rarely occur.
/// This drastically simplifies the reader and [`Result`] chaining.
///
/// TODO: These errors provide no context (byte offset).
tamer: obj::xmlo::reader: Begin conversion to ParseState This begins to transition XmloReader into a ParseState. Unlike previous changes where ParseStates were composed into a single ParseState, this is instead a lowering operation that will take the output of one Parser and provide it to another. The mess in ld::poc (...which still needs to be refactored and removed) shows the concept, which will be abstracted away. This won't actually get to the ASG in order to test that that this works with the wip-xmlo-xir-reader flag on (development hasn't gotten that far yet), but since it type-checks, it should conceptually work. Wiring lowering operations together is something that I've been dreading for months, but my approach of only abstracting after-the-fact has helped to guide a sane approach for this. For some definition of "sane". It's also worth noting that AsgBuilder will too become a ParseState implemented as another lowering operation, so: XIR -> XIRF -> XMLO -> ASG These steps will all be streaming, with iteration happening only at the topmost level. For this reason, it's important that ASG not be responsible for doing that pull, and further we should propagate Parsed::Incomplete rather than filtering it out and looping an indeterminate number of times outside of the toplevel. One final note: the choice of 64 for the maximum depth is entirely arbitrary and should be more than generous; it'll be finalized at some point in the future once I actually evaluate what maximum depth is reasonable based on how the system is used, with some added growing room. DEV-10863
2022-03-22 13:56:43 -04:00
#[derive(Debug, PartialEq, Eq)]
pub enum XmloError {
/// The root node was not an `lv:package`.
UnexpectedRoot(XirfToken),
/// A `preproc:sym` node was found, but is missing `@name`.
UnassociatedSym(Span),
/// The provided `preproc:sym/@type` is unknown or invalid.
InvalidType(SymbolId, Span),
/// The provided `preproc:sym/@dtype` is unknown or invalid.
InvalidDtype(SymbolId, Span),
/// The provided `preproc:sym/@dim` is invalid.
InvalidDim(SymbolId, Span),
/// A `preproc:sym-dep` element was found, but is missing `@name`.
UnassociatedSymDep(Span),
/// The `preproc:sym[@type="map"]` is missing a @name.
MapFromNameMissing(SymbolId, Span),
/// Multiple `preproc:from` nodes found.
MapFromMultiple(SymbolId, Span),
/// Invalid dependency in adjacency list
/// (`preproc:sym-dep/preproc:sym-ref`).
MalformedSymRef(SymbolId, Span),
/// A `preproc:fragment` element was found, but is missing `@id`.
UnassociatedFragment(Span),
/// A `preproc:fragment` element was found, but is missing `text()`.
MissingFragmentText(SymbolId, Span),
/// A token of input was unexpected.
///
/// Ideally we would provide a better error depending on the context,
/// but this serves as a fallback if the input is completely
/// unexpected.
UnexpectedToken(XirfToken),
}
impl Display for XmloError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
use XmloError::*;
match self {
UnexpectedRoot(_tok) => {
write!(fmt, "expected `package` root element")
}
UnassociatedSym(_) => {
write!(fmt, "unassociated symbol table entry")
}
InvalidType(ty, _) => {
write!(fmt, "invalid symbol type `{ty}`")
}
InvalidDtype(dtype, _) => {
write!(fmt, "invalid symbol dtype `{dtype}`")
}
InvalidDim(dim, _) => {
write!(fmt, "invalid dimensionality `{dim}`")
}
MapFromNameMissing(sym, _) => {
write!(fmt, "map `from` name missing for symbol `{sym}`")
}
MapFromMultiple(sym, _) => {
write!(fmt, "multiple map `from` for `{sym}`")
}
UnassociatedSymDep(_) => {
write!(fmt, "unassociated dependency list")
}
MalformedSymRef(name, _) => {
write!(fmt, "malformed dependency ref for symbol {name}")
}
UnassociatedFragment(_) => write!(fmt, "unassociated fragment"),
MissingFragmentText(sym, _) => {
write!(fmt, "missing fragment text for symbol `{sym}`")
}
UnexpectedToken(tok) => write!(fmt, "unexpected {tok}"),
}
}
}
impl std::error::Error for XmloError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl Diagnostic for XmloError {
fn describe(&self) -> Vec<AnnotatedSpan> {
use XmloError::*;
let malformed = "this `xmlo` file is malformed or corrupt; \
try removing it to force it to be rebuilt, \
and please report this error";
// Note that these errors _could_ potentially be more descriptive
// and contain more spans,
// but they are internal compiler errors and so it's not yet
// deemed to be worth the effort.
match self {
UnexpectedRoot(tok) => {
// TODO: If we recommend `<package`,
// we ought to have a span that guarantees that `<` will
// be included.
tok.span()
.error("`<package` expected here")
.with_help(
"an `xmlo` file was expected, \
but this appears to be something else",
)
.into()
}
UnassociatedSym(span) => span
.internal_error("`@name` is missing")
.with_help(malformed)
.into(),
InvalidType(_ty, span) => span
.internal_error("the type `{ty}` is unknown")
.with_help(malformed)
.into(),
InvalidDtype(_dtype, span) => span
.internal_error("the dtype `{dtype}` is unknown")
.with_help(malformed)
.into(),
InvalidDim(_dim, span) => span
.internal_error(
"the number of dimensions must be `0`, `1`, or `2`",
)
.with_help(malformed)
.into(),
MapFromNameMissing(_sym, span) => span
.internal_error("`@name` is missing")
.with_help(malformed)
.into(),
MapFromMultiple(_sym, span) => span
.internal_error("extra `preproc:from` here")
.with_help(malformed)
.into(),
UnassociatedSymDep(span) => span
.internal_error("`@name` is missing")
.with_help(malformed)
.into(),
MalformedSymRef(_name, span) => span
.internal_error("`@name` is missing")
.with_help(malformed)
.into(),
UnassociatedFragment(span) => span
.internal_error("@id is missing")
.with_help(malformed)
.into(),
MissingFragmentText(_sym, span) => span
.internal_error("missing fragment text")
.with_help(malformed)
.into(),
UnexpectedToken(tok) => tok
.span()
.internal_error("unknown or unexpected token")
.with_help(malformed)
.into(),
}
}
}