xo65 0.1.0

Library for parsing cc65 object files (xo65 format)
Documentation
use crate::expr::Expr;
use crate::parse::{parse_uleb128_at, ParseAt, ParseErrorContext as _, ParseResult};
use crate::xo65::Xo65Bytes;

#[derive(Debug)]
pub struct AssertTable<'data> {
    bytes: Xo65Bytes<'data>,

    asserts: Box<[Assert]>,
}

impl<'data> AssertTable<'data> {
    pub fn as_bytes(&self) -> &'data [u8] {
        self.bytes.as_inner()
    }

    pub fn count(&self) -> usize {
        self.asserts.len()
    }

    pub fn get(&self, idx: usize) -> Option<&Assert> {
        self.asserts.get(idx)
    }

    pub fn iter(
        &self,
    ) -> impl DoubleEndedIterator<Item = &Assert> + ExactSizeIterator + std::iter::FusedIterator + Clone
    {
        self.asserts.iter()
    }

    pub(crate) fn parse(bytes: Xo65Bytes<'data>) -> ParseResult<Self> {
        let mut off = 0;

        let count = parse_uleb128_at(&bytes, &mut off)? as usize;
        let mut asserts = Vec::<Assert>::with_capacity(count);

        for i in 0..count {
            let assert = Assert::parse_at(&bytes, &mut off)
                .with_context(|| format!("assert {i} parse error"))?;
            asserts.push(assert);
        }

        Ok(Self {
            bytes,
            asserts: asserts.into(),
        })
    }
}

#[derive(Debug)]
pub struct Assert {
    expr: Expr,
    action: AssertAction,
    message: u32,
    lines: Box<[u32]>,
}

impl Assert {
    pub fn expr(&self) -> &Expr {
        &self.expr
    }

    pub fn action(&self) -> AssertAction {
        self.action
    }

    pub fn message(&self) -> u32 {
        self.message
    }

    pub fn lines(&self) -> &[u32] {
        &self.lines
    }
}

impl ParseAt<'_> for Assert {
    fn parse_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Self> {
        let expr = Expr::parse_at(bytes, off)?;
        let action = parse_uleb128_at(bytes, off).map(AssertAction::new)?;
        let message = parse_uleb128_at(bytes, off)?;
        let lines = parse_lines_at(bytes, off)?;

        Ok(Self {
            expr,
            action,
            message,
            lines,
        })
    }
}

fn parse_lines_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Box<[u32]>> {
    let count = parse_uleb128_at(bytes, off)? as usize;
    let mut lines = Vec::<u32>::with_capacity(count);

    for _ in 0..count {
        let line = parse_uleb128_at(bytes, off)?;
        lines.push(line);
    }

    Ok(lines.into())
}

#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AssertAction {
    /// warn at assemble-time.
    AsmWarn = 0x00,
    /// error at assemble-time.
    AsmError = 0x01,
    /// warn at link-time.
    LinkWarn = 0x02,
    /// error at link-time.
    LinkError = 0x03,
    Unknown(u32),
}

impl AssertAction {
    fn new(inner: u32) -> Self {
        match inner {
            0x00 => Self::AsmWarn,
            0x01 => Self::AsmError,
            0x02 => Self::LinkWarn,
            0x03 => Self::LinkError,
            _ => Self::Unknown(inner),
        }
    }
}