// LeapType // LeapEnum // Name - StrongString - length, allowed symbols // todo: rename file to leapspec.rs? file per struct/enum? // todo: checks - enum variants should have unique names (eg. if need multiple variants of same type wrap in struct first) // todo: only allow structs to be variants of enum // todo: checks - type args should be unique relative to struct and enum names, same type arg names can be used in different types use crate::handle::Handle; use crate::naming; use crate::parser::position::Position; use crate::prop_recursion_check::PropRecursionCheck; use std::collections::HashMap; use std::fmt; use std::hash::{Hash, Hasher}; // todo: trait Name to String #[derive(Debug, Clone)] pub struct Name { name: String, alias: Option, pub position: Position, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum SimpleType { String, Integer, Float, Boolean, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ValueType { Simple(SimpleType), List(Box), TypeArg(Name), LeapType { name: Name, args: Vec }, } // todo: rename -> Property #[derive(Debug)] pub struct Prop { pub name: Name, pub prop_type: ValueType, pub position: Position, pub is_recursive: bool, } #[derive(Debug)] pub struct LeapStruct { pub name: Name, pub args: Vec, pub props: Vec, pub path: String, pub position: Position, } #[derive(Debug)] pub struct LeapEnum { pub name: Name, pub args: Vec, pub variants: Vec, pub path: String, pub position: Position, } #[derive(Debug)] pub enum LeapType { Struct(LeapStruct), Enum(LeapEnum), } pub type LeapTypeHandle = Handle; #[derive(Debug)] pub struct LeapSpec { types: Vec, name_to_type: HashMap, } #[derive(Debug)] pub struct Comment { pub comment: String, pub comment_type: CommentType, pub position: Position, } #[derive(Debug, Clone, Copy, PartialEq)] pub enum CommentType { // comment takes full line Line, // empty line Separator, // comment follows some code on the same line Trail, } fn aliased_from_aliases(name: &Name, aliases: &HashMap) -> Result { name.to_aliased_if_some(aliases.get(name.get()).cloned()) } impl Name { // todo: accept any name here? check names later, to simplify parsing (name can return error in parsing code) pub fn new(name: String, position: Position) -> Result { // todo: checks // - min, max length? // - allowed symbols? only control use of delimiter `-` (eg. can be only in the middle, no repeating)? // - allowed start symbol? // - allowed end symbol? Ok(Name { name, alias: None, position, }) } pub fn to_aliased(&self, alias: String) -> Result { // todo: check alias for same `name` rules // todo: when adding alias check there is no same name/alias, global type scoped, property names scoped, separate aliases for Types and for Props? Ok(Name { alias: Some(alias), ..self.clone() }) } pub fn to_aliased_if_some(&self, alias: Option) -> Result { if let Some(a) = alias { self.to_aliased(a) } else { Ok(Self::new(self.name.clone(), self.position)?) } } pub fn get(&self) -> &str { &self.name } fn get_aliased(&self) -> &str { if let Some(alias) = &self.alias { alias } else { &self.name } } pub fn apply_style(&self, style: naming::WritingStyle, separator: &str) -> String { naming::apply_style(style, separator, &naming::get_parts(self.get_aliased())) } } impl fmt::Display for Name { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.name) } } impl PartialEq for Name { fn eq(&self, other: &Self) -> bool { self.name == other.name } } impl Eq for Name {} impl Ord for Name { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.name.cmp(&other.name) } } impl PartialOrd for Name { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Hash for Name { fn hash(&self, state: &mut H) { self.name.hash(state); } } impl SimpleType { pub fn name(&self) -> String { match self { // todo: replace strings with constants SimpleType::String => "str".to_owned(), SimpleType::Integer => "int".to_owned(), SimpleType::Float => "float".to_owned(), SimpleType::Boolean => "bool".to_owned(), } } } impl fmt::Display for ValueType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Simple(t) => match t { SimpleType::String => write!(f, "str"), SimpleType::Integer => write!(f, "int"), SimpleType::Float => write!(f, "float"), SimpleType::Boolean => write!(f, "bool"), }, Self::List(t) => write!(f, "list[{}]", t), Self::TypeArg(n) => write!(f, "{}?", n), Self::LeapType { name, args } => { if args.is_empty() { write!(f, "{}", name) } else { let args = args .iter() .map(|a| format!("{}", a)) .collect::>() .join(" "); write!(f, "{}[{}]", name, args) } } } } } impl ValueType { pub fn to_aliased(&self, aliases: &HashMap) -> Result { match self { Self::List(t) => Ok(Self::List(Box::new(t.to_aliased(aliases)?))), Self::TypeArg(n) => Ok(Self::TypeArg(aliased_from_aliases(n, aliases)?)), Self::LeapType { name, args } => Ok(Self::LeapType { name: aliased_from_aliases(name, aliases)?, args: args .iter() .map(|a| a.to_aliased(aliases)) .collect::>()?, }), _ => Ok(self.clone()), } } pub fn name(&self) -> String { match self { Self::Simple(t) => t.name(), Self::List(_) => "list".to_owned(), Self::TypeArg(n) => n.get().to_owned(), Self::LeapType { name, .. } => name.get().to_owned(), } } pub fn args(&self) -> Vec { match self { Self::Simple(_) | Self::TypeArg(_) => vec![], Self::List(t) => vec![t.as_ref().clone()], Self::LeapType { args, .. } => args.clone(), } } pub fn apply_args(&self, applied_args: &HashMap<&Name, &ValueType>) -> Self { match self { Self::Simple(_) => self.clone(), Self::List(t) => Self::List(Box::new(t.apply_args(applied_args))), Self::TypeArg(name) => (*applied_args.get(name).unwrap()).clone(), Self::LeapType { name, args } => Self::LeapType { name: name.clone(), args: args.iter().map(|a| a.apply_args(applied_args)).collect(), }, } } } impl fmt::Display for Prop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}: {}", self.name, self.prop_type) } } impl Prop { pub fn to_aliased(&self, aliases: &HashMap) -> Result { Ok(Self { name: aliased_from_aliases(&self.name, aliases)?, prop_type: self.prop_type.to_aliased(aliases)?, position: self.position, is_recursive: self.is_recursive, }) } pub fn apply_args(&self, applied_args: &HashMap<&Name, &ValueType>) -> Self { Self { name: self.name.clone(), prop_type: self.prop_type.apply_args(applied_args), position: self.position, is_recursive: self.is_recursive, } } } impl fmt::Display for LeapStruct { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let args = self .args .iter() .map(|a| a.get()) .collect::>() .join(" "); let props = self .props .iter() .map(|p| format!("{}", p)) .collect::>() .join(", "); write!( f, "Struct({} args: [{}], props: [{}])", self.name, args, props ) } } impl LeapStruct { pub fn to_aliased(&self, aliases: &HashMap) -> Result { Ok(Self { name: aliased_from_aliases(&self.name, aliases)?, args: self .args .iter() .map(|a| aliased_from_aliases(a, aliases)) .collect::>()?, props: self .props .iter() .map(|p| p.to_aliased(aliases)) .collect::>()?, path: self.path.clone(), position: self.position, }) } pub fn map_args<'a>(&'a self, applied_args: &'a [ValueType]) -> HashMap<&Name, &ValueType> { let mut args_map = HashMap::new(); for (i, name) in self.args.iter().enumerate() { // applied_args should have same length as self.args args_map.insert(name, &applied_args[i]); } args_map } pub fn apply_args(&self, applied_args: &HashMap<&Name, &ValueType>) -> Self { Self { name: self.name.clone(), // as type args was applied there is no type args any more args: vec![], props: self .props .iter() .map(|p| p.apply_args(applied_args)) .collect(), path: self.path.clone(), position: self.position, } } pub fn expand_args(&self, applied_args: &HashMap<&Name, &ValueType>) -> Vec { self.args .iter() .map(|a| (*applied_args.get(a).unwrap()).clone()) .collect() } } impl fmt::Display for LeapEnum { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let args = self .args .iter() .map(|a| a.get()) .collect::>() .join(" "); let variants = self .variants .iter() .map(|v| format!("{}", v)) .collect::>() .join(", "); write!( f, "Enum({} args: [{}], variants: [{}])", self.name, args, variants ) } } impl LeapEnum { pub fn to_aliased(&self, aliases: &HashMap) -> Result { Ok(Self { name: aliased_from_aliases(&self.name, aliases)?, args: self .args .iter() .map(|a| aliased_from_aliases(a, aliases)) .collect::>()?, variants: self .variants .iter() .map(|v| v.to_aliased(aliases)) .collect::>()?, path: self.path.clone(), position: self.position, }) } pub fn expand_args(&self, applied_args: &HashMap<&Name, &ValueType>) -> Vec { self.args .iter() .map(|a| (*applied_args.get(a).unwrap()).clone()) .collect() } pub fn map_args<'a>(&'a self, applied_args: &'a [ValueType]) -> HashMap<&Name, &ValueType> { let mut args_map = HashMap::new(); for (i, name) in self.args.iter().enumerate() { // applied_args should have same length as self.args args_map.insert(name, &applied_args[i]); } args_map } pub fn apply_args(&self, applied_args: &HashMap<&Name, &ValueType>) -> Self { Self { name: self.name.clone(), // as type args was applied there is no type args any more args: vec![], variants: self .variants .iter() .map(|v| v.apply_args(applied_args)) .collect(), path: self.path.clone(), position: self.position, } } } impl fmt::Display for LeapType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Struct(t) => write!(f, "Type({})", t), Self::Enum(e) => write!(f, "Type({})", e), } } } impl LeapType { pub fn as_struct(&self) -> Option<&LeapStruct> { if let LeapType::Struct(s) = self { Some(s) } else { None } } pub fn as_enum(&self) -> Option<&LeapEnum> { if let LeapType::Enum(e) = self { Some(e) } else { None } } pub fn is_struct(&self) -> bool { matches!(self, LeapType::Struct(_)) } pub fn is_enum(&self) -> bool { matches!(self, LeapType::Enum(_)) } pub fn to_aliased(&self, aliases: &HashMap) -> Result { Ok(match self { Self::Struct(s) => Self::Struct(s.to_aliased(aliases)?), Self::Enum(e) => Self::Enum(e.to_aliased(aliases)?), }) } pub fn name(&self) -> &Name { match self { Self::Enum(e) => &e.name, Self::Struct(s) => &s.name, } } pub fn args(&self) -> &[Name] { match self { Self::Enum(e) => &e.args, Self::Struct(s) => &s.args, } } pub fn path(&self) -> &str { match self { Self::Enum(e) => &e.path, Self::Struct(s) => &s.path, } } pub fn set_path(&mut self, path: String) { match self { Self::Enum(e) => e.path = path, Self::Struct(s) => s.path = path, } } pub fn position(&self) -> &Position { match self { Self::Enum(e) => &e.position, Self::Struct(s) => &s.position, } } pub fn expand_args(&self, applied_args: &HashMap<&Name, &ValueType>) -> Vec { match self { Self::Enum(e) => e.expand_args(applied_args), Self::Struct(s) => s.expand_args(applied_args), } } pub fn apply_args(&self, args: &[ValueType]) -> Self { match self { LeapType::Struct(s) => LeapType::Struct(s.apply_args(&s.map_args(args))), LeapType::Enum(e) => LeapType::Enum(e.apply_args(&e.map_args(args))), } } } impl fmt::Display for LeapSpec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Spec({})", self.types .iter() .map(|t| format!("{}", t)) .collect::>() .join(", ") ) } } impl IntoIterator for LeapSpec { type Item = LeapType; type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.types.into_iter() } } impl LeapSpec { pub fn new(types: Vec) -> Self { let mut spec = Self { types: vec![], name_to_type: HashMap::new(), }; for leap_type in types.into_iter() { spec.push_type(leap_type); } spec } fn push_type(&mut self, leap_type: LeapType) { let name = leap_type.name().get().to_owned(); self.types.push(leap_type); self.name_to_type .insert(name, LeapTypeHandle::new((self.types.len() - 1) as u32)); } pub fn iter_type_refs(&self) -> impl Iterator { self.types.iter() } pub fn iter_types(&self) -> impl Iterator { (0..self.types.len()).map(|i| LeapTypeHandle::new(i as u32)) } pub fn join(&mut self, other: LeapSpec) { // todo: consume self, and return new spec? so new spec always created with `new` for leap_type in other.into_iter() { self.push_type(leap_type); } } pub fn get_type_ref(&self, handle: LeapTypeHandle) -> &LeapType { &self.types[handle.as_index()] } pub fn get_type_mut(&mut self, handle: LeapTypeHandle) -> &mut LeapType { &mut self.types[handle.as_index()] } pub fn get_handle_by_name(&self, name: &str) -> Option { self.name_to_type.get(name).copied() } pub fn get_type_by_name(&self, name: &str) -> Option<&LeapType> { self.get_handle_by_name(name).map(|h| self.get_type_ref(h)) } pub fn is_struct_name(&self, name: &str) -> bool { if let Some(t) = self.get_type_by_name(name) { t.is_struct() } else { false } } pub fn is_enum_name(&self, name: &str) -> bool { if let Some(t) = self.get_type_by_name(name) { t.is_enum() } else { false } } pub fn to_aliased(&self, aliases: &HashMap) -> Result { Ok(Self::new( self.types .iter() .map(|t| t.to_aliased(aliases)) .collect::>()?, )) } pub fn mark_recursive_props(&mut self) { for h in self.iter_types() { let mut recursive_props = vec![]; let t = self.get_type_ref(h); match t { LeapType::Struct(s) => { for (i, p) in s.props.iter().enumerate() { if PropRecursionCheck::is_recursive(self, t, p) { recursive_props.push(i); } } } LeapType::Enum(e) => { for (i, v) in e.variants.iter().enumerate() { if PropRecursionCheck::is_recursive(self, t, v) { recursive_props.push(i); } } } } if !recursive_props.is_empty() { let props = match self.get_type_mut(h) { LeapType::Struct(s) => &mut s.props, LeapType::Enum(e) => &mut e.variants, }; for i in recursive_props { props[i].is_recursive = true; } } } } } #[cfg(test)] mod test { use crate::parser::parser::Parser; use super::*; #[test] fn test_simple() { let spec_text = " .struct s1 a: s2 b: s3 .struct s2 a: s1 .struct s3 a: str "; let mut spec = LeapSpec::new(Parser::parse(spec_text).unwrap()); spec.mark_recursive_props(); let s = spec.get_type_by_name("s1").unwrap().as_struct().unwrap(); assert!(s.props[0].is_recursive); assert!(!s.props[1].is_recursive); } }