use crate::parse::{parse_bail, parse_uleb128_at, ParseAt, ParseResult};
use crate::xo65::Xo65Bytes;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Expr {
Null,
Literal { value: i64 },
Symbol { import_idx: u32 },
Section { section_idx: u32 },
Unary(Box<ExprUnary>),
Binary(Box<ExprBinary>),
}
impl Expr {
pub(crate) fn literal(value: i64) -> Self {
Self::Literal { value }
}
pub(crate) fn symbol(import_idx: u32) -> Self {
Self::Symbol { import_idx }
}
pub(crate) fn section(section_idx: u32) -> Self {
Self::Section { section_idx }
}
pub(crate) fn unary(op: OpUnary, expr: Expr) -> Self {
Self::Unary(Box::new(ExprUnary { op, expr }))
}
pub(crate) fn binary(op: OpBinary, lhs: Expr, rhs: Expr) -> Self {
Self::Binary(Box::new(ExprBinary { op, lhs, rhs }))
}
}
impl std::fmt::Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Null => write!(f, "<NULL>"),
Self::Literal { value } => write!(f, "{value:#X}"),
Self::Symbol { import_idx } => write!(f, "(Symbol {import_idx})"),
Self::Section { section_idx } => write!(f, "(Section {section_idx})"),
Self::Unary(unary) => unary.fmt(f),
Self::Binary(binary) => binary.fmt(f),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExprUnary {
pub op: OpUnary,
pub expr: Expr,
}
impl std::fmt::Display for ExprUnary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({} {})", self.op, self.expr)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OpUnary {
Neg,
BitNot,
U16SwapBytes,
Not,
Bank,
Byte0,
Byte1,
Byte2,
Byte3,
Word0,
Word1,
FarAddr,
Dword,
NearAddr,
}
impl OpUnary {
pub fn apply(self, x: i64) -> i64 {
match self {
Self::Neg => x.wrapping_neg(),
Self::BitNot => !x,
Self::U16SwapBytes => (x as u16).swap_bytes().into(),
Self::Not => (x == 0).into(),
Self::Bank => unimplemented!("EXPR_BANK is not supported"),
Self::Byte0 => x & 0xFF,
Self::Byte1 => (x & 0xFF00) >> 8,
Self::Byte2 => (x & 0xFF0000) >> 16,
Self::Byte3 => (x & 0xFF000000) >> 24,
Self::Word0 => x & 0xFFFF,
Self::Word1 => (x & 0xFFFF0000) >> 16,
Self::FarAddr => x & 0xFFFFFF,
Self::Dword => x & 0xFFFFFFFF,
Self::NearAddr => x & 0xFFFF,
}
}
}
impl std::fmt::Display for OpUnary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExprBinary {
pub op: OpBinary,
pub lhs: Expr,
pub rhs: Expr,
}
impl std::fmt::Display for ExprBinary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({} {} {})", self.op, self.lhs, self.rhs)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OpBinary {
Add,
Sub,
Mul,
Div,
Rem,
BitOr,
BitXor,
BitAnd,
Shl,
Shr,
Eq,
Ne,
Lt,
Gt,
Le,
Ge,
And,
Or,
Xor,
Max,
Min,
}
impl OpBinary {
pub fn apply(self, lhs: i64, rhs: i64) -> i64 {
match self {
Self::Add => lhs.wrapping_add(rhs),
Self::Sub => lhs.wrapping_sub(rhs),
Self::Mul => lhs.wrapping_mul(rhs),
Self::Div => lhs.wrapping_div(rhs),
Self::Rem => lhs.wrapping_rem(rhs),
Self::BitOr => lhs | rhs,
Self::BitXor => lhs ^ rhs,
Self::BitAnd => lhs & rhs,
Self::Shl => lhs.wrapping_shl(rhs as u32),
Self::Shr => lhs.wrapping_shr(rhs as u32),
Self::Eq => (lhs == rhs).into(),
Self::Ne => (lhs != rhs).into(),
Self::Lt => (lhs < rhs).into(),
Self::Gt => (lhs > rhs).into(),
Self::Le => (lhs <= rhs).into(),
Self::Ge => (lhs >= rhs).into(),
Self::And => ((lhs != 0) && (rhs != 0)).into(),
Self::Or => ((lhs != 0) || (rhs != 0)).into(),
Self::Xor => ((lhs != 0) ^ (rhs != 0)).into(),
Self::Max => lhs.max(rhs),
Self::Min => lhs.min(rhs),
}
}
}
impl std::fmt::Display for OpBinary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
impl ParseAt<'_> for Expr {
fn parse_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Self> {
let ty = u8::parse_at(bytes, off)?;
macro_rules! parse_literal {
() => {{
let value = i32::parse_at(bytes, off)?;
Self::literal(i64::from(value))
}};
}
macro_rules! parse_symbol {
() => {{
let import_idx = parse_uleb128_at(bytes, off)?;
Self::symbol(import_idx)
}};
}
macro_rules! parse_section {
() => {{
let section_idx = parse_uleb128_at(bytes, off)?;
Self::section(section_idx)
}};
}
macro_rules! parse_unary {
($op:expr) => {{
let expr = Self::parse_at(bytes, off)?;
let _rhs = Self::parse_at(bytes, off)?;
Self::unary($op, expr)
}};
}
macro_rules! parse_binary {
($op:expr) => {{
let lhs = Self::parse_at(bytes, off)?;
let rhs = Self::parse_at(bytes, off)?;
Self::binary($op, lhs, rhs)
}};
}
let expr = match ty {
0x00 => Self::Null,
0x01 => parse_binary!(OpBinary::Add),
0x02 => parse_binary!(OpBinary::Sub),
0x03 => parse_binary!(OpBinary::Mul),
0x04 => parse_binary!(OpBinary::Div),
0x05 => parse_binary!(OpBinary::Rem),
0x06 => parse_binary!(OpBinary::BitOr),
0x07 => parse_binary!(OpBinary::BitXor),
0x08 => parse_binary!(OpBinary::BitAnd),
0x09 => parse_binary!(OpBinary::Shl),
0x0A => parse_binary!(OpBinary::Shr),
0x0B => parse_binary!(OpBinary::Eq),
0x0C => parse_binary!(OpBinary::Ne),
0x0D => parse_binary!(OpBinary::Lt),
0x0E => parse_binary!(OpBinary::Gt),
0x0F => parse_binary!(OpBinary::Le),
0x10 => parse_binary!(OpBinary::Ge),
0x11 => parse_binary!(OpBinary::And),
0x12 => parse_binary!(OpBinary::Or),
0x13 => parse_binary!(OpBinary::Xor),
0x14 => parse_binary!(OpBinary::Max),
0x15 => parse_binary!(OpBinary::Min),
0x41 => parse_unary!(OpUnary::Neg),
0x42 => parse_unary!(OpUnary::BitNot),
0x43 => parse_unary!(OpUnary::U16SwapBytes),
0x44 => parse_unary!(OpUnary::Not),
0x45 => parse_unary!(OpUnary::Bank),
0x48 => parse_unary!(OpUnary::Byte0),
0x49 => parse_unary!(OpUnary::Byte1),
0x4A => parse_unary!(OpUnary::Byte2),
0x4B => parse_unary!(OpUnary::Byte3),
0x4C => parse_unary!(OpUnary::Word0),
0x4D => parse_unary!(OpUnary::Word1),
0x4E => parse_unary!(OpUnary::FarAddr),
0x4F => parse_unary!(OpUnary::Dword),
0x50 => parse_unary!(OpUnary::NearAddr),
0x81 => parse_literal!(),
0x82 => parse_symbol!(),
0x83 => parse_section!(),
_ => parse_bail!("unknown expr type: {ty:#X}"),
};
Ok(expr)
}
}