From a682cdd924e2b782fb8b798d45feb22f8fca8f95 Mon Sep 17 00:00:00 2001 From: Peter Duchovni Date: Fri, 31 Jan 2025 13:46:24 +1100 Subject: [PATCH 1/6] Implement EnumFromTo --- doodle-formats/src/format.rs | 6 +- src/codegen/mod.rs | 21 +++++-- src/codegen/typed_format.rs | 9 ++- src/decoder.rs | 115 +++++++++++++++++++++++++++-------- src/decoder/seq_kind.rs | 73 ++++++++++++++++++---- src/helper.rs | 6 ++ src/lib.rs | 33 +++++----- src/loc_decoder.rs | 96 ++++++++++++++++++++++------- src/output/tree.rs | 34 +++++++---- src/typecheck.rs | 23 ++++--- 10 files changed, 311 insertions(+), 105 deletions(-) diff --git a/doodle-formats/src/format.rs b/doodle-formats/src/format.rs index 1214ce69..cbe1afb1 100644 --- a/doodle-formats/src/format.rs +++ b/doodle-formats/src/format.rs @@ -108,10 +108,10 @@ mod test { ("len", base.u32be()), ( "mask", - Format::WithRelativeOffset( + with_relative_offset( None, - Box::new(var("len")), - Box::new(Format::Byte(mask_bytes)), + var("len"), + Format::Byte(mask_bytes), ), ), ( diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 1bbcc086..1f3d8ec8 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -876,7 +876,12 @@ fn embed_expr(expr: >Expr, info: ExprInfo) -> RustExpr { None => RustExpr::infix(x, op, y), } } - + TypedExpr::EnumFromTo(_, from, to) => { + let start = embed_expr_dft(from); + let stop = embed_expr_dft(to); + // FIXME - currently, we have no optimization to pre-optimize SeqIx(EnumFromTo)... + RustExpr::RangeExclusive(Box::new(start), Box::new(stop)) + } TypedExpr::IntRel(_, rel, lhs, rhs) => { // NOTE - because IntRel only deals with Copy types, we oughtn't need any embedded clones let x = embed_expr_dft(lhs); @@ -3536,6 +3541,7 @@ impl<'a> Elaborator<'a> { let gt = self.get_gt_from_index(index); TypedExpr::Seq(gt, t_elts) } + Expr::RecordProj(e, fld) => { self.codegen.name_gen.ctxt.push_atom(NameAtom::DeadEnd); let t_e = self.elaborate_expr(e); @@ -3752,10 +3758,16 @@ impl<'a> Elaborator<'a> { TypedExpr::FlatMapList(gt, Box::new(t_lambda), _ret_type.clone(), Box::new(t_seq)) } Expr::Dup(count, x) => { - let count_t = self.elaborate_expr(count); - let x_t = self.elaborate_expr(x); + let t_count = self.elaborate_expr(count); + let t_x = self.elaborate_expr(x); + let gt = self.get_gt_from_index(index); + TypedExpr::Dup(gt, Box::new(t_count), Box::new(t_x)) + } + Expr::EnumFromTo(from, to) => { + let t_from = self.elaborate_expr(from); + let t_to = self.elaborate_expr(to); let gt = self.get_gt_from_index(index); - TypedExpr::Dup(gt, Box::new(count_t), Box::new(x_t)) + TypedExpr::EnumFromTo(gt, Box::new(t_from), Box::new(t_to)) } Expr::LiftOption(opt) => { let t_expr = if let Some(expr) = opt { @@ -3845,7 +3857,6 @@ impl<'a> TypedDynScope<'a> { mod tests { use super::*; use crate::helper::compute; - use crate::output; use crate::{typecheck::Ctxt, TypeHint}; fn population_check(module: &FormatModule, f: &Format, label: Option<&'static str>) { diff --git a/src/codegen/typed_format.rs b/src/codegen/typed_format.rs index 598253b5..fec4958e 100644 --- a/src/codegen/typed_format.rs +++ b/src/codegen/typed_format.rs @@ -539,6 +539,7 @@ pub enum TypedExpr { Box>, ), Dup(TypeRep, Box>, Box>), + EnumFromTo(TypeRep, Box>, Box>), LiftOption(TypeRep, Option>>), Unary(TypeRep, UnaryOp, Box>), } @@ -633,6 +634,10 @@ impl std::hash::Hash for TypedExpr { n.hash(state); x.hash(state); } + TypedExpr::EnumFromTo(_, from, to) => { + from.hash(state); + to.hash(state); + } TypedExpr::LiftOption(_, opt) => opt.hash(state), } } @@ -689,7 +694,8 @@ impl TypedExpr { | TypedExpr::LeftFold(gt, ..) | TypedExpr::FlatMapList(gt, ..) | TypedExpr::LiftOption(gt, ..) - | TypedExpr::Dup(gt, ..) => Some(Cow::Borrowed(gt)), + | TypedExpr::Dup(gt, ..) + | TypedExpr::EnumFromTo(gt, ..) => Some(Cow::Borrowed(gt)), } } } @@ -844,6 +850,7 @@ mod __impls { Expr::FlatMapList(rebox(lambda), vt, rebox(seq)) } TypedExpr::Dup(_, count, x) => Expr::Dup(rebox(count), rebox(x)), + TypedExpr::EnumFromTo(_, start, stop) => Expr::EnumFromTo(rebox(start), rebox(stop)), TypedExpr::LiftOption(_, None) => Expr::LiftOption(None), TypedExpr::LiftOption(_, Some(x)) => Expr::LiftOption(Some(rebox(x))), } diff --git a/src/decoder.rs b/src/decoder.rs index 5f0d4eaf..0fe33dad 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -13,7 +13,8 @@ use std::collections::HashMap; use std::rc::Rc; pub mod seq_kind; -pub use seq_kind::SeqKind; +use seq_kind::sub_range; +pub use seq_kind::{SeqKind, ValueSeq}; pub(crate) fn extract_pair(mut vec: Vec) -> (T, T) { if vec.len() != 2 { @@ -36,6 +37,8 @@ pub enum Value { U32(u32), U64(u64), Char(char), + Usize(usize), + EnumFromTo(std::ops::Range), Option(Option>), Tuple(Vec), Record(Vec<(Label, Value)>), @@ -45,6 +48,12 @@ pub enum Value { Branch(usize, Box), } +impl From for Value { + fn from(value: usize) -> Value { + Value::Usize(value) + } +} + impl Value { fn tuple_proj(&self, index: usize) -> &Self { match self.coerce_mapped_value() { @@ -137,9 +146,10 @@ impl Value { } } - pub(crate) fn get_sequence(&self) -> Option<&SeqKind> { + pub(crate) fn get_sequence(&self) -> Option> { match self { - Value::Seq(elts) => Some(elts), + Value::Seq(elts) => Some(ValueSeq::ValueSeq(elts)), + Value::EnumFromTo(range) => Some(ValueSeq::IntRange(range.clone())), _ => None, } } @@ -179,6 +189,7 @@ impl Value { Value::U16(n) => usize::from(n), Value::U32(n) => usize::try_from(n).unwrap(), Value::U64(n) => usize::try_from(n).unwrap(), + Value::Usize(n) => n, _ => panic!("value is not a number"), } } @@ -187,7 +198,7 @@ impl Value { pub(crate) fn get_as_u8(&self) -> u8 { match self { Value::U8(n) => *n, - Value::U16(..) | Value::U32(..) | Value::U64(..) => panic!("value is numeric but not u8 (this may be a soft error, or even success, in future)"), + Value::U16(..) | Value::U32(..) | Value::U64(..) | Value::Usize(..) => panic!("value is numeric but not u8 (this may be a soft error, or even success, in future)"), _ => panic!("value is not a number"), } } @@ -559,36 +570,65 @@ impl Expr { } _ => panic!("SeqLength: expected Seq"), }, - Expr::SeqIx(seq, index) => cow_map(seq.eval(scope), |v| { + Expr::SeqIx(seq, index) => cow_remap(seq.eval(scope), |v| { match v.coerce_mapped_value().get_sequence() { - Some(values) => { - let index = index.eval_value(scope).unwrap_usize(); - &values[index] + Some(values) => match values { + ValueSeq::ValueSeq(values) => { + let index = index.eval_value(scope).unwrap_usize(); + Cow::Borrowed(&values[index]) + } + ValueSeq::IntRange(mut range) => { + let index = index.eval_value(scope).unwrap_usize(); + Cow::Owned(Value::from(range.nth(index).unwrap())) + } } - _ => panic!("SeqIx: expected Seq"), + _ => panic!("SeqIx: expected Seq (or RangeFromTo)"), } }), Expr::SubSeq(seq, start, length) => { match seq.eval(scope).coerce_mapped_value().get_sequence() { - Some(values) => { - let start = start.eval_value(scope).unwrap_usize(); - let length = length.eval_value(scope).unwrap_usize(); - Cow::Owned(Value::Seq(values.sub_seq(start, length))) + Some(values) => match values { + ValueSeq::ValueSeq(values) => { + let start = start.eval_value(scope).unwrap_usize(); + let length = length.eval_value(scope).unwrap_usize(); + Cow::Owned(Value::Seq(values.sub_seq(start, length))) + } + ValueSeq::IntRange(range) => { + let start = start.eval_value(scope).unwrap_usize(); + let length = length.eval_value(scope).unwrap_usize(); + Cow::Owned(Value::EnumFromTo(sub_range(range, start, length))) + } } _ => panic!("SubSeq: expected Seq"), } } Expr::SubSeqInflate(seq, start, length) => { match seq.eval(scope).coerce_mapped_value().get_sequence() { - Some(vs0) => { + Some(values) => { let start = start.eval_value(scope).unwrap_usize(); let length = length.eval_value(scope).unwrap_usize(); let mut vs = Vec::new(); - for i in 0..length { - if i + start < vs0.len() { - vs.push(vs0[i + start].clone()); - } else { - vs.push(vs[i + start - vs0.len()].clone()); + match values { + ValueSeq::ValueSeq(vs0) => { + for i in 0..length { + if i + start < vs0.len() { + vs.push(vs0[i + start].clone()); + } else { + vs.push(vs[i + start - vs0.len()].clone()); + } + } + } + ValueSeq::IntRange(range) => { + // REVIEW - double-check this logic + let len = range.len(); + let mut iter = range.skip(start); + for i in 0..length { + if let Some(val) = iter.next() { + vs.push(val.into()); + } else { + vs.push(vs[i + start - len].clone()); + } + } } } Cow::Owned(Value::Seq(vs.into())) @@ -601,10 +641,16 @@ impl Expr { Some(values) => { let mut vs = Vec::new(); for v in values { - if let Value::Seq(vn) = expr.eval_lambda(scope, v) { - vs.extend(vn); - } else { - panic!("FlatMap: expected Seq"); + match expr.eval_lambda(scope, &v) { + Value::Seq(vn) => { + vs.extend(vn); + } + Value::EnumFromTo(range) => { + vs.extend(range.map(Value::from)); + } + _ => { + panic!("FlatMap: expected Seq (or EnumFromTo)"); + } } } Cow::Owned(Value::Seq(vs.into())) @@ -668,6 +714,11 @@ impl Expr { let v = expr.eval_value(scope); Cow::Owned(Value::Seq(SeqKind::Dup(count, Box::new(v)))) } + Expr::EnumFromTo(start, stop) => { + let start = start.eval_value(scope).unwrap_usize(); + let stop = stop.eval_value(scope).unwrap_usize(); + Cow::Owned(Value::EnumFromTo(start..stop)) + } Expr::LiftOption(opt) => match opt { Some(expr) => Cow::Owned(Value::Option(Some(Box::new(expr.eval_value(scope))))), None => Cow::Owned(Value::Option(None)), @@ -1338,7 +1389,7 @@ impl Decoder { let bytes = { let raw = bytes.eval_value(scope); let seq_vals = raw.get_sequence().expect("bad type for DecodeBytes input"); - seq_vals.iter().map(|v| v.get_as_u8()).collect::>() + seq_vals.into_iter().map(|v| v.get_as_u8()).collect::>() }; let new_input = ReadCtxt::new(&bytes); let (va, rem_input) = a.parse(program, scope, new_input)?; @@ -1364,8 +1415,8 @@ impl Decoder { let val = expr.eval_value(scope); let seq = val.get_sequence().expect("bad type for ForEach input"); let mut v = Vec::with_capacity(seq.len()); - for e in seq.iter() { - let new_scope = Scope::Single(SingleScope::new(scope, lbl, e)); + for e in seq { + let new_scope = Scope::Single(SingleScope::new(scope, lbl, &e)); let (va, next_input) = a.parse(program, &new_scope, input)?; v.push(va); input = next_input; @@ -1650,6 +1701,18 @@ where } } +/// Like [`cow_map`], but applies a closure argument `f` that itself returns a `Cow<'i, U>` value (instead of a `&'i U` value) +pub(crate) fn cow_remap<'a, T, U>(x: Cow<'a, T>, f: impl for<'i> Fn(&'i T) -> Cow<'i, U>) -> Cow<'a, U> +where + T: 'static + Clone, + U: 'static + Clone + ToOwned, +{ + match x { + Cow::Borrowed(x) => f(x), + Cow::Owned(x) => Cow::Owned(f(&x).into_owned()), + } +} + #[cfg(test)] #[allow(clippy::redundant_clone)] mod tests { diff --git a/src/decoder/seq_kind.rs b/src/decoder/seq_kind.rs index eb8a9618..5770e169 100644 --- a/src/decoder/seq_kind.rs +++ b/src/decoder/seq_kind.rs @@ -1,4 +1,4 @@ -use std::{fmt::Debug, ops::Index}; +use std::{borrow::Cow, fmt::Debug, ops::Index}; use serde::Serialize; @@ -9,6 +9,61 @@ pub enum SeqKind { Dup(usize, Box), } +#[derive(Debug, Clone, PartialEq)] +pub enum ValueSeq<'a, V: Clone = super::Value> { + ValueSeq(&'a SeqKind), + IntRange(std::ops::Range), +} + +impl<'a, V: Clone> ValueSeq<'a, V> { + pub fn len(&self) -> usize { + match self { + ValueSeq::ValueSeq(vs) => vs.len(), + ValueSeq::IntRange(r) => r.len(), + } + } +} + +pub(crate) fn sub_range(range: std::ops::Range, start: usize, len: usize) -> std::ops::Range { + assert!(start + len < range.len(), "sub_range invalid: start={start} len={len} range={range:?}"); + range.start + start..range.start + start + len +} + +pub enum ValueIter<'a, V: Clone = super::Value> { + ValueIter(Iter<'a, V>), + IntRange(std::ops::Range), +} + +impl<'a, V> Iterator for ValueIter<'a, V> +where + V: Clone + From, +{ + type Item = Cow<'a, V>; + + fn next(&mut self) -> Option { + match self { + ValueIter::ValueIter(vs) => vs.next().map(Cow::Borrowed), + ValueIter::IntRange(r) => r.next().map(|i| Cow::Owned(V::from(i))), + } + } +} + +impl<'a, V: Clone> IntoIterator for ValueSeq<'a, V> +where + V: From +{ + type Item = Cow<'a, V>; + + type IntoIter = ValueIter<'a, V>; + + fn into_iter(self) -> Self::IntoIter { + match self { + ValueSeq::ValueSeq(vs) => ValueIter::ValueIter(vs.into_iter()), + ValueSeq::IntRange(r) => ValueIter::IntRange(r), + } + } +} + impl SeqKind { /// Constructs an empty (strict) `SeqKind` value. pub const fn new() -> Self { @@ -44,16 +99,11 @@ impl SeqKind { /// Return a reference to the value at index `ix` in the sequence, if it is in-bounds, or /// `None` if it is out-of-bounds. - pub fn get(&self, ix: usize) -> Option<&T> { + pub fn get(&self, ix: usize) -> Option<&T> + { match self { SeqKind::Strict(vs) => vs.get(ix), - SeqKind::Dup(n, v) => { - if ix >= *n { - None - } else { - Some(v) - } - } + SeqKind::Dup(n, v) => (ix < *n).then_some(&**v), } } @@ -71,7 +121,7 @@ impl SeqKind { SeqKind::Dup(len, v.clone()) } else { // REVIEW - we can either enforce `T: Debug` above, to add in the T-param, or keep it abstract - panic!("sub-seq out of bounds: {start}, {len} on SeqKind::Dup({n}, _)") + panic!("sub-seq out of bounds: start-index={start}, len={len} on SeqKind::Dup({n}, _)") } } } @@ -102,7 +152,8 @@ pub enum Iter<'a, T> { Dup(std::iter::RepeatN<&'a T>), } -impl<'a, T> Iterator for Iter<'a, T> { +impl<'a, T> Iterator for Iter<'a, T> +{ type Item = &'a T; fn next(&mut self) -> Option { diff --git a/src/helper.rs b/src/helper.rs index 5ce05602..461f1324 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -1292,3 +1292,9 @@ where Box::new(dep_format(Expr::Var(Label::Borrowed(clone_varname)))), ) } + + +/// Helper for [`Expr::EnumFromTo`]. +pub fn enum_from_to(start: Expr, end: Expr) -> Expr { + Expr::EnumFromTo(Box::new(start), Box::new(end)) +} diff --git a/src/lib.rs b/src/lib.rs index a56baa85..e865fef1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -170,7 +170,8 @@ pub enum Expr { /// FlatMapList :: ((\[U\], T) -> \[U\]) -> TypeRep U -> \[T\] -> \[U\] (lambda call yields a run of values to append to the final list) FlatMapList(Box, TypeHint, Box), - // EnumFromTo(Box, Box), + /// EnumFromTo :: (Num, Num) -> \[Num\] + EnumFromTo(Box, Box), /// LeftFold :: ((Acc, T) -> Acc) -> Acc -> \[T\] -> Acc LeftFold(Box, Box, TypeHint, Box), @@ -486,18 +487,18 @@ impl Expr { }, other => Err(anyhow!("FlatMapList: expected Lambda, found {other:?}")), }, - // Expr::EnumFromTo(start, end) => { - // let start_type = start.infer_type(scope)?; - // let end_type = end.infer_type(scope)?; - - // if !matches!(start_type, ValueType::Base(b) if b.is_numeric()) { - // return Err(anyhow!("EnumFromTo: start is not numeric: {start_type:?}")); - // } else if start_type != end_type { - // return Err(anyhow!("EnumFromTo: start and end do not agree: {start_type:?} != {end_type:?}")); - // } - - // Ok(ValueType::Seq(Box::new(start_type))) - // } + Expr::EnumFromTo(start, end) => { + let start_type = start.infer_type(scope)?; + let end_type = end.infer_type(scope)?; + + if !matches!(start_type, ValueType::Base(b) if b.is_numeric()) { + return Err(anyhow!("EnumFromTo: start is not numeric: {start_type:?}")); + } else if start_type != end_type { + return Err(anyhow!("EnumFromTo: start and end do not agree: {start_type:?} != {end_type:?}")); + } + + Ok(ValueType::Seq(Box::new(start_type))) + } Expr::Dup(count, expr) => { if count.infer_type(scope)? != ValueType::Base(BaseType::U32) { return Err(anyhow!("Dup: count is not U32: {count:?}")); @@ -566,9 +567,9 @@ impl Expr { | Expr::U64Be(x) | Expr::U64Le(x) | Expr::SeqLength(x) => x.is_shadowed_by(name), - // Expr::EnumFromTo(s, e) => { - // s.is_shadowed_by(name) || e.is_shadowed_by(name) - // } + Expr::EnumFromTo(s, e) => { + s.is_shadowed_by(name) || e.is_shadowed_by(name) + } Expr::SubSeq(x, s, l) | Expr::SubSeqInflate(x, s, l) => { x.is_shadowed_by(name) || s.is_shadowed_by(name) || l.is_shadowed_by(name) } diff --git a/src/loc_decoder.rs b/src/loc_decoder.rs index 745dfa48..496e0a47 100644 --- a/src/loc_decoder.rs +++ b/src/loc_decoder.rs @@ -1,5 +1,6 @@ use crate::byte_set::ByteSet; -use crate::decoder::{extract_pair, Compiler, ScopeEntry, SeqKind}; +use crate::decoder::seq_kind::sub_range; +use crate::decoder::{cow_remap, extract_pair, Compiler, ScopeEntry, SeqKind, ValueSeq}; use crate::error::{DecodeError, LocDecodeError}; use crate::read::ReadCtxt; use crate::UnaryOp; @@ -153,6 +154,17 @@ pub enum ParsedValue { Option(Option>), } +impl From for ParsedValue { + fn from(v: usize) -> Self { + ParsedValue::Flat(Parsed { + inner: Value::Usize(v), + loc: ParseLoc::Synthesized, + }) + } +} + + + impl ParsedValue { /// Returns the [`ParseLoc`] directly associated with this `ParsedValue`. pub fn get_loc(&self) -> ParseLoc { @@ -421,6 +433,8 @@ impl ParsedValue { | Value::U16(_) | Value::U32(_) | Value::U64(_) + | Value::Usize(_) + | Value::EnumFromTo(_) | Value::Char(_) => ParsedValue::Flat(Parsed { loc: ParseLoc::Synthesized, inner: expr_value, @@ -480,9 +494,10 @@ impl ParsedValue { }) } - fn get_sequence(&self) -> Option<&SeqKind> { + fn get_sequence(&self) -> Option> { match self { - ParsedValue::Seq(parsed) => Some(&parsed.inner), + ParsedValue::Seq(parsed) => Some(ValueSeq::ValueSeq(&parsed.inner)), + ParsedValue::Flat(Parsed { inner: Value::EnumFromTo(range), .. }) => Some(ValueSeq::IntRange(range.clone())), _ => None, } } @@ -868,15 +883,24 @@ impl Expr { let len = values.len(); Cow::Owned(ParsedValue::from_evaluated(Value::U32(len as u32))) } - _ => panic!("SeqLength: expected Seq"), + _ => panic!("SeqLength: expected Seq (or EnumFromTo)"), }, - Expr::SeqIx(seq, index) => cow_map(seq.eval_with_loc(scope), |v| { + Expr::SeqIx(seq, index) => cow_remap(seq.eval_with_loc(scope), |v| { match v.coerce_mapped_value().get_sequence() { Some(values) => { let index = index.eval_value_with_loc(scope).unwrap_usize(); - &values[index] + match values { + ValueSeq::ValueSeq(values) => { + Cow::Borrowed(&values[index]) + } + ValueSeq::IntRange(mut range) => { + Cow::Owned(ParsedValue::from_evaluated( + Value::Usize(range.nth(index).unwrap()) + )) + } + } } - _ => panic!("SeqIx: expected Seq"), + _ => panic!("SeqIx: expected Seq (or EnumFromTo)"), } }), Expr::SubSeq(seq, start, length) => { @@ -888,9 +912,18 @@ impl Expr { Some(values) => { let start = start.eval_value_with_loc(scope).unwrap_usize(); let length = length.eval_value_with_loc(scope).unwrap_usize(); - Cow::Owned(ParsedValue::from_evaluated_seq( - values.sub_seq(start, length), - )) + match values { + ValueSeq::ValueSeq(values) => { + Cow::Owned(ParsedValue::from_evaluated_seq( + values.sub_seq(start, length), + )) + } + ValueSeq::IntRange(range) => { + Cow::Owned(ParsedValue::from_evaluated( + Value::EnumFromTo(sub_range(range, start, length)) + )) + } + } } _ => panic!("SubSeq: expected Seq"), } @@ -901,15 +934,31 @@ impl Expr { .coerce_mapped_value() .get_sequence() { - Some(vs0) => { + Some(values) => { let start = start.eval_value_with_loc(scope).unwrap_usize(); let length = length.eval_value_with_loc(scope).unwrap_usize(); let mut vs = Vec::new(); - for i in 0..length { - if i + start < vs0.len() { - vs.push(vs0[i + start].clone()); - } else { - vs.push(vs[i + start - vs0.len()].clone()); + match values { + ValueSeq::ValueSeq(vs0) => { + for i in 0..length { + if i + start < vs0.len() { + vs.push(vs0[i + start].clone()); + } else { + vs.push(vs[i + start - vs0.len()].clone()); + } + } + } + ValueSeq::IntRange(range) => { + // REVIEW - double-check this logic + let len = range.len(); + let mut iter = range.skip(start); + for i in 0..length { + if let Some(val) = iter.next() { + vs.push(val.into()); + } else { + vs.push(vs[i + start - len].clone()); + } + } } } Cow::Owned(ParsedValue::from_evaluated_seq(vs)) @@ -925,8 +974,8 @@ impl Expr { { Some(values) => { let mut vs: Vec = Vec::new(); - for v in values.iter() { - if let Value::Seq(vn) = expr.eval_lambda_with_loc(scope, v) { + for v in values { + if let Value::Seq(vn) = expr.eval_lambda_with_loc(scope, &v) { vs.extend(vn); } else { panic!("FlatMap: expected Seq"); @@ -1007,6 +1056,11 @@ impl Expr { Box::new(v), )))) } + Expr::EnumFromTo(start, stop) => { + let start = start.eval_value_with_loc(scope).unwrap_usize(); + let stop = stop.eval_value_with_loc(scope).unwrap_usize(); + Cow::Owned(ParsedValue::from_evaluated(Value::EnumFromTo(start..stop))) + } Expr::LiftOption(opt) => Cow::Owned(ParsedValue::from_evaluated(Value::Option( opt.as_ref() .map(|expr| Box::new(expr.eval_value_with_loc(scope))), @@ -1235,8 +1289,8 @@ impl Decoder { let val = expr.eval_with_loc(scope); let seq = val.get_sequence().expect("bad type for ForEach input"); let mut v = Vec::with_capacity(seq.len()); - for e in seq.iter() { - let new_scope = LocScope::Single(LocSingleScope::new(scope, lbl, e)); + for e in seq { + let new_scope = LocScope::Single(LocSingleScope::new(scope, lbl, &e)); let (va, next_input) = a.parse_with_loc(program, &new_scope, input)?; v.push(va); input = next_input; @@ -1248,7 +1302,7 @@ impl Decoder { let bytes = { let raw = bytes.eval_value_with_loc(scope); let seq_vals = raw.get_sequence().expect("bad type for DecodeBytes input"); - seq_vals.iter().map(|v| v.get_as_u8()).collect::>() + seq_vals.into_iter().map(|v| v.get_as_u8()).collect::>() }; let new_input = ReadCtxt::new(&bytes); let (va, rem_input) = a.parse_with_loc(program, scope, new_input)?; diff --git a/src/output/tree.rs b/src/output/tree.rs index 3430bb2e..6dbf50fe 100644 --- a/src/output/tree.rs +++ b/src/output/tree.rs @@ -141,9 +141,11 @@ impl<'module> TreePrinter<'module> { Value::Char(_) => true, Value::Bool(_) => true, Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => true, + Value::Usize(_) => true, Value::Tuple(values) => values.is_empty(), Value::Record(fields) => fields.is_empty(), Value::Seq(values) => values.is_empty(), + Value::EnumFromTo(range) => range.is_empty(), // since this nominally represents a Seq, apply Seq-style logic Value::Variant(label, value) => match format { Some(Format::Variant(label2, format)) => { assert_eq!(label, label2); @@ -595,9 +597,17 @@ impl<'module> TreePrinter<'module> { Value::U16(i) => Fragment::DisplayAtom(Rc::new(*i)), Value::U32(i) => Fragment::DisplayAtom(Rc::new(*i)), Value::U64(i) => Fragment::DisplayAtom(Rc::new(*i)), + Value::Usize(i) => Fragment::DisplayAtom(Rc::new(*i)), Value::Char(c) => Fragment::DebugAtom(Rc::new(*c)), Value::Tuple(vals) => self.compile_tuple(vals, None), Value::Seq(vals) => self.compile_seq(vals, None), + Value::EnumFromTo(range) => { + Fragment::intervene( + Fragment::DisplayAtom(Rc::new(range.start)), + Fragment::string(".."), + Fragment::DisplayAtom(Rc::new(range.end)) + ).delimit(Fragment::Char('['), Fragment::Char(']')) + } Value::Record(fields) => self.compile_record(fields, None), Value::Variant(label, value) => self.compile_variant(label, value, None), Value::Mapped(orig, value) => { @@ -1711,18 +1721,18 @@ impl<'module> TreePrinter<'module> { prec, Precedence::FUN_APPLICATION, ), - // Expr::EnumFromTo(start, stop) => cond_paren( - // self.binary_op( - // " .. ", - // start, - // stop, - // Precedence::FUN_APPLICATION, // REVIEW - determine whether this precedence is proper - // Precedence::FUN_APPLICATION, - // ), - // prec, - // // REVIEW - determine whether this precedence is proper - // Precedence::FUN_APPLICATION, - // ), + Expr::EnumFromTo(start, stop) => cond_paren( + self.binary_op( + " .. ", + start, + stop, + Precedence::FUN_APPLICATION, // REVIEW - determine whether this precedence is proper + Precedence::FUN_APPLICATION, + ), + prec, + // REVIEW - determine whether this precedence is proper + Precedence::FUN_APPLICATION, + ), Expr::LiftOption(Some(expr)) => cond_paren( self.prefix_op("some", None, expr), prec, diff --git a/src/typecheck.rs b/src/typecheck.rs index 6e9f3830..8c64fd36 100644 --- a/src/typecheck.rs +++ b/src/typecheck.rs @@ -749,7 +749,7 @@ impl UintSet { let ranks = match val { IntWidth::Bits8 => [Rank::At(0), Rank::At(3), Rank::At(3), Rank::At(3)], IntWidth::Bits16 => [Rank::At(3), Rank::At(0), Rank::At(3), Rank::At(3)], - IntWidth::Bits32 => [Rank::At(3), Rank::At(3), Rank::At(0), Rank::At(0)], + IntWidth::Bits32 => [Rank::At(3), Rank::At(3), Rank::At(0), Rank::At(3)], IntWidth::Bits64 => [Rank::At(3), Rank::At(3), Rank::At(3), Rank::At(0)], }; UintSet { ranks } @@ -2216,15 +2216,18 @@ impl TypeChecker { ys_var } - // Expr::EnumFromTo(begin, end) => { - // let newvar = self.get_new_uvar(); - // let begin_var = self.infer_var_expr(begin, scope)?; - // let end_var = self.infer_var_expr(end, scope)?; - // self.unify_var_baseset(begin_var, BaseSet::USome)?; - // self.unify_var_pair(begin_var, end_var)?; - // self.unify_var_proj_elem(newvar, begin_var)?; - // newvar - // } + Expr::EnumFromTo(start, stop) => { + let newvar = self.get_new_uvar(); + let start_var = self.infer_var_expr(start, scope)?; + let stop_var = self.infer_var_expr(stop, scope)?; + + self.unify_var_baseset(start_var, BaseSet::USome)?; + self.unify_var_pair(start_var, stop_var)?; + + self.unify_var_proj_elem(newvar, start_var)?; + + newvar + } Expr::Dup(count, x) => { let newvar = self.get_new_uvar(); let count_var = self.infer_var_expr(count, scope)?; From 41337fef3faa60ecf3201af38044a19d3a2b2a4d Mon Sep 17 00:00:00 2001 From: Peter Duchovni Date: Fri, 31 Jan 2025 14:32:03 +1100 Subject: [PATCH 2/6] Rehashed gvar and glyf/loca using enum-from-to --- doodle-formats/src/format.rs | 6 +- doodle-formats/src/format/opentype.rs | 262 ++++++++++---------------- generated/gencode.rs | 134 ++++--------- src/codegen/typed_format.rs | 4 +- src/decoder.rs | 14 +- src/decoder/seq_kind.rs | 19 +- src/helper.rs | 8 +- src/lib.rs | 8 +- src/loc_decoder.rs | 38 ++-- src/output/tree.rs | 13 +- 10 files changed, 195 insertions(+), 311 deletions(-) diff --git a/doodle-formats/src/format.rs b/doodle-formats/src/format.rs index cbe1afb1..c80f4b85 100644 --- a/doodle-formats/src/format.rs +++ b/doodle-formats/src/format.rs @@ -108,11 +108,7 @@ mod test { ("len", base.u32be()), ( "mask", - with_relative_offset( - None, - var("len"), - Format::Byte(mask_bytes), - ), + with_relative_offset(None, var("len"), Format::Byte(mask_bytes)), ), ( "data", diff --git a/doodle-formats/src/format/opentype.rs b/doodle-formats/src/format/opentype.rs index 39e060df..34e992d6 100644 --- a/doodle-formats/src/format/opentype.rs +++ b/doodle-formats/src/format/opentype.rs @@ -3,6 +3,10 @@ use doodle::bounds::Bounds; use doodle::{helper::*, Expr, IntoLabel, Label}; use doodle::{BaseType, Format, FormatModule, FormatRef, Pattern, ValueType}; +fn id(x: T) -> T { + x +} + fn shadow_check(x: &Expr, name: &'static str) { if x.is_shadowed_by(name) { panic!("Shadow! Variable-name {name} already occurs in Expr {x:?}!"); @@ -97,6 +101,30 @@ fn embedded_singleton_alternation( Format::Record(accum) } +fn for_each_pair( + seq: Expr, + premap: (impl FnOnce(Expr) -> Expr, impl FnOnce(Expr) -> Expr), + labels: [&'static str; 2], + dep_format: Format, +) -> Format { + Format::Let( + Label::Borrowed("len"), + Box::new(pred(seq_length(seq.clone()))), + Box::new(for_each( + enum_from_to(Expr::U32(0), var("len")), + "ix", + with_tuple( + Expr::Tuple(vec![ + premap.0(index_unchecked(seq.clone(), var("ix"))), + premap.1(index_unchecked(seq.clone(), succ(var("ix")))), + ]), + labels, + dep_format, + ), + )), + ) +} + fn embedded_variadic_alternation( shared_fields: [(&'static str, Format); OUTER], discriminant: &'static str, @@ -171,69 +199,15 @@ fn vhea_long_metrics(vhea: Expr) -> Expr { record_proj(expr_unwrap(vhea), "number_of_long_metrics") } -/// attempting to index on its `.offsets` key through an option-unpacking indirection. +/// Attemptis to index on the `offsets` key of `loca` through an option-unpacking indirection. /// /// Helper function to handle the fact that though loca only appears alongside glyf, both are optional tables -fn loca_offset_pairs(loca: Expr) -> Expr { - let f = |loca_table: Expr| offsets_to_offset_pairs(record_proj(loca_table, "offsets")); - let loca_empty = variant("Offset32Pairs", seq_empty()); +fn loca_offsets(loca: Expr) -> Expr { + let f = |loca_table: Expr| record_proj(loca_table, "offsets"); + let loca_empty = variant("Offsets32", seq_empty()); expr_option_map_or(loca_empty, f, loca) } -/// Generic self-zip that returns `(values->[i], values->[i+1])` at index `i` in the result, -/// for `i` from `0` to `N - 1` (where `values` has `N + 1` total elements). -fn values_to_pairs(values: Expr, elem_type: ValueType) -> Expr { - flat_map_accum( - lambda_tuple( - ["last_value", "value"], - pair( - expr_some(var("value")), - expr_option_map_or( - seq_empty(), - |last| singleton(pair(last, var("value"))), - var("last_value"), - ), - ), - ), - expr_none(), - ValueType::Option(Box::new(elem_type)), - values, - ) -} - -/// Specialized transformation that converts a Variant-wrapped array of the structural type -/// -/// ```ignore -/// { Offsets16( seq(u16) ) | Offsets32( seq(u32) ) } -/// ``` -/// -/// into a sequence of normalized U32-kinded offset pairs, scaling `u16` values by 2x as is implicitly -/// demanded by the format of `loca` and `gvar`. -/// -/// The first element in the array will be the pair `(offsets[0], offsets[1])`, and the final will be `(offsets[N-1], offsets[N])`, -/// where the original offset-sequence contains `N + 1` raw elements. -fn offsets_to_offset_pairs(offsets: Expr) -> Expr { - expr_match( - offsets, - [ - ( - Pattern::Variant(Label::Borrowed("Offsets16"), Box::new(bind("half16s"))), - variant( - "Offset16Pairs", - values_to_pairs(var("half16s"), ValueType::Base(BaseType::U16)), - ), - ), - ( - Pattern::Variant(Label::Borrowed("Offsets32"), Box::new(bind("off32s"))), - variant( - "Offset32Pairs", - values_to_pairs(var("off32s"), ValueType::Base(BaseType::U32)), - ), - ), - ], - ) -} - /// Doubles a `U16`-kinded Expr into a `U32`-kinded output. fn scale2(half: Expr) -> Expr { mul(as_u32(half), Expr::U32(2)) @@ -1934,18 +1908,16 @@ pub fn main(module: &mut FormatModule, base: &BaseModule) -> FormatRef { ) }; - let offset_pairs_type = { - let mk_branch = |elem_t: ValueType| { - ValueType::Seq(Box::new(ValueType::Tuple(vec![elem_t.clone(), elem_t]))) - }; + let offsets_type = { + let mk_branch = |elem_t: ValueType| ValueType::Seq(Box::new(elem_t)); let mut branches = std::collections::BTreeMap::new(); // NOTE - at this layer, the u16-valued offsets are still half-value branches.insert( - Label::Borrowed("Offset16Pairs"), + Label::Borrowed("Offsets16"), mk_branch(ValueType::Base(BaseType::U16)), ); branches.insert( - Label::Borrowed("Offset32Pairs"), + Label::Borrowed("Offsets32"), mk_branch(ValueType::Base(BaseType::U32)), ); ValueType::Union(branches) @@ -1953,48 +1925,42 @@ pub fn main(module: &mut FormatModule, base: &BaseModule) -> FormatRef { module.define_format_args( "opentype.glyf_table", - vec![(Label::Borrowed("offset_pairs"), offset_pairs_type)], + vec![(Label::Borrowed("offsets"), offsets_type)], chain( pos32(), "start_offset", Format::Match( - Box::new(var("offset_pairs")), + Box::new(var("offsets")), vec![ ( Pattern::Variant( - Label::Borrowed("Offset16Pairs"), - Box::new(bind("half16_pairs")), + Label::Borrowed("Offsets16"), + Box::new(bind("half16s")), ), - for_each( - var("half16_pairs"), - "half16_pair", - with_tuple( - var("half16_pair"), - ["this_half16", "next_half16"], - glyf_table_entry( - var("start_offset"), - scale2(var("this_half16")), - scale2(var("next_half16")), - ), + for_each_pair( + var("half16s"), + (scale2, scale2), + ["this_offs", "next_offs"], + glyf_table_entry( + var("start_offset"), + var("this_offs"), + var("next_offs"), ), ), ), ( Pattern::Variant( - Label::Borrowed("Offset32Pairs"), - Box::new(bind("offs32_pairs")), + Label::Borrowed("Offsets32"), + Box::new(bind("off32s")), ), - for_each( - var("offs32_pairs"), - "offs32_pair", - with_tuple( - var("offs32_pair"), - ["this_offs32", "next_offs32"], - glyf_table_entry( - var("start_offset"), - var("this_offs32"), - var("next_offs32"), - ), + for_each_pair( + var("off32s"), + (id, id), + ["this_offs", "next_offs"], + glyf_table_entry( + var("start_offset"), + var("this_offs"), + var("next_offs"), ), ), ), @@ -4784,71 +4750,50 @@ pub fn main(module: &mut FormatModule, base: &BaseModule) -> FormatRef { ), ) }; - let glyph_variation_data_table_array = |axis_count: Expr| { - |offset_pairs: Expr| { - chain( - pos32(), - "array_start", - Format::Match( - Box::new(offset_pairs), - vec![ - ( - Pattern::Variant( - Label::Borrowed("Offset16Pairs"), - Box::new(bind("half16_pairs")), - ), - for_each( - var("half16_pairs"), - "half16_pair", - with_tuple( - var("half16_pair"), - ["this_half16", "next_half16"], - offset_linked_gvar_data_table( - axis_count.clone(), - var("array_start"), - scale2(var("this_half16")), - scale2(var("next_half16")), - ), - ), - ), + let glyph_variation_data_table_array = |axis_count: Expr, offsets: Expr| { + chain( + pos32(), + "array_start", + Format::Match( + Box::new(offsets), + vec![ + ( + Pattern::Variant( + Label::Borrowed("Offsets16"), + Box::new(bind("half16s")), ), - ( - Pattern::Variant( - Label::Borrowed("Offset32Pairs"), - Box::new(bind("offs32_pairs")), + for_each_pair( + var("half16s"), + (scale2, scale2), + ["this_offs", "next_offs"], + offset_linked_gvar_data_table( + axis_count.clone(), + var("array_start"), + var("this_offs"), + var("next_offs"), ), - for_each( - var("offs32_pairs"), - "offs32_pair", - with_tuple( - var("offs32_pair"), - ["this_offs32", "next_offs32"], - offset_linked_gvar_data_table( - axis_count, - var("array_start"), - var("this_offs32"), - var("next_offs32"), - ), - ), + ), + ), + ( + Pattern::Variant( + Label::Borrowed("Offsets32"), + Box::new(bind("off32s")), + ), + for_each_pair( + var("off32s"), + (id, id), + ["this_offs", "next_offs"], + offset_linked_gvar_data_table( + axis_count, + var("array_start"), + var("this_offs"), + var("next_offs"), ), ), - ], - ), - ) - } - }; - let array_from_offsets = |gvar_table_start: Expr, - glyph_variation_data_array_offset: Expr, - axis_count: Expr| { - |offsets: Expr| -> Format { - linked_offset32( - gvar_table_start, - glyph_variation_data_array_offset, - glyph_variation_data_table_array(axis_count)(offsets_to_offset_pairs( - offsets, - )), - ) - } + ), + ], + ), + ) }; let shared_tuples = |shared_tuple_count: Expr, axis_count: Expr| { repeat_count(shared_tuple_count, tuple_record.call_args(vec![axis_count])) @@ -4892,13 +4837,14 @@ pub fn main(module: &mut FormatModule, base: &BaseModule) -> FormatRef { ), ( "glyph_variation_data_array", - clone_hack( + // FIXME - this is a hack to force a clone to avoid use-after-move + fmt_let( var("glyph_variation_data_offsets"), - "offs", - array_from_offsets( + "offsets", + linked_offset32( var("gvar_table_start"), var("glyph_variation_data_array_offset"), - var("axis_count"), + glyph_variation_data_table_array(var("axis_count"), var("offsets")), ), ), ), @@ -4983,7 +4929,7 @@ pub fn main(module: &mut FormatModule, base: &BaseModule) -> FormatRef { START_VAR, var("tables"), magic(b"glyf"), - glyf_table.call_args(vec![loca_offset_pairs(var("loca"))]), + glyf_table.call_args(vec![loca_offsets(var("loca"))]), ), ), ( diff --git a/generated/gencode.rs b/generated/gencode.rs index 185aad9a..61724fd4 100644 --- a/generated/gencode.rs +++ b/generated/gencode.rs @@ -3772,9 +3772,6 @@ format: u16, __reserved: u16 } -#[derive(Debug, Clone)] -pub enum opentype_gvar_table_glyph_variation_data_array_POISON { Offset16Pairs(Vec<(u16, u16)>), Offset32Pairs(Vec<(u32, u32)>) } - #[derive(Debug, Clone)] pub struct opentype_glyf_simple_flags_raw { repeats: u8, @@ -6196,43 +6193,11 @@ let sz = (table.length.clone()) as usize<>; _input.start_slice(sz)?; let ret = ((|| PResult::Ok((Decoder36(_input, match loca { Some(ref x) => { -match x.offsets.clone() { -opentype_gvar_table_glyph_variation_data_offsets::Offsets16(half16s) => { -opentype_gvar_table_glyph_variation_data_array_POISON::Offset16Pairs((try_fold_map_curried(half16s.iter().cloned(), None, |tuple_var: (Option, u16)| PResult::Ok(match tuple_var { -(last_value, value) => { -(Some(value.clone()), match last_value { -Some(x) => { -[(x.clone(), value.clone())].to_vec() +x.offsets.clone() }, None => { -[].to_vec() -} -}) -} -})))?) -}, - -opentype_gvar_table_glyph_variation_data_offsets::Offsets32(off32s) => { -opentype_gvar_table_glyph_variation_data_array_POISON::Offset32Pairs((try_fold_map_curried(off32s.iter().cloned(), None, |tuple_var: (Option, u32)| PResult::Ok(match tuple_var { -(last_value, value) => { -(Some(value.clone()), match last_value { -Some(x) => { -[(x.clone(), value.clone())].to_vec() -}, - -None => { -[].to_vec() -} -}) -} -})))?) -} -} -}, - -None => { -opentype_gvar_table_glyph_variation_data_array_POISON::Offset32Pairs([].to_vec()) +opentype_gvar_table_glyph_variation_data_offsets::Offsets32([].to_vec()) } }))?))())?; _input.end_slice()?; @@ -7390,20 +7355,21 @@ unreachable!(r#"ExprMatch refuted: match refuted with unexpected value {_other:? PResult::Ok(opentype_loca_table { offsets }) } -fn Decoder36<'input>(_input: &mut Parser<'input>, offset_pairs: opentype_gvar_table_glyph_variation_data_array_POISON) -> Result, ParseError> { +fn Decoder36<'input>(_input: &mut Parser<'input>, offsets: opentype_gvar_table_glyph_variation_data_offsets) -> Result, ParseError> { let start_offset = { let inner = _input.get_offset_u64(); ((|x: u64| PResult::Ok(x as u32))(inner))? }; -PResult::Ok(match offset_pairs { -opentype_gvar_table_glyph_variation_data_array_POISON::Offset16Pairs(half16_pairs) => { +PResult::Ok(match offsets { +opentype_gvar_table_glyph_variation_data_offsets::Offsets16(half16s) => { +let len = pred((half16s.len()) as u32); let mut accum = Vec::new(); -for half16_pair in half16_pairs.clone() { -accum.push(match half16_pair { -(this_half16, next_half16) => { -match (next_half16 as u32) * 2u32 > (this_half16 as u32) * 2u32 { +for ix in 0u32..len { +accum.push(match (((half16s[ix as usize].clone()) as u32) * 2u32, ((half16s[(succ(ix)) as usize].clone()) as u32) * 2u32) { +(this_offs, next_offs) => { +match next_offs > this_offs { true => { -let tgt_offset = start_offset + (this_half16 as u32) * 2u32; +let tgt_offset = start_offset + this_offs; let _is_advance = _input.advance_or_seek(tgt_offset)?; let ret = ((|| PResult::Ok({ let inner = { @@ -7431,14 +7397,15 @@ opentype_glyf_table::EmptyGlyph accum }, -opentype_gvar_table_glyph_variation_data_array_POISON::Offset32Pairs(offs32_pairs) => { +opentype_gvar_table_glyph_variation_data_offsets::Offsets32(off32s) => { +let len = pred((off32s.len()) as u32); let mut accum = Vec::new(); -for offs32_pair in offs32_pairs.clone() { -accum.push(match offs32_pair { -(this_offs32, next_offs32) => { -match next_offs32 > this_offs32 { +for ix in 0u32..len { +accum.push(match (off32s[ix as usize].clone(), off32s[(succ(ix)) as usize].clone()) { +(this_offs, next_offs) => { +match next_offs > this_offs { true => { -let tgt_offset = start_offset + this_offs32; +let tgt_offset = start_offset + this_offs; let _is_advance = _input.advance_or_seek(tgt_offset)?; let ret = ((|| PResult::Ok({ let inner = { @@ -8807,7 +8774,7 @@ opentype_gvar_table_glyph_variation_data_offsets::Offsets16(inner) } }))())?; let glyph_variation_data_array = ((|| PResult::Ok({ -let offs = glyph_variation_data_offsets.clone(); +let offsets = glyph_variation_data_offsets.clone(); let tgt_offset = gvar_table_start + glyph_variation_data_array_offset; let _is_advance = _input.advance_or_seek(tgt_offset)?; let ret = ((|| PResult::Ok({ @@ -8815,49 +8782,18 @@ let array_start = { let inner = _input.get_offset_u64(); ((|x: u64| PResult::Ok(x as u32))(inner))? }; -match match offs { +match offsets { opentype_gvar_table_glyph_variation_data_offsets::Offsets16(half16s) => { -opentype_gvar_table_glyph_variation_data_array_POISON::Offset16Pairs((try_fold_map_curried(half16s.iter().cloned(), None, |tuple_var: (Option, u16)| PResult::Ok(match tuple_var { -(last_value, value) => { -(Some(value.clone()), match last_value { -Some(x) => { -[(x.clone(), value.clone())].to_vec() -}, - -None => { -[].to_vec() -} -}) -} -})))?) -}, - -opentype_gvar_table_glyph_variation_data_offsets::Offsets32(off32s) => { -opentype_gvar_table_glyph_variation_data_array_POISON::Offset32Pairs((try_fold_map_curried(off32s.iter().cloned(), None, |tuple_var: (Option, u32)| PResult::Ok(match tuple_var { -(last_value, value) => { -(Some(value.clone()), match last_value { -Some(x) => { -[(x.clone(), value.clone())].to_vec() -}, - -None => { -[].to_vec() -} -}) -} -})))?) -} -} { -opentype_gvar_table_glyph_variation_data_array_POISON::Offset16Pairs(half16_pairs) => { +let len = pred((half16s.len()) as u32); let mut accum = Vec::new(); -for half16_pair in half16_pairs.clone() { -accum.push(match half16_pair { -(this_half16, next_half16) => { -if (next_half16 as u32) * 2u32 > (this_half16 as u32) * 2u32 { -let tgt_offset = array_start + (this_half16 as u32) * 2u32; +for ix in 0u32..len { +accum.push(match (((half16s[ix as usize].clone()) as u32) * 2u32, ((half16s[(succ(ix)) as usize].clone()) as u32) * 2u32) { +(this_offs, next_offs) => { +if next_offs > this_offs { +let tgt_offset = array_start + this_offs; let _is_advance = _input.advance_or_seek(tgt_offset)?; let ret = ((|| PResult::Ok({ -let sz = (try_sub!((next_half16 as u32) * 2u32, (this_half16 as u32) * 2u32, 17170585774888887431u64)) as usize<>; +let sz = (try_sub!(next_offs, this_offs, 17170585774888887431u64)) as usize<>; _input.start_slice(sz)?; let ret = ((|| PResult::Ok((Decoder_opentype_var_glyph_variation_data_table(_input, axis_count.clone()))?))())?; _input.end_slice()?; @@ -8874,16 +8810,17 @@ None accum }, -opentype_gvar_table_glyph_variation_data_array_POISON::Offset32Pairs(offs32_pairs) => { +opentype_gvar_table_glyph_variation_data_offsets::Offsets32(off32s) => { +let len = pred((off32s.len()) as u32); let mut accum = Vec::new(); -for offs32_pair in offs32_pairs.clone() { -accum.push(match offs32_pair { -(this_offs32, next_offs32) => { -if next_offs32 > this_offs32 { -let tgt_offset = array_start + this_offs32; +for ix in 0u32..len { +accum.push(match (off32s[ix as usize].clone(), off32s[(succ(ix)) as usize].clone()) { +(this_offs, next_offs) => { +if next_offs > this_offs { +let tgt_offset = array_start + this_offs; let _is_advance = _input.advance_or_seek(tgt_offset)?; let ret = ((|| PResult::Ok({ -let sz = (try_sub!(next_offs32, this_offs32, 1548601315919054830u64)) as usize<>; +let sz = (try_sub!(next_offs, this_offs, 1548601315919054830u64)) as usize<>; _input.start_slice(sz)?; let ret = ((|| PResult::Ok((Decoder_opentype_var_glyph_variation_data_table(_input, axis_count.clone()))?))())?; _input.end_slice()?; @@ -33267,4 +33204,3 @@ PResult::Ok(((|tuple_var: (Vec, u8)| PResult::Ok(match tuple_var { } }))(inner))?) } - diff --git a/src/codegen/typed_format.rs b/src/codegen/typed_format.rs index fec4958e..534e5e42 100644 --- a/src/codegen/typed_format.rs +++ b/src/codegen/typed_format.rs @@ -850,7 +850,9 @@ mod __impls { Expr::FlatMapList(rebox(lambda), vt, rebox(seq)) } TypedExpr::Dup(_, count, x) => Expr::Dup(rebox(count), rebox(x)), - TypedExpr::EnumFromTo(_, start, stop) => Expr::EnumFromTo(rebox(start), rebox(stop)), + TypedExpr::EnumFromTo(_, start, stop) => { + Expr::EnumFromTo(rebox(start), rebox(stop)) + } TypedExpr::LiftOption(_, None) => Expr::LiftOption(None), TypedExpr::LiftOption(_, Some(x)) => Expr::LiftOption(Some(rebox(x))), } diff --git a/src/decoder.rs b/src/decoder.rs index 0fe33dad..d6860d17 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -581,7 +581,7 @@ impl Expr { let index = index.eval_value(scope).unwrap_usize(); Cow::Owned(Value::from(range.nth(index).unwrap())) } - } + }, _ => panic!("SeqIx: expected Seq (or RangeFromTo)"), } }), @@ -598,7 +598,7 @@ impl Expr { let length = length.eval_value(scope).unwrap_usize(); Cow::Owned(Value::EnumFromTo(sub_range(range, start, length))) } - } + }, _ => panic!("SubSeq: expected Seq"), } } @@ -1389,7 +1389,10 @@ impl Decoder { let bytes = { let raw = bytes.eval_value(scope); let seq_vals = raw.get_sequence().expect("bad type for DecodeBytes input"); - seq_vals.into_iter().map(|v| v.get_as_u8()).collect::>() + seq_vals + .into_iter() + .map(|v| v.get_as_u8()) + .collect::>() }; let new_input = ReadCtxt::new(&bytes); let (va, rem_input) = a.parse(program, scope, new_input)?; @@ -1702,7 +1705,10 @@ where } /// Like [`cow_map`], but applies a closure argument `f` that itself returns a `Cow<'i, U>` value (instead of a `&'i U` value) -pub(crate) fn cow_remap<'a, T, U>(x: Cow<'a, T>, f: impl for<'i> Fn(&'i T) -> Cow<'i, U>) -> Cow<'a, U> +pub(crate) fn cow_remap<'a, T, U>( + x: Cow<'a, T>, + f: impl for<'i> Fn(&'i T) -> Cow<'i, U>, +) -> Cow<'a, U> where T: 'static + Clone, U: 'static + Clone + ToOwned, diff --git a/src/decoder/seq_kind.rs b/src/decoder/seq_kind.rs index 5770e169..c7db6f0a 100644 --- a/src/decoder/seq_kind.rs +++ b/src/decoder/seq_kind.rs @@ -24,8 +24,15 @@ impl<'a, V: Clone> ValueSeq<'a, V> { } } -pub(crate) fn sub_range(range: std::ops::Range, start: usize, len: usize) -> std::ops::Range { - assert!(start + len < range.len(), "sub_range invalid: start={start} len={len} range={range:?}"); +pub(crate) fn sub_range( + range: std::ops::Range, + start: usize, + len: usize, +) -> std::ops::Range { + assert!( + start + len < range.len(), + "sub_range invalid: start={start} len={len} range={range:?}" + ); range.start + start..range.start + start + len } @@ -50,7 +57,7 @@ where impl<'a, V: Clone> IntoIterator for ValueSeq<'a, V> where - V: From + V: From, { type Item = Cow<'a, V>; @@ -99,8 +106,7 @@ impl SeqKind { /// Return a reference to the value at index `ix` in the sequence, if it is in-bounds, or /// `None` if it is out-of-bounds. - pub fn get(&self, ix: usize) -> Option<&T> - { + pub fn get(&self, ix: usize) -> Option<&T> { match self { SeqKind::Strict(vs) => vs.get(ix), SeqKind::Dup(n, v) => (ix < *n).then_some(&**v), @@ -152,8 +158,7 @@ pub enum Iter<'a, T> { Dup(std::iter::RepeatN<&'a T>), } -impl<'a, T> Iterator for Iter<'a, T> -{ +impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { diff --git a/src/helper.rs b/src/helper.rs index 461f1324..44942f3f 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -1282,18 +1282,14 @@ pub fn pos32() -> Format { /// Hack to get around gvar codegen issues where we need to persist a variable after /// it is moved (rather than cloned or referenced) in the current model of the codegen /// and the implementation of the Opentype Format-tree. -pub fn clone_hack(orig: Expr, clone_varname: &'static str, dep_format: F) -> Format -where - F: FnOnce(Expr) -> Format, -{ +pub fn fmt_let(orig: Expr, clone_varname: &'static str, dep_format: Format) -> Format { Format::Let( Label::Borrowed(clone_varname), Box::new(orig), - Box::new(dep_format(Expr::Var(Label::Borrowed(clone_varname)))), + Box::new(dep_format), ) } - /// Helper for [`Expr::EnumFromTo`]. pub fn enum_from_to(start: Expr, end: Expr) -> Expr { Expr::EnumFromTo(Box::new(start), Box::new(end)) diff --git a/src/lib.rs b/src/lib.rs index e865fef1..fe862368 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -494,7 +494,9 @@ impl Expr { if !matches!(start_type, ValueType::Base(b) if b.is_numeric()) { return Err(anyhow!("EnumFromTo: start is not numeric: {start_type:?}")); } else if start_type != end_type { - return Err(anyhow!("EnumFromTo: start and end do not agree: {start_type:?} != {end_type:?}")); + return Err(anyhow!( + "EnumFromTo: start and end do not agree: {start_type:?} != {end_type:?}" + )); } Ok(ValueType::Seq(Box::new(start_type))) @@ -567,9 +569,7 @@ impl Expr { | Expr::U64Be(x) | Expr::U64Le(x) | Expr::SeqLength(x) => x.is_shadowed_by(name), - Expr::EnumFromTo(s, e) => { - s.is_shadowed_by(name) || e.is_shadowed_by(name) - } + Expr::EnumFromTo(s, e) => s.is_shadowed_by(name) || e.is_shadowed_by(name), Expr::SubSeq(x, s, l) | Expr::SubSeqInflate(x, s, l) => { x.is_shadowed_by(name) || s.is_shadowed_by(name) || l.is_shadowed_by(name) } diff --git a/src/loc_decoder.rs b/src/loc_decoder.rs index 496e0a47..95cff485 100644 --- a/src/loc_decoder.rs +++ b/src/loc_decoder.rs @@ -163,8 +163,6 @@ impl From for ParsedValue { } } - - impl ParsedValue { /// Returns the [`ParseLoc`] directly associated with this `ParsedValue`. pub fn get_loc(&self) -> ParseLoc { @@ -497,7 +495,10 @@ impl ParsedValue { fn get_sequence(&self) -> Option> { match self { ParsedValue::Seq(parsed) => Some(ValueSeq::ValueSeq(&parsed.inner)), - ParsedValue::Flat(Parsed { inner: Value::EnumFromTo(range), .. }) => Some(ValueSeq::IntRange(range.clone())), + ParsedValue::Flat(Parsed { + inner: Value::EnumFromTo(range), + .. + }) => Some(ValueSeq::IntRange(range.clone())), _ => None, } } @@ -890,13 +891,11 @@ impl Expr { Some(values) => { let index = index.eval_value_with_loc(scope).unwrap_usize(); match values { - ValueSeq::ValueSeq(values) => { - Cow::Borrowed(&values[index]) - } + ValueSeq::ValueSeq(values) => Cow::Borrowed(&values[index]), ValueSeq::IntRange(mut range) => { - Cow::Owned(ParsedValue::from_evaluated( - Value::Usize(range.nth(index).unwrap()) - )) + Cow::Owned(ParsedValue::from_evaluated(Value::Usize( + range.nth(index).unwrap(), + ))) } } } @@ -913,16 +912,12 @@ impl Expr { let start = start.eval_value_with_loc(scope).unwrap_usize(); let length = length.eval_value_with_loc(scope).unwrap_usize(); match values { - ValueSeq::ValueSeq(values) => { - Cow::Owned(ParsedValue::from_evaluated_seq( - values.sub_seq(start, length), - )) - } - ValueSeq::IntRange(range) => { - Cow::Owned(ParsedValue::from_evaluated( - Value::EnumFromTo(sub_range(range, start, length)) - )) - } + ValueSeq::ValueSeq(values) => Cow::Owned( + ParsedValue::from_evaluated_seq(values.sub_seq(start, length)), + ), + ValueSeq::IntRange(range) => Cow::Owned(ParsedValue::from_evaluated( + Value::EnumFromTo(sub_range(range, start, length)), + )), } } _ => panic!("SubSeq: expected Seq"), @@ -1302,7 +1297,10 @@ impl Decoder { let bytes = { let raw = bytes.eval_value_with_loc(scope); let seq_vals = raw.get_sequence().expect("bad type for DecodeBytes input"); - seq_vals.into_iter().map(|v| v.get_as_u8()).collect::>() + seq_vals + .into_iter() + .map(|v| v.get_as_u8()) + .collect::>() }; let new_input = ReadCtxt::new(&bytes); let (va, rem_input) = a.parse_with_loc(program, scope, new_input)?; diff --git a/src/output/tree.rs b/src/output/tree.rs index 6dbf50fe..d0d61a2f 100644 --- a/src/output/tree.rs +++ b/src/output/tree.rs @@ -601,13 +601,12 @@ impl<'module> TreePrinter<'module> { Value::Char(c) => Fragment::DebugAtom(Rc::new(*c)), Value::Tuple(vals) => self.compile_tuple(vals, None), Value::Seq(vals) => self.compile_seq(vals, None), - Value::EnumFromTo(range) => { - Fragment::intervene( - Fragment::DisplayAtom(Rc::new(range.start)), - Fragment::string(".."), - Fragment::DisplayAtom(Rc::new(range.end)) - ).delimit(Fragment::Char('['), Fragment::Char(']')) - } + Value::EnumFromTo(range) => Fragment::intervene( + Fragment::DisplayAtom(Rc::new(range.start)), + Fragment::string(".."), + Fragment::DisplayAtom(Rc::new(range.end)), + ) + .delimit(Fragment::Char('['), Fragment::Char(']')), Value::Record(fields) => self.compile_record(fields, None), Value::Variant(label, value) => self.compile_variant(label, value, None), Value::Mapped(orig, value) => { From 8124c37e29e8b6cfc8b49717183aeaf417cf05ab Mon Sep 17 00:00:00 2001 From: Peter Duchovni Date: Fri, 31 Jan 2025 14:32:54 +1100 Subject: [PATCH 3/6] Remove doc-comment for `fmt_let` (formerly `clone_hack`) --- src/helper.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/helper.rs b/src/helper.rs index 44942f3f..36c1e546 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -1279,9 +1279,6 @@ pub fn pos32() -> Format { map(Format::Pos, lambda("x", Expr::AsU32(Box::new(var("x"))))) } -/// Hack to get around gvar codegen issues where we need to persist a variable after -/// it is moved (rather than cloned or referenced) in the current model of the codegen -/// and the implementation of the Opentype Format-tree. pub fn fmt_let(orig: Expr, clone_varname: &'static str, dep_format: Format) -> Format { Format::Let( Label::Borrowed(clone_varname), @@ -1290,6 +1287,7 @@ pub fn fmt_let(orig: Expr, clone_varname: &'static str, dep_format: Format) -> F ) } + /// Helper for [`Expr::EnumFromTo`]. pub fn enum_from_to(start: Expr, end: Expr) -> Expr { Expr::EnumFromTo(Box::new(start), Box::new(end)) From c1d3a9bf4c60ba94fe4d13fd6f726dded8200137 Mon Sep 17 00:00:00 2001 From: Peter Duchovni Date: Mon, 3 Feb 2025 17:31:54 +1100 Subject: [PATCH 4/6] Apply `ref_hack` to all pattern-variants (except when Copy) --- doodle-formats/src/format/opentype.rs | 17 +++-- generated/gencode.rs | 54 +++++++------- src/codegen/mod.rs | 12 ++- src/codegen/typed_format.rs | 32 ++++++++ src/codegen/typed_format/variables.rs | 102 ++++++++++++++++++++++++++ src/helper.rs | 1 - 6 files changed, 181 insertions(+), 37 deletions(-) create mode 100644 src/codegen/typed_format/variables.rs diff --git a/doodle-formats/src/format/opentype.rs b/doodle-formats/src/format/opentype.rs index 34e992d6..acf41520 100644 --- a/doodle-formats/src/format/opentype.rs +++ b/doodle-formats/src/format/opentype.rs @@ -4838,15 +4838,18 @@ pub fn main(module: &mut FormatModule, base: &BaseModule) -> FormatRef { ( "glyph_variation_data_array", // FIXME - this is a hack to force a clone to avoid use-after-move - fmt_let( - var("glyph_variation_data_offsets"), - "offsets", - linked_offset32( - var("gvar_table_start"), - var("glyph_variation_data_array_offset"), - glyph_variation_data_table_array(var("axis_count"), var("offsets")), + // fmt_let( + // var("glyph_variation_data_offsets"), + // "offsets", + linked_offset32( + var("gvar_table_start"), + var("glyph_variation_data_array_offset"), + glyph_variation_data_table_array( + var("axis_count"), + var("glyph_variation_data_offsets"), ), ), + // ), ), ]), ) diff --git a/generated/gencode.rs b/generated/gencode.rs index 61724fd4..097dfd9b 100644 --- a/generated/gencode.rs +++ b/generated/gencode.rs @@ -327,7 +327,7 @@ distance: u16 } #[derive(Debug, Clone)] -pub enum deflate_main_codes__dupX1 { literal(u8), reference(deflate_main_codes_reference) } +pub enum deflate_main_codes { literal(u8), reference(deflate_main_codes_reference) } #[derive(Debug, Clone)] pub struct deflate_dynamic_huffman { @@ -340,7 +340,7 @@ literal_length_distance_alphabet_code_lengths_value: Vec, literal_length_alphabet_code_lengths_value: Vec, distance_alphabet_code_lengths_value: Vec, codes: Vec, -codes_values: Vec +codes_values: Vec } #[derive(Debug, Clone)] @@ -360,7 +360,7 @@ extra: Option #[derive(Debug, Clone)] pub struct deflate_fixed_huffman { codes: Vec, -codes_values: Vec +codes_values: Vec } #[derive(Debug, Clone)] @@ -369,23 +369,23 @@ align: (), len: u16, nlen: u16, bytes: Vec, -codes_values: Vec +codes_values: Vec } #[derive(Debug, Clone)] -pub enum deflate_main_codes { dynamic_huffman(deflate_dynamic_huffman), fixed_huffman(deflate_fixed_huffman), uncompressed(deflate_uncompressed) } +pub enum deflate_main_codes__dupX1 { dynamic_huffman(deflate_dynamic_huffman), fixed_huffman(deflate_fixed_huffman), uncompressed(deflate_uncompressed) } #[derive(Debug, Clone)] pub struct deflate_block { r#final: u8, r#type: u8, -data: deflate_main_codes +data: deflate_main_codes__dupX1 } #[derive(Debug, Clone)] pub struct deflate_main { blocks: Vec, -codes: Vec, +codes: Vec, inflate: Vec } @@ -7361,7 +7361,7 @@ let inner = _input.get_offset_u64(); ((|x: u64| PResult::Ok(x as u32))(inner))? }; PResult::Ok(match offsets { -opentype_gvar_table_glyph_variation_data_offsets::Offsets16(half16s) => { +opentype_gvar_table_glyph_variation_data_offsets::Offsets16(ref half16s) => { let len = pred((half16s.len()) as u32); let mut accum = Vec::new(); for ix in 0u32..len { @@ -7397,7 +7397,7 @@ opentype_glyf_table::EmptyGlyph accum }, -opentype_gvar_table_glyph_variation_data_offsets::Offsets32(off32s) => { +opentype_gvar_table_glyph_variation_data_offsets::Offsets32(ref off32s) => { let len = pred((off32s.len()) as u32); let mut accum = Vec::new(); for ix in 0u32..len { @@ -8774,7 +8774,6 @@ opentype_gvar_table_glyph_variation_data_offsets::Offsets16(inner) } }))())?; let glyph_variation_data_array = ((|| PResult::Ok({ -let offsets = glyph_variation_data_offsets.clone(); let tgt_offset = gvar_table_start + glyph_variation_data_array_offset; let _is_advance = _input.advance_or_seek(tgt_offset)?; let ret = ((|| PResult::Ok({ @@ -8782,8 +8781,8 @@ let array_start = { let inner = _input.get_offset_u64(); ((|x: u64| PResult::Ok(x as u32))(inner))? }; -match offsets { -opentype_gvar_table_glyph_variation_data_offsets::Offsets16(half16s) => { +match glyph_variation_data_offsets { +opentype_gvar_table_glyph_variation_data_offsets::Offsets16(ref half16s) => { let len = pred((half16s.len()) as u32); let mut accum = Vec::new(); for ix in 0u32..len { @@ -8810,7 +8809,7 @@ None accum }, -opentype_gvar_table_glyph_variation_data_offsets::Offsets32(off32s) => { +opentype_gvar_table_glyph_variation_data_offsets::Offsets32(ref off32s) => { let len = pred((off32s.len()) as u32); let mut accum = Vec::new(); for ix in 0u32..len { @@ -17300,26 +17299,26 @@ accum.push(elem); accum }))())?; let codes = ((|| PResult::Ok((try_flat_map_vec(blocks.iter().cloned(), |x: deflate_block| PResult::Ok(match x.data.clone() { -deflate_main_codes::uncompressed(y) => { +deflate_main_codes__dupX1::uncompressed(ref y) => { y.codes_values.clone() }, -deflate_main_codes::fixed_huffman(y) => { +deflate_main_codes__dupX1::fixed_huffman(ref y) => { y.codes_values.clone() }, -deflate_main_codes::dynamic_huffman(y) => { +deflate_main_codes__dupX1::dynamic_huffman(ref y) => { y.codes_values.clone() } })))?))())?; -let inflate = ((|| PResult::Ok((try_flat_map_append_vec(codes.iter().cloned(), |tuple_var: (&Vec, deflate_main_codes__dupX1)| PResult::Ok(match tuple_var { +let inflate = ((|| PResult::Ok((try_flat_map_append_vec(codes.iter().cloned(), |tuple_var: (&Vec, deflate_main_codes)| PResult::Ok(match tuple_var { (buffer, symbol) => { match symbol { -deflate_main_codes__dupX1::literal(b) => { +deflate_main_codes::literal(b) => { [b].to_vec() }, -deflate_main_codes__dupX1::reference(r) => { +deflate_main_codes::reference(r) => { { let ix = (try_sub!((buffer.len()) as u32, (r.distance.clone()) as u32, 11669649807369914251u64)) as usize; (slice_ext(&buffer, ix..ix + (((r.length.clone()) as u32) as usize))).to_vec() @@ -17344,17 +17343,17 @@ let field1 = ((|| PResult::Ok((Decoder166(_input))?))())?; let data = ((|| PResult::Ok(match r#type { 0u8 => { let inner = (Decoder_deflate_uncompressed(_input))?; -deflate_main_codes::uncompressed(inner) +deflate_main_codes__dupX1::uncompressed(inner) }, 1u8 => { let inner = (Decoder_deflate_fixed_huffman(_input))?; -deflate_main_codes::fixed_huffman(inner) +deflate_main_codes__dupX1::fixed_huffman(inner) }, 2u8 => { let inner = (Decoder_deflate_dynamic_huffman(_input))?; -deflate_main_codes::dynamic_huffman(inner) +deflate_main_codes__dupX1::dynamic_huffman(inner) }, _other => { @@ -17435,7 +17434,7 @@ let field7 = ((|| PResult::Ok((Decoder166(_input))?))())?; } accum }))())?; -let codes_values = ((|| PResult::Ok((try_flat_map_vec(bytes.iter().cloned(), |x: u8| PResult::Ok([deflate_main_codes__dupX1::literal(x)].to_vec())))?))())?; +let codes_values = ((|| PResult::Ok((try_flat_map_vec(bytes.iter().cloned(), |x: u8| PResult::Ok([deflate_main_codes::literal(x)].to_vec())))?))())?; PResult::Ok(deflate_uncompressed { align, len, nlen, bytes, codes_values }) } @@ -18214,7 +18213,7 @@ let codes_values = ((|| PResult::Ok((try_flat_map_vec(codes.iter().cloned(), |x: 257u16..=285u16 => { match x.extra.clone() { Some(ref rec) => { -[deflate_main_codes__dupX1::reference(deflate_main_codes_reference { length: rec.length.clone(), distance: rec.distance_record.distance.clone() })].to_vec() +[deflate_main_codes::reference(deflate_main_codes_reference { length: rec.length.clone(), distance: rec.distance_record.distance.clone() })].to_vec() }, _ => { @@ -18228,7 +18227,7 @@ return Err(ParseError::ExcludedBranch(8880661182590738257u64)); }, _ => { -[deflate_main_codes__dupX1::literal((x.code.clone()) as u8)].to_vec() +[deflate_main_codes::literal((x.code.clone()) as u8)].to_vec() } })))?))())?; PResult::Ok(deflate_fixed_huffman { codes, codes_values }) @@ -18914,7 +18913,7 @@ let codes_values = ((|| PResult::Ok((try_flat_map_vec(codes.iter().cloned(), |x: 257u16..=285u16 => { match x.extra.clone() { Some(ref rec) => { -[deflate_main_codes__dupX1::reference(deflate_main_codes_reference { length: rec.length.clone(), distance: rec.distance_record.distance.clone() })].to_vec() +[deflate_main_codes::reference(deflate_main_codes_reference { length: rec.length.clone(), distance: rec.distance_record.distance.clone() })].to_vec() }, _ => { @@ -18928,7 +18927,7 @@ return Err(ParseError::ExcludedBranch(13685962128001446815u64)); }, _ => { -[deflate_main_codes__dupX1::literal((x.code.clone()) as u8)].to_vec() +[deflate_main_codes::literal((x.code.clone()) as u8)].to_vec() } })))?))())?; PResult::Ok(deflate_dynamic_huffman { hlit, hdist, hclen, code_length_alphabet_code_lengths, literal_length_distance_alphabet_code_lengths, literal_length_distance_alphabet_code_lengths_value, literal_length_alphabet_code_lengths_value, distance_alphabet_code_lengths_value, codes, codes_values }) @@ -33204,3 +33203,4 @@ PResult::Ok(((|tuple_var: (Vec, u8)| PResult::Ok(match tuple_var { } }))(inner))?) } + diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 1f3d8ec8..80c7821d 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -626,7 +626,15 @@ fn embed_pattern_t(pat: >Pattern) -> RustPattern { let constr = Constructor::Compound(tname.clone(), vname.clone()); let inner_pat = match inner.as_ref() { TypedPattern::Wildcard(..) => RustPattern::Fill, - _ => embed_pattern_t(inner), + _ => { + let inner_t = inner.get_type(); + let tmp = embed_pattern_t(inner); + if inner_t.is_copy() { + tmp + } else { + tmp.ref_hack() + } + } }; RustPattern::Variant(constr, Box::new(inner_pat)) } @@ -1034,7 +1042,7 @@ fn embed_expr(expr: >Expr, info: ExprInfo) -> RustExpr { // REVIEW - lexical scopes, shadowing, and variable-name sanitization may not be quite right in the current implementation let loc = RustExpr::local(vname.clone()); match info { - ExprInfo::EmbedCloned => RustExpr::CloneOf(Box::new(loc)), + ExprInfo::EmbedCloned => RustExpr::CloneOf(Box::new(loc)), ExprInfo::Natural => loc, } } diff --git a/src/codegen/typed_format.rs b/src/codegen/typed_format.rs index 534e5e42..cde679a3 100644 --- a/src/codegen/typed_format.rs +++ b/src/codegen/typed_format.rs @@ -8,6 +8,8 @@ use crate::bounds::Bounds; use crate::byte_set::ByteSet; use crate::{Arith, IntRel, Label, TypeHint, UnaryOp}; +pub(crate) mod variables; + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub(crate) enum GenType { Inline(RustType), @@ -42,6 +44,15 @@ impl GenType { _ => None, } } + + /// Determines whether a given [`GenType`] implements the `Copy` trait. + pub(crate) fn is_copy(&self) -> bool { + match self { + GenType::Inline(rust_type) => rust_type.can_be_copy(), + // TODO - infer recursive Copy of local definitions, if possible + GenType::Def(_, rust_type_def) => rust_type_def.can_be_copy(), + } + } } impl std::hash::Hash for TypedFormat { @@ -718,6 +729,27 @@ pub enum TypedPattern { Option(TypeRep, Option>>), } +impl TypedPattern { + pub(crate) fn get_type(&self) -> Cow<'_, GenType> { + match self { + TypedPattern::U8(..) => Cow::Owned(GenType::from(PrimType::U8)), + TypedPattern::U16(..) => Cow::Owned(GenType::from(PrimType::U16)), + TypedPattern::U32(..) => Cow::Owned(GenType::from(PrimType::U32)), + TypedPattern::U64(..) => Cow::Owned(GenType::from(PrimType::U64)), + TypedPattern::Char(..) => Cow::Owned(GenType::from(PrimType::Char)), + TypedPattern::Bool(..) => Cow::Owned(GenType::from(PrimType::Bool)), + + TypedPattern::Wildcard(gt) + | TypedPattern::Binding(gt, ..) + | TypedPattern::Tuple(gt, ..) + | TypedPattern::Option(gt, ..) + | TypedPattern::Int(gt, ..) + | TypedPattern::Variant(gt, ..) + | TypedPattern::Seq(gt, ..) => Cow::Borrowed(gt), + } + } +} + impl std::hash::Hash for TypedPattern { fn hash(&self, state: &mut H) { core::mem::discriminant(self).hash(state); diff --git a/src/codegen/typed_format/variables.rs b/src/codegen/typed_format/variables.rs new file mode 100644 index 00000000..7b594876 --- /dev/null +++ b/src/codegen/typed_format/variables.rs @@ -0,0 +1,102 @@ +#![allow(dead_code)] +#![allow(unused_imports)] + +use super::{GenType, TypedExpr}; +use crate::Label; + +impl TypedExpr { + pub(crate) fn get_vars(&self) -> Variables { + let mut vars = Variables::new(); + let ctxt = VarCtxt::new(); + self.poll_vars(&mut vars, ctxt); + vars + } + + /// Extends a collection with a list of all the variable-names used in an Expr, as well + /// as whether their provenance at site-of-use is internal or external, and how they are + /// accessed. + /// + /// If the expression is itself a raw `TypedExpr::Var`, returns the variable's verbatim identifier, otherwise None. + fn poll_vars<'a>(&'a self, _vars: &mut Variables, _ctxt: VarCtxt<'a>) -> Option<&'a Label> { + match self { + TypedExpr::U8(..) + | TypedExpr::U16(..) + | TypedExpr::U32(..) + | TypedExpr::U64(..) + | TypedExpr::Bool(..) => None, + + _ => unimplemented!(), + } + } +} + +// REVIEW - consider HashMap? +type ScopeContainer = std::collections::BTreeMap; + +#[derive(Debug, Clone, Default)] +enum VarCtxt<'a> { + #[default] + Empty, + SingleCtxt(Option<&'a VarCtxt<'a>>, &'a Label), +} + +impl<'a> VarCtxt<'a> { + pub fn new() -> Self { + VarCtxt::Empty + } +} + +#[derive(Clone, Debug, Default)] +struct PerScope { + scopes: ScopeContainer, +} + +impl PerScope { + pub fn new() -> Self { + PerScope { + scopes: ScopeContainer::new(), + } + } + + pub fn insert(&mut self, provenance: Provenance, value: T) -> Option { + self.scopes.insert(provenance, value) + } +} + +#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default, Hash)] +enum Provenance { + #[default] + External, + // De Bruijn index. 1 indicates usage-site is in the exact binding-scope of the definition site + DeBruijn(std::num::NonZeroU32), +} + +type VarStore = std::collections::HashMap; + +#[derive(Debug, Clone)] +pub(crate) struct Variables { + _store: VarStore>, +} + +impl Variables { + pub fn new() -> Self { + Variables { + _store: VarStore::new(), + } + } + + fn entry( + &mut self, + vname: Label, + ) -> std::collections::hash_map::Entry<'_, Label, PerScope> { + self._store.entry(vname) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)] +pub(crate) enum AccessKind { + /// How `x` is accessed in `let y = x;` + Rebind, + /// How `x` is accessed in `match x { .. }` + Switch, +} diff --git a/src/helper.rs b/src/helper.rs index 36c1e546..f6fc31e2 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -1287,7 +1287,6 @@ pub fn fmt_let(orig: Expr, clone_varname: &'static str, dep_format: Format) -> F ) } - /// Helper for [`Expr::EnumFromTo`]. pub fn enum_from_to(start: Expr, end: Expr) -> Expr { Expr::EnumFromTo(Box::new(start), Box::new(end)) From 8fb07c4b665d377d1c1c5c78d3a8ffab7bca0319 Mon Sep 17 00:00:00 2001 From: Peter Duchovni Date: Mon, 3 Feb 2025 18:09:55 +1100 Subject: [PATCH 5/6] Refactor find_table into with_table to clean up gencode --- doodle-formats/src/format/deflate.rs | 4 +- doodle-formats/src/format/opentype.rs | 133 ++--- generated/gencode.rs | 668 ++++++-------------------- src/helper.rs | 63 ++- 4 files changed, 256 insertions(+), 612 deletions(-) diff --git a/doodle-formats/src/format/deflate.rs b/doodle-formats/src/format/deflate.rs index f68071bb..3ea8b849 100644 --- a/doodle-formats/src/format/deflate.rs +++ b/doodle-formats/src/format/deflate.rs @@ -37,7 +37,7 @@ fn bits_value_u8(name: &'static str, n: usize) -> Expr { nodes.push(shl_u8(tuple_proj(var(name), i), cast(i))); } // construct a balanced binary tree of bitor operations - balanced_bitor_max16(nodes) + balanced_bitor(nodes) } /// Maps an `Expr::Tuple` consisting of 5 bit-values into the `u8`-typed Expr @@ -75,7 +75,7 @@ fn bits_value_u16(name: &'static str, n: usize) -> Expr { } // construct a balanced binary tree of bitor operations - balanced_bitor_max16(nodes) + balanced_bitor(nodes) } /// Parse a 5-bit Fixed Huffman distance-code and map it into its corresponding distance-symbol diff --git a/doodle-formats/src/format/opentype.rs b/doodle-formats/src/format/opentype.rs index acf41520..4d876803 100644 --- a/doodle-formats/src/format/opentype.rs +++ b/doodle-formats/src/format/opentype.rs @@ -385,19 +385,23 @@ const START_VAR: Expr = Expr::Var(Label::Borrowed("start")); const START_ARG: (Label, ValueType) = (Label::Borrowed("start"), ValueType::Base(BaseType::U32)); /// Given `Expr`s `table_records` and a `query_table_id` of the appropriate type, -/// seeks through `table_records` and extracts the first one whose projected field `table_id` -/// is a one-to-one match for `query_table_id`. -/// +/// seeks through `table_records` and filters the elements whose projected field `table_id` +/// is a one-to-one match for `query_table_id`; then, applies the dependent Format-closure +/// `dep_format` to the expression representing this array computation. + /// # Notes /// -/// Currently, only existence (and not uniqueness) of a match is required for -/// this construction to function correctly, but non-unique matches are not -/// intentionally supported and may be an error in future. +/// Callers should be aware that while not allowed by the standard, there is no type-level +/// guarantee the Expr-Seq `dep_format` is applied to, is only ever singleton-or-empty. /// /// Also note that while in principle a binary search is ideal, the constructions /// we have on hand only allow for linear search without short-circuiting for /// `Θ(N)` performance regardless of where the match is in the list. -fn find_table(table_records: Expr, query_table_id: u32) -> Expr { +fn with_table( + table_records: Expr, + query_table_id: u32, + dep_format: impl FnOnce(Expr) -> Format, +) -> Format { // FIXME[epic=perf] - this is a naive algorithm that could be improved with the right primitives in-hand // TODO[epic=binary-search]: accelerate using binary search // TODO: make use of `search_range` etc. @@ -419,8 +423,7 @@ fn find_table(table_records: Expr, query_table_id: u32) -> Expr { ), table_records, ); - // FIXME - we need either an Expr::Let or Expr::ListToOption primitive to distinguish no-match, 1-match, and multi-match - index_checked(matching_tables, Expr::U32(0)) + dep_format(matching_tables) } /// Given a raw Format `format` and an absolute buffer-offset `abs_offset`, @@ -607,18 +610,25 @@ pub fn main(module: &mut FormatModule, base: &BaseModule) -> FormatRef { id: u32, table_format: Format, ) -> Format { - Format::Let( - Label::Borrowed("matching_table"), - Box::new(expr_unwrap(find_table(table_records, id))), - Box::new(linked_offset32( - sof_offset, - record_proj(var("matching_table"), "offset"), - Format::Slice( - Box::new(record_proj(var("matching_table"), "length")), - Box::new(table_format), - ), - )), - ) + let dep_format = |table_matches: Expr| -> Format { + fmt_let( + "table_matches", + table_matches, + fmt_let( + "matching_table", + unwrap_singleton(var("table_matches")), + linked_offset32( + sof_offset, + record_proj(var("matching_table"), "offset"), + Format::Slice( + Box::new(record_proj(var("matching_table"), "length")), + Box::new(table_format), + ), + ), + ), + ) + }; + with_table(table_records, id, dep_format) } fn required_table_with_len( @@ -627,21 +637,29 @@ pub fn main(module: &mut FormatModule, base: &BaseModule) -> FormatRef { id: u32, table_format_ref: FormatRef, ) -> Format { - Format::Let( - Label::Borrowed("matching_table"), - Box::new(expr_unwrap(find_table(table_records, id))), - Box::new(linked_offset32( - sof_offset, - record_proj(var("matching_table"), "offset"), - Format::Slice( - Box::new(record_proj(var("matching_table"), "length")), - Box::new( - table_format_ref - .call_args(vec![record_proj(var("matching_table"), "length")]), - ), - ), - )), - ) + let dep_format = + |table_matches: Expr| -> Format { + fmt_let( + "table_matches", + table_matches, + fmt_let( + "matching_table", + unwrap_singleton(var("table_matches")), + linked_offset32( + sof_offset, + record_proj(var("matching_table"), "offset"), + Format::Slice( + Box::new(record_proj(var("matching_table"), "length")), + Box::new(table_format_ref.call_args(vec![record_proj( + var("matching_table"), + "length", + )])), + ), + ), + ), + ) + }; + with_table(table_records, id, dep_format) } fn optional_table( @@ -650,27 +668,28 @@ pub fn main(module: &mut FormatModule, base: &BaseModule) -> FormatRef { id: u32, table_format: Format, ) -> Format { - Format::Let( - Label::Borrowed("matching_table"), - Box::new(find_table(table_records, id)), - Box::new(Format::Match( - Box::new(var("matching_table")), - vec![ - ( - pat_some(bind("table")), - format_some(linked_offset32( - sof_offset, - record_proj(var("table"), "offset"), - Format::Slice( - Box::new(record_proj(var("table"), "length")), - Box::new(table_format), - ), - )), - ), - (pat_none(), format_none()), - ], - )), - ) + let cond_fmt = |matching_table: Expr| -> Format { + fmt_let( + "table", + matching_table, + format_some(linked_offset32( + sof_offset, + record_proj(var("table"), "offset"), + Format::Slice( + Box::new(record_proj(var("table"), "length")), + Box::new(table_format), + ), + )), + ) + }; + let dep_format = move |table_matches: Expr| -> Format { + fmt_let( + "table_matches", + table_matches, + maybe_singleton(var("table_matches"), cond_fmt), + ) + }; + with_table(table_records, id, dep_format) } let encoding_id = |_platform_id: Expr| base.u16be(); diff --git a/generated/gencode.rs b/generated/gencode.rs index 097dfd9b..cbaac3d4 100644 --- a/generated/gencode.rs +++ b/generated/gencode.rs @@ -327,7 +327,7 @@ distance: u16 } #[derive(Debug, Clone)] -pub enum deflate_main_codes { literal(u8), reference(deflate_main_codes_reference) } +pub enum deflate_main_codes__dupX1 { literal(u8), reference(deflate_main_codes_reference) } #[derive(Debug, Clone)] pub struct deflate_dynamic_huffman { @@ -340,7 +340,7 @@ literal_length_distance_alphabet_code_lengths_value: Vec, literal_length_alphabet_code_lengths_value: Vec, distance_alphabet_code_lengths_value: Vec, codes: Vec, -codes_values: Vec +codes_values: Vec } #[derive(Debug, Clone)] @@ -360,7 +360,7 @@ extra: Option #[derive(Debug, Clone)] pub struct deflate_fixed_huffman { codes: Vec, -codes_values: Vec +codes_values: Vec } #[derive(Debug, Clone)] @@ -369,23 +369,23 @@ align: (), len: u16, nlen: u16, bytes: Vec, -codes_values: Vec +codes_values: Vec } #[derive(Debug, Clone)] -pub enum deflate_main_codes__dupX1 { dynamic_huffman(deflate_dynamic_huffman), fixed_huffman(deflate_fixed_huffman), uncompressed(deflate_uncompressed) } +pub enum deflate_main_codes { dynamic_huffman(deflate_dynamic_huffman), fixed_huffman(deflate_fixed_huffman), uncompressed(deflate_uncompressed) } #[derive(Debug, Clone)] pub struct deflate_block { r#final: u8, r#type: u8, -data: deflate_main_codes__dupX1 +data: deflate_main_codes } #[derive(Debug, Clone)] pub struct deflate_main { blocks: Vec, -codes: Vec, +codes: Vec, inflate: Vec } @@ -5603,7 +5603,7 @@ PResult::Ok(opentype_table_record { table_id, checksum, offset, length }) fn Decoder_opentype_table_directory_table_links<'input>(_input: &mut Parser<'input>, start: u32, tables: Vec) -> Result { let cmap = ((|| PResult::Ok({ -let matching_table = match match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1668112752u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1668112752u32 { true => { [table.clone()].to_vec() }, @@ -5611,24 +5611,9 @@ true => { false => { [].to_vec() } -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1668112752u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?[0u32 as usize]) -}, - -false => { -None -} -} { -Some(ref x) => { +})))?; +let matching_table = match table_matches.as_slice() { +[x] => { x.clone() }, @@ -5649,17 +5634,7 @@ _input.close_peek_context()?; ret }))())?; let head = ((|| PResult::Ok({ -let matching_table = match match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1751474532u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1751474532u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1751474532u32 { true => { [table.clone()].to_vec() }, @@ -5667,14 +5642,9 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -} { -Some(ref x) => { +})))?; +let matching_table = match table_matches.as_slice() { +[x] => { x.clone() }, @@ -5695,17 +5665,7 @@ _input.close_peek_context()?; ret }))())?; let hhea = ((|| PResult::Ok({ -let matching_table = match match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1751672161u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1751672161u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1751672161u32 { true => { [table.clone()].to_vec() }, @@ -5713,14 +5673,9 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -} { -Some(ref x) => { +})))?; +let matching_table = match table_matches.as_slice() { +[x] => { x.clone() }, @@ -5741,17 +5696,7 @@ _input.close_peek_context()?; ret }))())?; let maxp = ((|| PResult::Ok({ -let matching_table = match match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1835104368u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1835104368u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1835104368u32 { true => { [table.clone()].to_vec() }, @@ -5759,14 +5704,9 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -} { -Some(ref x) => { +})))?; +let matching_table = match table_matches.as_slice() { +[x] => { x.clone() }, @@ -5787,7 +5727,7 @@ _input.close_peek_context()?; ret }))())?; let hmtx = ((|| PResult::Ok({ -let matching_table = match match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1752003704u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1752003704u32 { true => { [table.clone()].to_vec() }, @@ -5795,24 +5735,9 @@ true => { false => { [].to_vec() } -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1752003704u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?[0u32 as usize]) -}, - -false => { -None -} -} { -Some(ref x) => { +})))?; +let matching_table = match table_matches.as_slice() { +[x] => { x.clone() }, @@ -5833,17 +5758,7 @@ _input.close_peek_context()?; ret }))())?; let name = ((|| PResult::Ok({ -let matching_table = match match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1851878757u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1851878757u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1851878757u32 { true => { [table.clone()].to_vec() }, @@ -5851,14 +5766,9 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -} { -Some(ref x) => { +})))?; +let matching_table = match table_matches.as_slice() { +[x] => { x.clone() }, @@ -5879,17 +5789,7 @@ _input.close_peek_context()?; ret }))())?; let os2 = ((|| PResult::Ok({ -let matching_table = match match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1330851634u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1330851634u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1330851634u32 { true => { [table.clone()].to_vec() }, @@ -5897,14 +5797,9 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -} { -Some(ref x) => { +})))?; +let matching_table = match table_matches.as_slice() { +[x] => { x.clone() }, @@ -5925,17 +5820,7 @@ _input.close_peek_context()?; ret }))())?; let post = ((|| PResult::Ok({ -let matching_table = match match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1886352244u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1886352244u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1886352244u32 { true => { [table.clone()].to_vec() }, @@ -5943,14 +5828,9 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -} { -Some(ref x) => { +})))?; +let matching_table = match table_matches.as_slice() { +[x] => { x.clone() }, @@ -5971,7 +5851,7 @@ _input.close_peek_context()?; ret }))())?; let cvt = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1668707360u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1668707360u32 { true => { [table.clone()].to_vec() }, @@ -5979,25 +5859,10 @@ true => { false => { [].to_vec() } -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1668707360u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6034,23 +5899,13 @@ ret ((|val: Vec| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let fpgm = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1718642541u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1718642541u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1718642541u32 { true => { [table.clone()].to_vec() }, @@ -6058,15 +5913,10 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6103,23 +5953,13 @@ ret ((|val: Vec| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let loca = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1819239265u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1819239265u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1819239265u32 { true => { [table.clone()].to_vec() }, @@ -6127,15 +5967,10 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6152,13 +5987,13 @@ ret ((|val: opentype_loca_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let glyf = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1735162214u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1735162214u32 { true => { [table.clone()].to_vec() }, @@ -6166,25 +6001,10 @@ true => { false => { [].to_vec() } -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1735162214u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6209,23 +6029,13 @@ ret ((|val: Vec| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let prep = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1886545264u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1886545264u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1886545264u32 { true => { [table.clone()].to_vec() }, @@ -6233,15 +6043,10 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6278,23 +6083,13 @@ ret ((|val: Vec| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let gasp = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1734439792u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1734439792u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1734439792u32 { true => { [table.clone()].to_vec() }, @@ -6302,15 +6097,10 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6327,13 +6117,13 @@ ret ((|val: opentype_gasp_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let base = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1111577413u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1111577413u32 { true => { [table.clone()].to_vec() }, @@ -6341,25 +6131,10 @@ true => { false => { [].to_vec() } -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1111577413u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6376,23 +6151,13 @@ ret ((|val: opentype_base_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let gdef = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1195656518u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1195656518u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1195656518u32 { true => { [table.clone()].to_vec() }, @@ -6400,15 +6165,10 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6425,23 +6185,13 @@ ret ((|val: opentype_gdef_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let gpos = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1196445523u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1196445523u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1196445523u32 { true => { [table.clone()].to_vec() }, @@ -6449,15 +6199,10 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6474,13 +6219,13 @@ ret ((|val: opentype_gpos_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let gsub = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1196643650u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1196643650u32 { true => { [table.clone()].to_vec() }, @@ -6488,25 +6233,10 @@ true => { false => { [].to_vec() } -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1196643650u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6523,23 +6253,13 @@ ret ((|val: opentype_gsub_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let fvar = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1719034226u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1719034226u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1719034226u32 { true => { [table.clone()].to_vec() }, @@ -6547,15 +6267,10 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6572,23 +6287,13 @@ ret ((|val: opentype_fvar_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let gvar = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1735811442u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1735811442u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1735811442u32 { true => { [table.clone()].to_vec() }, @@ -6596,15 +6301,10 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6621,13 +6321,13 @@ ret ((|val: opentype_gvar_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let kern = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1801810542u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1801810542u32 { true => { [table.clone()].to_vec() }, @@ -6635,25 +6335,10 @@ true => { false => { [].to_vec() } -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1801810542u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6670,23 +6355,13 @@ ret ((|val: opentype_kern_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let stat = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1398030676u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1398030676u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1398030676u32 { true => { [table.clone()].to_vec() }, @@ -6694,15 +6369,10 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6719,23 +6389,13 @@ ret ((|val: opentype_stat_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let vhea = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1986553185u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1986553185u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1986553185u32 { true => { [table.clone()].to_vec() }, @@ -6743,15 +6403,10 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6768,13 +6423,13 @@ ret ((|val: opentype_hhea_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } }))())?; let vmtx = ((|| PResult::Ok({ -let matching_table = match 0u32 < (((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1986884728u32 { +let table_matches = (try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1986884728u32 { true => { [table.clone()].to_vec() }, @@ -6782,25 +6437,10 @@ true => { false => { [].to_vec() } -})))?.len()) as u32) { -true => { -Some((try_flat_map_vec(tables.iter().cloned(), |table: opentype_table_record| PResult::Ok(match table.table_id.clone() == 1986884728u32 { -true => { -[table.clone()].to_vec() -}, - -false => { -[].to_vec() -} -})))?[0u32 as usize]) -}, - -false => { -None -} -}; -match matching_table { -Some(ref table) => { +})))?; +match table_matches.as_slice() { +[x] => { +let table = x.clone(); let inner = { let tgt_offset = start + table.offset.clone(); let _is_advance = _input.advance_or_seek(tgt_offset)?; @@ -6825,7 +6465,7 @@ ret ((|val: opentype_hmtx_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } @@ -14332,15 +13972,7 @@ let mut acc = false; loop { if ((|tuple_var: (bool, &Vec)| PResult::Ok(match tuple_var { (_has_instructions, seq) => { -match match match (seq.len()) as u32 { -1u32.. => { -true -}, - -_ => { -false -} -} { +match match ((seq.len()) as u32) != 0u32 { true => { Some(seq[(pred((seq.len()) as u32)) as usize].clone()) }, @@ -17299,26 +16931,26 @@ accum.push(elem); accum }))())?; let codes = ((|| PResult::Ok((try_flat_map_vec(blocks.iter().cloned(), |x: deflate_block| PResult::Ok(match x.data.clone() { -deflate_main_codes__dupX1::uncompressed(ref y) => { +deflate_main_codes::uncompressed(ref y) => { y.codes_values.clone() }, -deflate_main_codes__dupX1::fixed_huffman(ref y) => { +deflate_main_codes::fixed_huffman(ref y) => { y.codes_values.clone() }, -deflate_main_codes__dupX1::dynamic_huffman(ref y) => { +deflate_main_codes::dynamic_huffman(ref y) => { y.codes_values.clone() } })))?))())?; -let inflate = ((|| PResult::Ok((try_flat_map_append_vec(codes.iter().cloned(), |tuple_var: (&Vec, deflate_main_codes)| PResult::Ok(match tuple_var { +let inflate = ((|| PResult::Ok((try_flat_map_append_vec(codes.iter().cloned(), |tuple_var: (&Vec, deflate_main_codes__dupX1)| PResult::Ok(match tuple_var { (buffer, symbol) => { match symbol { -deflate_main_codes::literal(b) => { +deflate_main_codes__dupX1::literal(b) => { [b].to_vec() }, -deflate_main_codes::reference(r) => { +deflate_main_codes__dupX1::reference(r) => { { let ix = (try_sub!((buffer.len()) as u32, (r.distance.clone()) as u32, 11669649807369914251u64)) as usize; (slice_ext(&buffer, ix..ix + (((r.length.clone()) as u32) as usize))).to_vec() @@ -17343,17 +16975,17 @@ let field1 = ((|| PResult::Ok((Decoder166(_input))?))())?; let data = ((|| PResult::Ok(match r#type { 0u8 => { let inner = (Decoder_deflate_uncompressed(_input))?; -deflate_main_codes__dupX1::uncompressed(inner) +deflate_main_codes::uncompressed(inner) }, 1u8 => { let inner = (Decoder_deflate_fixed_huffman(_input))?; -deflate_main_codes__dupX1::fixed_huffman(inner) +deflate_main_codes::fixed_huffman(inner) }, 2u8 => { let inner = (Decoder_deflate_dynamic_huffman(_input))?; -deflate_main_codes__dupX1::dynamic_huffman(inner) +deflate_main_codes::dynamic_huffman(inner) }, _other => { @@ -17434,7 +17066,7 @@ let field7 = ((|| PResult::Ok((Decoder166(_input))?))())?; } accum }))())?; -let codes_values = ((|| PResult::Ok((try_flat_map_vec(bytes.iter().cloned(), |x: u8| PResult::Ok([deflate_main_codes::literal(x)].to_vec())))?))())?; +let codes_values = ((|| PResult::Ok((try_flat_map_vec(bytes.iter().cloned(), |x: u8| PResult::Ok([deflate_main_codes__dupX1::literal(x)].to_vec())))?))())?; PResult::Ok(deflate_uncompressed { align, len, nlen, bytes, codes_values }) } @@ -18213,7 +17845,7 @@ let codes_values = ((|| PResult::Ok((try_flat_map_vec(codes.iter().cloned(), |x: 257u16..=285u16 => { match x.extra.clone() { Some(ref rec) => { -[deflate_main_codes::reference(deflate_main_codes_reference { length: rec.length.clone(), distance: rec.distance_record.distance.clone() })].to_vec() +[deflate_main_codes__dupX1::reference(deflate_main_codes_reference { length: rec.length.clone(), distance: rec.distance_record.distance.clone() })].to_vec() }, _ => { @@ -18227,7 +17859,7 @@ return Err(ParseError::ExcludedBranch(8880661182590738257u64)); }, _ => { -[deflate_main_codes::literal((x.code.clone()) as u8)].to_vec() +[deflate_main_codes__dupX1::literal((x.code.clone()) as u8)].to_vec() } })))?))())?; PResult::Ok(deflate_fixed_huffman { codes, codes_values }) @@ -18913,7 +18545,7 @@ let codes_values = ((|| PResult::Ok((try_flat_map_vec(codes.iter().cloned(), |x: 257u16..=285u16 => { match x.extra.clone() { Some(ref rec) => { -[deflate_main_codes::reference(deflate_main_codes_reference { length: rec.length.clone(), distance: rec.distance_record.distance.clone() })].to_vec() +[deflate_main_codes__dupX1::reference(deflate_main_codes_reference { length: rec.length.clone(), distance: rec.distance_record.distance.clone() })].to_vec() }, _ => { @@ -18927,7 +18559,7 @@ return Err(ParseError::ExcludedBranch(13685962128001446815u64)); }, _ => { -[deflate_main_codes::literal((x.code.clone()) as u8)].to_vec() +[deflate_main_codes__dupX1::literal((x.code.clone()) as u8)].to_vec() } })))?))())?; PResult::Ok(deflate_dynamic_huffman { hlit, hdist, hclen, code_length_alphabet_code_lengths, literal_length_distance_alphabet_code_lengths, literal_length_distance_alphabet_code_lengths_value, literal_length_alphabet_code_lengths_value, distance_alphabet_code_lengths_value, codes, codes_values }) diff --git a/src/helper.rs b/src/helper.rs index f6fc31e2..2639967f 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -907,11 +907,35 @@ pub fn expr_opt_if(scrutinee: Expr, if_true: Expr) -> Expr { /// Performs a fallible destructuring of the provided `expr` within the Expr layer, /// either extracting the contents of a `Some(_)` value, or resulting in an ExcludedBranch -/// error at runtime due to a fallible pattern-match. +/// error at runtime due to a failed pattern-match. pub fn expr_unwrap(expr: Expr) -> Expr { Expr::Match(Box::new(expr), vec![(pat_some(bind("x")), var("x"))]) } +/// Performs a fallible destructuring of the provided `expr` within the Expr layer, +/// either extracting the single element of a length-1 array, or resulting in an +/// ExcludedBranch error at runtime due to a failed pattern-match. +pub fn unwrap_singleton(expr: Expr) -> Expr { + Expr::Match( + Box::new(expr), + vec![(Pattern::Seq(vec![bind("x")]), var("x"))], + ) +} + +/// Evaluates to `f(x)` where `x` is the sole element of `expr`, +/// or `fmt_none` if `expr` is not a singleton +/// +/// Implicitly relies on the ValueType of the output of `opt_f` being Option<_>. +pub fn maybe_singleton(expr: Expr, opt_f: impl FnOnce(Expr) -> Format) -> Format { + Format::Match( + Box::new(expr), + vec![ + (Pattern::Seq(vec![bind("x")]), opt_f(var("x"))), + (Pattern::Wildcard, format_none()), + ], + ) +} + /// Performs an index operation on an expression `seq` with an index `index`, without checking for OOB array access. /// /// This will result in a runtime panic during parse-evaluation if the index is out of bounds. @@ -1100,38 +1124,7 @@ pub fn slice(len: Expr, inner: Format) -> Format { /// nodes of type Expr. /// /// Will yield an unbalanced AST if there are more than 16 elements in `nodes` -pub fn balanced_bitor_max16(nodes: Vec) -> Expr { - /* - let n = nodes.len(); - - let (l, r) = match () { - _ if n > 8 => ( - balanced_bitor_max16(nodes.drain(0..8).collect::>()), - balanced_bitor_max16(nodes.drain(..).collect::>()), - ), - _ if n > 4 => ( - balanced_bitor_max16(nodes.drain(0..4).collect::>()), - balanced_bitor_max16(nodes.drain(..).collect::>()), - ), - _ if n > 2 => ( - balanced_bitor_max16(nodes.drain(0..2).collect::>()), - balanced_bitor_max16(nodes.drain(..).collect::>()), - ), - _ if n == 2 => { - let mut two_shot = nodes.drain(..); - let l = two_shot.next().unwrap(); - let r = two_shot.next().unwrap(); - (l, r) - } - _ if n == 1 => { - return nodes.drain(..).next().unwrap(); - } - _ => { - panic!("balanced_bitor_max16 called with n == 0") - } - }; - bit_or(l, r) - */ +pub fn balanced_bitor(nodes: Vec) -> Expr { balance_merge((), move |_| nodes, bit_or) } @@ -1203,7 +1196,7 @@ pub fn accum_until( /// Computes the final element of a sequence-typed Expr, evaluating to None if it is empty pub fn seq_last_checked(seq: Expr) -> Expr { expr_opt_if( - is_within(seq_length(seq.clone()), Bounds::at_least(1)), + is_nonzero::(seq_length(seq.clone())), index_unchecked(seq.clone(), pred(seq_length(seq))), ) } @@ -1279,7 +1272,7 @@ pub fn pos32() -> Format { map(Format::Pos, lambda("x", Expr::AsU32(Box::new(var("x"))))) } -pub fn fmt_let(orig: Expr, clone_varname: &'static str, dep_format: Format) -> Format { +pub fn fmt_let(clone_varname: &'static str, orig: Expr, dep_format: Format) -> Format { Format::Let( Label::Borrowed(clone_varname), Box::new(orig), From c72e4df4f001253fd08c4ff9594d21e8e96f202c Mon Sep 17 00:00:00 2001 From: Peter Duchovni Date: Tue, 4 Feb 2025 15:29:22 +1100 Subject: [PATCH 6/6] Rename and clarify purpose of shadow_check and is_shadowed_by --- doodle-formats/src/format/opentype.rs | 20 +++--- src/codegen/typed_format/variables.rs | 97 +++++++++++++++++++++------ src/lib.rs | 97 ++++++++++++++++++++------- 3 files changed, 161 insertions(+), 53 deletions(-) diff --git a/doodle-formats/src/format/opentype.rs b/doodle-formats/src/format/opentype.rs index 4d876803..f3c48f5a 100644 --- a/doodle-formats/src/format/opentype.rs +++ b/doodle-formats/src/format/opentype.rs @@ -3,13 +3,13 @@ use doodle::bounds::Bounds; use doodle::{helper::*, Expr, IntoLabel, Label}; use doodle::{BaseType, Format, FormatModule, FormatRef, Pattern, ValueType}; -fn id(x: T) -> T { +const fn id(x: T) -> T { x } -fn shadow_check(x: &Expr, name: &'static str) { - if x.is_shadowed_by(name) { - panic!("Shadow! Variable-name {name} already occurs in Expr {x:?}!"); +fn preclude_shadowing(x: &Expr, name: &'static str) { + if x.contains_unbound_reference(name) { + panic!("potential shadowing found: variable-name {name} already occurs in Expr {x:?}!"); } } @@ -487,14 +487,14 @@ fn link_forward_unchecked(abs_offset: Expr, format: Format) -> Format { /// desired offset, `None` is returned in any case where the relative-delta to reach the target offset is /// non-positive. fn offset16_mandatory(base_offset: Expr, format: Format, base: &BaseModule) -> Format { - shadow_check(&base_offset, "offset"); + preclude_shadowing(&base_offset, "offset"); // REVIEW - there is an argument to be made that we should use `chain` instead of `record` to elide the offset and flatten the link record([ ("offset", base.u16be()), ( "link", if_then_else( - is_nonzero_u16(var("offset")), + is_nonzero::(var("offset")), // because link-checked can also return format_none, it has to be the one to wrap format_some around the parse link_forward_checked(pos_add_u16(base_offset, var("offset")), format), format_none(), @@ -519,14 +519,14 @@ fn offset16_mandatory(base_offset: Expr, format: Format, base: &BaseModule) -> F /// To handle irregular inputs that would otherwise require moving *backwards* to reach the desired offset, /// `None` is returned in any case where the relative-delta to reach the target offset is non-positive. fn offset16_nullable(base_offset: Expr, format: Format, base: &BaseModule) -> Format { - shadow_check(&base_offset, "offset"); + preclude_shadowing(&base_offset, "offset"); // REVIEW - there is an argument to be made that we should use `chain` instead of `record` to elide the offset and flatten the link record([ ("offset", base.u16be()), ( "link", if_then_else( - is_nonzero_u16(var("offset")), + is_nonzero::(var("offset")), // because link-checked can also return format_none, it has to be the one to wrap format_some around the parse link_forward_checked(pos_add_u16(base_offset, var("offset")), format), format_none(), @@ -551,14 +551,14 @@ fn offset16_nullable(base_offset: Expr, format: Format, base: &BaseModule) -> Fo /// To handle irregular inputs that would otherwise require moving *backwards* to reach the desired offset, /// `None` is returned in any case where the relative-delta to reach the target offset is non-positive. fn offset32(base_offset: Expr, format: Format, base: &BaseModule) -> Format { - shadow_check(&base_offset, "offset"); + preclude_shadowing(&base_offset, "offset"); // FIXME - should we use `chain` instead of `record` to elide the offset and flatten the link? record([ ("offset", base.u32be()), ( "link", if_then_else( - is_nonzero_u32(var("offset")), + is_nonzero::(var("offset")), linked_offset32(base_offset, var("offset"), format_some(format)), format_none(), ), diff --git a/src/codegen/typed_format/variables.rs b/src/codegen/typed_format/variables.rs index 7b594876..23e59d0b 100644 --- a/src/codegen/typed_format/variables.rs +++ b/src/codegen/typed_format/variables.rs @@ -1,35 +1,94 @@ #![allow(dead_code)] #![allow(unused_imports)] +use std::num::NonZeroUsize; + use super::{GenType, TypedExpr}; use crate::Label; -impl TypedExpr { - pub(crate) fn get_vars(&self) -> Variables { - let mut vars = Variables::new(); - let ctxt = VarCtxt::new(); - self.poll_vars(&mut vars, ctxt); - vars - } +pub(crate) trait QueryExternVar { + fn query_extern_var(&self, var: &str, persistent: bool) -> VarInfo; +} - /// Extends a collection with a list of all the variable-names used in an Expr, as well - /// as whether their provenance at site-of-use is internal or external, and how they are - /// accessed. - /// - /// If the expression is itself a raw `TypedExpr::Var`, returns the variable's verbatim identifier, otherwise None. - fn poll_vars<'a>(&'a self, _vars: &mut Variables, _ctxt: VarCtxt<'a>) -> Option<&'a Label> { +// REVIEW - should we cache variables we see that aren't `var`, to avoid repeated traversal? +impl QueryExternVar for TypedExpr { + fn query_extern_var(&self, var: &str, persistent: bool) -> VarInfo { + let mut ret = VarInfo::new(); match self { - TypedExpr::U8(..) - | TypedExpr::U16(..) - | TypedExpr::U32(..) - | TypedExpr::U64(..) - | TypedExpr::Bool(..) => None, + TypedExpr::Var(_, lbl) => { + if *lbl == var { + if persistent { + ret.add_persist(); + } else { + ret.add_reference(); + } + } + } + TypedExpr::IntRel(_, _, lhs, rhs) + | TypedExpr::Arith(_, _, lhs, rhs) => { + ret += lhs.query_extern_var(var, persistent); + ret += rhs.query_extern_var(var, persistent); + } + TypedExpr::AsU8(inner) + | TypedExpr::AsU16(inner) + | TypedExpr::AsU32(inner) + | TypedExpr::AsU64(inner) + | TypedExpr::AsChar(inner) => { + ret += inner.query_extern_var(var, persistent) + } + _ => todo!(), + } + ret + } +} - _ => unimplemented!(), +#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct VarInfo { + n_references: u32, + n_persists: u32, +} + +impl std::ops::Add for VarInfo { + type Output = VarInfo; + + fn add(self, rhs: VarInfo) -> Self::Output { + VarInfo { + n_references: self.n_references + rhs.n_references, + n_persists: self.n_persists + rhs.n_persists, } } } +impl std::ops::AddAssign for VarInfo { + fn add_assign(&mut self, rhs: VarInfo) { + self.n_references += rhs.n_references; + self.n_persists += rhs.n_persists; + } +} + +impl VarInfo { + pub fn new() -> Self { + VarInfo { + n_references: 0, + n_persists: 0, + } + } + + pub const fn is_unused(&self) -> bool { + self.n_references == 0 && self.n_persists == 0 + } + + pub fn add_reference(&mut self) { + self.n_references += 1; + } + + pub fn add_persist(&mut self) { + self.n_persists += 1; + } +} + + + // REVIEW - consider HashMap? type ScopeContainer = std::collections::BTreeMap; diff --git a/src/lib.rs b/src/lib.rs index fe862368..71ad84e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -515,30 +515,79 @@ impl Expr { } } - /// Returns `true` if the evaluation of `self` contains any references to an external variable with a given identifier - /// of `name`. This occurs when the expression contains `Expr::Var(name)` that it does not itself provide a local binding for - /// (e.g. in a pattern-match or lambda head). - pub fn is_shadowed_by(&self, name: &str) -> bool { + /// Determines whether a given expression contains an unbound reference to an external variable with identifier `name`. + /// + /// Returns `true` if there is such an unbound reference, and `false` otherwise (whether all references to the variable + /// are locally-bound within `self`, or whether the variable identifier in question does not appear). + /// + /// # Notes + /// + /// This method can be used to avoid hidden shadowing between a top-level and mid-level outer context, especially + /// in any helper function that takes in a raw `Expr` and, in its body, potentially shadows a variable external to + /// its own AST. + /// + /// For example, consider the following construction: + /// + /// ```ignore + /// let helper = |scope_c: Expr| -> Format { + /// let scope_b = record([ + /// ("byte", ANY_BYTE), + /// ("data", if_then_else(expr_eq(scope_c, var("byte")), Format::EMPTY, Format::Fail)), + /// ]); + /// scope_b + /// }; + /// /* ... */ + /// let scope_a = record([ + /// ("byte", ANY_BYTE), + /// ("x", helper(var("byte"))), + /// ]) + /// ``` + /// + /// In this case, we represent three scopes, A > B > C. The call to `helper` + /// within `scope_a` contains a bare variable-reference to `var("byte")`, which + /// locally is intended to refer to the A-scoped binding of `"byte"`; however, + /// the definition of `scope_b` causes a B-scoped binding of `"byte"` to shadow + /// the intended evaluation of `scope_c`, which can either cause silent errors (if the types align) + /// or cause more glaring errors (if the types do not align), which are nevertheless difficult to diagnose. + /// + /// The solution to this is to use `contains_unbound_reference` to determine, within + /// `helper`, whether any immediate bindings generated by `scope_b` will lead to silent shadowing + /// between scopes A and C, which can then be detected and corrected. + /// + /// # Examples + /// + /// ``` + /// # use doodle::Expr; + /// # use doodle::helper::{var}; + /// assert!(var("x").contains_unbound_reference("x")); + /// assert!(!var("x").contains_unbound_reference("y")); + /// assert!(!Expr::Record(vec![(Label::Borrowed("x"), Expr::U16(0)), (Label::Borrowed("y"), var("x"))]).contains_unbound_reference("x")); + /// assert!(!Expr::Lambda(Label::Borrowed("x"), Box::new(var("x")).contains_unbound_reference("x")); + /// ``` + pub fn contains_unbound_reference(&self, name: &str) -> bool { match self { - Expr::Var(vname) => vname == name, + Expr::Var(vname) => *vname == name, Expr::Lambda(head_var, body) => { if head_var == name { + // if the head-variable is itself a shadowing binding of `name`, + // the lambda body cannot be influenced by externally-scoped + // rebindings of `name` false } else { - body.is_shadowed_by(name) + body.contains_unbound_reference(name) } } Expr::Arith(_, x, y) | Expr::IntRel(_, x, y) => { - x.is_shadowed_by(name) || y.is_shadowed_by(name) + x.contains_unbound_reference(name) || y.contains_unbound_reference(name) } - Expr::Unary(_, x) => x.is_shadowed_by(name), - Expr::Dup(x, y) => x.is_shadowed_by(name) || y.is_shadowed_by(name), + Expr::Unary(_, x) => x.contains_unbound_reference(name), + Expr::Dup(x, y) => x.contains_unbound_reference(name) || y.contains_unbound_reference(name), Expr::Bool(_) | Expr::U8(_) | Expr::U16(_) | Expr::U32(_) | Expr::U64(_) => false, - Expr::Tuple(ts) => ts.iter().any(|x| x.is_shadowed_by(name)), - Expr::TupleProj(tup, _) => tup.is_shadowed_by(name), + Expr::Tuple(ts) => ts.iter().any(|x| x.contains_unbound_reference(name)), + Expr::TupleProj(tup, _) => tup.contains_unbound_reference(name), Expr::Record(fs) => { for (fname, fld) in fs.iter() { - if fld.is_shadowed_by(name) { + if fld.contains_unbound_reference(name) { // NOTE - the first field-expr that is shadowed by `name` wins return true; } else if fname == name { @@ -548,14 +597,14 @@ impl Expr { } false } - Expr::RecordProj(rec, _) => rec.is_shadowed_by(name), - Expr::Variant(_, inner) => inner.is_shadowed_by(name), - Expr::Seq(elts) => elts.iter().any(|x| x.is_shadowed_by(name)), + Expr::RecordProj(rec, _) => rec.contains_unbound_reference(name), + Expr::Variant(_, inner) => inner.contains_unbound_reference(name), + Expr::Seq(elts) => elts.iter().any(|x| x.contains_unbound_reference(name)), Expr::Match(head, arms) => { - head.is_shadowed_by(name) + head.contains_unbound_reference(name) || arms .iter() - .any(|(pat, x)| !pat.shadows(name) && x.is_shadowed_by(name)) + .any(|(pat, x)| !pat.shadows(name) && x.contains_unbound_reference(name)) } Expr::AsU8(x) | Expr::AsU16(x) @@ -568,19 +617,19 @@ impl Expr { | Expr::U32Le(x) | Expr::U64Be(x) | Expr::U64Le(x) - | Expr::SeqLength(x) => x.is_shadowed_by(name), - Expr::EnumFromTo(s, e) => s.is_shadowed_by(name) || e.is_shadowed_by(name), + | Expr::SeqLength(x) => x.contains_unbound_reference(name), + Expr::EnumFromTo(s, e) => s.contains_unbound_reference(name) || e.contains_unbound_reference(name), Expr::SubSeq(x, s, l) | Expr::SubSeqInflate(x, s, l) => { - x.is_shadowed_by(name) || s.is_shadowed_by(name) || l.is_shadowed_by(name) + x.contains_unbound_reference(name) || s.contains_unbound_reference(name) || l.contains_unbound_reference(name) } - Expr::SeqIx(x, i) => x.is_shadowed_by(name) || i.is_shadowed_by(name), + Expr::SeqIx(x, i) => x.contains_unbound_reference(name) || i.contains_unbound_reference(name), Expr::FlatMap(f, x) | Expr::FlatMapList(f, _, x) => { - f.is_shadowed_by(name) || x.is_shadowed_by(name) + f.contains_unbound_reference(name) || x.contains_unbound_reference(name) } Expr::FlatMapAccum(f, z, _, x) | Expr::LeftFold(f, z, _, x) => { - f.is_shadowed_by(name) || z.is_shadowed_by(name) || x.is_shadowed_by(name) + f.contains_unbound_reference(name) || z.contains_unbound_reference(name) || x.contains_unbound_reference(name) } - Expr::LiftOption(opt_x) => opt_x.as_ref().is_some_and(|x| x.is_shadowed_by(name)), + Expr::LiftOption(opt_x) => opt_x.as_ref().is_some_and(|x| x.contains_unbound_reference(name)), } }