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

@fordi-org/bsimp

v1.0.4

Published

A boolean expression simplifier

Downloads

2,809

Readme

bsimp

A boolean expression simplifier

Demo

parse(string)

Convert a string representation of a logical expression into an expression object

import { parse } from '@fordi-org/bsimp/parse';

console.log(parse('A+A!B'));
[
  Symbol( ∪ ),
  Symbol(A),
  [ Symbol( ∩ ), Symbol(A), [ Symbol(!), Symbol(B) ] ]
]

toString(expression)

Convert an expression object into a string

import { parse, toString, SET } from '@fordi-org/bsimp';

console.log(toString(parse('A+A!B'), SET));
console.log(toString(parse('A+A!B'), LOGIC));
console.log(toString(parse('A+A!B'), POLISH));
console.log(toString(parse('A+A!B'), SOURCE));
A ∪ (A ∖ B)
A+A!B
(∪ A (∩ A !B))
[OR, A, [AND, A, [NOT, B]]]

simplify(expression, [steps])

Simplify a symbolized boolean expression. Optionally, pass an array to collect steps.

import { simplify, parse, toString, LOGIC } from '@fordi-org/bsimp/simplify';

const toSimplify = [
  'A+B!C+C!A',
  'AB!C+ABC+A!B!C+B!A!C',
];

const result = toSimplify.map(expStr => {
  const exp = parse(expStr);
  const steps = [];
  const simple = simplify(exp, steps);
  return {
    simplified: toString(simple, LOGIC),
    steps,
  };
});

console.log(result);
[ 
  {
    simplified: 'A+B+C',
    steps: [
      [
        'absorption',
        null,
        [OR, A, [AND, B, [NOT, C]], [AND, C, [NOT, A]]],
        [OR, A, C, B]
      ]
    ]
  },
  {
    simplified: 'AB+A!C+B!C',
    steps: [...],
  }
]

The structure of a step is a tuple of [transform, parentOperation, fromExpr, toExpr].

symbolize(termStr)

Convert a string into a symbol. For AND, OR, NOT, TRUE, and FALSE, this will convert the string to the internal symbol. For any other string, you'll get term(termStr).

term(termStr)

Convert a string into a symbol. For any previously named term, you'll get the same symbol back for the same input string (this differs from how Symbol(termStr) works, in that every Symbol that is created is unique).


Expression Objects

Formally...

  • Expression: [Operator, ...Operand]
  • Operator: AND | OR | NOT
  • Operand: TRUE | FALSE | Symbol | Expression

That is, it's an array where the first element must be an Operator, and all the following symbols must be Operands. Operators are the symbols AND, OR, and NOT (exported from @fordi-org/bsimp). Operands can be the symbols TRUE or FALSE, another Expression, or any user-defined Symbol.

Expression strings

The grammar is very forgiving, and allows the use of the following:

Polish notation

{OP} {TERM} {TERM}*

e.g.,

"(and A B C)" becomes [AND, A, B, C]

Infix notation

{TERM} ({OP} {TERM})*

e.g.,

(P | Q | R) becomes [OR, P, Q, R]

Ternary

The parser understands Ternary expressions, and the CODE stringifier knows how to convert expressions to them, however, they are expressed as a logical combination of AND, OR, and NOT. A ternary, if you're unaware, is like an if / then / else statment, where each piece is a term.

So, C ? T : F evaluates to [OR, [AND, C, T], [AND, [NOT, C], F]]. You can nest ternaries, but they must be grouped to avoid ambiguity.

Support for ternaries is meant to be an assist in passing expressions into the simplifier - while the CODE stringifier can identify and subsume the simple form of this pattern, nested ternaries will usually be simplified into something the CODE stringifier cannot identify (for example, a ? (b ? c : d) : e will become (a && b && c) || (e && !a) || (a && !b && d), which is not ideal).

I may work more on this, but ternary support was initially meant to be for input.

Implicit AND

{TERM}{TERM}*

e.g.,

XYZ becomes [AND, X, Y, Z]

Note: because of the need for implicit AND, the parser limits terms to single-letter, case-sensitive. The simplifier is not so-limited, and you can do something like this:

const [a, b] = [
  term('longName'),
  term('longerName'),
];
const simple = simplify([OR, a, [AND, a, [NOT, b]]]);

The two use cases I had in mind were a boolean simplifier for linters (where the linter could choose completely random symbol names), and an on-page logic simplifier widget (where a user can enter tight logic strings and get back their simplification).


Development concerns

To run the test suite:

npm run test

To compile the boolean grammar:

npm run build:peg

To build the whole thing:

npm run build