Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read simple glyph flags #392

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open

Read simple glyph flags #392

wants to merge 7 commits into from

Conversation

wezm
Copy link
Contributor

@wezm wezm commented Aug 25, 2022

I've forged ahead with some ideas in order to make it possible to read the contours of simple glyphs. I've added the following primitives:

  • u16_from_u8 — does what it says
    • I haven't added all the other permutations of safe conversions like this. Open to suggestions here.
  • or_succeed — read a format if the supplied Bool is true, else succeed with the supplied default value.
  • repeat_until_full — perhaps the most controversial inclusion.
    • repeat_until_full : U16 -> fun (A : Format) -> (Repr A -> U16) -> Format
    • Repr: Array16 len (Repr format)
    • Takes a length, format, and a function that returns how many times to add (Repr format) to the array.
  • array_map — map a function over the elements of an existing array to read another array

Comment on lines 505 to 506
// TODO: Do we need to force replicate as well?
// let replicate = self.elim_env().force(replicate);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum. Yeah we might actually. We don’t directly match on the value (which is what would usually make you reach for force), but it seems like ElimEnv::fun_app doesn’t force the value before matching itself. Not sure if it’s common to force in eliminator helpers like fun_app… might need to take a look at the elaboration-zoo. 🤔

Even if ElimEnv::fun_app did, because we don’t have mutable flexvars at the moment, it’s probably good for performance to force once before iterating.

Comment on lines 193 to 196
scope.to_scope(core::Term::FunType(
Span::Empty,
None,
&U16_TYPE,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think it could be worth documenting this parameter with env.name("max_len")

@brendanzab
Copy link
Member

Probably would recommend putting let flag_repeat = fun (f : Repr flag) => u16_from_u8 f.repeat + (1 : U16) in a local let binding seeing as it doesn't close over anything useful. This would avoid it showing up in the parsed output.

@wezm wezm force-pushed the wezm/read-simple-glyph-flags branch from e61707a to 5063353 Compare September 1, 2022 00:56
@wezm wezm force-pushed the wezm/read-simple-glyph-flags branch from 55718c9 to 7961ce5 Compare September 1, 2022 02:05
@wezm wezm marked this pull request as ready for review September 1, 2022 02:17
@wezm wezm force-pushed the wezm/read-simple-glyph-flags branch from 7961ce5 to cd4cc01 Compare September 1, 2022 02:17
Comment on lines +509 to +532
### Map formats

There are four array map formats, corresponding to the four [array types](#arrays).
These allow mapping a supplied function over the elements of an array in order
to parse another array:

- `array8_map : fun (len : U8) -> fun (A : Type) -> (A -> Format) -> Array8 len A -> Format`
- `array16_map : fun (len : U16) -> fun (A : Type) -> (A -> Format) -> Array16 len A -> Format`
- `array32_map : fun (len : U32) -> fun (A : Type) -> (A -> Format) -> Array32 len A -> Format`
- `array64_map : fun (len : U64) -> fun (A : Type) -> (A -> Format) -> Array64 len A -> Format`

#### Representation of map formats

The [representation](#format-representations) of the array map formats preserve the
lengths, and use the representation of the map function as the element types
of the host [array types](#array-types).

| format | `Repr` format |
|----------------------------------|-----------------------------|
| `array8_map len A map_fn array` | `Array8 len (Repr map_fn)` |
| `array16_map len A map_fn array` | `Array16 len (Repr map_fn)` |
| `array32_map len A map_fn array` | `Array32 len (Repr map_fn)` |
| `array64_map len A map_fn array` | `Array64 len (Repr map_fn)` |

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks suspicious. Is the idea that each element of the array can produce a different format? If so this isn’t really represented in the definition of Repr... and we don’t have a heterogeneous array type to properly model this (where the types of the elements could be different).

Relatedly, there‘s also a type error in Repr map_fn. The type signature of map_fn is A -> Format, but Repr expects an argument of Format. Sorry if I didn’t catch this!

Alas I don’t have any ideas that come to mind as yet, other than constrained formats to constrain the array*_map formats, and map format to let you map each element format to ensure they have a common representation:

array8_map : fun (len : U8) (A : Type) (B : Type) -> (A -> Format [Repr = B]) -> Array8 len A -> Format

Repr (array8_map len A B map_fn array) = Array8 len B
map : fun (f : Format) (B : Type) -> (Repr f -> B) -> Format

Repr (map f B fn) = B

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like map is something that happens to parsed arrays to produce other parsed arrays, rather than being a format itself 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the idea that each element of the array can produce a different format?

To be honest I'm not sure. My grasp on this whole thing is tenuous at best—each time I think I have a handle on it is escapes my grasp. In the motivating example the key thing is this function:

let read_coord = fun (is_short : Repr flag -> Bool) => fun (is_same : Repr flag -> Bool) => fun (f : Repr flag) => {
    coord <- match (is_short f) {
        true => u8,
        false => match (is_same f) {
            true => succeed S16 0,
            false => s16be,
        }
    }
},

In order to read a coordinate you need to take the corresponding flag for that coordinate and based on the bits that are set in it you will read 0, 1, or 2 bytes from the input. In my perhaps broken mental model this was the same format, the same function is used to read all values.

Copy link
Member

@brendanzab brendanzab Sep 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like map is something that happens to parsed arrays to produce other parsed arrays, rather than being a format itself 🤔

Yeah the the naming is probably a bit off at any rate - I think this is more like… traverse from Haskell land? I think?

traverse : (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
traverse : fun {len} {A, B} -> (A -> FormatOf B) -> Array len A -> FormatOf B

where:

  • f is FormatOf
  • t is Array len

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants