preserves/implementations/rust/preserves-schema/src/compiler/types.rs

319 lines
12 KiB
Rust

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<TField>),
Set(Box<TField>),
Map(Box<TField>, Box<TField>),
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<NamedAlternative>,
) -> 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<NamedPattern>,
) -> 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<u8>".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::D"],
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<Ref>) -> 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<Ref>) -> 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<Ref>) -> 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),
}
}
}