use std::fmt::{self, Debug};
use crate::{Expression, FieldId, Ident, Load, PcodeResolver, Range, TableId, pretty_print_ident};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum LabelOrNode<S = ()> {
Label(Box<str>),
Node(Box<str>),
Expr(Expression<S>),
}
impl<S> LabelOrNode<S> {
pub fn strip_span(self) -> LabelOrNode<()> {
match self {
LabelOrNode::Label(name) => LabelOrNode::Label(name),
LabelOrNode::Node(name) => LabelOrNode::Node(name),
LabelOrNode::Expr(expr) => LabelOrNode::Expr(expr.strip_span()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DelaySlotArg {
Bytes(u64),
Field(FieldId),
Deferred(Box<str>),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AstNode<S = ()> {
Assignment {
lhs: Ident,
size: Option<usize>,
rhs: Expression<S>,
},
LoadAssignment {
lhs: Load<S>,
size: Option<usize>,
rhs: Expression<S>,
},
RangeAssignment {
lhs: Range<S>,
size: Option<usize>,
rhs: Expression<S>,
},
Build(TableId),
DelaySlot(DelaySlotArg),
DeferredBuild(Box<str>),
Label(Box<str>),
Branch {
target: LabelOrNode<S>,
},
ConditionalBranch {
condition: Expression<S>,
target: LabelOrNode<S>,
},
BranchIndirect {
target: Expression<S>,
},
Call {
target: LabelOrNode<S>,
},
CallIndirect {
target: Expression<S>,
},
Return {
target: Expression<S>,
},
Export(Expression<S>),
Expression(Expression<S>),
}
impl AstNode {
pub fn pretty_print(&self, spec: &impl PcodeResolver) -> String {
match self {
AstNode::Assignment { lhs, size, rhs } => format!(
"{}{} = {};",
pretty_print_ident(spec, lhs),
pretty_print_size(*size),
rhs.pretty_print(spec)
),
AstNode::LoadAssignment { lhs, size, rhs } => format!(
"{}{} = {};",
lhs.pretty_print(spec),
pretty_print_size(*size),
rhs.pretty_print(spec)
),
AstNode::RangeAssignment { lhs, size, rhs } => format!(
"{}{} = {};",
lhs.pretty_print(spec),
pretty_print_size(*size),
rhs.pretty_print(spec)
),
AstNode::Build(table_id) => format!("build table{};", usize::from(*table_id)),
AstNode::DelaySlot(arg) => match arg {
DelaySlotArg::Bytes(n) => format!("delayslot({n});"),
DelaySlotArg::Field(id) => {
format!("delayslot({});", spec.field_name(*id))
}
DelaySlotArg::Deferred(name) => format!("delayslot({name});"),
},
AstNode::DeferredBuild(name) => format!("build {name};"),
AstNode::Label(name) => format!("<{name}>"),
AstNode::Branch { target } => format!("goto {};", pretty_print_target(spec, target)),
AstNode::ConditionalBranch { condition, target } => format!(
"if {} goto {};",
condition.pretty_print(spec),
pretty_print_target(spec, target)
),
AstNode::BranchIndirect { target } => {
format!("goto [{}];", target.pretty_print(spec))
}
AstNode::Call { target } => format!("call {};", pretty_print_target(spec, target)),
AstNode::CallIndirect { target } => {
format!("call [{}];", target.pretty_print(spec))
}
AstNode::Return { target } => format!("return [{}];", target.pretty_print(spec)),
AstNode::Export(expr) => format!("export {};", expr.pretty_print(spec)),
AstNode::Expression(expr) => format!("{};", expr.pretty_print(spec)),
}
}
}
impl<S> AstNode<S> {
pub fn strip_span(self) -> AstNode<()> {
match self {
AstNode::Assignment { lhs, size, rhs } => AstNode::Assignment {
lhs,
size,
rhs: rhs.strip_span(),
},
AstNode::LoadAssignment { lhs, size, rhs } => AstNode::LoadAssignment {
lhs: lhs.strip_span(),
size,
rhs: rhs.strip_span(),
},
AstNode::RangeAssignment { lhs, size, rhs } => AstNode::RangeAssignment {
lhs: lhs.strip_span(),
size,
rhs: rhs.strip_span(),
},
AstNode::Build(table_id) => AstNode::Build(table_id),
AstNode::DelaySlot(arg) => AstNode::DelaySlot(arg),
AstNode::DeferredBuild(name) => AstNode::DeferredBuild(name),
AstNode::Label(name) => AstNode::Label(name),
AstNode::Branch { target } => AstNode::Branch {
target: target.strip_span(),
},
AstNode::ConditionalBranch { condition, target } => AstNode::ConditionalBranch {
condition: condition.strip_span(),
target: target.strip_span(),
},
AstNode::BranchIndirect { target } => AstNode::BranchIndirect {
target: target.strip_span(),
},
AstNode::Call { target } => AstNode::Call {
target: target.strip_span(),
},
AstNode::CallIndirect { target } => AstNode::CallIndirect {
target: target.strip_span(),
},
AstNode::Return { target } => AstNode::Return {
target: target.strip_span(),
},
AstNode::Export(expr) => AstNode::Export(expr.strip_span()),
AstNode::Expression(expr) => AstNode::Expression(expr.strip_span()),
}
}
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Ast<S = ()> {
pub ty: AstNode<S>,
pub span: S,
}
impl<S: Debug> Debug for Ast<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.ty.fmt(f)
}
}
impl<S> Ast<S> {
pub fn strip_span(self) -> Ast<()> {
Ast {
ty: self.ty.strip_span(),
span: (),
}
}
}
impl From<AstNode> for Ast {
fn from(ty: AstNode) -> Self {
Self { ty, span: () }
}
}
impl Ast {
pub fn pretty_print(&self, spec: &impl PcodeResolver) -> String {
self.ty.pretty_print(spec)
}
}
fn pretty_print_target(spec: &impl PcodeResolver, target: &LabelOrNode) -> String {
match target {
LabelOrNode::Label(name) => format!("<{name}>"),
LabelOrNode::Node(name) => (*name).to_string(),
LabelOrNode::Expr(expr) => expr.pretty_print(spec),
}
}
fn pretty_print_size(size: Option<usize>) -> String {
size.map(|size| format!(":{size}")).unwrap_or_default()
}