-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Currently, there is no way to apply relatively simple Expr-based tweaks to a numeric value without knowing its underlying Machine-Type/Integer-rep (i.e. which of BaseType::{U8, U16, U32, U64} it conforms to). Though the direct need for these specific constructions may be obviated partially via value-model redesigns (e.g. #228), there is arguably a case to be made for type/rep-agnostic Successor and Predecessor primitives in the Expr-model, ideally as UnaryOp variants.
In particular, we can already see a couple of cases in opentype.rs where we either add or subtract a fundamentally invariant constant of 1, which can only be constructed within the Expr model with full knowledge of the inferred ValueType of an incoming operand:
let last_ix = sub(seq_length(var(seq_var)), Expr::U32(1)); sub(Expr::AsU16(Box::new(var("n"))), Expr::U16(1)), doodle/doodle-formats/src/format/opentype.rs
Line 467 in 84a7b12
add(Expr::U16(1), subheader_index(var("sub_header_keys"))), doodle/doodle-formats/src/format/opentype.rs
Line 1236 in 84a7b12
repeat_count(add(var("num_glyphs"), Expr::U16(1)), base.u16be()), doodle/doodle-formats/src/format/opentype.rs
Line 1241 in 84a7b12
repeat_count(add(var("num_glyphs"), Expr::U16(1)), base.u32be()), doodle/doodle-formats/src/format/opentype.rs
Lines 1304 to 1307 in 84a7b12
add( Expr::AsU16(Box::new(record_proj(var("flags"), "repeats"))), Expr::U16(1), ), doodle/doodle-formats/src/format/opentype.rs
Lines 1330 to 1336 in 84a7b12
add( Expr::AsU32(Box::new(record_proj( var("packed"), "repeats", ))), Expr::U32(1), ), doodle/doodle-formats/src/format/opentype.rs
Line 1417 in 84a7b12
compute(add(Expr::U16(1), last_elem("end_points_of_contour"))),
It is worth noting that the vast majority of these cases are in the context of working with various forms of array-boundary conditions:
LastIndex(seq) := Pred(SeqLength(seq))
and the natural converse thereof,
NumRepeats(MaxIndex) := Succ(MaxIndex)
where seq is an instantiated incoming Expr that has kind Seq, and MaxIndex is an instantiated incoming Expr representing the maximum index we wish to capture in a RepeatCount-style format we are generating the iteration-count value for.
Similarly, we observe that the array of first partial differences of a pre-existing numeric array has the property
SeqLength(SeqDeltas(seq)) = Pred(SeqLength(seq))
and that, though slightly more niche, the length of the optional loca table in OpenType is Succ(NumGlyphs).
Given all these constructions, it seems fairly reasonable to introduce a Pred and Succ operator with the implicit type-signature Num a => a -> a (or, as in line with other proposed UnaryOp variants as in #228, an optionally-overloaded casting op (Num a, Num b) => a -> b). In this way, the actual type of an Expr that depends on the possibly-obfuscated ValueType of an incoming variable, or the abstract type-level semantics of a fixed operator like Expr::SeqLength or Format::Pos, can be left out, and errant Expr::U16(1) and similar can be avoided when the significance of the value 1 is arguably a workaround for the lack of a dedicated Succ or Pred primitive.