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

Feature idea: collect a map intead of a list #49

Open
matthiasnoback opened this issue May 25, 2021 · 0 comments
Open

Feature idea: collect a map intead of a list #49

matthiasnoback opened this issue May 25, 2021 · 0 comments

Comments

@matthiasnoback
Copy link
Contributor

matthiasnoback commented May 25, 2021

Since I use collect() to capture values that are to be fed into an AST node, it would be very helpful to use a map instead of a list. Similarly to how you can capture named groups with regular expressions. Here's an example where I parse a Markdown heading:

return collect(
            keepFirst(atLeastOne(char('#')), skipSpace1()),
            atLeastOne(satisfy(fn (string $char) => ! in_array($char, ["\n"], true))),
            self::newLineOrEof()
        )->map(fn (array $output) => new Heading(strlen($output[1]), $output[2], $output[0]));

Suggested improvement (last line)

return collect(
            keepFirst(atLeastOne(char('#')), skipSpace1()),
            atLeastOne(satisfy(fn (string $char) => ! in_array($char, ["\n"], true))),
            self::newLineOrEof()
        )->map(fn (array $output) => new Heading(strlen($output['level']), $output['title']));

One way could be to use label() for each collected parser and use the label as the array key passed to map():

return collect(
            keepFirst(atLeastOne(char('#')), skipSpace1())->label('level'),
            atLeastOne(satisfy(fn (string $char) => ! in_array($char, ["\n"], true)))->label('title'),
            self::newLineOrEof()
        )->map(fn (array $output) => new Heading(strlen($output['level']), $output['title']));

The advantage being that label() is already there and used for a similar purpose. However, this might lead to values being overwritten if you collect two parsers with the same name. Another option is to provide the map upfront:

return collect([
            'level' => keepFirst(atLeastOne(char('#')), skipSpace1()),
            'title' => atLeastOne(satisfy(fn (string $char) => ! in_array($char, ["\n"], true))),
            'eol' => self::newLineOrEof()
        ])->map(fn (array $output) => new Heading(strlen($output['level']), $output['title']));

It might be useful to have separate collectList() and collectMap() functions by the way.

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