everscale-asm 0.0.9

Rust implementation of TVM Assembler
Documentation
mod opcodes;
mod util;

use everscale_types::prelude::*;

use self::opcodes::{cp0, Context};
use crate::ast;

pub fn assemble(ast: &[ast::Stmt], span: ast::Span) -> Result<Cell, AsmError> {
    let opcodes = cp0();

    let mut context = Context::new();
    for stmt in ast {
        context.add_stmt(opcodes, stmt)?;
    }

    context
        .into_builder(span)?
        .build()
        .map_err(|e| AsmError::StoreError { inner: e, span })
}

pub fn check(ast: &[ast::Stmt], span: ast::Span) -> Vec<AsmError> {
    let opcodes = cp0();

    let mut errors = Vec::new();
    let mut context = Context::new();
    context.set_allow_invalid();
    for stmt in ast {
        if let Err(e) = context.add_stmt(opcodes, stmt) {
            errors.push(e);
        }
    }

    if let Err(e) = context.into_builder(span).and_then(|b| {
        b.build()
            .map_err(|e| AsmError::StoreError { inner: e, span })
    }) {
        errors.push(e);
    }

    errors
}

impl ast::InstrArgValue<'_> {
    fn ty(&self) -> ArgType {
        match self {
            ast::InstrArgValue::Nat(_) => ArgType::Nat,
            ast::InstrArgValue::SReg(_) => ArgType::StackRegister,
            ast::InstrArgValue::CReg(_) => ArgType::ControlRegister,
            ast::InstrArgValue::Slice(_) => ArgType::Slice,
            ast::InstrArgValue::Block(_) => ArgType::Block,
            ast::InstrArgValue::Invalid => ArgType::Invalid,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub enum ArgType {
    Nat,
    StackRegister,
    ControlRegister,
    Slice,
    Block,
    Invalid,
}

impl ArgType {
    pub fn expected_exact(self) -> ExpectedArgType {
        ExpectedArgType::Exact(self)
    }

    pub fn expected_or(self, other: ArgType) -> ExpectedArgType {
        ExpectedArgType::OneOf(self, Box::new(other.expected_exact()))
    }
}

impl std::fmt::Display for ArgType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            Self::Nat => "number",
            Self::StackRegister => "stack register",
            Self::ControlRegister => "control register",
            Self::Slice => "cell slice",
            Self::Block => "instruction block",
            Self::Invalid => "invalid",
        })
    }
}

#[derive(Debug)]
pub enum ExpectedArgType {
    Exact(ArgType),
    OneOf(ArgType, Box<Self>),
}

impl ExpectedArgType {
    pub fn join(mut self, other: ExpectedArgType) -> Self {
        fn join_inner(this: &mut ExpectedArgType, other: ExpectedArgType) {
            let mut value = std::mem::replace(this, ExpectedArgType::Exact(ArgType::Invalid));
            match &mut value {
                ExpectedArgType::Exact(exact) => {
                    *this = ExpectedArgType::OneOf(*exact, Box::new(other))
                }
                ExpectedArgType::OneOf(_, rest) => {
                    join_inner(rest, other);
                    *this = value
                }
            }
        }

        join_inner(&mut self, other);
        self
    }
}

impl std::fmt::Display for ExpectedArgType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Exact(arg_type) => std::fmt::Display::fmt(arg_type, f),
            Self::OneOf(a, rest) => {
                std::fmt::Display::fmt(a, f)?;
                let mut rest = rest.as_ref();
                loop {
                    match rest {
                        Self::Exact(b) => return write!(f, " or {b}"),
                        Self::OneOf(b, next) => {
                            write!(f, ", {b}")?;
                            rest = next;
                        }
                    }
                }
            }
        }
    }
}

#[derive(thiserror::Error, Debug)]
pub enum AsmError {
    #[error("unknown opcode: {name}")]
    UnknownOpcode { name: Box<str>, span: ast::Span },
    #[error("unaligned continuation of {bits} bits")]
    UnalignedCont { bits: u16, span: ast::Span },
    #[error("expected {expected}, got {found}")]
    ArgTypeMismatch {
        span: ast::Span,
        found: ArgType,
        expected: ExpectedArgType,
    },
    #[error("invalid register")]
    InvalidRegister(ast::Span),
    #[error("too many args")]
    TooManyArgs(ast::Span),
    #[error("not enough args")]
    NotEnoughArgs(ast::Span),
    #[error("out of range")]
    OutOfRange(ast::Span),
    #[error("invalid usage: {details}")]
    WrongUsage {
        details: &'static str,
        span: ast::Span,
    },
    #[error("store error: {inner}")]
    StoreError {
        inner: everscale_types::error::Error,
        span: ast::Span,
    },
    #[error("multiple: {0:?}")]
    Multiple(Box<[AsmError]>),
}

impl AsmError {
    pub fn can_ignore(&self) -> bool {
        matches!(
            self,
            Self::ArgTypeMismatch {
                found: ArgType::Invalid,
                ..
            }
        )
    }

    pub fn span(&self) -> ast::Span {
        match self {
            Self::UnknownOpcode { span, .. }
            | Self::UnalignedCont { span, .. }
            | Self::ArgTypeMismatch { span, .. }
            | Self::InvalidRegister(span)
            | Self::TooManyArgs(span)
            | Self::NotEnoughArgs(span)
            | Self::OutOfRange(span)
            | Self::WrongUsage { span, .. }
            | Self::StoreError { span, .. } => *span,
            Self::Multiple(items) => match items.as_ref() {
                [] => ast::Span::splat(0),
                [first, rest @ ..] => {
                    let mut res = first.span();
                    for item in rest {
                        let item_span = item.span();
                        res.start = std::cmp::min(res.start, item_span.start);
                        res.end = std::cmp::max(res.end, item_span.end);
                    }
                    res
                }
            },
        }
    }
}

pub trait JoinResults<T> {
    fn join_results(self) -> Result<T, AsmError>;
}

impl<T1, T2> JoinResults<(T1, T2)> for (Result<T1, AsmError>, Result<T2, AsmError>) {
    fn join_results(self) -> Result<(T1, T2), AsmError> {
        match self {
            (Ok(a1), Ok(a2)) => Ok((a1, a2)),
            (Ok(_), Err(e)) | (Err(e), Ok(_)) => Err(e),
            (Err(e1), Err(e2)) => Err(AsmError::Multiple(Box::from([e1, e2]))),
        }
    }
}

impl<T1, T2, T3> JoinResults<(T1, T2, T3)>
    for (
        Result<T1, AsmError>,
        Result<T2, AsmError>,
        Result<T3, AsmError>,
    )
{
    fn join_results(self) -> Result<(T1, T2, T3), AsmError> {
        match self {
            (Ok(a1), Ok(a2), Ok(a3)) => Ok((a1, a2, a3)),
            (Ok(_), Ok(_), Err(e)) | (Ok(_), Err(e), Ok(_)) | (Err(e), Ok(_), Ok(_)) => Err(e),
            (Ok(_), Err(e1), Err(e2)) | (Err(e1), Err(e2), Ok(_)) | (Err(e1), Ok(_), Err(e2)) => {
                Err(AsmError::Multiple(Box::from([e1, e2])))
            }
            (Err(e1), Err(e2), Err(e3)) => Err(AsmError::Multiple(Box::from([e1, e2, e3]))),
        }
    }
}