653 lines
24 KiB
Rust
653 lines
24 KiB
Rust
use crate::gen::schema::*;
|
|
use crate::syntax::block::constructors::*;
|
|
use crate::syntax::block::escape_bytes;
|
|
use crate::syntax::block::escape_string;
|
|
use crate::syntax::block::Item;
|
|
use crate::*;
|
|
|
|
use preserves::value::AtomClass;
|
|
use preserves::value::CompoundClass;
|
|
use preserves::value::IOValue;
|
|
use preserves::value::NestedValue;
|
|
use preserves::value::ValueClass;
|
|
|
|
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 ReaderPlugin;
|
|
|
|
impl compiler::Plugin for ReaderPlugin {
|
|
fn generate_definition(
|
|
&self,
|
|
module_ctxt: &mut ModuleContext,
|
|
definition_name: &str,
|
|
definition: &Definition,
|
|
) {
|
|
if let ModuleContextMode::TargetGeneric = module_ctxt.mode {
|
|
gen_definition_reader(module_ctxt, definition_name, definition)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct BoundaryTracker {
|
|
tracker_name: String,
|
|
item_expr: &'static str,
|
|
}
|
|
|
|
impl BoundaryTracker {
|
|
fn unwrap(
|
|
ctxt: &mut FunctionContext,
|
|
body: &mut Vec<Item>,
|
|
open_expr: &'static str,
|
|
e: Option<&BoundaryTracker>,
|
|
) -> Self {
|
|
match e {
|
|
None => Self::new(ctxt, body, open_expr, "_support::B::Item::SequenceValue"),
|
|
Some(b) => b.clone(),
|
|
}
|
|
}
|
|
|
|
fn new(
|
|
ctxt: &mut FunctionContext,
|
|
body: &mut Vec<Item>,
|
|
open_expr: &'static str,
|
|
item_expr: &'static str,
|
|
) -> Self {
|
|
let tracker_name = ctxt.gentempname();
|
|
body.push(item(open_expr));
|
|
body.push(item(seq![
|
|
"let mut ",
|
|
tracker_name.clone(),
|
|
" = _support::B::Type::default();"
|
|
]));
|
|
BoundaryTracker {
|
|
tracker_name,
|
|
item_expr,
|
|
}
|
|
}
|
|
|
|
fn emit_boundary(&self, body: &mut Vec<Item>) {
|
|
body.push(item(seq![
|
|
self.tracker_name.clone(),
|
|
".shift(Some(",
|
|
self.item_expr,
|
|
"));"
|
|
]));
|
|
body.push(item(seq!["r.boundary(&", self.tracker_name.clone(), ")?;"]));
|
|
}
|
|
|
|
fn emit_loop(&self, body: &mut Vec<Item>, inner: Vec<Item>) {
|
|
body.push(item(seq![
|
|
"while !r.close_compound",
|
|
parens![
|
|
seq!["&mut ", self.tracker_name.clone()],
|
|
seq!["&", self.item_expr]
|
|
],
|
|
"? ",
|
|
codeblock(inner)
|
|
]))
|
|
}
|
|
}
|
|
|
|
pub fn gen_definition_reader(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);
|
|
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, 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(),
|
|
anglebrackets![
|
|
"'de",
|
|
seq![ctxt.m.any_type(), ": preserves::value::NestedValue"],
|
|
seq!["R: _support::Reader<'de, ", ctxt.m.any_type(), ">"]
|
|
],
|
|
"(r: &mut R) -> ",
|
|
"std::result::Result<",
|
|
names::render_constructor(n),
|
|
ty.generic_arg(ctxt.m),
|
|
", _support::ParseError> ",
|
|
codeblock(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];
|
|
let mut need_restore = false;
|
|
ps.extend(pattern_n);
|
|
ctxt.define_atom(&mut body, "_mark", item("r.mark()?"));
|
|
for e in &ps {
|
|
if need_restore {
|
|
body.push(item("r.restore(&_mark)?;"));
|
|
} else {
|
|
need_restore = true;
|
|
}
|
|
named_pattern_reader(&mut ctxt, e, None, &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, 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",
|
|
anglebrackets![seq![ctxt.m.any_type(), ": preserves::value::NestedValue"]],
|
|
" _support::Deserialize",
|
|
anglebrackets![ctxt.m.any_type()],
|
|
" for ",
|
|
names::render_constructor(n),
|
|
ty.generic_arg(ctxt.m),
|
|
" ",
|
|
codeblock![seq![
|
|
"fn deserialize",
|
|
anglebrackets![
|
|
"'de",
|
|
seq!["R: _support::Reader<'de, ", ctxt.m.any_type(), ">"]
|
|
],
|
|
"(r: &mut R) -> ",
|
|
"std::result::Result<Self, _support::ParseError> ",
|
|
codeblock(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(_) | TField::Any | TField::Embedded => 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<(IOValue, LiteralContinuation)>;
|
|
type LiteralSeqCases = Vec<(Vec<IOValue>, 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 => ",
|
|
codeblock(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(true)? ", codeblock(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::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())),
|
|
},
|
|
" => ",
|
|
codeblock(inner)]));
|
|
}
|
|
subcases.push(item(seq!["_ => return ", ctxt.err_code(), "?,"]));
|
|
item(seq!["preserves::value::Token::Atom(v) => match v.value() ", codeblock(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) => ",
|
|
codeblock(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) => ",
|
|
codeblock(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(true)? ", codeblock(cases)]));
|
|
}
|
|
|
|
fn simple_pattern_reader(
|
|
ctxt: &mut FunctionContext,
|
|
p: &SimplePattern,
|
|
boundary_tracker: Option<&BoundaryTracker>,
|
|
body: &mut Vec<Item>,
|
|
) -> String {
|
|
let dest = ctxt.gentempname();
|
|
match p {
|
|
SimplePattern::Any => {
|
|
ctxt.define_atom(body, &dest, item("r.demand_next(true)?"));
|
|
dest
|
|
}
|
|
SimplePattern::Atom { atom_kind: k } => {
|
|
let reader = match &**k {
|
|
AtomKind::Boolean => "r.next_boolean()?",
|
|
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 { .. } => {
|
|
ctxt.define_atom(
|
|
body,
|
|
&dest,
|
|
item("r.demand_next(true)?.value().to_embedded()?.clone()"),
|
|
);
|
|
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 } => {
|
|
let compound_dest = ctxt.gentempname();
|
|
ctxt.with_definite_mode(|ctxt| {
|
|
let boundary_tracker =
|
|
BoundaryTracker::unwrap(ctxt, body, "r.open_sequence()?;", boundary_tracker);
|
|
let mut inner = Vec::new();
|
|
boundary_tracker.emit_boundary(&mut inner);
|
|
let item_dest = simple_pattern_reader(ctxt, pattern, None, &mut inner);
|
|
inner.push(item(seq![
|
|
compound_dest.to_owned(),
|
|
".push(",
|
|
store_wrap(true, &field_type(pattern), &item_dest),
|
|
");"
|
|
]));
|
|
ctxt.declare_compound(body, &compound_dest, item("std::vec::Vec::new()"));
|
|
boundary_tracker.emit_loop(body, inner);
|
|
});
|
|
ctxt.define_atom(body, &dest, item(compound_dest));
|
|
dest
|
|
}
|
|
SimplePattern::Setof { pattern } => {
|
|
let compound_dest = ctxt.gentempname();
|
|
ctxt.with_definite_mode(|ctxt| {
|
|
let boundary_tracker = BoundaryTracker::new(
|
|
ctxt,
|
|
body,
|
|
"r.open_set()?;",
|
|
"_support::B::Item::SetValue",
|
|
);
|
|
let mut inner = Vec::new();
|
|
boundary_tracker.emit_boundary(&mut inner);
|
|
let item_dest = simple_pattern_reader(ctxt, pattern, None, &mut inner);
|
|
inner.push(item(seq![
|
|
compound_dest.to_owned(),
|
|
".insert(",
|
|
store_wrap(true, &field_type(pattern), &item_dest),
|
|
");"
|
|
]));
|
|
ctxt.declare_compound(body, &compound_dest, item("preserves::value::Set::new()"));
|
|
boundary_tracker.emit_loop(body, inner);
|
|
});
|
|
ctxt.define_atom(body, &dest, item(compound_dest));
|
|
dest
|
|
}
|
|
SimplePattern::Dictof { key, value } => {
|
|
let compound_dest = ctxt.gentempname();
|
|
ctxt.with_definite_mode(|ctxt| {
|
|
let mut boundary_tracker = BoundaryTracker::new(
|
|
ctxt,
|
|
body,
|
|
"r.open_dictionary()?;",
|
|
"_support::B::Item::DictionaryKey",
|
|
);
|
|
let mut inner = Vec::new();
|
|
boundary_tracker.emit_boundary(&mut inner);
|
|
let key_dest = simple_pattern_reader(ctxt, key, None, &mut inner);
|
|
boundary_tracker.item_expr = "_support::B::Item::DictionaryValue";
|
|
boundary_tracker.emit_boundary(&mut inner);
|
|
let value_dest = simple_pattern_reader(ctxt, value, None, &mut inner);
|
|
inner.push(item(seq![
|
|
compound_dest.to_owned(),
|
|
".insert(",
|
|
store_wrap(true, &field_type(key), &key_dest),
|
|
", ",
|
|
store_wrap(true, &field_type(value), &value_dest),
|
|
");"
|
|
]));
|
|
ctxt.declare_compound(body, &compound_dest, item("preserves::value::Map::new()"));
|
|
boundary_tracker.item_expr = "_support::B::Item::DictionaryKey";
|
|
boundary_tracker.emit_loop(body, inner);
|
|
});
|
|
ctxt.define_atom(body, &dest, item(compound_dest));
|
|
dest
|
|
}
|
|
SimplePattern::Ref(r) => {
|
|
let tf = name![ctxt.m.render_ref(&**r, RefRenderStyle::Bare), "deserialize"];
|
|
ctxt.define_atom(body, &dest, item(seq![tf, "(r)?"]));
|
|
dest
|
|
}
|
|
}
|
|
}
|
|
|
|
fn named_pattern_reader(
|
|
ctxt: &mut FunctionContext,
|
|
p: &NamedPattern,
|
|
boundary_tracker: Option<&BoundaryTracker>,
|
|
body: &mut Vec<Item>,
|
|
) {
|
|
match p {
|
|
NamedPattern::Anonymous(p) => {
|
|
pattern_reader(ctxt, p, boundary_tracker, body);
|
|
}
|
|
NamedPattern::Named(b) => {
|
|
let Binding { name, pattern } = &**b;
|
|
let dest = simple_pattern_reader(ctxt, pattern, boundary_tracker, body);
|
|
let capture_ty = field_type(pattern);
|
|
ctxt.capture(names::render_fieldname(name), capture_ty, dest);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn pattern_reader(
|
|
ctxt: &mut FunctionContext,
|
|
p: &Pattern,
|
|
boundary_tracker: Option<&BoundaryTracker>,
|
|
body: &mut Vec<Item>,
|
|
) -> Option<String> {
|
|
match p {
|
|
Pattern::SimplePattern(s) => Some(simple_pattern_reader(ctxt, s, boundary_tracker, body)),
|
|
Pattern::CompoundPattern(c) => {
|
|
match &**c {
|
|
CompoundPattern::Rec { label, fields } => {
|
|
let mut boundary_tracker = BoundaryTracker::new(
|
|
ctxt,
|
|
body,
|
|
"r.open_record(None)?;",
|
|
"_support::B::Item::RecordLabel",
|
|
);
|
|
boundary_tracker.emit_boundary(body);
|
|
boundary_tracker.item_expr = "_support::B::Item::RecordField";
|
|
named_pattern_reader(ctxt, &**label, None, body);
|
|
named_pattern_reader(ctxt, &**fields, Some(&boundary_tracker), body);
|
|
}
|
|
CompoundPattern::Tuple { patterns } => {
|
|
let boundary_tracker = BoundaryTracker::unwrap(
|
|
ctxt,
|
|
body,
|
|
"r.open_sequence()?;",
|
|
boundary_tracker,
|
|
);
|
|
for p in patterns {
|
|
boundary_tracker.emit_boundary(body);
|
|
named_pattern_reader(ctxt, p, None, body);
|
|
}
|
|
body.push(item(seq![
|
|
"r.ensure_complete",
|
|
parens![
|
|
boundary_tracker.tracker_name.clone(),
|
|
seq!["&", boundary_tracker.item_expr]
|
|
],
|
|
"?;"
|
|
]));
|
|
}
|
|
CompoundPattern::TuplePrefix { fixed, variable } => {
|
|
let boundary_tracker = BoundaryTracker::unwrap(
|
|
ctxt,
|
|
body,
|
|
"r.open_sequence()?;",
|
|
boundary_tracker,
|
|
);
|
|
for p in fixed {
|
|
boundary_tracker.emit_boundary(body);
|
|
named_pattern_reader(ctxt, p, None, body);
|
|
}
|
|
named_pattern_reader(ctxt, &promote(variable), Some(&boundary_tracker), body);
|
|
}
|
|
CompoundPattern::Dict { entries } => {
|
|
let boundary_tracker = BoundaryTracker::new(
|
|
ctxt,
|
|
body,
|
|
"r.open_dictionary()?;",
|
|
"_support::B::Item::DictionaryKey",
|
|
);
|
|
let mut inner = Vec::new();
|
|
boundary_tracker.emit_boundary(&mut inner);
|
|
let mut val_boundary_tracker = boundary_tracker.clone();
|
|
val_boundary_tracker.item_expr = "_support::B::Item::DictionaryValue";
|
|
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 val_boundary_tracker = val_boundary_tracker.clone();
|
|
let f: LiteralContinuation = Box::new(
|
|
move |ctxt: &mut FunctionContext, innerinner: &mut Vec<Item>| {
|
|
val_boundary_tracker.emit_boundary(innerinner);
|
|
named_pattern_reader(ctxt, &promote(&value_pat), None, innerinner);
|
|
innerinner.push(item("continue;"));
|
|
});
|
|
(key_lit.clone(), f)
|
|
}).collect());
|
|
}));
|
|
boundary_tracker.emit_loop(body, inner);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
}
|
|
}
|