use crate::gen::schema::*; use crate::syntax::block::constructors::*; use crate::syntax::block::{escape_string, Emittable, Item}; use crate::*; use std::cell::Cell; use std::rc::Rc; use super::context::{FunctionContext, ModuleContext, ModuleContextMode}; use super::names; use super::types::*; #[derive(Debug)] pub struct UnparserPlugin; impl compiler::Plugin for UnparserPlugin { fn generate_definition( &self, module_ctxt: &mut ModuleContext, definition_name: &str, definition: &Definition, ) { if let ModuleContextMode::TargetGeneric = module_ctxt.mode { gen_definition_unparser(module_ctxt, definition_name, definition) } } } 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, } } fn normal_src(src: String, is_struct: bool) -> ValueContext { ValueContext { src: Some(src), sink: ValueSink::Normal, is_struct, } } pub fn gen_definition_unparser(m: &mut ModuleContext, n: &str, d: &Definition) { let ty = definition_type(&m.module_path, Purpose::Codegen, n, d); m.define_function(n, |mut ctxt| { let mut body = vec![]; 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 self ", codeblock( 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), ); body.push(item(seq!["let ", patpat, " = self;"])); body.push(item(seq![ "preserves::value::merge(vec!", brackets( ps.iter() .map(|p| named_pattern_unparser(&mut ctxt, p, &vc)) .collect() ), ").expect", parens![escape_string( &("merge of ".to_owned() + &ctxt.fully_qualified_error_context()) )] ])); } Definition::Pattern(p) => { let (patpat, vc) = destruct( &mut ctxt, item(names::render_constructor(n)), true, &pattern_type(p), ); body.push(item(seq!["let ", patpat, " = self;"])); body.push(pattern_unparser(&mut ctxt, p, &vc)); } } item(seq![ "impl", ctxt.m.parse_unparse_generic_decls(&ty), " _support::Unparse", anglebrackets!["_L", ctxt.m.any_type()], " for ", names::render_constructor(n), ty.generic_arg(ctxt.m), " ", codeblock![seq![ "fn unparse(&self, _ctxt: _L) -> ", ctxt.m.any_type(), " ", codeblock(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 } => match &**k { AtomKind::Symbol => item(seq![ "preserves::value::Value::symbol(", src.as_ref().unwrap().to_owned(), ").wrap()" ]), AtomKind::ByteString => item(seq![ "preserves::value::Value::ByteString(", src.as_ref().unwrap().to_owned(), ".clone()).wrap()" ]), _ => item(seq![ "preserves::value::Value::from(", src.as_ref().unwrap().to_owned(), ").wrap()" ]), }, SimplePattern::Embedded { .. } => item(seq![ "preserves::value::Value::Embedded(", src.as_ref().unwrap().to_owned(), ".clone()).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(), " ", codeblock![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![ src.as_ref().unwrap().to_owned(), if vc.is_struct { "" } else { ".as_ref()" }, ".unparse(_ctxt)" ]), } } 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(); let init_expr = item(seq![ "preserves::value::Record(vec![", named_pattern_unparser(ctxt, label, &normal_none(vc.is_struct)), "])" ]); ctxt.declare_compound(&mut body, &rtmp, init_expr); 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, })))), 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 = ctxt.gentempname(); let mut body = Vec::new(); ctxt.declare_compound(&mut body, &dtmp, 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(codeblock(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 = ctxt.gentempname(); let mut body = Vec::new(); ctxt.declare_compound(&mut body, &rtmp, item("std::vec::Vec::new()")); FieldsSink { finish: item(seq![ "preserves::value::Value::Sequence", parens![rtmp.clone()], ".wrap()" ]), vec_expr: item(rtmp), body, } } } } fn finish(mut fields_sink: FieldsSink) -> Item { fields_sink.body.push(fields_sink.finish); item(codeblock(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))], ";" ])); } }