use crate::*; use crate::syntax::block::{Item, Emittable}; use crate::syntax::block::constructors::*; use crate::gen::schema::*; use preserves::value::Set; use super::context::BundleContext; use super::context::ModuleContext; use super::context::ModuleContextMode; use super::context::RefRenderStyle; use super::names; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct TDefinition { pub self_ref: Ref, pub body: TDefinitionBody, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub enum TDefinitionBody { Union(Vec<(String, TSimple)>), Simple(TSimple), } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub enum TSimple { Field(TField), Record(TRecord), } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub enum TField { Unit, Any, Embedded, Array(Box), Set(Box), Map(Box, Box), Ref(Ref), Base(String), } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct TRecord(pub Vec<(String, TField)>); #[derive(Debug)] pub struct TypePlugin; impl compiler::Plugin for TypePlugin { fn generate_module(&self, m: &mut ModuleContext) { if let EmbeddedTypeName::Ref(r) = &m.schema.embedded_type { m.define_type(item(vertical(false, seq![ seq!["pub type _Dom = ", m.render_ref(&*r, RefRenderStyle::Bare), ";"], seq!["pub type _Ptr = std::sync::Arc<_Dom>;"], seq!["pub type _Any = preserves::value::ArcValue<_Ptr>;"] ]))); } } fn generate_definition(&self, m: &mut ModuleContext, n: &str, d: &Definition) { if let ModuleContextMode::TargetGeneric = m.mode { let ty = definition_type(&m.module_path, n, d); m.define_type(item(ty.render(m, n))); m.define_type(item(seq![ "impl", ty.generic_decl(m), " preserves::value::Domain for ", names::render_constructor(n), ty.generic_arg(m), " {}"])); } } } pub fn definition_type(module: &ModulePath, n: &str, d: &Definition) -> TDefinition { TDefinition { self_ref: Ref { module: module.clone(), name: n.to_owned() }, body: match d { Definition::Or { pattern_0, pattern_1, pattern_n } => TDefinitionBody::Union(or_definition_type(pattern_0, pattern_1, pattern_n)), Definition::And { pattern_0, pattern_1, pattern_n } => TDefinitionBody::Simple(and_definition_type(pattern_0, pattern_1, pattern_n)), Definition::Pattern(p) => TDefinitionBody::Simple(pattern_type(p)), } } } pub fn or_definition_type( p0: &NamedAlternative, p1: &NamedAlternative, pn: &Vec, ) -> Vec<(String, TSimple)> { let mut entries = Vec::new(); entries.push((p0.variant_label.to_owned(), pattern_type(&p0.pattern))); entries.push((p1.variant_label.to_owned(), pattern_type(&p1.pattern))); for e in pn { entries.push((e.variant_label.to_owned(), pattern_type(&e.pattern))); } entries } pub fn and_definition_type( p0: &NamedPattern, p1: &NamedPattern, pn: &Vec, ) -> TSimple { let mut arms = vec![p0, p1]; arms.extend(pn); record_type(&arms) } pub fn pattern_type(p: &Pattern) -> TSimple { match p { Pattern::SimplePattern(p) => TSimple::Field(field_type(p)), Pattern::CompoundPattern(_) => record_type(&vec![&NamedPattern::Anonymous(Box::new(p.clone()))]), } } pub fn record_type(ps: &Vec<&NamedPattern>) -> TSimple { let fs = gather_fields(ps, Vec::new()); if fs.is_empty() { TSimple::Field(TField::Unit) } else { TSimple::Record(TRecord(fs)) } } pub fn gather_fields(ps: &Vec<&NamedPattern>, mut fs: Vec<(String, TField)>) -> Vec<(String, TField)> { for p in ps.iter() { fs = gather_field(p, fs); } fs } pub fn gather_field(p: &NamedPattern, mut fs: Vec<(String, TField)>) -> Vec<(String, TField)> { match p { NamedPattern::Named(b) => { let Binding { name, pattern } = &**b; fs.push((name.to_owned(), field_type(pattern))); fs }, NamedPattern::Anonymous(p) => match &**p { Pattern::SimplePattern(_) => fs, Pattern::CompoundPattern(c) => match &**c { CompoundPattern::Rec { label, fields } => gather_field(&*fields, gather_field(&*label, fs)), CompoundPattern::Tuple { patterns } => gather_fields(&patterns.iter().collect(), fs), CompoundPattern::TuplePrefix { fixed, variable } => gather_field(&promote(&**variable), gather_fields(&fixed.iter().collect(), fs)), CompoundPattern::Dict { entries } => { for (_k, p) in &entries.0 { fs = gather_field(&promote(&p), fs); } fs } } } } } pub fn promote(p: &NamedSimplePattern) -> NamedPattern { match p { NamedSimplePattern::Anonymous(p) => NamedPattern::Anonymous(Box::new(Pattern::SimplePattern(p.clone()))), NamedSimplePattern::Named(n) => NamedPattern::Named(n.clone()), } } pub fn field_type(p: &SimplePattern) -> TField { match p { SimplePattern::Any => TField::Any, SimplePattern::Atom { atom_kind: k } => match **k { AtomKind::Boolean => TField::Base("bool".to_owned()), AtomKind::Float => TField::Base("preserves::value::Float".to_owned()), AtomKind::Double => TField::Base("preserves::value::Double".to_owned()), AtomKind::SignedInteger => TField::Base("preserves::value::signed_integer::SignedInteger".to_owned()), AtomKind::String => TField::Base("std::string::String".to_owned()), AtomKind::ByteString => TField::Base("std::vec::Vec".to_owned()), AtomKind::Symbol => TField::Base("std::string::String".to_owned()), }, SimplePattern::Embedded { .. } => TField::Embedded, SimplePattern::Lit { .. } => TField::Unit, SimplePattern::Seqof { pattern: t } => TField::Array(Box::new(field_type(t))), SimplePattern::Setof { pattern: t } => TField::Set(Box::new(field_type(t))), SimplePattern::Dictof { key: k, value: v } => TField::Map(Box::new(field_type(k)), Box::new(field_type(v))), SimplePattern::Ref(r) => TField::Ref((**r).clone()), } } impl TField { fn render(&self, ctxt: &ModuleContext, box_needed: bool) -> impl Emittable { match self { TField::Unit => seq!["()"], TField::Any => seq!["_Value"], TField::Embedded => seq!["_Value::Embedded"], TField::Array(t) => seq!["std::vec::Vec<", t.render(ctxt, false), ">"], TField::Set(t) => seq!["preserves::value::Set<", t.render(ctxt, false), ">"], TField::Map(k, v) => seq!["preserves::value::Map", anglebrackets![k.render(ctxt, false), v.render(ctxt, false)]], TField::Ref(r) => if box_needed { seq!["std::boxed::Box", anglebrackets![ ctxt.render_ref(r, RefRenderStyle::Qualified)]] } else { seq![ctxt.render_ref(r, RefRenderStyle::Qualified)] }, TField::Base(n) => seq![n.to_owned()], } } pub fn has_embedded(&self, default_module_path: &ModulePath, ctxt: &BundleContext, seen: &mut Set) -> bool { match self { TField::Unit => false, TField::Any => true, // at least potentially true TField::Embedded => true, TField::Array(f) => f.has_embedded(default_module_path, ctxt, seen), TField::Set(f) => f.has_embedded(default_module_path, ctxt, seen), TField::Map(k, v) => k.has_embedded(default_module_path, ctxt, seen) || v.has_embedded(default_module_path, ctxt, seen), TField::Ref(r) => { let r = r.qualify(default_module_path); if seen.contains(&r) { false } else { seen.insert(r.clone()); ctxt.type_for_name(&r).map(|ty| ty._has_embedded(ctxt, seen)).unwrap_or(false) // ^ TODO: should the "false" be configurable? // cf. ModuleContext::ref_has_embedded. } } TField::Base(_) => false, } } } impl TSimple { pub fn render(&self, ctxt: &ModuleContext, ptr: Item, is_struct: bool, n: &str) -> impl Emittable { let semi = if is_struct { seq![";"] } else { seq![] }; let ppub = if is_struct { "pub " } else { "" }; seq![names::render_constructor(n), ptr.to_owned(), match self { TSimple::Record(TRecord(fs)) => seq![" ", vertical(false, braces( fs.iter().map(|(n, d)| item( seq![ppub, names::render_fieldname(n), ": ", d.render(ctxt, !is_struct)] )).collect()))], TSimple::Field(TField::Unit) => semi, TSimple::Field(t) => seq![parens![seq![ppub, t.render(ctxt, !is_struct)]], semi], }] } pub fn has_embedded(&self, default_module_path: &ModulePath, ctxt: &BundleContext, seen: &mut Set) -> bool { match self { TSimple::Field(f) => f.has_embedded(default_module_path, ctxt, seen), TSimple::Record(TRecord(fs)) => fs.iter().any(|(_k, v)| v.has_embedded(default_module_path, ctxt, seen)), } } } impl TDefinition { pub fn generic_decl(&self, ctxt: &ModuleContext) -> Item { if self.has_embedded(ctxt.bundle) { ctxt.generic_decls() } else { item("") } } pub fn generic_decl_with_defaults(&self, ctxt: &ModuleContext) -> Item { if self.has_embedded(ctxt.bundle) { ctxt.generic_decls_with_defaults() } else { item("") } } pub fn generic_arg(&self, ctxt: &ModuleContext) -> Item { if self.has_embedded(ctxt.bundle) { ctxt.generic_arg() } else { item("") } } pub fn render(&self, ctxt: &ModuleContext, n: &str) -> impl Emittable { vertical(false, seq![ "#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Hash)]", match &self.body { TDefinitionBody::Union(items) => seq!["pub enum ", names::render_constructor(n), self.generic_decl_with_defaults(ctxt), " ", vertical(false, braces(items.iter().map( |(n, d)| item(d.render(ctxt, item(""), false, n))).collect()))], TDefinitionBody::Simple(s) => seq!["pub struct ", s.render(ctxt, self.generic_decl_with_defaults(ctxt), true, n)], }]) } pub fn has_embedded(&self, ctxt: &BundleContext) -> bool { self._has_embedded(ctxt, &mut Set::new()) } fn _has_embedded(&self, ctxt: &BundleContext, seen: &mut Set) -> bool { match &self.body { TDefinitionBody::Union(entries) => entries.iter().any(|(_k, v)| v.has_embedded(&self.self_ref.module, ctxt, seen)), TDefinitionBody::Simple(t) => t.has_embedded(&self.self_ref.module, ctxt, seen), } } }