preserves/implementations/rust/preserves-schema/src/compiler/unparsers.rs

441 lines
14 KiB
Rust

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<String>;
#[derive(Clone)]
struct FieldsSink {
finish: Item,
vec_expr: Item,
body: Vec<Item>,
}
#[derive(Clone)]
enum ValueSink {
Normal,
Fields(Rc<Cell<Option<FieldsSink>>>),
}
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<NamedPattern>,
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))],
";"
]));
}
}