wavebeem / bread-n-butter

Parser combinators for TypeScript and JavaScript
https://bread-n-butter.wavebeem.com/
MIT License
35 stars 6 forks source link

API Proposal: parser.flatten #35

Open hillin opened 2 years ago

hillin commented 2 years ago

It's very easy to create parsers which generates recursively nested values. When writing a lexer, we'd expect the output to be a flat (1D) array of tokens (or bnb.ParseNodes). So I find myself constantly mapping the parse result with a flatten function:

type Node = bnb.ParseNode<string, string>;
type NodeOrArrayOfNode = Node | NodeOrArrayOfNode[];

function flatten(nodes: NodeOrArrayOfNode) {
  const result: Node[] = [];
  function addNode(node: NodeOrArrayOfNode) {
    if (Array.isArray(node)) {
      for (const innerNode of node) {
        addNode(innerNode);
      }
    } else {
      result.push(node);
    }
  }

  addNode(nodes);
  return result;
}

// usage:
const parser = bnb
  .text('a')
  .node('a')
  .and(bnb.text('b').node('b'))
  .or(bnb.text('c').node('c'))  // => [bnb.ParseNode<"a", "a">, bnb.ParseNode<"b", "b">] | bnb.ParseNode<"c", "c">
  .map(flatten);  // => bnb.ParseNode<string, string>[]

It would be great if this mechanism is provided as a method of bnb.Parser.

This also helps make sense of the inferred type information, otherwise it would quickly collapse into a pile of gibberish (for instance, in the example above, for such a simple parser, the type yielded before the map call is already not quite readable).

wavebeem commented 2 years ago

Could you give me a more complete example? I haven't really found myself wanting this

hillin commented 2 years ago

e.g.

// without flatten
bnb.text('1').and(bnb.text('2')).and(bnb.text('3')).parse('123')
// => [["1","2"],"3"]
// the nested array is obviously hard to deal with

// with flatten
bnb.text('1').and(bnb.text('2')).and(bnb.text('3')).flatten().parse('123')
// => ['1', '2', '3']
wavebeem commented 2 years ago

Yeah, so repeated and should be replaced with all instead. Maybe that could be more obvious in the docs? all links to and but and doesn't link to all