Stream: show and tell

Topic: We need to parse deeper!


view this post on Zulip Ian McLerran (Feb 12 2025 at 21:13):

Roc obviously has a dearth of parser-combinator packages, so I wrote another one! :sweat_smile:

roc-tinyparse

Started out writing this for another project, but ended up really liking the API and wanting to use it in some other projects, so I published it as it's own package, and thought I'd share.

import Parse exposing [char, integer, string, filter, rhs, lhs, both, zip_3, map, maybe, finalize]

dot = char |> filter(|c| c == '.')

expect
    pattern = maybe(string("v")) |> rhs(integer) |> lhs(dot) |> both(integer) |> lhs(dot) |> both(integer)
    parser = pattern |> map(|((major, minor), patch)| Ok({major, minor, patch}))
    parser("v1.2.34") |> finalize == Ok({major: 1, minor: 2, patch: 34 })

expect
    parser = rhs(maybe(string("v")), zip_3(integer, rhs(dot, integer), rhs(dot, integer)))
    parser("v1.2.34_abc") |> finalize_lazy == Ok((1, 2, 34))

view this post on Zulip Ian McLerran (Feb 12 2025 at 21:25):

Gotta admit, I'm looking forward to static dispatch and doing this!

parser = string("v").rhs(integer).lhs(dot).both(integer).lhs(dot).both(integer)

view this post on Zulip Joshua Warner (Feb 15 2025 at 06:40):

What is lhs vs rhs vs both? I don't recognize those as common combinator names?

view this post on Zulip Sam Mohr (Feb 15 2025 at 06:57):

## keep the result of the left parser
lhs : Parser a _, Parser b _ -> Parser a _
lhs = |parser_l, parser_r|
    zip(parser_l, parser_r) |> map(|(l, _r)| Ok(l))

## keep the result of the right parser
rhs : Parser a _, Parser b _ -> Parser b _
rhs = |parser_l, parser_r|
    zip(parser_l, parser_r) |> map(|(_l, r)| Ok(r))

view this post on Zulip Sam Mohr (Feb 15 2025 at 06:57):

Our Rust parser code calls them skip_second and skip_first, not sure what the normal names are

view this post on Zulip Ian McLerran (Feb 15 2025 at 07:22):

I think skip_second and skip_first are pretty common names, also keep_left and keep_right. I was looking for really concise names to enable writing more complex grammars in a single line.

view this post on Zulip Ian McLerran (Feb 15 2025 at 07:30):

And both is just an alias for zip. Kinda silly since it’s actually longer than zip but I added that to be similar in flavor to lhs/rhs. :sweat_smile:

view this post on Zulip Kilian Vounckx (Feb 15 2025 at 20:28):

Sam Mohr said:

Our Rust parser code calls them skip_second and skip_first, not sure what the normal names are

The normal names are obviously <* and *> :rolling_on_the_floor_laughing:

view this post on Zulip Ian McLerran (Feb 15 2025 at 23:14):

Truth be told, I was trying to get as close as I could to <*, *>, and <*> as I could! :joy:


Last updated: Jul 06 2025 at 12:14 UTC