use std::{borrow::Cow, slice};
pub use crate::instruction::{Emitter, Planner, SizeInference};
use crate::{
AstNode, BinaryOperator, Builtin, Expression, ExpressionTy, Ident, LabelOrNode, Load,
PCodeOpId, PcodeSpaceRef, RangeParam, SpaceId, UnaryOperator,
};
pub trait ExprNode: Copy {
type Args: Iterator<Item = Self> + ExactSizeIterator + Clone;
fn size(self) -> Option<usize>;
fn kind<'a>(self) -> ExprKind<'a, Self>
where
Self: 'a;
}
#[derive(Debug, Clone)]
pub enum ExprKind<'a, E: ExprNode> {
SizedInt {
value: u64,
size: Option<usize>,
},
Ident(Ident),
Load(LoadNode<'a, E>),
Range(RangeNode<E>),
SubPieceMsb {
src: E,
count: usize,
},
SubPieceLsb {
src: E,
count: usize,
},
FunctionCall {
builtin: Builtin,
args: E::Args,
},
PcodeOp {
id: PCodeOpId,
args: E::Args,
},
Unop {
op: UnaryOperator,
e: E,
},
Binop {
op: BinaryOperator,
lhs: E,
rhs: E,
},
Internal(&'static str),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoadSpace<'a> {
Default,
Resolved(SpaceId),
Deferred(&'a str),
}
impl<'a> From<&'a Option<PcodeSpaceRef>> for LoadSpace<'a> {
fn from(space: &'a Option<PcodeSpaceRef>) -> Self {
match space {
None => LoadSpace::Default,
Some(PcodeSpaceRef::Resolved(id)) => LoadSpace::Resolved(*id),
Some(PcodeSpaceRef::Deferred(name)) => LoadSpace::Deferred(name),
}
}
}
impl From<LoadSpace<'_>> for Option<PcodeSpaceRef> {
fn from(space: LoadSpace<'_>) -> Self {
match space {
LoadSpace::Default => None,
LoadSpace::Resolved(id) => Some(PcodeSpaceRef::Resolved(id)),
LoadSpace::Deferred(name) => Some(PcodeSpaceRef::Deferred(name.into())),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct LoadNode<'a, E> {
pub space: LoadSpace<'a>,
pub size: Option<usize>,
pub ptr: E,
}
impl<'a, E> LoadNode<'a, E> {
fn from_load<S>(load: &'a Load<S>, node: impl FnOnce(&'a Expression<S>) -> E) -> Self {
Self {
space: (&load.space).into(),
size: load.size,
ptr: node(&load.ptr),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct RangeNode<E> {
pub value: E,
pub start: RangeParam,
pub size: RangeParam,
}
#[derive(Debug, Clone)]
pub enum TargetNode<'a, E> {
Label(Cow<'a, str>),
Node(&'a str),
Expr(E),
}
impl<'a, S> From<&'a LabelOrNode<S>> for TargetNode<'a, &'a Expression<S>> {
fn from(target: &'a LabelOrNode<S>) -> Self {
match target {
LabelOrNode::Label(name) => TargetNode::Label(Cow::Borrowed(name)),
LabelOrNode::Node(name) => TargetNode::Node(name),
LabelOrNode::Expr(expr) => TargetNode::Expr(expr),
}
}
}
#[derive(Debug, Clone)]
pub enum StmtKind<'a, E: ExprNode> {
Assignment {
lhs: Ident,
size: Option<usize>,
rhs: E,
},
LoadAssignment {
load: LoadNode<'a, E>,
size: Option<usize>,
rhs: E,
},
RangeAssignment {
range: RangeNode<E>,
size: Option<usize>,
rhs: E,
},
Label(Cow<'a, str>),
Branch {
target: TargetNode<'a, E>,
},
ConditionalBranch {
condition: E,
target: TargetNode<'a, E>,
},
BranchIndirect {
target: E,
},
Call {
target: TargetNode<'a, E>,
},
CallIndirect {
target: E,
},
Return {
target: E,
},
Expression(E),
Internal(&'static str),
}
impl<'a, S> From<&'a AstNode<S>> for StmtKind<'a, &'a Expression<S>> {
fn from(statement: &'a AstNode<S>) -> Self {
match statement {
AstNode::Assignment { lhs, size, rhs } => StmtKind::Assignment {
lhs: lhs.clone(),
size: *size,
rhs,
},
AstNode::LoadAssignment { lhs, size, rhs } => StmtKind::LoadAssignment {
load: LoadNode::from_load(lhs, |ptr| ptr),
size: *size,
rhs,
},
AstNode::RangeAssignment { lhs, size, rhs } => StmtKind::RangeAssignment {
range: RangeNode {
value: &lhs.value,
start: lhs.start,
size: lhs.size,
},
size: *size,
rhs,
},
AstNode::Build(_) => StmtKind::Internal("build statement"),
AstNode::DelaySlot(_) => StmtKind::Internal("delay-slot directive"),
AstNode::DeferredBuild(_) => StmtKind::Internal("deferred build statement"),
AstNode::Label(name) => StmtKind::Label(Cow::Borrowed(name)),
AstNode::Branch { target } => StmtKind::Branch {
target: target.into(),
},
AstNode::ConditionalBranch { condition, target } => StmtKind::ConditionalBranch {
condition,
target: target.into(),
},
AstNode::BranchIndirect { target } => StmtKind::BranchIndirect { target },
AstNode::Call { target } => StmtKind::Call {
target: target.into(),
},
AstNode::CallIndirect { target } => StmtKind::CallIndirect { target },
AstNode::Return { target } => StmtKind::Return { target },
AstNode::Export(_) => StmtKind::Internal("export statement"),
AstNode::Expression(expr) => StmtKind::Expression(expr),
}
}
}
impl<'a, S> ExprNode for &'a Expression<S> {
type Args = slice::Iter<'a, Expression<S>>;
fn size(self) -> Option<usize> {
self.size
}
fn kind<'b>(self) -> ExprKind<'b, Self>
where
Self: 'b,
{
match &self.ty {
ExpressionTy::SizedInt { value, size } => ExprKind::SizedInt {
value: *value,
size: *size,
},
ExpressionTy::Ident(ident) => ExprKind::Ident(ident.clone()),
ExpressionTy::Load(load) => ExprKind::Load(LoadNode::from_load(load, |ptr| ptr)),
ExpressionTy::Range(range) => ExprKind::Range(RangeNode {
value: &range.value,
start: range.start,
size: range.size,
}),
ExpressionTy::SubPieceMsb { src, count } => {
ExprKind::SubPieceMsb { src, count: *count }
}
ExpressionTy::SubPieceLsb { src, count } => {
ExprKind::SubPieceLsb { src, count: *count }
}
ExpressionTy::FunctionCall { builtin, args } => ExprKind::FunctionCall {
builtin: *builtin,
args: args.iter(),
},
ExpressionTy::PcodeOp { id, args } => ExprKind::PcodeOp {
id: *id,
args: args.iter(),
},
ExpressionTy::MacroCall { .. } => ExprKind::Internal("macro call"),
ExpressionTy::DeferredCall { .. } => ExprKind::Internal("deferred call"),
ExpressionTy::Unop(unop) => ExprKind::Unop {
op: unop.op,
e: &unop.e,
},
ExpressionTy::Binop(binop) => ExprKind::Binop {
op: binop.op,
lhs: &binop.lhs,
rhs: &binop.rhs,
},
}
}
}
#[cfg(test)]
mod tests {
use super::{ExprKind, ExprNode, LoadSpace, StmtKind};
use crate::{
AstNode, Expression, ExpressionTy, Ident, Load, PcodeSpaceRef, RegisterId, SpaceId,
};
fn load(space: Option<PcodeSpaceRef>) -> Expression {
Expression {
ty: ExpressionTy::Load(Load {
space,
size: Some(4),
ptr: Box::new(Expression {
ty: ExpressionTy::Ident(Ident::Register(RegisterId::new(0))),
size: Some(8),
span: (),
}),
}),
size: Some(4),
span: (),
}
}
#[test]
fn deferred_load_space_keeps_its_name() {
let deferred = Some(PcodeSpaceRef::Deferred("segment".into()));
let expr = load(deferred.clone());
let ExprKind::Load(node) = (&expr).kind() else {
panic!("a load");
};
assert_eq!(node.space, LoadSpace::Deferred("segment"));
assert_eq!(Option::<PcodeSpaceRef>::from(node.space), deferred);
let store = AstNode::LoadAssignment {
lhs: Load {
space: deferred.clone(),
size: Some(4),
ptr: Box::new(expr.clone()),
},
size: None,
rhs: expr,
};
let StmtKind::LoadAssignment { load, .. } = StmtKind::from(&store) else {
panic!("a store");
};
assert_eq!(load.space, LoadSpace::Deferred("segment"));
assert_eq!(
LoadSpace::from(&Some(PcodeSpaceRef::Resolved(SpaceId::new(3)))),
LoadSpace::Resolved(SpaceId::new(3))
);
assert_eq!(LoadSpace::from(&None), LoadSpace::Default);
}
}