use crate::ident::Ident;
use crate::{Function, Invoke, MacroInvoke, Print, Receiver, Type, TypeNode, ValueNode, ValueRef};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use ref_cast::RefCast;
use std::collections::BTreeSet as Set;
#[derive(Debug)]
pub(crate) struct Program {
pub crates: Vec<Ident>,
pub impls: Vec<CompleteImpl>,
}
#[derive(Debug)]
pub(crate) struct CompleteImpl {
pub trait_ty: Option<Type>,
pub ty: Type,
pub functions: Vec<CompleteFunction>,
}
#[derive(Debug)]
pub(crate) struct CompleteFunction {
pub self_ty: Option<Type>,
pub f: Function,
pub values: Vec<ValueNode>,
pub invokes: Vec<Invoke>,
pub macros: Vec<MacroInvoke>,
pub ret: Option<ValueRef>,
}
impl Program {
pub fn compile(&self) -> TokenStream {
let impls = self.impls.iter().map(CompleteImpl::compile);
quote! {
#(#impls)*
}
}
}
impl CompleteImpl {
fn compile(&self) -> TokenStream {
let functions = self.functions.iter().map(CompleteFunction::compile);
let (name, params, constraints) = self.ty.name_and_generics();
let params = if params.is_empty() {
None
} else {
let params = params.iter().map(Print::ref_cast);
Some(quote!(<#(#params),*>))
};
let where_clause = if constraints.is_empty() {
None
} else {
let constraints = constraints.iter().map(Print::ref_cast);
Some(quote!(where #(#constraints,)*))
};
if let Some(trait_ty) = &self.trait_ty {
let trait_ty = Print::ref_cast(trait_ty);
quote! {
impl #params #trait_ty for #name #params #where_clause {
#(#functions)*
}
}
} else {
quote! {
impl #params #name #params #where_clause {
#(#functions)*
}
}
}
}
}
impl CompleteFunction {
fn compile(&self) -> TokenStream {
let name = Ident::new(&self.f.name);
let mut inputs = Vec::new();
inputs.extend(receiver_tokens(self.f.sig.receiver));
for (i, input) in self.f.sig.inputs.iter().enumerate() {
let binding = Ident::new(format!("__arg{}", i));
let ty = Print::ref_cast(input);
inputs.push(quote! {
#binding : #ty
});
}
let output = match &self.f.sig.output {
Type(TypeNode::Tuple(types)) if types.is_empty() => None,
other => {
let ty = Print::ref_cast(other);
Some(quote!(-> #ty))
}
};
let reachable = self.compute_reachability();
let mutable = self.compute_mutability();
let values = self.refs().filter_map(|v| {
if let ValueNode::Str(_) = self.values[v.0] {
return None;
}
let expr = self.compile_value(v);
if reachable.contains(&v) {
let let_mut = if mutable.contains(&v) {
quote!(let mut)
} else {
quote!(let)
};
let binding = v.binding();
Some(quote! {
#let_mut #binding = #expr;
})
} else if self.is_important(v) {
Some(quote! {
let _ = #expr;
})
} else {
None
}
});
let ret = self.ret.map(ValueRef::binding);
quote! {
fn #name (#(#inputs),*) #output {
#(#values)*
#ret
}
}
}
fn refs(&self) -> impl Iterator<Item = ValueRef> {
(0..self.values.len()).map(ValueRef)
}
fn compute_reachability(&self) -> Set<ValueRef> {
let mut reachable = Set::new();
let mut stack: Vec<_> = self.refs().filter(|v| self.is_important(*v)).collect();
if let Some(ret) = self.ret {
stack.push(ret);
reachable.insert(ret);
}
use crate::ValueNode::*;
while let Some(v) = stack.pop() {
match &self.values[v.0] {
Tuple(values) => {
for &v in values {
if reachable.insert(v) {
stack.extend(values);
}
}
}
Str(s) => {}
Reference(v) | ReferenceMut(v) | Dereference(v) => {
if reachable.insert(*v) {
stack.push(*v);
}
}
Binding { name, .. } => {}
Invoke(invoke) => {
for &v in &self.invokes[invoke.0].args {
if reachable.insert(v) {
stack.push(v);
}
}
}
MacroInvocation(invoke) => {
for &v in &self.macros[invoke.0].args {
if reachable.insert(v) {
stack.push(v);
}
}
}
Destructure { parent, .. } => {
if reachable.insert(*parent) {
stack.push(*parent);
}
}
DataStructure { .. } => unimplemented!(),
}
}
reachable
}
fn compute_mutability(&self) -> Set<ValueRef> {
let mut mutable = Set::new();
for v in self.refs() {
if let ValueNode::ReferenceMut(referent) = self.values[v.0] {
mutable.insert(referent);
}
}
mutable
}
fn is_important(&self, v: ValueRef) -> bool {
if let ValueNode::Invoke(_) | ValueNode::MacroInvocation(_) = self.values[v.0] {
return true;
}
false
}
fn compile_value(&self, v: ValueRef) -> TokenStream {
match &self.values[v.0] {
ValueNode::Tuple(values) => {
let values = self.make_values_list(values);
quote! {
( #values )
}
}
ValueNode::Str(s) => quote! { #s },
ValueNode::Reference(v) => {
let v = v.binding();
quote! { &#v }
}
ValueNode::ReferenceMut(v) => {
let v = v.binding();
quote! { &mut #v }
}
ValueNode::Dereference(v) => {
let v = v.binding();
quote! { *#v }
}
ValueNode::Binding { name, .. } => quote! { #name },
ValueNode::Invoke(invoke) => {
let invoke = &self.invokes[invoke.0];
let parent = match &invoke.function.parent {
Some(parent) => {
let print = Print::ref_cast(parent);
Some(quote!(#print ::))
}
None => None,
};
let name = Ident::new(&invoke.function.name);
let args = self.make_values_list(&invoke.args);
quote! {
#parent #name ( #args )
}
}
ValueNode::Destructure {
parent, accessor, ..
} => {
let parent = parent.binding();
let accessor = Print::ref_cast(accessor);
quote! {
&#parent.#accessor
}
}
ValueNode::DataStructure { .. } => unimplemented!(),
ValueNode::MacroInvocation(invoke) => {
let invoke = &self.macros[invoke.0];
let path = Print::ref_cast(&invoke.macro_path);
let args = self.make_values_list(&invoke.args);
let tokens = quote! {
#path ! ( #args )
};
tokens
}
}
}
fn make_values_list(&self, values: &[ValueRef]) -> TokenStream {
let values = values.iter().map(|value| match &self.values[value.0] {
ValueNode::Str(s) => self.compile_value(*value),
_ => value.binding().to_token_stream(),
});
quote! { #(#values),* }
}
}
fn receiver_tokens(receiver: Receiver) -> Option<TokenStream> {
match receiver {
Receiver::NoSelf => None,
Receiver::SelfByValue => Some(quote!(self)),
Receiver::SelfByReference => Some(quote!(&self)),
Receiver::SelfByReferenceMut => Some(quote!(&mut self)),
}
}
impl ValueRef {
fn binding(self) -> Ident {
Ident::new(format!("__v{}", self.0))
}
}