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

[lang-model]: 'Forgetful' Format chaining (>> ~ >>= : X ~ Format::LetFormat) (trivial, narrow, sem) #227

Open
Tracked by #186
archaephyrryx opened this issue Oct 17, 2024 · 1 comment

Comments

@archaephyrryx
Copy link
Contributor

archaephyrryx commented Oct 17, 2024

There are several common patterns of parsing where a particular Format-parse is meaningful to perform, but where the value it yields is immaterial.

Examples of this include

  • Skipping certain tokens within a Peek/PeekNot context (which is either impossible to rewrite as WithRelativeOffset due to non-static lengths of the skipped fields, or doing so would require opaque magic numbers we would prefer to avoid)
  • Processing an operationally significant ('side-effectful') Unit-typed Format such as Format::Align(N) or Format::PeekNot(F)

Currently, our options are:

  • Perform sequential parsing using a Record or Tuple construction we then have to project into to extract the significant value of
  • (Within an existing tuple or record) Allow a vacuous positional-argument/field to persist
  • Use LetFormat with a dummy (often "_") name for the fused chain-variable (like f >>= \_ -> g in a Haskell without >>)

There are two separate aspects to this proposal:

  • Introduce a construction that mirrors LetFormat but which only performs the LHS Format-parse for its side-effects rather than for a dependent parse or the RHS
    • We might further want to reduce nesting depth by instead having an operation Last([ F0, F1, ..., FN ]) (modulo naming) that performs each of F0 through FN-1 in sequence without preserving the values they yield, and finally parses FN and yields its result as the overall operation result (presuming none of Fi yield a parse-failure)
  • (Bidirectionality) Introduce a left-biased sequencing operator that permits the parsing of later parse-tokens but which preserves the return-value of the leftmost operand
    • Similarly, First([ F0, F1, ..., FN ]) could be a broader use-case for parsing F0 and storing its result, but processing each of F1 through FN prospectively, until either the parse finishes (in which case F0's value is returned) or parsing fails (in which case F0's return value is discarded and the current parse-subtree is marked as failing).
    • Otherwise, this can be simulated with LetFormat(F0, "x", LetFormat(Tuple([F1, ..., FN), "_", Format::Compute(var("x"))))
@archaephyrryx
Copy link
Contributor Author

In line with this notion, we have a potential avenue for defining a more customizable and ergonomic (though higher-overhead) variation on the theme of Format::Record, using sequential chains of multiple formats followed by a final Compute(Expr::Record(...)) to bring together any values we have captured as scope-bound temporaries into a permanent field:

fn chains(seq: &[(Label, Format)], finally: Format) -> Format {
    if seq.is_empty() {
        finally
    } else {
        chain(seq[0].1.clone(), seq[0].0.clone(), chains(&seq[1..]))
    }
}

fn record<Name: IntoLabel + AsRef<str>>(items: impl AsRef<[(Name, Format)]>) -> Format {
    let fields = items.as_ref();
    let names = fields
        .iter()
        .filter_map(|(name, _)| (!name.as_ref().starts_with('_')).then_some(|| var(name)))
        .collect::<Vec<Expr>>();
    let finally = compute(Expr::Record(names));
    
    chains(fields, finally)
}

or similar.

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

No branches or pull requests

1 participant