diff --git a/Cargo.toml b/Cargo.toml index d7c9c2d..8bf2b35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,17 @@ [workspace] members = [ - "sing_derive", + "sing_macros", "sing_parse", + "sing_util", ] -# [package] -# name = "sing" -# version = "0.1.0" -# edition = "2021" -# -# [lib] -# bench = false +[package] +name = "sing" +version = "0.1.0" +edition = "2021" + +[lib] +bench = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # build = "build.rs" @@ -18,8 +19,7 @@ members = [ # [build-dependencies] # lalrpop = "0.19" # -# [dependencies] -# syn = "1" -# regex = "1" -# lalrpop-util = "0.19" -# lalrpop = "0.19" \ No newline at end of file +[dependencies] +serde = { version = "1.0", features = ["derive"] } +syn = { version = "1.0", features = ["full"] } + diff --git a/sing_derive/src/lib.rs b/sing_derive/src/lib.rs deleted file mode 100644 index da1e896..0000000 --- a/sing_derive/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ - -use std::{collections::HashMap, io::Read, fmt::Write}; - -struct TraitProfile{} - -struct FunctionProfile{} - -struct Evaluator { - traits: HashMap, - functions: HashMap, - input: R, - output: W, - err: W, - worker: T -} - - -#[cfg(test)] -mod tests { - - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } - -} diff --git a/sing_macros/Cargo.toml b/sing_macros/Cargo.toml new file mode 100644 index 0000000..a9b0e9f --- /dev/null +++ b/sing_macros/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "sing_derive" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +sing_util = { path = "../sing_util" } +proc-macro2 = "1.0" +ron = "0.7" +rand = "0.7" +serde = { version = "1.0", features = ["derive"] } +syn = { version = "1.0", features = ["full"] } +quote = "1.0" diff --git a/sing_macros/src/lib.rs b/sing_macros/src/lib.rs new file mode 100644 index 0000000..67ee217 --- /dev/null +++ b/sing_macros/src/lib.rs @@ -0,0 +1,102 @@ +use std::{ + collections::HashMap, + env, + fs, + error::Error, + io::{Write, Read}, fmt::format, path::Path, +}; +use rand::rngs::adapter::ReseedingRng; +use serde::{Serialize, Deserialize}; +use syn::{parse_macro_input, DeriveInput, AttributeArgs, ItemImpl, ImplItem}; +use quote::{quote, spanned::Spanned}; +use proc_macro2::{Span, Spacing, Delimiter, TokenStream, Literal, TokenTree, Ident, Punct, Group}; + +extern crate sing_util; +extern crate proc_macro; + +fn get_run_id () -> Result> { + // Get a random uid for this compiler run and save it as an environment variable. + // Load it if it's already there. + match env::var("COMP_RUN_ID") { + Ok(v) => Ok(v.parse()?), + Err(_) => { + let id: usize = rand::random(); + let id_str = format!("{}", id); + env::set_var("COMP_RUN_ID", id_str); + Ok(id) + } + } +} + +fn get_state_file_path() -> Result> { + let compilation_run_identifier = get_run_id()?; + Ok(format!("/tmp/sing-trait-store-{}", compilation_run_identifier)) +} + +fn save_trait_state(map: HashMap) -> Result> { + let state_file_path = get_state_file_path()?; + let mut state_file = fs::File::create(state_file_path)?; + + let state_string = ron::to_string(&map)?; + Ok(state_file.write(state_string.as_bytes())?) +} + +fn load_trait_state() -> Result, Box> { + let stat_file_path = get_state_file_path()?; + let mut state_file = fs::File::open(stat_file_path)?; + + let mut state_string= String::from(""); + state_file.read_to_string(&mut state_string)?; + Ok(ron::from_str(&state_string)?) +} + +#[proc_macro_attribute] +pub fn my_derive(input: proc_macro::TokenStream, annotated_item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as ItemImpl); + let annotated_item = parse_macro_input!(annotated_item as AttributeArgs); + + let trait_name; + match input.trait_ { + Some((_b, p, _f)) => { + trait_name = p; + }, + None => { + syn::Error::new(input.__span(), "This attribute can only be used on trait impl blocks.") + .to_compile_error(); + }, + }; + + let mut trait_functions = vec!(); + for fun in input.items { + match fun { + ImplItem::Method(m) => trait_functions.push(m.sig), + _ => { + syn::Error::new(input.__span(), "Found unexpected item that is not a method.") + .to_compile_error(); + }, + } + } + + let trait_state = load_trait_state().unwrap(); + + + save_trait_state(trait_state).unwrap(); + + // Build the output, possibly using quasi-quotation + let output = quote! { + // ... + }; + + proc_macro::TokenStream::from(output) +} + +#[cfg(test)] +mod tests { + + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } + +} diff --git a/sing_derive/Cargo.toml b/sing_util/Cargo.toml similarity index 62% rename from sing_derive/Cargo.toml rename to sing_util/Cargo.toml index 703a0b8..c936ca5 100644 --- a/sing_derive/Cargo.toml +++ b/sing_util/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "sing_derive" +name = "sing_util" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -proc-macro = true - - [dependencies] +ron = "0.7" +rand = "0.7" +serde = { version = "1.0", features = ["derive"] } + diff --git a/sing_util/src/lib.rs b/sing_util/src/lib.rs new file mode 100644 index 0000000..c9b2daa --- /dev/null +++ b/sing_util/src/lib.rs @@ -0,0 +1,70 @@ +use std::{ + collections::HashMap, + env, + fs, + error::Error, + io::{Write, Read}, fmt::format, +}; +use rand::rngs::adapter::ReseedingRng; +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FunctionProfile{ + inputs: String, + outputs: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TraitProfile{ + functions: HashMap, +} + +impl TraitProfile { + pub fn get_fun_profile(&self, query: &String) -> Option<&FunctionProfile> { + self.functions.get(query) + } +} + +#[derive(Debug, Clone)] +enum DeduplicatedFunctionProfile { + Single(String, FunctionProfile), + Multiple(Vec, FunctionProfile), +} + + +/*impl Evaluator { + fn deduplicate_functions(&self) -> Self { + let mut self = self.clone(); + let mut functions: HashMap; + + for (t_name, t_profile) in self.traits { + for (fun_name, fun_profile) in t_profile.functions { + if !functions.contains_key(fun_name) { + self.functions.insert(fun_name, DeduplicatedFunctionProfile::Single(t_name, fun_profile)) + } else { + let other = self.functions.remove(fun_name).unwrap(); + + let traits: Vec; + match other { + DeduplicatedFunctionProfile::Single(t, _) => traits = vec!(t), + DeduplicatedFunctionProfile::Multiple(ts, _) => traits = ts, + } + + self.functions.insert(fun_name, DeduplicatedFunctionProfile::Multiple(traits, fun_profile)) + } + } + } + } +} +*/ + +#[cfg(test)] +mod tests { + + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } + +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..71e27f0 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,55 @@ +use core::fmt::Debug; + +use serde::{Serialize, Deserialize}; +use serde_wrapper::ImplItemMethodWrapper; + +mod serde_wrapper; + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct TraitProfile{ + //functions: HashMap, + functions: Vec, +} + +#[derive(Debug, Clone)] +enum DeduplicatedFunctionProfile { + Single(String, ImplItemMethodWrapper), + Multiple(Vec, ImplItemMethodWrapper), +} + +/*impl Evaluator { + fn deduplicate_functions(&self) -> Self { + let mut self = self.clone(); + let mut functions: HashMap; + + for (t_name, t_profile) in self.traits { + for (fun_name, fun_profile) in t_profile.functions { + if !functions.contains_key(fun_name) { + self.functions.insert(fun_name, DeduplicatedFunctionProfile::Single(t_name, fun_profile)) + } else { + let other = self.functions.remove(fun_name).unwrap(); + + let traits: Vec; + match other { + DeduplicatedFunctionProfile::Single(t, _) => traits = vec!(t), + DeduplicatedFunctionProfile::Multiple(ts, _) => traits = ts, + } + + self.functions.insert(fun_name, DeduplicatedFunctionProfile::Multiple(traits, fun_profile)) + } + } + } + } +} +*/ + +#[cfg(test)] +mod tests { + + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } + +} diff --git a/src/serde_wrapper.rs b/src/serde_wrapper.rs new file mode 100644 index 0000000..a773d09 --- /dev/null +++ b/src/serde_wrapper.rs @@ -0,0 +1,220 @@ +use serde::{Serialize, Deserialize, Serializer, Deserializer, ser::SerializeStruct}; +use syn::{ImplItemMethod, Visibility, Signature, Pat, FnArg, Type, PatIdent, Ident, __private::Span, Receiver, ReturnType, Generics}; +use core::fmt::{Debug, Formatter}; +use std::error::Error; + + +#[derive(Clone)] +pub (crate) struct ImplItemMethodWrapper(ImplItemMethod); + +impl Debug for ImplItemMethodWrapper { + fn fmt(&self, _: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + todo!() + } +} + +impl Serialize for ImplItemMethodWrapper { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer + { + let inner = &self.0; + let mut state = serializer.serialize_struct("Color", 4)?; + + // Attribute macros are not important in our case. + state.serialize_field("vis", &VisibilityWrapper(&inner.vis)); + // Defaultness are also not important here. + state.serialize_field("sig", &SignatureWrapper(&inner.sig)); + // We can throw away the block too. + + state.end() + } +} + +impl<'de> Deserialize<'de> for ImplItemMethodWrapper { + fn deserialize(_: D) -> Result>::Error> where D: Deserializer<'de> { + todo!() + } +} + +struct VisibilityWrapper<'a>(&'a Visibility); + +impl Serialize for VisibilityWrapper<'_> { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer + { + match &self.0 { + Visibility::Public(_) => serializer.serialize_str("pub"), + Visibility::Crate(_) => serializer.serialize_str("crate"), + Visibility::Restricted(r) => { + let mut path_str = String::from(""); + + for seg in &r.path.segments { + // There should be no arguments in the paths passed here. + path_str.push_str(seg.ident.to_string().as_str()); + path_str.push_str("::"); + } + + serializer.serialize_str( + format!("pub ({})", path_str).as_str() + ) + }, + Visibility::Inherited => serializer.serialize_str("inherited"), + } + } +} + +struct SignatureWrapper<'a>(&'a Signature); + +impl Serialize for SignatureWrapper<'_> { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer + { + let inner = &self.0; + let mut state = serializer.serialize_struct("Signature", 7)?; + + let mut prefixes = vec!(); + if inner.constness.is_some() { + prefixes.push("const") + } + if inner.asyncness.is_some() { + prefixes.push("async") + } + if inner.unsafety.is_some() { + prefixes.push("unsafe") + } + state.serialize_field("prefixes", &prefixes)?; + + match &inner.abi { + Some(a) => state.serialize_field( + "abi", + &Some( + format!( + "extern {}", + match &a.name { + Some(n) => n.value(), + None => String::from(""), + } + ).as_str() + ) + ), + None => state.serialize_field("abi", &None::), + }?; + + // No need to serialize the Fn token + state.serialize_field("ident", &inner.ident.to_string()); + + state.serialize_field("generics", &GenericsWrapper(&inner.generics)); + + // Parentheses do not need to be serialized either + + serialize_iterator( + &mut state, + Box::new(inner + .inputs + .clone() + .into_iter() + ), + "inputs", + | inp: &FnArg | -> Result<(PatWrapper, Option), Box> { + match inp { + FnArg::Receiver(r) => Ok(patopt_from_receiver(r.clone())), + FnArg::Typed(t) => Ok((PatWrapper(*t.pat.clone()), Some(TypeWrapper(*t.ty.clone())))), + } + } + ); + + state.serialize_field("variadic", match inner.variadic{ + Some(_) => "...", + None => "", + }); + + match &inner.output{ + ReturnType::Default => {state.serialize_field("output", "()");}, + ReturnType::Type(_, t) => {(state.serialize_field("output", &TypeWrapper(*t.clone())));} + }; + + state.end() + } +} + +struct GenericsWrapper<'a>(&'a Generics); + +impl Serialize for GenericsWrapper<'_> { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer + { + todo!() + } +} + +struct PatWrapper(Pat); + +impl Serialize for PatWrapper { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer + { + todo!() + } +} + +struct TypeWrapper( Type); + +impl Serialize for TypeWrapper { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer + { + todo!() + } +} + +fn patopt_from_receiver(r: Receiver) -> (PatWrapper, Option) { + let mut ident = String::from(""); + + match &r.reference { + Some((_, lt)) => { + ident.push_str("&"); + + match lt { + Some(l) => ident.push_str( + &l.ident + .to_string() + .as_str() + ), + None => {}, + } + + ident.push_str(" "); + }, + None => {}, + } + + (PatWrapper(Pat::Ident(PatIdent{ + attrs: vec!(), + by_ref: None, + mutability: r.mutability, + ident: Ident::new(&ident, Span::call_site()), + subpat: None, + })), None) +} + +fn serialize_iterator (serializer: &mut S, iterator: Box>, field_name: &'static str, get_string_fun: F) + -> Result<(), Box> +where + F: Fn(&T) -> Result>, + S: SerializeStruct, + U: Serialize, +{ + let mut p_vec = vec!(); + for item in iterator { + p_vec.push(get_string_fun(&item)?); + } + serializer.serialize_field(field_name, &p_vec); + Ok(()) +} +