use crate::gen::schema::*; use crate::syntax::block::constructors::*; use crate::syntax::block::{Emittable, Item}; use crate::*; 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, Clone, Copy, PartialOrd, Ord)] pub enum Purpose { Codegen, Xref, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct TDefinition { pub purpose: Purpose, 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, Purpose::Codegen, 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, purpose: Purpose, n: &str, d: &Definition, ) -> TDefinition { TDefinition { purpose, 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::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()), } } type WalkState<'a, 'b> = super::cycles::WalkState<&'a BundleContext<'b>>; impl TField { fn render(&self, ctxt: &ModuleContext, box_needed: bool) -> impl Emittable { match self { TField::Unit => seq!["()"], TField::Any => seq![ctxt.any_type()], TField::Embedded => seq![ctxt.any_type(), "::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()], } } fn language_types(&self, s: &mut WalkState, ts: &mut Set) { match self { TField::Unit | TField::Any | TField::Embedded | TField::Base(_) => (), TField::Array(f) => f.language_types(s, ts), TField::Set(f) => f.language_types(s, ts), TField::Map(k, v) => { k.language_types(s, ts); v.language_types(s, ts); } TField::Ref(r) => s.cycle_check( r, |ctxt, r| ctxt.type_for_name(r), |s, t| match t { Some(ty) if ty.purpose == Purpose::Codegen => ty._language_types(s, ts), Some(_) | None => { let xmts = &s .context .config .external_modules .get(&r.module.0) .unwrap() .rust_language_types; if let Some(f) = xmts.definitions.get(&r.name).or(xmts.fallback.as_ref()) { ts.extend(f(s.context.any_type())); } } }, || (), ), } } fn has_embedded(&self, s: &mut WalkState) -> bool { match self { TField::Unit | TField::Base(_) => false, TField::Any | TField::Embedded => true, // at least potentially true TField::Array(f) => f.has_embedded(s), TField::Set(f) => f.has_embedded(s), TField::Map(k, v) => k.has_embedded(s) || v.has_embedded(s), TField::Ref(r) => // v TODO: should the "false" be configurable? cf. ModuleContext::ref_has_embedded. { s.cycle_check( r, |ctxt, r| ctxt.type_for_name(r), |s, t| t.map(|t| t._has_embedded(s)).unwrap_or(false), || 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], } ] } fn language_types(&self, s: &mut WalkState, ts: &mut Set) { match self { TSimple::Field(f) => f.language_types(s, ts), TSimple::Record(TRecord(fs)) => fs.iter().for_each(|(_k, v)| v.language_types(s, ts)), } } fn has_embedded(&self, s: &mut WalkState) -> bool { match self { TSimple::Field(f) => f.has_embedded(s), TSimple::Record(TRecord(fs)) => fs.iter().any(|(_k, v)| v.has_embedded(s)), } } } impl TDefinition { pub fn generic_decl(&self, ctxt: &ModuleContext) -> Item { if self.has_embedded(ctxt.bundle) { item(anglebrackets![seq![ ctxt.any_type(), ": preserves::value::NestedValue" ]]) } else { item("") } } pub fn generic_decl_with_defaults(&self, ctxt: &ModuleContext) -> Item { if self.has_embedded(ctxt.bundle) { item(anglebrackets![seq![ ctxt.any_type(), ": preserves::value::NestedValue = ", match ctxt.schema.embedded_type { EmbeddedTypeName::False => "preserves::value::IOValue", EmbeddedTypeName::Ref(_) => "_Any", } ]]) } else { item("") } } pub fn generic_arg(&self, ctxt: &ModuleContext) -> Item { if self.has_embedded(ctxt.bundle) { item(anglebrackets![ctxt.any_type()]) } 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) ], } ], ) } fn walk_state<'a, 'b>(&self, ctxt: &'a BundleContext<'b>) -> WalkState<'a, 'b> { WalkState::new(ctxt, self.self_ref.module.clone()) } pub fn language_types(&self, ctxt: &BundleContext) -> Set { let mut ts = Set::new(); self._language_types(&mut self.walk_state(ctxt), &mut ts); ts } fn _language_types(&self, s: &mut WalkState, ts: &mut Set) { match &self.body { TDefinitionBody::Union(entries) => { entries.iter().for_each(|(_k, v)| v.language_types(s, ts)) } TDefinitionBody::Simple(t) => t.language_types(s, ts), } } pub fn has_embedded(&self, ctxt: &BundleContext) -> bool { self._has_embedded(&mut self.walk_state(ctxt)) } fn _has_embedded(&self, s: &mut WalkState) -> bool { match &self.body { TDefinitionBody::Union(entries) => entries.iter().any(|(_k, v)| v.has_embedded(s)), TDefinitionBody::Simple(t) => t.has_embedded(s), } } }