167 lines
5.7 KiB
Rust
167 lines
5.7 KiB
Rust
use std::str::FromStr;
|
|
|
|
use anyhow::{anyhow, Context, Result};
|
|
use proc_macro2::{Group, Ident, TokenStream, TokenTree};
|
|
use quote::quote;
|
|
|
|
use crate::isa::{parse_signed, parse_unsigned, Field, HexLiteral, Isa, SignedHexLiteral};
|
|
|
|
/// A condition that must be met for a modifier to be applied
|
|
/// or for a simplified mnemonic to be matched.
|
|
#[derive(Clone, Debug)]
|
|
pub struct Condition<'a> {
|
|
pub field: &'a Field,
|
|
pub field_mask: u32,
|
|
pub op: ConditionOp,
|
|
pub value: ConditionValue<'a>,
|
|
}
|
|
|
|
/// The operation of a complex condition.
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub enum ConditionOp {
|
|
/// Equal to. (e.g. `MB == 0`)
|
|
Eq,
|
|
/// Not equal to. (e.g. `simm != -0x8000`)
|
|
Ne,
|
|
/// Less than. (e.g. `simm < 0`)
|
|
Lt,
|
|
/// Greater than. (e.g. `SH > 16`)
|
|
Gt,
|
|
/// Less than or equal to. (e.g. `SH <= 16`)
|
|
Lte,
|
|
/// Greater than or equal to. (e.g. `SH >= 32 - MB`)
|
|
Gte,
|
|
}
|
|
|
|
/// The value of a condition.
|
|
#[derive(Clone, Debug)]
|
|
pub enum ConditionValue<'a> {
|
|
/// A constant unsigned value. (e.g. `MB == 0`)
|
|
ConstantUnsigned(u32),
|
|
/// A constant signed value. (e.g. `simm != -0x8000`)
|
|
ConstantSigned(i32),
|
|
/// A field value. (e.g. `rB == rS`)
|
|
Field(&'a Field),
|
|
/// A complex condition. (e.g. `SH >= 32 - MB`)
|
|
Complex(&'a str),
|
|
}
|
|
|
|
/// Parse a condition string into a list of conditions. (e.g. `SH >= 32 - MB && MB == 0`)
|
|
/// Maybe turn this into a real lexer/parser if it gets more complex.
|
|
pub fn parse_conditions<'a>(condition: &'a str, isa: &'a Isa) -> Result<Vec<Condition<'a>>> {
|
|
let mut conditions = Vec::new();
|
|
for tok in condition.split(" && ") {
|
|
let (mut field, value, op) = if let Some((field, value)) = tok.split_once(" == ") {
|
|
(field, value, ConditionOp::Eq)
|
|
} else if let Some((field, value)) = tok.split_once(" != ") {
|
|
(field, value, ConditionOp::Ne)
|
|
} else if let Some((field, value)) = tok.split_once(" < ") {
|
|
(field, value, ConditionOp::Lt)
|
|
} else if let Some((field, value)) = tok.split_once(" > ") {
|
|
(field, value, ConditionOp::Gt)
|
|
} else if let Some((field, value)) = tok.split_once(" <= ") {
|
|
(field, value, ConditionOp::Lte)
|
|
} else if let Some((field, value)) = tok.split_once(" >= ") {
|
|
(field, value, ConditionOp::Gte)
|
|
} else {
|
|
log::error!("Invalid condition: {}", tok);
|
|
continue;
|
|
};
|
|
let mut field_mask = u32::MAX;
|
|
if let Some((n, mask)) = field.split_once(" & ") {
|
|
field = n;
|
|
field_mask = parse_unsigned(mask)?;
|
|
};
|
|
let field = isa
|
|
.find_field(field)
|
|
.with_context(|| format!("Condition references unknown field {}", field))?;
|
|
let value = if let Ok(value) = parse_unsigned(value) {
|
|
ConditionValue::ConstantUnsigned(value)
|
|
} else if let Ok(value) = parse_signed(value) {
|
|
ConditionValue::ConstantSigned(value)
|
|
} else if let Some(field) = isa.find_field(value) {
|
|
ConditionValue::Field(field)
|
|
} else {
|
|
ConditionValue::Complex(value)
|
|
};
|
|
conditions.push(Condition { field, field_mask, op, value });
|
|
}
|
|
Ok(conditions)
|
|
}
|
|
|
|
impl Condition<'_> {
|
|
pub fn to_token_stream(&self, isa: &Isa, self_ident: Ident) -> Result<TokenStream> {
|
|
// Accessor
|
|
let field = self.field;
|
|
let mut accessor = quote!(#self_ident.#field());
|
|
if self.field_mask != u32::MAX {
|
|
let mask = HexLiteral(self.field_mask);
|
|
accessor = quote! { (#accessor & #mask) };
|
|
}
|
|
|
|
// Operator
|
|
let op = match self.op {
|
|
ConditionOp::Eq => quote! { == },
|
|
ConditionOp::Ne => quote! { != },
|
|
ConditionOp::Lt => quote! { < },
|
|
ConditionOp::Gt => quote! { > },
|
|
ConditionOp::Lte => quote! { <= },
|
|
ConditionOp::Gte => quote! { >= },
|
|
};
|
|
|
|
// Value
|
|
let value = match self.value {
|
|
ConditionValue::ConstantUnsigned(v) => {
|
|
let v = HexLiteral(v);
|
|
quote! { #v }
|
|
}
|
|
ConditionValue::ConstantSigned(v) => {
|
|
let v = SignedHexLiteral(v);
|
|
quote! { #v }
|
|
}
|
|
ConditionValue::Field(f) => {
|
|
quote! { #self_ident.#f() }
|
|
}
|
|
ConditionValue::Complex(c) => {
|
|
replace_fields(c, isa, |f| Ok(quote! { #self_ident.#f() }))?
|
|
}
|
|
};
|
|
|
|
Ok(quote! { #accessor #op #value })
|
|
}
|
|
}
|
|
|
|
pub fn replace_fields(
|
|
s: &str,
|
|
isa: &Isa,
|
|
mut accessor: impl FnMut(&Field) -> Result<TokenStream>,
|
|
) -> Result<TokenStream> {
|
|
fn replace_inner(
|
|
s: TokenStream,
|
|
isa: &Isa,
|
|
accessor: &mut impl FnMut(&Field) -> Result<TokenStream>,
|
|
) -> Result<TokenStream> {
|
|
s.into_iter()
|
|
.map(|t| -> Result<TokenStream> {
|
|
match &t {
|
|
TokenTree::Ident(ident) => {
|
|
if let Some(field) = isa.find_field(&ident.to_string()) {
|
|
return accessor(field);
|
|
}
|
|
}
|
|
TokenTree::Group(g) => {
|
|
return Ok(TokenStream::from(TokenTree::Group(Group::new(
|
|
g.delimiter(),
|
|
replace_inner(g.stream(), isa, accessor)?,
|
|
))));
|
|
}
|
|
_ => {}
|
|
}
|
|
Ok(TokenStream::from(t))
|
|
})
|
|
.collect::<Result<TokenStream>>()
|
|
}
|
|
let stream = TokenStream::from_str(s).map_err(|e| anyhow!("{}", e))?;
|
|
replace_inner(stream, isa, &mut accessor)
|
|
}
|