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

412 lines
17 KiB
Rust

use crate::*;
use crate::gen::schema::*;
use crate::syntax::block::Item;
use crate::syntax::block::escape_string;
use crate::syntax::block::escape_bytes;
use crate::syntax::block::constructors::*;
use preserves::value::AtomClass;
use preserves::value::CompoundClass;
use preserves::value::NestedValue;
use preserves::value::ValueClass;
use super::context::{ModuleContext, FunctionContext};
use super::names;
use super::types::*;
pub fn gen_definition_reader(m: &mut ModuleContext, n: &str, d: &Definition) {
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);
ctxt.define_atom(&mut body, "_mark", item("r.mark()?"));
for NamedAlternative { variant_label: name, pattern: pat } in ps {
let fname = seq!["read_", 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_reader(&mut ctxt, pat, false, &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(), anglebrackets![
"'de",
"R: _support::Reader<'de, _Ptr, _Any>"],
"(r: &mut R) -> ",
"std::result::Result<", names::render_constructor(n), ", _support::ParseError> ",
block(body)])
});
body.push(item(seq![
"match ", fname, "(r) { ",
"Err(e) if e.is_conformance_error() => r.restore(&_mark)?, ",
"result => return result }"]));
}
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_reader(&mut ctxt, e, false, &mut body);
}
construct(&ctxt, item(names::render_constructor(n)), true, &record_type(&ps), None, &mut body);
}
Definition::Pattern(p) => {
let dest = pattern_reader(&mut ctxt, p, false, &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", anglebrackets!["'de", "R: _support::Reader<'de, _Ptr, _Any>"], " ",
"_support::Deserialize", anglebrackets!["'de", "_Ptr", "_Any", "R"], " ",
"for ", names::render_constructor(n), " ", block![
seq!["fn deserialize(r: &mut R) -> ",
"std::result::Result<Self, _support::ParseError> ",
block(body)]]])
});
}
fn construct(
ctxt: &FunctionContext,
ctorname: Item,
is_struct: bool,
ty: &TSimple,
dest: Option<&str>,
body: &mut Vec<Item>,
) {
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(_) => expr.to_owned(),
}
}
fn group_by<T, K, Key>(mut items: Vec<T>, mut key: Key) -> Vec<(K, Vec<T>)>
where
K: Ord + Clone,
Key: FnMut(&T) -> K,
{
let mut result = Vec::new();
let mut current_key: Option<K> = None;
let mut buf = Vec::new();
items.sort_by(|a, b| key(a).cmp(&key(b)));
for (k, v) in items.into_iter().map(|t| (key(&t), t)) {
match current_key.cmp(&Some(k.clone())) {
std::cmp::Ordering::Equal => (),
std::cmp::Ordering::Less |
std::cmp::Ordering::Greater => {
if let Some(k) = current_key {
result.push((k, std::mem::take(&mut buf)));
}
current_key = Some(k);
}
}
buf.push(v)
}
if let Some(k) = current_key {
result.push((k.clone(), std::mem::take(&mut buf)));
}
result
}
type LiteralContinuation = Box<dyn FnOnce(&mut FunctionContext, &mut Vec<Item>) -> ()>;
type LiteralCases = Vec<(_Any, LiteralContinuation)>;
type LiteralSeqCases = Vec<(Vec<_Any>, LiteralContinuation)>;
fn read_expected_literal_seqs(
ctxt: &mut FunctionContext,
body: &mut Vec<Item>,
possibilities: LiteralSeqCases,
) {
let grouped = group_by(possibilities, |(vs, _f)| if vs.is_empty() {
None
} else {
Some(vs[0].clone())
});
let mut cases = Vec::new();
let mut nested: LiteralCases = Vec::new();
for (head, group) in grouped.into_iter() {
match head {
None => {
let mut inner = Vec::new();
group.into_iter().next().unwrap().1(ctxt, &mut inner);
cases.push(item(seq!["preserves::value::Token::End => ", block(inner)]));
},
Some(h) => {
let tails = group.into_iter().map(|(mut vs, f)| {
vs.remove(0);
(vs, f)
}).collect();
nested.push((h, Box::new(|ctxt: &mut FunctionContext, b: &'_ mut Vec<Item>| {
read_expected_literal_seqs(ctxt, b, tails)
})));
}
}
}
cases.extend(read_expected_literals_cases(ctxt, nested));
body.push(item(seq!["match r.next_token(false)? ", block(cases)]));
}
fn read_expected_literals_cases(
ctxt: &mut FunctionContext,
possibilities: LiteralCases,
) -> Vec<Item> {
let grouped = group_by(possibilities, |(v, _f)| v.value_class());
let mut cases = grouped.into_iter().map(|(n, group)| {
match n {
ValueClass::Atomic(cls) => {
let mut subcases = Vec::new();
for p in group {
let mut inner = Vec::new();
p.1(ctxt, &mut inner);
subcases.push(item(seq![
format!("preserves::value::Value::{:?}(w)", cls),
match cls {
AtomClass::Boolean => match p.0.value().to_boolean().unwrap() {
true => " if *w".to_owned(),
false => " if !*w".to_owned(),
},
AtomClass::Float |
AtomClass::Double =>
format!(" if w.0 == {:?}", p.0),
AtomClass::SignedInteger =>
format!(" if *w == ({:?}).into()", p.0),
AtomClass::String =>
format!(" if w == {}", escape_string(p.0.value().to_string().unwrap())),
AtomClass::ByteString =>
format!(" if w == {}", escape_bytes(p.0.value().to_bytestring().unwrap())),
AtomClass::Symbol =>
format!(" if w == {}", escape_string(p.0.value().to_symbol().unwrap())),
},
" => ",
block(inner)]));
}
subcases.push(item(seq!["_ => return ", ctxt.err_code(), "?,"]));
item(seq!["preserves::value::Token::Atom(v) => match v.value() ", block(subcases)])
}
ValueClass::Compound(CompoundClass::Record) => {
let mut subcases = Vec::new();
read_expected_literal_seqs(ctxt, &mut subcases, group.into_iter().map(|(v, f)| {
let r = v.value().to_record(None).unwrap();
(r.0.clone(), f)
}).collect());
item(seq![
"preserves::value::Token::Compound(preserves::value::CompoundClass::Record) => ",
block(subcases)])
}
ValueClass::Compound(CompoundClass::Sequence) => {
let mut subcases = Vec::new();
read_expected_literal_seqs(ctxt, &mut subcases, group.into_iter().map(|(v, f)| {
let s = v.value().to_sequence().unwrap().clone();
(s, f)
}).collect());
item(seq![
"preserves::value::Token::Compound(preserves::value::CompoundClass::Sequence) => ",
block(subcases)])
}
ValueClass::Compound(CompoundClass::Set) => {
panic!("Sets in literal constants in Schema not yet supported");
}
ValueClass::Compound(CompoundClass::Dictionary) => {
panic!("Dictionaries in literal constants in Schema not yet supported");
}
ValueClass::Embedded => {
panic!("Embedded values in literal constants in Schema not yet supported");
}
}
}).collect::<Vec<_>>();
cases.push(item(seq!["_ => return ", ctxt.err_code(), "?,"]));
cases
}
fn read_expected_literals(
ctxt: &mut FunctionContext,
body: &mut Vec<Item>,
possibilities: LiteralCases,
) {
let cases = read_expected_literals_cases(ctxt, possibilities);
body.push(item(seq!["match r.next_token(false)? ", block(cases)]));
}
fn simple_pattern_reader(
ctxt: &mut FunctionContext,
p: &SimplePattern,
in_sequence: bool,
body: &mut Vec<Item>,
) -> String {
let dest = ctxt.gentempname();
match p {
SimplePattern::Any => {
ctxt.define_atom(body, &dest, item("r.demand_next(false)?"));
dest
},
SimplePattern::Atom { atom_kind: k } => {
let reader = match &**k {
AtomKind::Boolean => "r.next_boolean()?",
AtomKind::Float => "r.next_float()?",
AtomKind::Double => "r.next_double()?",
AtomKind::SignedInteger => "r.next_signedinteger()?",
AtomKind::String => "r.next_str()?.into_owned()",
AtomKind::ByteString => "r.next_bytestring()?.into_owned()",
AtomKind::Symbol => "r.next_symbol()?.into_owned()",
};
ctxt.define_atom(body, &dest, item(reader.to_owned()));
dest
},
SimplePattern::Embedded { .. } => {
body.push(item("r.open_embedded()?;"));
ctxt.define_atom(body, &dest, item("r.demand_next(false)?.value().to_embedded()?.clone()"));
body.push(item("r.close_embedded()?;"));
dest
},
SimplePattern::Lit { value } => {
let f = Box::new(|_ctxt: &mut FunctionContext, _: &'_ mut Vec<Item>| ());
read_expected_literals(ctxt, body, vec![(value.clone(), f)]);
ctxt.define_atom(body, &dest, item("()"));
dest
},
SimplePattern::Seqof { pattern } => {
if !in_sequence {
body.push(item("r.open_sequence()?;"));
}
let mut inner = Vec::new();
let item_dest = simple_pattern_reader(ctxt, pattern, false, &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!["while !(r.close_compound()?) ", block(inner)]));
dest
},
SimplePattern::Setof { pattern } => {
body.push(item("r.open_set()?;"));
let mut inner = Vec::new();
let item_dest = simple_pattern_reader(ctxt, pattern, false, &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!["while !(r.close_compound()?) ", block(inner)]));
dest
},
SimplePattern::Dictof { key, value } => {
body.push(item("r.open_dictionary()?;"));
let mut inner = Vec::new();
let key_dest = simple_pattern_reader(ctxt, key, false, &mut inner);
let value_dest = simple_pattern_reader(ctxt, value, false, &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!["while !(r.close_compound()?) ", block(inner)]));
dest
},
SimplePattern::Ref(r) => {
let tf = name![ctxt.m.render_ref(&**r), "deserialize"];
ctxt.define_atom(body, &dest, item(seq![tf, "(r)?"]));
dest
},
}
}
fn named_pattern_reader(
ctxt: &mut FunctionContext,
p: &NamedPattern,
in_sequence: bool,
body: &mut Vec<Item>,
) {
match p {
NamedPattern::Anonymous(p) => {
pattern_reader(ctxt, p, in_sequence, body);
},
NamedPattern::Named(b) => {
let Binding { name, pattern} = &**b;
let dest = simple_pattern_reader(ctxt, pattern, in_sequence, body);
let capture_ty = field_type(pattern);
ctxt.capture(names::render_fieldname(name), capture_ty, dest);
}
}
}
fn pattern_reader(
ctxt: &mut FunctionContext,
p: &Pattern,
in_sequence: bool,
body: &mut Vec<Item>,
) -> Option<String> {
match p {
Pattern::SimplePattern(s) =>
Some(simple_pattern_reader(ctxt, s, in_sequence, body)),
Pattern::CompoundPattern(c) => {
match &**c {
CompoundPattern::Rec { label, fields } => {
body.push(item("r.open_record(None)?;"));
named_pattern_reader(ctxt, &**label, false, body);
named_pattern_reader(ctxt, &**fields, true, body);
},
CompoundPattern::Tuple { patterns } => {
if !in_sequence {
body.push(item("r.open_sequence()?;"));
}
for p in patterns { named_pattern_reader(ctxt, p, false, body); }
body.push(item("r.ensure_complete()?;"));
},
CompoundPattern::TuplePrefix { fixed, variable } => {
if !in_sequence {
body.push(item("r.open_sequence()?;"));
}
for p in fixed { named_pattern_reader(ctxt, p, false, body); }
named_pattern_reader(ctxt, &promote(variable), true, body);
},
CompoundPattern::Dict { entries } => {
body.push(item("r.open_dictionary()?;"));
let mut inner = Vec::new();
body.extend(ctxt.with_indefinite_mode(|ctxt| {
read_expected_literals(ctxt, &mut inner, entries.0.iter().map(move |(key_lit, value_pat)| {
let value_pat = value_pat.clone();
let f: LiteralContinuation = Box::new(
move |ctxt: &mut FunctionContext, innerinner: &mut Vec<Item>| {
named_pattern_reader(ctxt, &promote(&value_pat), false, innerinner);
innerinner.push(item("continue;"));
});
(key_lit.clone(), f)
}).collect());
}));
body.push(item(seq!["while !(r.close_compound()?) ", block(inner)]));
}
}
None
},
}
}