use crate::*; use crate::gen::schema::*; use crate::syntax::block::constructors::*; use crate::syntax::block::{Emittable, Item}; use std::cell::Cell; use std::rc::Rc; use super::codegen::*; use super::context::{ModuleContext, FunctionContext}; use super::names; use super::types::*; type ValueSource = Option; #[derive(Clone)] struct FieldsSink { finish: Item, vec_expr: Item, body: Vec, } #[derive(Clone)] enum ValueSink { Normal, Fields(Rc>>), } impl std::fmt::Debug for ValueSink { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { match self { ValueSink::Normal => write!(f, "ValueSink::Normal"), ValueSink::Fields(_) => write!(f, "ValueSink::Normal"), } } } #[derive(Debug)] struct ValueContext { src: ValueSource, sink: ValueSink, is_struct: bool, } fn normal_none(is_struct: bool) -> ValueContext { ValueContext { src: None, sink: ValueSink::Normal, is_struct: is_struct } } fn normal_src(src: String, is_struct: bool) -> ValueContext { ValueContext { src: Some(src), sink: ValueSink::Normal, is_struct: is_struct } } pub fn gen_definition_unparser(m: &mut ModuleContext, n: &str, d: &Definition) { m.define_function( |mut ctxt| { let mut body = Vec::new(); match d { Definition::Or { pattern_0, pattern_1, pattern_n } => { let mut ps = vec![&**pattern_0, &**pattern_1]; ps.extend(pattern_n); body.push(item(seq!["match value ", block(ps.iter().map( | NamedAlternative { variant_label: name, pattern: pat } | ctxt.branch(|ctxt| { let ctorname = item(name![names::render_constructor(n), names::render_constructor(name)]); let (patpat, vc) = destruct(ctxt, ctorname, false, &pattern_type(pat)); item(seq![patpat, " => ", pattern_unparser(ctxt, pat, &vc), ","]) })).collect())])); } Definition::And { pattern_0, pattern_1, pattern_n } => { let mut ps = vec![&**pattern_0, &**pattern_1]; ps.extend(pattern_n); let (patpat, vc) = destruct(&mut ctxt, item(names::render_constructor(n)), true, &record_type(&ps)); push_let(&mut body, item(patpat), item("value")); body.push(item(seq!["preserves::value::merge(vec!", brackets(ps.iter().map( |p| named_pattern_unparser(&mut ctxt, p, &vc)).collect()), ")"])); } Definition::Pattern(p) => { let (patpat, vc) = destruct(&mut ctxt, item(names::render_constructor(n)), true, &pattern_type(p)); push_let(&mut body, item(patpat), item("value")); body.push(pattern_unparser(&mut ctxt, p, &vc)); } } item(seq!["impl std::convert::From", anglebrackets![seq!["&", names::render_constructor(n)]], " for preserves::value::IOValue ", block![ seq!["fn from(value: &", names::render_constructor(n), ") -> Self ", block(body)]]]) }); } fn destruct( ctxt: &mut FunctionContext, ctorname: Item, is_struct: bool, ty: &TSimple, ) -> (impl Emittable, ValueContext) { match ty { TSimple::Field(TField::Unit) => (seq![ctorname], normal_none(is_struct)), TSimple::Field(_) => { let src = ctxt.gentempname(); (seq![ctorname, parens![src.to_owned()]], normal_src(src, is_struct)) } TSimple::Record(TRecord(fs)) => { for (fname, fty) in fs { let fsrc = ctxt.gentempname(); ctxt.capture(names::render_fieldname(fname), fty.clone(), fsrc); } (seq![ ctorname, " ", braces(ctxt.captures.iter().map( |c| item(seq![c.field_name.clone(), ": ", c.source_expr.clone()])).collect())], normal_none(is_struct)) } } } fn simple_pattern_unparser( ctxt: &mut FunctionContext, p: &SimplePattern, vc: &ValueContext, ) -> Item { let src = &vc.src; match p { SimplePattern::Any => item(seq![src.as_ref().unwrap().to_owned(), ".clone()"]), SimplePattern::Atom { atom_kind: k } => { if let AtomKind::Symbol = &**k { item(seq!["preserves::value::Value::symbol(", src.as_ref().unwrap().to_owned(), ").wrap()"]) } else { item(seq!["preserves::value::Value::from(", src.as_ref().unwrap().to_owned(), ").wrap()"]) } } SimplePattern::Embedded { .. } => item(seq!["preserves::value::Value::Domain(", src.as_ref().unwrap().to_owned(), ").wrap()"]), SimplePattern::Lit { value } => item(seq![parens![ctxt.m.define_literal(value)], ".clone()"]), SimplePattern::Seqof { pattern } => { let mut fields_sink = sequenceify(ctxt, vc); let tmp = ctxt.gentempname(); fields_sink.body.push(item(seq![ "for ", tmp.to_owned(), " in ", src.as_ref().unwrap().to_owned(), " ", block![ seq![fields_sink.vec_expr.clone(), ".push(", simple_pattern_unparser(ctxt, pattern, &normal_src(tmp, true)), ");"]]])); finish(fields_sink) } SimplePattern::Setof { pattern } => { let tmp = ctxt.gentempname(); item(seq!["preserves::value::Value::Set(", src.as_ref().unwrap().to_owned(), ".iter().map(|", tmp.to_owned(), "| ", simple_pattern_unparser(ctxt, pattern, &normal_src(tmp, true)), ").collect()).wrap()"]) } SimplePattern::Dictof { key, value } => { let tmp_key = ctxt.gentempname(); let tmp_value = ctxt.gentempname(); item(seq!["preserves::value::Value::Dictionary(", src.as_ref().unwrap().to_owned(), ".iter().map(|(", tmp_key.to_owned(), ", ", tmp_value.to_owned(), ")| ", parens![simple_pattern_unparser(ctxt, key, &normal_src(tmp_key, true)), simple_pattern_unparser(ctxt, value, &normal_src(tmp_value, true))], ").collect()).wrap()"]) } SimplePattern::Ref(_r) => item(seq!["preserves::value::IOValue::from(", src.as_ref().unwrap().to_owned(), if vc.is_struct { "" } else { ".as_ref()" }, ")"]), } } fn named_pattern_unparser( ctxt: &mut FunctionContext, p: &NamedPattern, vc: &ValueContext, ) -> Item { match p { NamedPattern::Anonymous(p) => pattern_unparser(ctxt, p, vc), NamedPattern::Named(b) => { let Binding { name, pattern} = &**b; let src = ctxt.lookup_capture(&names::render_fieldname(name)).source_expr.to_owned(); simple_pattern_unparser(ctxt, pattern, &ValueContext { src: Some(src), sink: vc.sink.clone(), is_struct: vc.is_struct, }) } } } fn pattern_unparser( ctxt: &mut FunctionContext, p: &Pattern, vc: &ValueContext, ) -> Item { match p { Pattern::SimplePattern(s) => simple_pattern_unparser(ctxt, s, vc), Pattern::CompoundPattern(c) => { match &**c { CompoundPattern::Rec { label, fields } => { let rtmp = ctxt.gentempname(); let mut body = Vec::new(); push_let_mut(&mut body, item(rtmp.to_owned()), item(seq!["preserves::value::Record(vec![", named_pattern_unparser(ctxt, label, &normal_none(vc.is_struct)), "])"])); named_pattern_unparser(ctxt, fields, &ValueContext { src: None, sink: ValueSink::Fields(Rc::new(Cell::new(Some(FieldsSink { finish: item(seq![rtmp.clone(), ".finish().wrap()"]), vec_expr: item(seq![rtmp.clone(), ".fields_vec_mut()"]), body: body, })))), is_struct: vc.is_struct, }) }, CompoundPattern::Tuple { patterns } => { let mut fields_sink = sequenceify(ctxt, vc); fixed_sequence_parser(ctxt, patterns, &mut fields_sink, vc.is_struct); finish(fields_sink) }, CompoundPattern::TuplePrefix { fixed, variable } => { let mut fields_sink = sequenceify(ctxt, vc); fixed_sequence_parser(ctxt, fixed, &mut fields_sink, vc.is_struct); named_pattern_unparser(ctxt, &promote(variable), &ValueContext { src: vc.src.clone(), sink: ValueSink::Fields(Rc::new(Cell::new(Some(fields_sink)))), is_struct: true, }) }, CompoundPattern::Dict { entries } => { let dtmp = item(ctxt.gentempname()); let mut body = Vec::new(); push_let_mut(&mut body, dtmp.clone(), item("preserves::value::Map::new()")); for (key_lit, value_pat) in entries.0.iter() { body.push(item(seq![dtmp.clone(), ".insert", parens![ seq![parens![ctxt.m.define_literal(key_lit)], ".clone()"], named_pattern_unparser(ctxt, &promote(value_pat), &normal_none(vc.is_struct))], ";"])); } body.push(item(seq!["preserves::value::Value::Dictionary(", dtmp, ").wrap()"])); item(block(body)) } } }, } } fn sequenceify<'a>( ctxt: &mut FunctionContext, vc: &'a ValueContext, ) -> FieldsSink { match vc { ValueContext { sink: ValueSink::Fields(fields_sink), .. } => (**fields_sink).take().unwrap(), _ => { let rtmp = item(ctxt.gentempname()); let mut body = Vec::new(); push_let_mut(&mut body, rtmp.clone(), item("vec![]")); FieldsSink { finish: item(seq![ "preserves::value::Value::Sequence", parens![rtmp.clone()], ".wrap()"]), vec_expr: rtmp, body: body, } } } } fn finish(mut fields_sink: FieldsSink) -> Item { fields_sink.body.push(fields_sink.finish); item(block(fields_sink.body)) } fn fixed_sequence_parser( ctxt: &mut FunctionContext, patterns: &Vec, fields_sink: &mut FieldsSink, is_struct: bool, ) { for p in patterns { fields_sink.body.push(item(seq![fields_sink.vec_expr.clone(), ".push", parens![ named_pattern_unparser(ctxt, p, &normal_none(is_struct))], ";"])); } }