tamer: obj::xmlo::reader: Move Xmlo{Result,Error,Event}

These will need an API change, but are otherwise shared.  This means that
only the XmloReader is gated.
main
Mike Gerwitz 2021-10-25 12:26:25 -04:00
parent f7d8aa1e4f
commit c76fe87acd
3 changed files with 200 additions and 180 deletions

View File

@ -17,16 +17,211 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use crate::tpwrap::quick_xml::{Error as XmlError, InnerXmlError};
use crate::{
ir::legacyir::{PackageAttrs, SymAttrs},
sym::SymbolId,
};
use std::{fmt::Display, io::BufRead};
#[cfg(feature = "wip-xmlo-xir-reader")]
use std::io::BufRead;
use crate::ir::xir::reader::XmlXirReader;
#[cfg(not(feature = "wip-xmlo-xir-reader"))]
mod quickxml;
#[cfg(not(feature = "wip-xmlo-xir-reader"))]
pub use quickxml::{XmloError, XmloEvent, XmloReader, XmloResult};
pub use quickxml::XmloReader;
/// A [`Result`] with a hard-coded [`XmloError`] error type.
///
/// This is the result of every [`XmloReader`] operation that could
/// potentially fail in error.
pub type XmloResult<T> = Result<T, XmloError>;
#[cfg(feature = "wip-xmlo-xir-reader")]
pub struct XmloReader<B: BufRead> {
todo: std::marker::PhantomData<B>,
_reader: XmlXirReader<B>,
}
#[cfg(feature = "wip-xmlo-xir-reader")]
impl<B: BufRead> XmloReader<B> {
pub fn new(reader: B) -> Self {
let reader = XmlXirReader::new(reader);
Self { _reader: reader }
}
pub fn read_event<'a>(&mut self) -> XmloResult<XmloEvent> {
todo!("XmloReader::read_event")
}
}
impl<B> Iterator for XmloReader<B>
where
B: BufRead,
{
type Item = XmloResult<XmloEvent>;
/// Invoke [`XmloReader::read_event`] and yield the result via an
/// [`Iterator`] API.
///
/// *Warning*: This will always return [`Some`] for now.
/// Future changes may alter this behavior.
/// To terminate the iterator,
/// it's recommended that you use [`Iterator::take_while`] to filter
/// on the desired predicate,
/// such as [`XmloEvent::Eoh`].
fn next(&mut self) -> Option<Self::Item> {
Some(self.read_event())
}
}
impl<B> From<B> for XmloReader<B>
where
B: BufRead,
{
fn from(buf: B) -> Self {
Self::new(buf)
}
}
/// `xmlo` reader events.
///
/// All data are parsed rather than being returned as [`u8`] slices,
/// which avoids having to deal with awkward borrows or data copying since
/// these data will likely be persisted in memory anyway.
///
/// To avoid extra data copying,
/// we should instead prefer not to put data into object files that won't
/// be useful and can't be easily skipped without parsing.
#[derive(Debug, PartialEq, Eq)]
pub enum XmloEvent {
/// Package declaration.
///
/// This contains data gathered from the root `lv:package` node.
Package(PackageAttrs),
/// Symbol declaration.
///
/// This represents an entry in the symbol table,
/// which includes a symbol along with its variable metadata as
/// [`SymAttrs`].
SymDecl(SymbolId, SymAttrs),
/// Dependencies of a given symbol.
///
/// Note that, for simplicity, an owned vector is returned rather than a
/// slice into an internal buffer.
SymDeps(SymbolId, Vec<SymbolId>),
/// Text (compiled code) fragment for a given symbol.
///
/// This contains the compiler output for a given symbol,
/// and is returned here as an owned value.
/// Given that fragments can be quite large,
/// a caller not interested in these data should choose to skip
/// fragments entirely rather than simply ignoring fragment events.
Fragment(SymbolId, SymbolId),
/// End-of-header.
///
/// The header of an `xmlo` file is defined as the symbol table;
/// dependency list; and fragments.
/// This event is emitted at the closing `preproc:fragment` node.
Eoh,
}
/// 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).
#[derive(Debug, PartialEq)]
pub enum XmloError {
/// XML parsing error.
XmlError(XmlError),
/// The root node was not an `lv:package`.
UnexpectedRoot,
/// A `preproc:sym` node was found, but is missing `@name`.
UnassociatedSym,
/// The provided `preproc:sym/@type` is unknown or invalid.
InvalidType(String),
/// The provided `preproc:sym/@dtype` is unknown or invalid.
InvalidDtype(String),
/// The provided `preproc:sym/@dim` is invalid.
InvalidDim(String),
/// A `preproc:sym-dep` element was found, but is missing `@name`.
UnassociatedSymDep,
/// The `preproc:sym[@type="map"]` contains unexpected or invalid data.
InvalidMapFrom(String),
/// Invalid dependency in adjacency list
/// (`preproc:sym-dep/preproc:sym-ref`).
MalformedSymRef(String),
/// A `preproc:fragment` element was found, but is missing `@id`.
UnassociatedFragment,
/// A `preproc:fragment` element was found, but is missing `text()`.
MissingFragmentText(SymbolId),
}
impl From<InnerXmlError> for XmloError {
fn from(e: InnerXmlError) -> Self {
XmloError::XmlError(e.into())
}
}
impl Display for XmloError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
XmloError::XmlError(e) => e.fmt(fmt),
XmloError::UnexpectedRoot => {
write!(fmt, "unexpected package root (is this a package?)")
}
XmloError::UnassociatedSym => write!(
fmt,
"unassociated symbol table entry: preproc:sym/@name missing"
),
XmloError::InvalidType(ty) => {
write!(fmt, "invalid preproc:sym/@type `{}`", ty)
}
XmloError::InvalidDtype(dtype) => {
write!(fmt, "invalid preproc:sym/@dtype `{}`", dtype)
}
XmloError::InvalidDim(dim) => {
write!(fmt, "invalid preproc:sym/@dim `{}`", dim)
}
XmloError::InvalidMapFrom(msg) => {
write!(fmt, "invalid preproc:sym[@type=\"map\"]: {}", msg)
}
XmloError::UnassociatedSymDep => write!(
fmt,
"unassociated dependency list: preproc:sym-dep/@name missing"
),
XmloError::MalformedSymRef(msg) => {
write!(fmt, "malformed dependency ref: {}", msg)
}
XmloError::UnassociatedFragment => write!(
fmt,
"unassociated fragment: preproc:fragment/@id missing"
),
XmloError::MissingFragmentText(symname) => write!(
fmt,
"fragment found, but missing text for symbol `{}`",
symname,
),
}
}
}
impl std::error::Error for XmloError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::XmlError(e) => Some(e),
_ => None,
}
}
}

View File

@ -136,6 +136,7 @@
//! # }
//! ```
use super::{XmloError, XmloEvent, XmloResult};
use crate::ir::legacyir::{PackageAttrs, SymAttrs, SymType};
use crate::sym::{GlobalSymbolInternUnchecked, GlobalSymbolResolve, SymbolId};
#[cfg(test)]
@ -144,7 +145,6 @@ use crate::test::quick_xml::MockBytesStart as BytesStart;
use crate::test::quick_xml::MockXmlEvent as XmlEvent;
#[cfg(test)]
use crate::test::quick_xml::MockXmlReader as XmlReader;
use crate::tpwrap::quick_xml::{Error as XmlError, InnerXmlError};
#[cfg(not(test))]
use quick_xml::events::BytesStart;
#[cfg(not(test))]
@ -152,17 +152,10 @@ use quick_xml::events::Event as XmlEvent;
#[cfg(not(test))]
use quick_xml::Reader as XmlReader;
use std::convert::TryInto;
use std::fmt::Display;
use std::io::BufRead;
use std::iter::Iterator;
use std::result::Result;
/// A [`Result`] with a hard-coded [`XmloError`] error type.
///
/// This is the result of every [`XmloReader`] operation that could
/// potentially fail in error.
pub type XmloResult<T> = Result<T, XmloError>;
/// Wrapper around [`quick_xml::Reader`] for reading and parsing `xmlo`
/// object files.
///
@ -690,174 +683,5 @@ where
}
}
impl<B> Iterator for XmloReader<B>
where
B: BufRead,
{
type Item = XmloResult<XmloEvent>;
/// Invoke [`XmloReader::read_event`] and yield the result via an
/// [`Iterator`] API.
///
/// *Warning*: This will always return [`Some`] for now.
/// Future changes may alter this behavior.
/// To terminate the iterator,
/// it's recommended that you use [`Iterator::take_while`] to filter
/// on the desired predicate,
/// such as [`XmloEvent::Eoh`].
fn next(&mut self) -> Option<Self::Item> {
Some(self.read_event())
}
}
impl<B> From<B> for XmloReader<B>
where
B: BufRead,
{
fn from(buf: B) -> Self {
Self::new(buf)
}
}
/// `xmlo` reader events.
///
/// All data are parsed rather than being returned as [`u8`] slices,
/// which avoids having to deal with awkward borrows or data copying since
/// these data will likely be persisted in memory anyway.
///
/// To avoid extra data copying,
/// we should instead prefer not to put data into object files that won't
/// be useful and can't be easily skipped without parsing.
#[derive(Debug, PartialEq, Eq)]
pub enum XmloEvent {
/// Package declaration.
///
/// This contains data gathered from the root `lv:package` node.
Package(PackageAttrs),
/// Symbol declaration.
///
/// This represents an entry in the symbol table,
/// which includes a symbol along with its variable metadata as
/// [`SymAttrs`].
SymDecl(SymbolId, SymAttrs),
/// Dependencies of a given symbol.
///
/// Note that, for simplicity, an owned vector is returned rather than a
/// slice into an internal buffer.
SymDeps(SymbolId, Vec<SymbolId>),
/// Text (compiled code) fragment for a given symbol.
///
/// This contains the compiler output for a given symbol,
/// and is returned here as an owned value.
/// Given that fragments can be quite large,
/// a caller not interested in these data should choose to skip
/// fragments entirely rather than simply ignoring fragment events.
Fragment(SymbolId, SymbolId),
/// End-of-header.
///
/// The header of an `xmlo` file is defined as the symbol table;
/// dependency list; and fragments.
/// This event is emitted at the closing `preproc:fragment` node.
Eoh,
}
/// 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).
#[derive(Debug, PartialEq)]
pub enum XmloError {
/// XML parsing error.
XmlError(XmlError),
/// The root node was not an `lv:package`.
UnexpectedRoot,
/// A `preproc:sym` node was found, but is missing `@name`.
UnassociatedSym,
/// The provided `preproc:sym/@type` is unknown or invalid.
InvalidType(String),
/// The provided `preproc:sym/@dtype` is unknown or invalid.
InvalidDtype(String),
/// The provided `preproc:sym/@dim` is invalid.
InvalidDim(String),
/// A `preproc:sym-dep` element was found, but is missing `@name`.
UnassociatedSymDep,
/// The `preproc:sym[@type="map"]` contains unexpected or invalid data.
InvalidMapFrom(String),
/// Invalid dependency in adjacency list
/// (`preproc:sym-dep/preproc:sym-ref`).
MalformedSymRef(String),
/// A `preproc:fragment` element was found, but is missing `@id`.
UnassociatedFragment,
/// A `preproc:fragment` element was found, but is missing `text()`.
MissingFragmentText(SymbolId),
}
impl From<InnerXmlError> for XmloError {
fn from(e: InnerXmlError) -> Self {
XmloError::XmlError(e.into())
}
}
impl Display for XmloError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
XmloError::XmlError(e) => e.fmt(fmt),
XmloError::UnexpectedRoot => {
write!(fmt, "unexpected package root (is this a package?)")
}
XmloError::UnassociatedSym => write!(
fmt,
"unassociated symbol table entry: preproc:sym/@name missing"
),
XmloError::InvalidType(ty) => {
write!(fmt, "invalid preproc:sym/@type `{}`", ty)
}
XmloError::InvalidDtype(dtype) => {
write!(fmt, "invalid preproc:sym/@dtype `{}`", dtype)
}
XmloError::InvalidDim(dim) => {
write!(fmt, "invalid preproc:sym/@dim `{}`", dim)
}
XmloError::InvalidMapFrom(msg) => {
write!(fmt, "invalid preproc:sym[@type=\"map\"]: {}", msg)
}
XmloError::UnassociatedSymDep => write!(
fmt,
"unassociated dependency list: preproc:sym-dep/@name missing"
),
XmloError::MalformedSymRef(msg) => {
write!(fmt, "malformed dependency ref: {}", msg)
}
XmloError::UnassociatedFragment => write!(
fmt,
"unassociated fragment: preproc:fragment/@id missing"
),
XmloError::MissingFragmentText(symname) => write!(
fmt,
"fragment found, but missing text for symbol `{}`",
symname,
),
}
}
}
impl std::error::Error for XmloError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::XmlError(e) => Some(e),
_ => None,
}
}
}
#[cfg(test)]
mod test;

View File

@ -17,6 +17,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use super::super::{InnerXmlError, XmlError};
use super::*;
use crate::ir::legacyir::{SymDtype, SymType};
use crate::sym::GlobalSymbolIntern;