use crate::gen::schema::*; use crate::syntax::block::constructors::*; use crate::syntax::block::Item; use crate::*; use super::context::FunctionContext; use super::context::ModuleContext; use super::context::ModuleContextMode; use super::context::RefRenderStyle; use super::names; use super::types::*; #[derive(Debug)] pub struct ParserPlugin; impl compiler::Plugin for ParserPlugin { fn generate_definition( &self, module_ctxt: &mut ModuleContext, definition_name: &str, definition: &Definition, ) { if let ModuleContextMode::TargetGeneric = module_ctxt.mode { gen_definition_parser(module_ctxt, definition_name, definition) } } } pub fn gen_definition_parser(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); for NamedAlternative { variant_label: name, pattern: pat, } in ps { let fname = seq![ "parse_", names::render_fieldname(n), "_", names::render_fieldname(name) ]; let ctorname = item(name![ names::render_constructor(n), names::render_constructor(name) ]); ctxt.m .define_function(&(n.to_owned() + "::" + name), |mut ctxt| { let mut body = Vec::new(); let dest = pattern_parser(&mut ctxt, pat, "value", None, &mut body); let dest = dest.as_ref().map(String::as_str); construct(&ctxt, ctorname, false, &pattern_type(pat), dest, &mut body); item(seq![ "fn ", fname.clone(), ctxt.m.parse_unparse_generic_decls(&ty), "(_ctxt: _L, value: &", ctxt.m.any_type(), ") -> ", "std::result::Result<", names::render_constructor(n), ty.generic_arg(ctxt.m), ", _support::ParseError> ", codeblock(body) ]) }); body.push(item(seq![ "if let Ok(r) = ", fname, "(_ctxt, value) { return Ok(r); }" ])); } body.push(item(seq![ctxt.err_code()])); } Definition::And { pattern_0, pattern_1, pattern_n, } => { let mut ps = vec![&**pattern_0, &**pattern_1]; ps.extend(pattern_n); for e in &ps { named_pattern_parser(&mut ctxt, e, "value", None, &mut body); } construct( &ctxt, item(names::render_constructor(n)), true, &record_type(&ps), None, &mut body, ); } Definition::Pattern(p) => { let dest = pattern_parser(&mut ctxt, p, "value", None, &mut body); let dest = dest.as_ref().map(String::as_str); construct( &ctxt, item(names::render_constructor(n)), true, &pattern_type(p), dest, &mut body, ); } } item(seq![ "impl", ctxt.m.parse_unparse_generic_decls(&ty), " _support::Parse", anglebrackets!["_L", ctxt.m.any_type()], " for ", names::render_constructor(n), ty.generic_arg(ctxt.m), " ", codeblock![seq![ "fn parse(_ctxt: _L, value: &", ctxt.m.any_type(), ")", " -> std::result::Result ", codeblock(body) ]] ]) }); } fn construct( ctxt: &FunctionContext, ctorname: Item, is_struct: bool, ty: &TSimple, dest: Option<&str>, body: &mut Vec, ) { match ty { TSimple::Field(TField::Unit) => body.push(item(seq!["Ok(", ctorname, ")"])), TSimple::Field(fieldty) => body.push(item(seq![ "Ok(", ctorname, parens![store_wrap(is_struct, fieldty, dest.unwrap())], ")" ])), TSimple::Record(_) => body.push(item(seq![ "Ok(", ctorname, " ", braces( ctxt.captures .iter() .map(|c| item(seq![ c.field_name.clone(), ": ", store_wrap(is_struct, &c.ty, &c.source_expr) ])) .collect() ), ")" ])), } } fn store_wrap(is_struct: bool, ty: &TField, expr: &str) -> String { match ty { TField::Unit | TField::Array(_) | TField::Set(_) | TField::Map(_, _) => expr.to_owned(), TField::Ref(_) => { if is_struct { expr.to_owned() } else { format!("std::boxed::Box::new({})", expr) } } TField::Base(_) | TField::Any | TField::Embedded => format!("{}.clone()", expr), } } fn simple_pattern_parser( ctxt: &mut FunctionContext, p: &SimplePattern, src: &str, sequence_base: Option, body: &mut Vec, ) -> String { let dest = ctxt.gentempname(); match p { SimplePattern::Any => { ctxt.define_atom(body, &dest, item(src.to_owned())); dest } SimplePattern::Atom { atom_kind: k } => { let converter = match &**k { AtomKind::Boolean => "to_boolean", AtomKind::Double => "to_double", AtomKind::SignedInteger => "to_signedinteger", AtomKind::String => "to_string", AtomKind::ByteString => "to_bytestring", AtomKind::Symbol => "to_symbol", }; ctxt.define_atom( body, &dest, item(seq![src.to_owned(), ".value().", converter, "()?"]), ); dest } SimplePattern::Embedded { .. } => { ctxt.define_atom( body, &dest, item(seq![parens![seq![ src.to_owned(), ".value().to_embedded()?" ]]]), ); dest } SimplePattern::Lit { value } => { body.push(item(seq![ "if ", src.to_owned(), " != ", ctxt.m.define_literal(value), " { return ", ctxt.err_code(), "; }" ])); ctxt.define_atom(body, &dest, item("()")); dest } SimplePattern::Seqof { pattern } => { let (src, n) = sequenceify(ctxt, src, sequence_base, body); let tmp = ctxt.gentempname(); let mut inner = Vec::new(); let item_dest = simple_pattern_parser(ctxt, pattern, &tmp, None, &mut inner); inner.push(item(seq![ dest.to_owned(), ".push(", store_wrap(true, &field_type(pattern), &item_dest), ");" ])); ctxt.declare_compound(body, &dest, item("std::vec::Vec::new()")); body.push(item(seq![ "for ", tmp.to_owned(), " in &", src.to_owned(), brackets![seq![n.to_string(), ".."]], " ", codeblock(inner) ])); dest } SimplePattern::Setof { pattern } => { let tmp = ctxt.gentempname(); let mut inner = Vec::new(); let item_dest = simple_pattern_parser(ctxt, pattern, &tmp, None, &mut inner); inner.push(item(seq![ dest.to_owned(), ".insert(", store_wrap(true, &field_type(pattern), &item_dest), ");" ])); ctxt.declare_compound(body, &dest, item("preserves::value::Set::new()")); body.push(item(seq![ "for ", tmp.to_owned(), " in ", src.to_owned(), ".value().to_set()?", " ", codeblock(inner) ])); dest } SimplePattern::Dictof { key, value } => { let tmp_key = ctxt.gentempname(); let tmp_value = ctxt.gentempname(); let mut inner = Vec::new(); let key_dest = simple_pattern_parser(ctxt, key, &tmp_key, None, &mut inner); let value_dest = simple_pattern_parser(ctxt, value, &tmp_value, None, &mut inner); inner.push(item(seq![ dest.to_owned(), ".insert(", store_wrap(true, &field_type(key), &key_dest), ", ", store_wrap(true, &field_type(value), &value_dest), ");" ])); ctxt.declare_compound(body, &dest, item("preserves::value::Map::new()")); body.push(item(seq![ "for (", tmp_key.to_owned(), ", ", tmp_value.to_owned(), ")", " in ", src.to_owned(), ".value().to_dictionary()?", " ", codeblock(inner) ])); dest } SimplePattern::Ref(r) => { let tf = name![ctxt.m.render_ref(&**r, RefRenderStyle::Bare), "parse"]; ctxt.define_atom( body, &dest, item(seq![tf, parens!["_ctxt", src.to_owned()], "?"]), ); dest } } } fn sequenceify( ctxt: &mut FunctionContext, src: &str, sequence_base: Option, body: &mut Vec, ) -> (String, usize) { match sequence_base { Some(n) => (src.to_owned(), n), None => { let tmp = ctxt.gentempname(); ctxt.define_atom( body, &tmp, item(seq![src.to_owned(), ".value().to_sequence()?"]), ); (tmp, 0) } } } fn fixed_sequence_parser( ctxt: &mut FunctionContext, base: usize, ps: &[NamedPattern], src: &str, body: &mut Vec, ) { let mut i = base; let required_count = ps.len(); if required_count > 0 { body.push(item(seq![ "if ", src.to_owned(), ".len()", if base > 0 { seq![" - ", base.to_string()] } else { seq![] }, " < ", required_count.to_string(), " { return ", ctxt.err_code(), "; }" ])); } for p in ps { named_pattern_parser(ctxt, p, &format!("(&{}[{}])", src, i), None, body); i += 1; } } fn named_pattern_parser( ctxt: &mut FunctionContext, p: &NamedPattern, src: &str, sequence_base: Option, body: &mut Vec, ) { match p { NamedPattern::Anonymous(p) => { pattern_parser(ctxt, p, src, sequence_base, body); } NamedPattern::Named(b) => { let Binding { name, pattern } = &**b; let dest = simple_pattern_parser(ctxt, pattern, src, sequence_base, body); let capture_ty = field_type(pattern); ctxt.capture(names::render_fieldname(name), capture_ty, dest); } } } fn pattern_parser( ctxt: &mut FunctionContext, p: &Pattern, src: &str, sequence_base: Option, body: &mut Vec, ) -> Option { match p { Pattern::SimplePattern(s) => Some(simple_pattern_parser(ctxt, s, src, sequence_base, body)), Pattern::CompoundPattern(c) => { match &**c { CompoundPattern::Rec { label, fields } => { let rtmp = ctxt.gentempname(); ctxt.define_atom( body, &rtmp, item(seq![src.to_owned(), ".value().to_record(None)?"]), ); named_pattern_parser(ctxt, &**label, &format!("{}.label()", rtmp), None, body); named_pattern_parser( ctxt, &**fields, &format!("{}.fields()", rtmp), Some(0), body, ); } CompoundPattern::Tuple { patterns } => { let (src, n) = sequenceify(ctxt, src, sequence_base, body); fixed_sequence_parser(ctxt, n, patterns, &src, body); } CompoundPattern::TuplePrefix { fixed, variable } => { let (src, n) = sequenceify(ctxt, src, sequence_base, body); fixed_sequence_parser(ctxt, n, fixed, &src, body); named_pattern_parser( ctxt, &promote(variable), &src, Some(n + fixed.len()), body, ); } CompoundPattern::Dict { entries } => { let dtmp = ctxt.gentempname(); ctxt.define_atom( body, &dtmp, item(seq![src.to_owned(), ".value().to_dictionary()?"]), ); for (key_lit, value_pat) in entries.0.iter() { let vtmp = ctxt.gentempname(); let init_expr = item(seq![ dtmp.to_owned(), ".get", parens![ctxt.m.define_literal(key_lit)], ".ok_or_else(|| ", ctxt.conformance_err_code(), ")?" ]); ctxt.define_atom(body, &vtmp, init_expr); named_pattern_parser(ctxt, &promote(value_pat), &vtmp, None, body); } } } None } } }