diff --git a/rust-core/src/fiodl.rs b/rust-core/src/fiodl.rs index d5b331f7..57de339f 100644 --- a/rust-core/src/fiodl.rs +++ b/rust-core/src/fiodl.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, sync::Arc}; use nom::{ branch::alt, bytes::complete::{is_not, tag, take_until, take_while}, character::complete::{alphanumeric1, char, digit1, multispace0}, combinator::{fail, opt, recognize}, error::ParseError, multi::{many0, separated_list0}, number::complete::{double, float}, sequence::{delimited, preceded, terminated, tuple}, IResult, Parser }; -use petgraph::visit::{EdgeRef, NodeIndexable}; +use petgraph::visit::NodeIndexable; use crate::{EdgeInfo, ModelDriver, OpaqueTrait, SystemGraph, Trait, Vertex, VertexProperty}; diff --git a/rust-core/src/lib.rs b/rust-core/src/lib.rs index 5e1669b4..238f9724 100644 --- a/rust-core/src/lib.rs +++ b/rust-core/src/lib.rs @@ -66,106 +66,108 @@ pub enum VertexProperty { MapProperty(HashMap), } -impl TryFrom for bool { +impl TryFrom<&VertexProperty> for bool { type Error = String; - fn try_from(value: VertexProperty) -> Result { + fn try_from(value: &VertexProperty) -> Result { match value { - VertexProperty::BooleanProperty(v) => Ok(v), + VertexProperty::BooleanProperty(v) => Ok(*v), _ => Err("VertexProperty is not a boolean".to_string()), } } } -impl TryFrom for i32 { +impl TryFrom<&VertexProperty> for i32 { type Error = String; - fn try_from(value: VertexProperty) -> Result { + fn try_from(value: &VertexProperty) -> Result { match value { - VertexProperty::IntProperty(v) => Ok(v), + VertexProperty::IntProperty(v) => Ok(*v), _ => Err("VertexProperty is not an integer".to_string()), } } } -impl TryFrom for u32 { +impl TryFrom<&VertexProperty> for u32 { type Error = String; - fn try_from(value: VertexProperty) -> Result { + fn try_from(value: &VertexProperty) -> Result { match value { - VertexProperty::UIntProperty(v) => Ok(v), + VertexProperty::UIntProperty(v) => Ok(*v), _ => Err("VertexProperty is not an unsigned integer".to_string()), } } } -impl TryFrom for i64 { +impl TryFrom<&VertexProperty> for i64 { type Error = String; - fn try_from(value: VertexProperty) -> Result { + fn try_from(value: &VertexProperty) -> Result { match value { - VertexProperty::LongProperty(v) => Ok(v), + VertexProperty::LongProperty(v) => Ok(*v), _ => Err("VertexProperty is not a long".to_string()), } } } -impl TryFrom for u64 { +impl TryFrom<&VertexProperty> for u64 { type Error = String; - fn try_from(value: VertexProperty) -> Result { + fn try_from(value: &VertexProperty) -> Result { match value { - VertexProperty::ULongProperty(v) => Ok(v), + VertexProperty::ULongProperty(v) => Ok(*v), _ => Err("VertexProperty is not an unsigned long".to_string()), } } } -impl TryFrom for f32 { +impl TryFrom<&VertexProperty> for f32 { type Error = String; - fn try_from(value: VertexProperty) -> Result { + fn try_from(value: &VertexProperty) -> Result { match value { - VertexProperty::FloatProperty(v) => Ok(v), + VertexProperty::FloatProperty(v) => Ok(*v), _ => Err("VertexProperty is not a float".to_string()), } } } -impl TryFrom for f64 { +impl TryFrom<&VertexProperty> for f64 { type Error = String; - fn try_from(value: VertexProperty) -> Result { + fn try_from(value: &VertexProperty) -> Result { match value { - VertexProperty::DoubleProperty(v) => Ok(v), + VertexProperty::DoubleProperty(v) => Ok(*v), _ => Err("VertexProperty is not a double".to_string()), } } } -impl TryFrom for String { +impl TryFrom<&VertexProperty> for String { type Error = String; - fn try_from(value: VertexProperty) -> Result { + fn try_from(value: &VertexProperty) -> Result { match value { - VertexProperty::StringProperty(v) => Ok(v), + VertexProperty::StringProperty(v) => Ok(v.clone()), _ => Err("VertexProperty is not a string".to_string()), } } } -impl> TryFrom for Vec -where - String: From<>::Error> +impl<'a, T> TryFrom<&'a VertexProperty> for Vec +where + T: TryFrom<&'a VertexProperty>, + String: From<>::Error> { type Error = String; - fn try_from(value: VertexProperty) -> Result { + fn try_from(value: &'a VertexProperty) -> Result { match value { VertexProperty::ArrayProperty(v) => { let mut result = Vec::new(); for item in v { - result.push(T::try_from(item)?); + let n = T::try_from(item)?; + result.push(n); } Ok(result) }, @@ -174,18 +176,18 @@ where } } -impl> TryFrom for HashMap +impl<'a, T: TryFrom<&'a VertexProperty>> TryFrom<&'a VertexProperty> for HashMap where - String: From<>::Error> + String: From<>::Error> { type Error = String; - fn try_from(value: VertexProperty) -> Result { + fn try_from(value: &'a VertexProperty) -> Result { match value { VertexProperty::MapProperty(v) => { let mut mapping = HashMap::new(); for (key, item) in v { - mapping.insert(key, T::try_from(item)?); + mapping.insert(key.clone(), T::try_from(item)?); } Ok(mapping) }, diff --git a/rust-generator/Cargo.toml b/rust-generator/Cargo.toml index fde5e676..4c6fe2f3 100644 --- a/rust-generator/Cargo.toml +++ b/rust-generator/Cargo.toml @@ -13,3 +13,4 @@ quote.workspace = true serde.workspace = true serde_json.workspace = true syn.workspace = true +petgraph.workspace = true diff --git a/rust-generator/src/lib.rs b/rust-generator/src/lib.rs index ddac4eff..6dd16aa0 100644 --- a/rust-generator/src/lib.rs +++ b/rust-generator/src/lib.rs @@ -1,7 +1,10 @@ +use petgraph::visit::Dfs; +use petgraph::Directed; +use petgraph::Graph; use proc_macro2::{Ident, TokenStream}; use quote::format_ident; use quote::quote; -use std::{collections::HashMap, sync::Arc}; +use std::collections::HashMap; use serde::{Deserialize, Serialize}; @@ -11,9 +14,9 @@ pub struct PortSpec { #[serde(alias = "htmlDescription")] pub html_description: Option, #[serde(skip)] - pub vertex_trait: Option>, + pub vertex_trait: Option, #[serde(skip)] - pub edge_trait: Option>, + pub edge_trait: Option, #[serde(alias = "vertexTrait")] pub vertex_trait_name: String, #[serde(alias = "edgeTrait")] @@ -63,7 +66,7 @@ pub struct VertexTraitSpec { #[serde(alias = "htmlDescription")] pub html_description: Option, #[serde(skip)] - pub refined_traits: Vec>, + pub refined_traits: Vec, #[serde(alias = "refinedTraits")] pub refined_traits_names: Vec, #[serde(alias = "requiredPorts")] @@ -72,6 +75,12 @@ pub struct VertexTraitSpec { pub required_properties: HashMap } +impl PartialEq for VertexTraitSpec { + fn eq(&self, other: &Self) -> bool { + self.canonical_name == other.canonical_name + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EdgeTraitSpec { #[serde(alias = "canonicalName")] @@ -79,7 +88,7 @@ pub struct EdgeTraitSpec { #[serde(alias = "htmlDescription")] pub html_description: Option, #[serde(skip)] - pub refined_traits: Vec>, + pub refined_traits: Vec, #[serde(alias = "refinedTraits")] pub refined_traits_names: Vec, } @@ -94,54 +103,49 @@ pub struct TraitHierarchySpec { pub edge_traits: HashMap, } -fn vertex_refinement_closure(hierarchy: &TraitHierarchySpec) -> HashMap> { -let mut closure = HashMap::new(); - let traits = hierarchy.vertex_traits.keys().collect::>(); - for (name, vt) in hierarchy.vertex_traits.iter() { - closure.insert(name.clone(), vt.refined_traits_names.clone()); - } - for k in &traits { - for i in &traits { - for j in &traits { - let i_has_k = closure.get(*i).map(|vi| vi.contains(*k)).unwrap_or(false); - let k_has_j = closure.get(*k).map(|vk| vk.contains(*j)).unwrap_or(false); - let i_has_j = closure.get(*i).map(|vi| vi.contains(*j)).unwrap_or(false); - if i_has_k && k_has_j && !i_has_j { - if let Some(vi) = closure.get_mut(*i) { - vi.push(j.to_string()); - }; - } +pub type VertexTraitHierachy = Graph; + +pub type EdgeTraitHierachy = Graph; + +impl From<&TraitHierarchySpec> for VertexTraitHierachy { + fn from(value: &TraitHierarchySpec) -> Self { + let mut idx_to_node = HashMap::new(); + let mut graph = Graph::new(); + for (name, vt) in &value.vertex_traits { + let node = graph.add_node(vt.clone()); + idx_to_node.insert(name, node); + } + for (name, vt) in &value.vertex_traits { + for rt in &vt.refined_traits_names { + let node = idx_to_node.get(name).unwrap(); + let refined_node = idx_to_node.get(rt).unwrap(); + graph.add_edge(*node, *refined_node, ()); } } + graph } - closure - } -fn edge_refinement_closure(hierarchy: &TraitHierarchySpec) -> HashMap> { - let mut closure = HashMap::new(); - let traits = hierarchy.edge_traits.keys().collect::>(); - for (name, et) in hierarchy.edge_traits.iter() { - closure.insert(name.clone(), et.refined_traits_names.clone()); - } - for k in &traits { - for i in &traits { - for j in &traits { - let i_has_k = closure.get(*i).map(|vi| vi.contains(*k)).unwrap_or(false); - let k_has_j = closure.get(*k).map(|vk| vk.contains(*j)).unwrap_or(false); - let i_has_j = closure.get(*i).map(|vi| vi.contains(*j)).unwrap_or(false); - if i_has_k && k_has_j && !i_has_j { - if let Some(vi) = closure.get_mut(*i) { - vi.push(j.to_string()); - }; - } +impl From<&TraitHierarchySpec> for EdgeTraitHierachy { + fn from(value: &TraitHierarchySpec) -> Self { + let mut idx_to_node = HashMap::new(); + let mut graph = Graph::new(); + for (name, vt) in &value.edge_traits { + let node = graph.add_node(vt.clone()); + idx_to_node.insert(name, node); + } + for (name, vt) in &value.edge_traits { + for rt in &vt.refined_traits_names { + let node = idx_to_node.get(name).unwrap(); + let refined_node = idx_to_node.get(rt).unwrap(); + graph.add_edge(*node, *refined_node, ()); } } + graph } - closure - } + fn from_canonical_name(canonical_name: &str) -> TokenStream { let splitted = canonical_name.split("::").map(|x| format_ident!("{}", x)); quote! { @@ -149,6 +153,71 @@ fn from_canonical_name(canonical_name: &str) -> TokenStream { } } +impl From<&PropertySpecType> for TokenStream { + fn from(value: &PropertySpecType) -> Self { + match value { + PropertySpecType::IntegerPropertyType { bits, unsigned } => { + if bits <= &8 { + if *unsigned { quote! { u8 } } else { quote! { i8 } } + } else if bits <= &16 { + if *unsigned { quote! { u16 } } else { quote! { i16 } } + } else if bits <= &32 { + if *unsigned { quote! { u32 } } else { quote! { i32 } } + } else if bits <= &64 { + if *unsigned { quote! { u64 } } else { quote! { i64 } } + } else { + if *unsigned { quote! { u128 } } else { quote! { i128 } } + } + }, + PropertySpecType::RealPropertyType { bits } => { + if bits <= &32 { + quote! { f32 } + } else { + quote! { f64 } + } + }, + PropertySpecType::BooleanPropertyType => { + quote! { + bool + } + }, + PropertySpecType::StringPropertyType => { + quote! { + String + } + }, + PropertySpecType::ArrayPropertyType { value_type } => { + let value_type = TokenStream::from(value_type.as_ref()); + quote! { + Vec<#value_type> + } + }, + PropertySpecType::MapPropertyType { key_type, value_type } => { + let key_type = TokenStream::from(key_type.as_ref()); + let value_type = TokenStream::from(value_type.as_ref()); + quote! { + std::collections::HashMap<#key_type, #value_type> + } + } + } + } +} + +impl From<&PropertySpec> for TokenStream { + fn from(value: &PropertySpec) -> Self { + let simple_name = &value.name; + let func_name = format_ident!("get_{}", &value.name); + let return_type = TokenStream::from(&value.property_type); + quote! { + fn #func_name(&self) -> #return_type { + <#return_type>::try_from( + self.get_vertex_properties().get(#simple_name).expect(&format!("Property {} for vertex {} should exist but does not.", #simple_name, self.get_identifier())) + ).expect(&format!("Property {} for vertex {} is of a wrong type.", #simple_name, self.get_identifier())) + } + } + } +} + impl From<&PortSpec> for TokenStream { fn from(value: &PortSpec) -> Self { let simple_name = value.vertex_trait_name.split("::").last().unwrap_or(&value.vertex_trait_name); @@ -243,6 +312,88 @@ impl From<&PortSpec> for TokenStream { } } +fn vertex_code_from_trait_hierarchy(value: &VertexTraitSpec, hierarchy: &VertexTraitHierachy) -> TokenStream { + let canonical_name = &value.canonical_name; + let simple_name = value.canonical_name.split("::").last().unwrap_or(&value.canonical_name); + let viewer_ident = format_ident!("{}Viewer", simple_name); + let trait_ident = format_ident!("Is{}", simple_name); + let mut all_refined: Vec<&VertexTraitSpec> = vec![]; + let value_idx = hierarchy.node_indices().find(|idx| hierarchy[*idx].canonical_name == value.canonical_name).unwrap(); + let mut dfs = Dfs::new(hierarchy, value_idx); + while let Some(nx) = dfs.next(&hierarchy) { + let value = &hierarchy[nx]; + if !all_refined.contains(&value) { + all_refined.push(value); + } + } + all_refined.remove(0); + let refinements = all_refined.iter().map(|rt| { + let simple_name = rt.canonical_name.split("::").last().unwrap_or(&rt.canonical_name); + format_ident!("Is{}", simple_name) + }); + let refinements_implementations = all_refined.iter().map(|rt| { + let simple_name = rt.canonical_name.split("::").last().unwrap_or(&rt.canonical_name); + let i = format_ident!("Is{}", simple_name); + quote! { impl<'view> #i for #viewer_ident<'view> {} } + }).chain( + std::iter::once(quote! { impl<'view> #trait_ident for #viewer_ident<'view> {} }) + ); + let ports_methods = value.required_ports.iter().map(|(_, port_spec)| { + TokenStream::from(port_spec) + }); + let properties_methods = value.required_properties.iter().map(|(_, property_spec)| { + TokenStream::from(property_spec) + }); + let html_documentation = value.html_description.as_ref().map(String::as_str).unwrap_or("") + .replace("@deprecated", "deprecated:") + .replace("

", "") + .replace("

", ""); + let doc_lines = html_documentation.split("\n") + .map(|s| format!(" {}", s.trim())); + let trait_msg = format!(" This struct is the generated vertex viewer for the trait `{}`.", value.canonical_name); + quote! { + + pub trait #trait_ident: forsyde_io_core::VertexViewer #(+ #refinements) * { + #(#ports_methods)* + + #(#properties_methods)* + } + + #(#[doc = #doc_lines])* + #[doc = #trait_msg] + /// Use the function `try_view` to create a viewer for a vertex if it has the trait." + pub struct #viewer_ident<'view> { + pub vertex: &'view forsyde_io_core::Vertex, + pub system_graph: &'view forsyde_io_core::SystemGraph + } + + impl<'view, 'sg: 'view> #viewer_ident<'view> { + pub fn try_view(vertex: &'sg forsyde_io_core::Vertex, system_graph: &'sg forsyde_io_core::SystemGraph) -> Option { + if vertex.traits.iter().any(|t| t.get_name() == #canonical_name) { + let new_vertex_ref = vertex; + let new_system_graph_ref = system_graph; + Some(Self { vertex: new_vertex_ref, system_graph: new_system_graph_ref }) + } else { + None + } + } + + } + + impl<'view> forsyde_io_core::VertexViewer for #viewer_ident<'view> { + fn get_vertex(&self) -> &forsyde_io_core::Vertex { + self.vertex + } + fn get_system_graph(&self) -> &forsyde_io_core::SystemGraph { + self.system_graph + } + } + + #(#refinements_implementations)* + + } +} + impl From<&TraitHierarchySpec> for TokenStream { fn from(hierarchy: &TraitHierarchySpec) -> Self { let hierarchy_module_ident = format_ident!("{}", hierarchy.canonical_name.split("::").last().unwrap_or(&hierarchy.canonical_name.as_str())); @@ -262,9 +413,16 @@ impl From<&TraitHierarchySpec> for TokenStream { #canonical_name => Some(std::sync::Arc::new(crate::#hierarchy_module_ident::VertexTraits::#simple_ident) as std::sync::Arc) } }); - let vertex_refines_closure = vertex_refinement_closure(hierarchy); - let edge_refines_closure = edge_refinement_closure(hierarchy); - let vtrait_refine_match = vertex_refines_closure.iter().map(|(name, refined)| { + let vtraits_hierarchy = VertexTraitHierachy::from(hierarchy); + let etraits_hierarchy = EdgeTraitHierachy::from(hierarchy); + let vtrait_refine_match = hierarchy.vertex_traits.iter().map(|(name, _)| { + let vt_idx = vtraits_hierarchy.node_indices().find(|idx| &vtraits_hierarchy[*idx].canonical_name == name).unwrap(); + let mut dfs = Dfs::new(&vtraits_hierarchy, vt_idx); + let mut refined = vec![]; + while let Some(nx) = dfs.next(&vtraits_hierarchy) { + let value = &vtraits_hierarchy[nx]; + refined.push(&value.canonical_name); + } let this_match = if refined.len() > 0 { quote! { match other.get_name() { #(#refined => true),*, @@ -280,7 +438,14 @@ impl From<&TraitHierarchySpec> for TokenStream { #name => #this_match } }); - let etrait_refines_match = edge_refines_closure.iter().map(|(name, refined)| { + let etrait_refines_match = hierarchy.edge_traits.iter().map(|(name, _)| { + let et_idx = etraits_hierarchy.node_indices().find(|idx| &etraits_hierarchy[*idx].canonical_name == name).unwrap(); + let mut dfs = Dfs::new(&etraits_hierarchy, et_idx); + let mut refined = vec![]; + while let Some(nx) = dfs.next(&etraits_hierarchy) { + let value = &etraits_hierarchy[nx]; + refined.push(&value.canonical_name); + } let this_match = if refined.len() > 0 { quote! { match other.get_name() { #(#refined => true),*, @@ -310,77 +475,8 @@ impl From<&TraitHierarchySpec> for TokenStream { #canonical_name => Some(std::sync::Arc::new(crate::#hierarchy_module_ident::EdgeTraits::#simple_ident) as std::sync::Arc) } }); - let vtraits_methods = hierarchy.vertex_traits.iter().map(|(canonical_name,vtrait)| { - let simple_name = canonical_name.split("::").last().unwrap_or(&canonical_name); - let simple_ident = format_ident!("Is{}", simple_name); - let refinements = vtrait.refined_traits_names.iter().map(|rt| { - let simple_name = rt.split("::").last().unwrap_or(&rt); - format_ident!("Is{}", simple_name) - }); - let ports_methods = vtrait.required_ports.iter().map(|(port_name, port_spec)| { - let port_code: TokenStream = port_spec.into(); - quote! { - #port_code - } - }); - quote! { - pub trait #simple_ident: forsyde_io_core::VertexViewer #(+ #refinements) * { - #(#ports_methods)* - } - } - }); - let vtraits_viewers = hierarchy.vertex_traits.iter().map(|(canonical_name,vt)| { - let simple_name = canonical_name.split("::").last().unwrap_or(&canonical_name); - let simple_ident = format_ident!("{}Viewer", simple_name); - let trait_ident = format_ident!("Is{}", simple_name); - let refinements = vertex_refines_closure.get(canonical_name).unwrap().iter().map(|rt| { - let simple_name = rt.split("::").last().unwrap_or(&rt); - let i = format_ident!("Is{}", simple_name); - quote! { impl<'view> #i for #simple_ident<'view> {} } - }).chain( - std::iter::once(quote! { impl<'view> #trait_ident for #simple_ident<'view> {} }) - ); - let html_documentation = vt.html_description.as_ref().map(String::as_str).unwrap_or("") - .replace("@deprecated", "deprecated:") - .replace("

", "") - .replace("

", ""); - let doc_lines = html_documentation.split("\n") - .map(|s| format!(" {}", s.trim())); - let trait_msg = format!(" This struct is the generated vertex viewer for the trait `{}`.", canonical_name); - quote! { - - #(#[doc = #doc_lines])* - #[doc = #trait_msg] - /// Use the function `try_view` to create a viewer for a vertex if it has the trait." - pub struct #simple_ident<'view> { - pub vertex: &'view forsyde_io_core::Vertex, - pub system_graph: &'view forsyde_io_core::SystemGraph - } - - impl<'view, 'sg: 'view> #simple_ident<'view> { - pub fn try_view(vertex: &'sg forsyde_io_core::Vertex, system_graph: &'sg forsyde_io_core::SystemGraph) -> Option { - if vertex.traits.iter().any(|t| t.get_name() == #canonical_name) { - let new_vertex_ref = vertex; - let new_system_graph_ref = system_graph; - Some(Self { vertex: new_vertex_ref, system_graph: new_system_graph_ref }) - } else { - None - } - } - } - - impl<'view> forsyde_io_core::VertexViewer for #simple_ident<'view> { - fn get_vertex(&self) -> &forsyde_io_core::Vertex { - self.vertex - } - fn get_system_graph(&self) -> &forsyde_io_core::SystemGraph { - self.system_graph - } - } - - #(#refinements)* - - } + let vtraits_code = hierarchy.vertex_traits.iter().map(|(_,vtrait)| { + vertex_code_from_trait_hierarchy(vtrait, &vtraits_hierarchy) }); let code = quote! { // Automatically generated code by forsyde-io-generator: DO NOT EDIT MANUALLY @@ -389,6 +485,7 @@ impl From<&TraitHierarchySpec> for TokenStream { pub mod #hierarchy_module_ident { use petgraph; use petgraph::visit::EdgeRef; + use forsyde_io_core; #[allow(dead_code)] enum VertexTraits { @@ -448,9 +545,7 @@ impl From<&TraitHierarchySpec> for TokenStream { } } - #(#vtraits_methods)* - - #(#vtraits_viewers)* + #(#vtraits_code)* } }; code diff --git a/rust-libforsyde/src/lib.rs b/rust-libforsyde/src/lib.rs index 70e4fb6d..50133d30 100644 --- a/rust-libforsyde/src/lib.rs +++ b/rust-libforsyde/src/lib.rs @@ -26,7 +26,7 @@ mod tests { println!("Actor {} is an SDF actor", viewer.get_identifier()); } if let Some(viewer) = ForSyDeHierarchy::SDFChannelViewer::try_view(v, &system_graph) { - println!("Channel {} is an SDF channel", viewer.get_identifier()); + println!("Channel {} is an SDF channel with {} initial tokens", viewer.get_identifier(), viewer.get_numInitialTokens()); if let Some(p) = viewer.get_producer() { println!("Producer: {}", p.get_identifier()); }