npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@conartist6/rd-parse

v4.0.0-rc.0

Published

Recursive descent parser generator. Define your grammar in pure Javascript.

Downloads

10

Readme

@conartist6/rd-parse

A generic, minimalist, recursive-descent parser which allows you to define your grammar directly in Javascript in a syntax closely related to EBNF. Avoiding the need to compile the grammar makes the resultant parser compact and quick to initialize.

You will be bound by the limitations of recursive descent parsing, for example you must remove left recursion in your grammar.

Usage

npm i rd-parse # or
yarn add rd-parse

Once installed you can import the Parser class and the grammar combinators and build your parser. Here is an example which uses rd-parse to recognize a subset of valid JSON.

const {
  Parser,
  Node,
  All,
  Any,
  Star,
  Y,
} = require('@conartist6/rd-parse');

// `Y` lets us use a function recursively before we're finished defining it
const dict = Y((dict) => {
  // Match empty space and do not use the result
  const _ = Ignore(/\s*/);
  const string = Node(
    All('"', /[^"]*/, '"'),
    ([text]) => text,
  );
  const key = string;
  // `dict` is the top level grammar returned below
  const value = Any(string, dict);
  const entry = Node(
    All(key, _, ':', _, value),
    (entry) => entry,
  );
  // Define `dict` as the object built from [key, value] entries
  return Node(
    All('{', _, Star(entry), _, '}'),
    Object.fromEntries,
  );
});

const dictParser = new Parser(dict);

dictParser.parse('{ "foo": "bar" }');
// { foo: 'bar' }

dictParser.parse('{ "foo": { "bar": "baz" } }');
// { foo: { bar: 'baz' }}

Grammar

The main task in using rd-parse is to define a grammar. A grammar is made up of rules, where a rule may be a token or a combinator. Tokens can be represented as string literals or regex literals, and each token is itself a grammar. For example the /\w/ token is a grammar that matches any single lowercase latin letter.

Making a useful grammar is a process of combining token grammars using combinators such as All, Optional, and Plus. Each combinator returns a new grammar derived from an input grammar or grammars according to the rules documented below:

  • 'foo' matches if the characters foo are next in the input
  • /[0-9]/ matches if the next character in the input is a digit
  • All(...grammars) matches when all grammars match
  • Any(...grammars) matches when any grammar of grammars matches
  • Optional(grammar) always matches
  • Ignore(grammar) matches when grammar matches
  • Plus(grammar) matches grammar repeated one or more times
  • Star(grammar) matches grammar repeated zero or more times

The last step of parsing - extracting and AST from the grammar - is done with the special Node combinator, which takes a (stack) => node callback. The stack is built up by tokens and combinators, and is always transformed to a single value (usually an object) by Node. The specifics of how the stack grows are documented below:

  • 'foo' does not add anything to the stack
  • /[0-9]/ adds match[0] to the stack, or null
  • All(...grammars) concatenates stacks from all grammars
  • Any(...grammars) uses stack from first matching grammar of grammars
  • Optional(grammar) adds to the stack when grammar matches
  • Ignore(grammar) never adds to the stack
  • Plus(grammar) concatenates stacks from all repetitions of grammar
  • Star(grammar) concatenates stacks from all repetitions of grammar

Fork

This project is forked from dmaevsky's rd-parse. I wanted the freedom to tweak several aspects of the upstream library at once.

In particular the documentation in this version has been rewritten and several notable changes have been made to the API:

  • Undocumented exports are removed, particularly START, and Use.
  • Regex token changes:
    • Capture groups no longer have meaning. Instead the full match result is used.
    • required ^ is now prepended to expression for you
  • Ignore renamed to IgnoreBetween. Ignore is a new rule type.