preserves-schema 5.996.0

Implementation of Preserves Schema code generation and support for Rust.
Documentation
//! Interpreter for instances of Preserves Schema Metaschema, for schema-directed dynamic
//! parsing and unparsing of terms.

use crate::gen::schema::*;

use preserves::value::merge::merge2;
use preserves::value::Map;
use preserves::value::NestedValue;
use preserves::value::Value;

/// Represents an environment mapping schema module names to [Schema] instances.
pub type Env<V> = Map<Vec<String>, Schema<V>>;

/// Context for a given interpretation of a [Schema].
#[derive(Debug)]
pub struct Context<'a, V: NestedValue> {
    pub env: &'a Env<V>,
    module: Vec<String>,
}

#[derive(Debug)]
enum DynField<V: NestedValue> {
    Simple(V),
    Compound(Map<V, V>),
}

impl<'a, V: NestedValue> Context<'a, V> {
    /// Construct a new [Context] with the given [Env].
    pub fn new(env: &'a Env<V>) -> Self {
        Context {
            env,
            module: Vec::new(),
        }
    }

    /// Parse `v` using the rule named `name` from the module at path `module` in `self.env`.
    /// Yields `Some(...)` if the parse succeeds, and `None` otherwise.
    pub fn dynamic_parse(&mut self, module: &Vec<String>, name: &str, v: &V) -> Option<V> {
        let old_module =
            (module.len() > 0).then(|| std::mem::replace(&mut self.module, module.clone()));
        let schema = self.env.get(&self.module);
        let definition = schema.and_then(|s| s.definitions.0.get(name));
        let result = definition.and_then(|d| d.dynamic_parse(self, v));
        if let Some(m) = old_module {
            self.module = m;
        }
        result
    }

    #[doc(hidden)]
    pub fn dynamic_unparse(&mut self, _module: &Vec<String>, _name: &str, _w: &V) -> Option<V> {
        panic!("Not yet implemented");
    }
}

impl<V: NestedValue> Definition<V> {
    fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<V> {
        match self {
            Definition::Or {
                pattern_0,
                pattern_1,
                pattern_n,
            } => pattern_0
                .dynamic_parse(ctxt, v)
                .or_else(|| pattern_1.dynamic_parse(ctxt, v))
                .or_else(|| pattern_n.iter().find_map(|p| p.dynamic_parse(ctxt, v))),
            Definition::And {
                pattern_0,
                pattern_1,
                pattern_n,
            } => pattern_0
                .dynamic_parse(ctxt, v)
                .and_then(|w0| {
                    pattern_1.dynamic_parse(ctxt, v).and_then(|w1| {
                        pattern_n.iter().fold(merge(w0, w1), |w, p| {
                            w.and_then(|w| p.dynamic_parse(ctxt, v).and_then(|wn| merge(w, wn)))
                        })
                    })
                })
                .map(|w| DynField::Compound(w).finish()),
            Definition::Pattern(p) => p.dynamic_parse(ctxt, v).map(|w| w.finish()),
        }
    }
}

impl<V: NestedValue> NamedAlternative<V> {
    fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<V> {
        self.pattern.dynamic_parse(ctxt, v).map(|w| {
            let mut r = Value::simple_record(&self.variant_label, 1);
            match w {
                DynField::Simple(field) => r.fields_vec_mut().push(field),
                DynField::Compound(fields) => {
                    if fields.len() > 0 {
                        r.fields_vec_mut().push(V::new(fields))
                    }
                }
            }
            r.finish().wrap()
        })
    }
}

impl<V: NestedValue> NamedPattern<V> {
    fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<Map<V, V>> {
        match self {
            NamedPattern::Named(b) => {
                let binding = &**b;
                binding
                    .pattern
                    .dynamic_parse(ctxt, v)
                    .map(|w| w.to_map(Some(&binding.name)))
            }
            NamedPattern::Anonymous(b) => b.dynamic_parse(ctxt, v).map(|w| w.to_map(None)),
        }
    }
}

impl<V: NestedValue> NamedSimplePattern<V> {
    fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<DynField<V>> {
        match self {
            NamedSimplePattern::Named(b) => {
                let binding = &**b;
                binding
                    .pattern
                    .dynamic_parse(ctxt, v)
                    .map(|w| DynField::Compound(w.to_map(Some(&binding.name))))
            }
            NamedSimplePattern::Anonymous(b) => b.dynamic_parse(ctxt, v),
        }
    }
}

impl<V: NestedValue> SimplePattern<V> {
    fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<DynField<V>> {
        match self {
            SimplePattern::Any => Some(DynField::Simple(v.clone())),
            SimplePattern::Atom { atom_kind } => match &**atom_kind {
                AtomKind::Boolean => v.value().is_boolean().then(|| DynField::Simple(v.clone())),
                AtomKind::Double => v.value().is_double().then(|| DynField::Simple(v.clone())),
                AtomKind::SignedInteger => v
                    .value()
                    .is_signedinteger()
                    .then(|| DynField::Simple(v.clone())),
                AtomKind::String => v.value().is_string().then(|| DynField::Simple(v.clone())),
                AtomKind::ByteString => v
                    .value()
                    .is_bytestring()
                    .then(|| DynField::Simple(v.clone())),
                AtomKind::Symbol => v.value().is_symbol().then(|| DynField::Simple(v.clone())),
            },
            SimplePattern::Embedded { .. } => {
                v.value().is_embedded().then(|| DynField::Simple(v.clone()))
            }
            SimplePattern::Lit { value } => (v == value).then(|| DynField::Compound(Map::new())),
            SimplePattern::Seqof { pattern } => v
                .value()
                .as_sequence()
                .and_then(|vs| {
                    vs.iter()
                        .map(|v| (**pattern).dynamic_parse(ctxt, v).map(|w| w.finish()))
                        .collect::<Option<Vec<V>>>()
                })
                .map(|ws| DynField::Simple(V::new(ws))),
            SimplePattern::Setof { pattern } => v
                .value()
                .as_set()
                .and_then(|vs| {
                    vs.iter()
                        .map(|v| (**pattern).dynamic_parse(ctxt, v).map(|w| w.finish()))
                        .collect::<Option<Vec<V>>>()
                })
                .map(|ws| DynField::Simple(V::new(ws))),
            SimplePattern::Dictof { key, value } => v
                .value()
                .as_dictionary()
                .and_then(|d| {
                    d.iter()
                        .map(|(k, v)| {
                            (**key).dynamic_parse(ctxt, k).and_then(|kw| {
                                (**value)
                                    .dynamic_parse(ctxt, v)
                                    .map(|vw| (kw.finish(), vw.finish()))
                            })
                        })
                        .collect::<Option<Map<V, V>>>()
                })
                .map(|d| DynField::Simple(V::new(d))),
            SimplePattern::Ref(r) => ctxt
                .dynamic_parse(&r.module.0, &r.name, v)
                .map(DynField::Simple),
        }
    }
}

impl<V: NestedValue> CompoundPattern<V> {
    fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<Map<V, V>> {
        match self {
            CompoundPattern::Rec { label, fields } => v.value().as_record(None).and_then(|r| {
                (**label).dynamic_parse(ctxt, r.label()).and_then(|lw| {
                    (**fields)
                        .dynamic_parse(ctxt, &V::new(r.fields().to_vec()))
                        .and_then(|fsw| merge(lw, fsw))
                })
            }),
            CompoundPattern::Tuple { patterns } => v.value().as_sequence().and_then(|vs| {
                if vs.len() >= patterns.len() {
                    patterns
                        .iter()
                        .zip(vs)
                        .fold(Some(Map::new()), |acc, (p, v)| {
                            acc.and_then(|acc| p.dynamic_parse(ctxt, v).and_then(|w| merge(acc, w)))
                        })
                } else {
                    None
                }
            }),
            CompoundPattern::TuplePrefix { fixed, variable } => {
                v.value().as_sequence().and_then(|vs| {
                    if vs.len() >= fixed.len() {
                        fixed
                            .iter()
                            .zip(vs)
                            .fold(Some(Map::new()), |acc, (p, v)| {
                                acc.and_then(|acc| {
                                    p.dynamic_parse(ctxt, v).and_then(|w| merge(acc, w))
                                })
                            })
                            .and_then(|fixed_ws| {
                                let remainder = V::new(vs[fixed.len()..].to_vec());
                                (**variable).dynamic_parse(ctxt, &remainder).and_then(
                                    |variable_ws| merge(fixed_ws, variable_ws.unwrap_compound()),
                                )
                            })
                    } else {
                        None
                    }
                })
            }
            CompoundPattern::Dict { entries } => v.value().as_dictionary().and_then(|d| {
                (**entries).0.iter().fold(Some(Map::new()), |acc, (k, p)| {
                    acc.and_then(|acc| {
                        d.get(k).and_then(|v| {
                            p.dynamic_parse(ctxt, v)
                                .and_then(|w| merge(acc, w.unwrap_compound()))
                        })
                    })
                })
            }),
        }
    }
}

impl<V: NestedValue> Pattern<V> {
    fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<DynField<V>> {
        match self {
            Pattern::SimplePattern(b) => (**b).dynamic_parse(ctxt, v),
            Pattern::CompoundPattern(b) => (**b).dynamic_parse(ctxt, v).map(DynField::Compound),
        }
    }
}

impl<V: NestedValue> DynField<V> {
    fn finish(self) -> V {
        match self {
            DynField::Simple(v) => v,
            DynField::Compound(v) => V::new(v),
        }
    }

    fn to_map(self, key: Option<&str>) -> Map<V, V> {
        match self {
            DynField::Simple(v) => {
                let mut d = Map::new();
                if let Some(k) = key {
                    d.insert(V::symbol(k), v);
                }
                d
            }
            DynField::Compound(d) => d,
        }
    }

    fn unwrap_compound(self) -> Map<V, V> {
        match self {
            DynField::Simple(_) => panic!("Cannot unwrap DynField::Simple to compound fields"),
            DynField::Compound(d) => d,
        }
    }
}

fn merge<V: NestedValue>(a: Map<V, V>, b: Map<V, V>) -> Option<Map<V, V>> {
    merge2(V::new(a), V::new(b)).map(|d| {
        d.value_owned()
            .into_dictionary()
            .expect("merge to yield Dictionary")
    })
}