diff --git a/doodle-formats/src/format.rs b/doodle-formats/src/format.rs index 1214ce69..c80f4b85 100644 --- a/doodle-formats/src/format.rs +++ b/doodle-formats/src/format.rs @@ -108,11 +108,7 @@ mod test { ("len", base.u32be()), ( "mask", - Format::WithRelativeOffset( - None, - Box::new(var("len")), - Box::new(Format::Byte(mask_bytes)), - ), + with_relative_offset(None, var("len"), Format::Byte(mask_bytes)), ), ( "data", 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 39e060df..f3c48f5a 100644 --- a/doodle-formats/src/format/opentype.rs +++ b/doodle-formats/src/format/opentype.rs @@ -3,9 +3,13 @@ use doodle::bounds::Bounds; use doodle::{helper::*, Expr, IntoLabel, Label}; use doodle::{BaseType, Format, FormatModule, FormatRef, Pattern, ValueType}; -fn shadow_check(x: &Expr, name: &'static str) { - if x.is_shadowed_by(name) { - panic!("Shadow! Variable-name {name} already occurs in Expr {x:?}!"); +const fn id(x: T) -> T { + 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:?}!"); } } @@ -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)) @@ -411,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. @@ -445,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`, @@ -510,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(), @@ -542,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(), @@ -574,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(), ), @@ -633,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( @@ -653,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( @@ -676,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(); @@ -1934,18 +1927,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 +1944,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 +4769,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,15 +4856,19 @@ pub fn main(module: &mut FormatModule, base: &BaseModule) -> FormatRef { ), ( "glyph_variation_data_array", - clone_hack( - var("glyph_variation_data_offsets"), - "offs", - array_from_offsets( - var("gvar_table_start"), - var("glyph_variation_data_array_offset"), + // 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("glyph_variation_data_offsets"), ), ), + // ), ), ]), ) @@ -4983,7 +4951,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..cbaac3d4 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, @@ -5606,17 +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 { -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() == 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() }, @@ -5624,14 +5611,9 @@ true => { false => { [].to_vec() } -})))?[0u32 as usize]) -}, - -false => { -None -} -} { -Some(ref x) => { +})))?; +let matching_table = match table_matches.as_slice() { +[x] => { x.clone() }, @@ -5652,7 +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 { +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() }, @@ -5660,24 +5642,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() == 1751474532u32 { -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() }, @@ -5698,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() }, @@ -5716,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() }, @@ -5744,7 +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 { +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() }, @@ -5752,24 +5704,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() == 1835104368u32 { -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() }, @@ -5790,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() }, @@ -5798,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() }, @@ -5836,7 +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 { +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() }, @@ -5844,24 +5766,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() == 1851878757u32 { -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() }, @@ -5882,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() }, @@ -5900,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() }, @@ -5928,7 +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 { +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() }, @@ -5936,24 +5828,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() == 1886352244u32 { -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() }, @@ -5974,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() }, @@ -5982,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)?; @@ -6037,13 +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 { +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() }, @@ -6051,25 +5913,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() == 1718642541u32 { -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)?; @@ -6106,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() }, @@ -6130,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)?; @@ -6155,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() }, @@ -6169,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)?; @@ -6196,43 +6013,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()?; @@ -6244,13 +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 { +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() }, @@ -6258,25 +6043,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() == 1886545264u32 { -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)?; @@ -6313,13 +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 { +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() }, @@ -6327,25 +6097,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() == 1734439792u32 { -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)?; @@ -6362,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() }, @@ -6376,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)?; @@ -6411,13 +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 { +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() }, @@ -6425,25 +6165,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() == 1195656518u32 { -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)?; @@ -6460,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() }, @@ -6484,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)?; @@ -6509,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() }, @@ -6523,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)?; @@ -6558,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() }, @@ -6582,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)?; @@ -6607,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() }, @@ -6631,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)?; @@ -6656,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() }, @@ -6670,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)?; @@ -6705,13 +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 { +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() }, @@ -6719,25 +6369,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() == 1398030676u32 { -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)?; @@ -6754,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() }, @@ -6778,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)?; @@ -6803,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() }, @@ -6817,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)?; @@ -6860,7 +6465,7 @@ ret ((|val: opentype_hmtx_table| PResult::Ok(Some(val)))(inner))? }, -None => { +_ => { None } } @@ -7390,20 +6995,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(ref 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 +7037,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(ref 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 +8414,6 @@ opentype_gvar_table_glyph_variation_data_offsets::Offsets16(inner) } }))())?; let glyph_variation_data_array = ((|| PResult::Ok({ -let offs = 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 +8421,18 @@ let array_start = { let inner = _input.get_offset_u64(); ((|x: u64| PResult::Ok(x as u32))(inner))? }; -match match offs { -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) => { +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 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 +8449,17 @@ None accum }, -opentype_gvar_table_glyph_variation_data_array_POISON::Offset32Pairs(offs32_pairs) => { +opentype_gvar_table_glyph_variation_data_offsets::Offsets32(ref 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()?; @@ -14396,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()) }, @@ -17363,15 +16931,15 @@ 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::uncompressed(ref y) => { y.codes_values.clone() }, -deflate_main_codes::fixed_huffman(y) => { +deflate_main_codes::fixed_huffman(ref y) => { y.codes_values.clone() }, -deflate_main_codes::dynamic_huffman(y) => { +deflate_main_codes::dynamic_huffman(ref y) => { y.codes_values.clone() } })))?))())?; diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 1bbcc086..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)) } @@ -876,7 +884,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); @@ -1029,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, } } @@ -3536,6 +3549,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 +3766,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 +3865,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..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 { @@ -539,6 +550,7 @@ pub enum TypedExpr { Box>, ), Dup(TypeRep, Box>, Box>), + EnumFromTo(TypeRep, Box>, Box>), LiftOption(TypeRep, Option>>), Unary(TypeRep, UnaryOp, Box>), } @@ -633,6 +645,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 +705,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)), } } } @@ -712,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); @@ -844,6 +882,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::LiftOption(_, None) => Expr::LiftOption(None), TypedExpr::LiftOption(_, Some(x)) => Expr::LiftOption(Some(rebox(x))), } diff --git a/src/codegen/typed_format/variables.rs b/src/codegen/typed_format/variables.rs new file mode 100644 index 00000000..23e59d0b --- /dev/null +++ b/src/codegen/typed_format/variables.rs @@ -0,0 +1,161 @@ +#![allow(dead_code)] +#![allow(unused_imports)] + +use std::num::NonZeroUsize; + +use super::{GenType, TypedExpr}; +use crate::Label; + +pub(crate) trait QueryExternVar { + fn query_extern_var(&self, var: &str, persistent: bool) -> VarInfo; +} + +// 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::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 + } +} + +#[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; + +#[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/decoder.rs b/src/decoder.rs index 5f0d4eaf..d6860d17 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] - } - _ => panic!("SeqIx: expected Seq"), + 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 (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,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.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 +1418,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 +1704,21 @@ 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..c7db6f0a 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,68 @@ 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 { @@ -47,13 +109,7 @@ impl SeqKind { 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 +127,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}, _)") } } } diff --git a/src/helper.rs b/src/helper.rs index 5ce05602..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,16 +1272,15 @@ 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 clone_hack(orig: Expr, clone_varname: &'static str, dep_format: F) -> Format -where - F: FnOnce(Expr) -> Format, -{ +pub fn fmt_let(clone_varname: &'static str, orig: Expr, 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 a56baa85..71ad84e7 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,20 @@ 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:?}")); @@ -512,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 { @@ -545,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) @@ -565,21 +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)), } } diff --git a/src/loc_decoder.rs b/src/loc_decoder.rs index 745dfa48..95cff485 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,15 @@ 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 +431,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 +492,13 @@ 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 +884,22 @@ 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 +911,14 @@ 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 +929,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 +969,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 +1051,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 +1284,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 +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.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..d0d61a2f 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,16 @@ 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 +1720,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)?;