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

libparsec

v0.0.1

Published

A monadic parser combinator library.

Downloads

4

Readme

Libparsec

Libparsec is a parsing library inspired by the Haskell library Parsec. You can make parsers by combining and composing smaller parsers.

Simple parsers

Simple strings

import {stringP} from "libparsec";
console.log(stringP("hello").parse("hello"));
// => Either { _isRight: true, right: 'hello' }

Calling .parse on a parser returns an object with the result of parsing assigned to the "right" key on success.

If parsing fails, the error is returned through the "left" key.

console.log(stringP("hello").parse("xyz").left)
// => { message: 'Expected \'h\' got \'x\'',
//      state: { input: 'xyz', offset: 0 } } }
import {oneOf, noneOf} from "libparsec";
// Parsing one of many characters
console.log(oneOf("abcd").parse("b").right) // => b

// Characters other than given chars
console.log(noneOf("abcd").parse("x").right) // => x
console.log(noneOf("abcd").parse("a").right) // => undefined

Basic combinators

Function many takes a parser and returns a parser that matches multiple consecutive instances and returns an array.

Function many1 does the same thing except it requires atleast 1 instance to succeed.

// multiple instances
import {many} from "libparsec";
many(stringP("ab")).parse("").right // => []
many(stringP("ab")).parse("ab").right // => ["ab"]
many(stringP("ab")).parse("abab").right // => ["ab", "ab"]
// multiple items seperated by a seperator
stringP("ab").sepBy(stringP(","))
  .parse("ab,ab").right // => [ 'ab', 'ab' ]
import {Parser} from "libparsec";
// Parser that returns a value without consuming any input
Parser.unit("asdf").parse("pqrs").right // => "asdf"

Parser with multiple options

stringP("abc")
  .or(stringP("xyz"))
  .or(stringP("pqr")).parse("xyz") // => "xyz"

Chaining multiple parsers

parser.consume method takes in a Parser instance and returns a parser that consumes the first parser and then second parser and returns the result of first parser ignoring the result of second parser

stringP("asdf")
  .consume(many(stringP(" ")))
  .parse("asdf     ").right // => 'asdf'
  

.then(p: Parser) is used to ignore the return values of previous parsers

stringP("function ")
  .then(variableP)

Here, result of the first parser (stringP("function ")) will be ignored and the parser will return the result of variableP parser.

Transforming the result

Parser is a monad which means you can call map and flatMap on it. .map method transforms the result of a parse using the given function

import {digit} from "libparsec";
numberP = many1(noneOf(digit));
numberP.parse("234").right // => "234"
numberP.map(parseInt)
  .parse("234").right // => 234

Passing parse results to chained parsers

Parser<A>.flatMap(A => Parser<B>) matches using the starting parser and passes the result of parse to the function argument, which returns another parser

stringP("true")
  .flatMap(t => ...) // Here t will be bound to the value
                     // "true" in case of successful match

flatMap can be used to pass the parsed value down a chain of parsers. In case of parsers that must collect results from a sequence of parsers, nested flatMaps can become unweildy. In such a case, assign method is useful. assign is used to build up the result of chained parsers.

Parser.unit({}) // start with a parser that returns an empty 
                // object and consumes no input
  .consume(stringP("function")) // match string "function"
  .consume(whiteSpace) // ignore whitespace
  .assign("name", many1(alphabet)) // assign function name to object
  .consume(optionalWhiteSpace)
  .consume(charP('('))
  .consume(optionalWhiteSpace)
  .assign("args", many1(alphabet).sepBy(comma)) // comma seperated list of parameters
  .consume(optionalWhiteSpace)
  .consume(charP(")"))
  .consume(optionalWhiteSpace)
  .consume(charP('{'))
  .consume(optionalWhiteSpace)
  .assign("body", stringP("body"))
  .consume(optionalWhiteSpace)
  .consume(charP("}"))
  .parse("function f(x,y){body}")
  /* => Either {
   _isRight: true,
   right: { name: 'f', args: [ 'x', 'y' ], body: 'body' } } */

Note that .assign should only be called on parsers with object return types otherwise, it will throw an exception.

Mutually recursive parsers

In case of parsers that depend on each other, normal parsers don't work. In such a caes, use the Parser.lazy function to make sure that parsers aren't evaluated until parsing starts. Parser.lazy takes a parser wrapped in an annonymous function.

let expression: Parser<boolean> = Parser.lazy( () =>
  stringP("true").map(x => true)
  .or(stringP("false").map(x => false))
  .or(
    charP("(")
    .then(expression)
    .consume(charP(")")))
);

expression.parse("true").right // => true
expression.parse("((true))").right // => true

Note that for typescript, type annotation for expression is necessary or else .then(expression) will throw a type error

Other examples

Check out the json example for a mostly complete json example in less than 100 lines.