ppc750cl/macros/src/writer.rs

171 lines
5.6 KiB
Rust

use std::iter::FromIterator;
use std::string::ToString;
use proc_macro2::{Delimiter, Group, TokenStream};
use quote::quote;
use quote::ToTokens;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{Expr, ExprLit, ExprPath, Ident};
struct Arguments {
formatter: Expr,
ins: Expr,
args: Punctuated<Argument, syn::token::Semi>,
}
impl Parse for Arguments {
fn parse(input: ParseStream) -> syn::Result<Self> {
let formatter = input.parse()?;
input.parse::<syn::token::Comma>()?;
let ins = input.parse()?;
input.parse::<syn::token::FatArrow>()?;
let content;
syn::braced!(content in input);
let args = Punctuated::parse_terminated(&content)?;
Ok(Self {
formatter,
ins,
args,
})
}
}
/// A single part of an instruction.
///
/// Examples:
/// ```ignore
/// (op.mnemonic, rc, oe) -> mnemonic;
/// d -> fpr;
/// ```
struct Argument {
sources: Vec<Expr>,
target: Ident,
}
impl Parse for Argument {
fn parse(input: ParseStream) -> syn::Result<Self> {
// Parse source part.
let lookahead = input.lookahead1();
let sources;
if lookahead.peek(syn::token::Paren) {
// Parse multiple if we found a parenthesis.
let content;
syn::parenthesized!(content in input);
sources = content
.parse_terminated::<Expr, syn::token::Comma>(Expr::parse)?
.into_iter()
.collect();
} else if lookahead.peek(syn::LitStr) || lookahead.peek(syn::LitInt) {
let expr = input.parse::<ExprLit>()?.into();
sources = vec![expr];
} else {
let expr = input.parse::<ExprPath>()?.into();
sources = vec![expr];
}
input.parse::<syn::token::Colon>()?;
let target = input.parse()?;
Ok(Self { sources, target })
}
}
impl Arguments {
fn format_mnemonic(&self) -> Vec<TokenStream> {
let arg = &self.args[0];
assert!(!arg.sources.is_empty());
// Print the mnemonic.
let mut calls = vec![self.format_call(&arg.target, self.ins_call(&arg.sources[0]))];
// Print any mnemonic suffixes.
for src in arg.sources.iter().skip(1) {
calls.push(self.format_call(
&Ident::new(&src.into_token_stream().to_string(), src.span()),
self.ins_call(src),
));
}
calls
}
fn format_call(&self, method_arg: &Ident, args: TokenStream) -> TokenStream {
let arg_str = method_arg.to_string();
let method_name = format!("write_{}", arg_str);
let method_name = Ident::new(&method_name, method_arg.span());
let formatter = &self.formatter;
if arg_str == "branch_target" {
quote!(#formatter.write_branch_target(#args, self.addr)?)
} else {
quote!(#formatter.#method_name(#args)?)
}
}
fn ins_call(&self, call: &Expr) -> TokenStream {
match call {
Expr::Lit(_) => call.to_token_stream(),
_ => {
let ins = &self.ins;
quote!(#ins.#call())
}
}
}
}
pub(crate) fn write_asm(input: TokenStream) -> syn::Result<TokenStream> {
let arguments: Arguments = syn::parse2(input)?;
assert!(!arguments.args.is_empty());
// Create a list of calls to execute.
let mut calls = Vec::<TokenStream>::new();
calls.extend(arguments.format_mnemonic());
let mut offset_open = false;
for (i, arg) in arguments.args.iter().enumerate().skip(1) {
// Separate operands from one another unless the last one was an offset.
if !offset_open {
if i == 1 {
calls.push(
arguments
.format_call(&Ident::new("opcode_separator", arg.target.span()), quote!()),
);
} else {
calls.push(arguments.format_call(
&Ident::new("operand_separator", arg.target.span()),
quote!(),
));
}
}
// Arguments to out.write_x(...);
let format_args = arg.sources.iter().map(|src| arguments.ins_call(src));
let format_args_punct: Punctuated<TokenStream, syn::token::Comma> =
Punctuated::from_iter(format_args);
// Create call.
if arg.target.to_string().starts_with("offset") {
// Offsets are a special case since we need to call close afterwards.
if offset_open {
return Err(syn::Error::new(
arg.target.span(),
"two consecutive offset arguments",
));
}
calls.push(arguments.format_call(
&Ident::new(&(arg.target.to_string() + "_open"), arg.target.span()),
format_args_punct.to_token_stream(),
));
offset_open = true;
} else {
calls.push(arguments.format_call(&arg.target, format_args_punct.to_token_stream()));
if offset_open {
calls.push(
arguments.format_call(&Ident::new("offset_close", arg.target.span()), quote!()),
);
offset_open = false;
}
}
}
// Wrap calls in a block returning Ok(()).
calls.push(quote!(std::io::Result::Ok(())));
let statements = Punctuated::<TokenStream, syn::token::Semi>::from_iter(calls);
let tokens = Group::new(Delimiter::Brace, statements.to_token_stream());
Ok(tokens.to_token_stream())
}