462 lines
15 KiB
Rust
462 lines
15 KiB
Rust
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<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, 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<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::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()),
|
|
}
|
|
}
|
|
|
|
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<String>) {
|
|
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<String>) {
|
|
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<String> {
|
|
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<String>) {
|
|
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),
|
|
}
|
|
}
|
|
}
|