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 🙏

© 2026 – Pkg Stats / Ryan Hefner

polish-engine

v2.0.0

Published

Polish notation math engine for evaluating complex mathematical expressions.

Readme

Polish Engine

Polish Engine is a lightweight TypeScript math engine for evaluating expressions with Reverse Polish Notation (RPN), normalized output, complex number support, and step-by-step evaluation traces.

It is designed for calculators, educational tools, math UIs, and any project that needs predictable expression evaluation in JavaScript or TypeScript.

Features

  • Converts infix expressions to Reverse Polish Notation (RPN).
  • Evaluates real and complex numbers with complex.js.
  • Supports variables, implicit multiplication, common math functions, operator precedence, and factorial.
  • Returns normalized values by default for stable UI and API output.
  • Returns evaluation steps by default for debugging, tracing, and educational experiences.
  • Written in TypeScript and tested with Vitest.

Installation

npm install polish-engine

Quick Start

import { polishEngine } from "polish-engine";

const engine = new polishEngine();

engine.evaluate("2 + 3");
// {
//   result: { type: "real", value: 5, display: "5" },
//   steps: [
//     {
//       type: "Operator",
//       name: "+",
//       operands: [
//         { type: "real", value: 2, display: "2" },
//         { type: "real", value: 3, display: "3" }
//       ],
//       result: { type: "real", value: 5, display: "5" },
//       stackBefore: [
//         { type: "real", value: 2, display: "2" },
//         { type: "real", value: 3, display: "3" }
//       ],
//       stackAfter: [
//         { type: "real", value: 5, display: "5" }
//       ]
//     }
//   ]
// }

Breaking Changes in v2

In v1, evaluate() returned the evaluated value directly:

engine.evaluate("2 + 3");
// 5

In v2.0.0, steps is enabled by default, so evaluate() returns an object with both the final result and the evaluation trace:

engine.evaluate("2 + 3");
// {
//   result: { type: "real", value: 5, display: "5" },
//   steps: [...]
// }

To get only the result, pass { steps: false }:

engine.evaluate("2 + 3", { steps: false });
// { type: "real", value: 5, display: "5" }

To get the old direct raw value style, disable both steps and normalization:

engine.evaluate("2 + 3", {
  steps: false,
  normalize: false
});
// 5

evaluate

evaluate(expression, options) evaluates an expression string.

By default:

  • steps is true.
  • normalize is true.
  • The return value is { result, steps }.
engine.evaluate("2 + 3");
// {
//   result: { type: "real", value: 5, display: "5" },
//   steps: [...]
// }

Disable steps to return only the final normalized result:

engine.evaluate("2 + 3", { steps: false });
// { type: "real", value: 5, display: "5" }

Disable normalization to return raw evaluation values instead of NormalizedValue objects. If steps are still enabled, the output still includes { result, steps }:

engine.evaluate("2 + 3", { normalize: false });
// {
//   result: 5,
//   steps: [
//     {
//       type: "Operator",
//       name: "+",
//       operands: [2, 3],
//       result: 5,
//       stackBefore: [2, 3],
//       stackAfter: [5]
//     }
//   ]
// }

Disable both steps and normalization to return only the raw result:

engine.evaluate("2 + 3", {
  steps: false,
  normalize: false
});
// 5

EvaluateOptions

evaluate accepts either the legacy variables object or the v2 options object.

type Variables = Record<string, number>;

interface EvaluateOptions {
  variables?: Variables;
  steps?: boolean;
  normalize?: boolean;
}

Options:

  • variables: Values used when the expression contains variables.
  • steps: When true, return { result, steps }. Defaults to true.
  • normalize: When true, convert raw numbers and Complex values into NormalizedValue. Defaults to true.

NormalizedValue

When normalization is enabled, values use one of these shapes.

For UI rendering, prefer the display property instead of manually formatting value, re, or im.

type NormalizedValue =
  | {
      type: "real";
      value: number;
      display: string;
    }
  | {
      type: "complex";
      re: number;
      im: number;
      display: string;
    };

Examples:

engine.evaluate("2 + 3", { steps: false });
// { type: "real", value: 5, display: "5" }

engine.evaluate("sqrt(-4)", { steps: false });
// { type: "complex", re: 0, im: 2, display: "2i" }

Using display

The display property is intended for user interfaces and formatted output.

const result = engine.evaluate("sqrt(-4)", {
  steps: false
});

console.log(result.display);
// "2i"

For real numbers:

const result = engine.evaluate("2 + 3", {
  steps: false
});

console.log(result.display);
// "5"

Getting the display value

const output = engine.evaluate("sqrt(-4)");

console.log(output.result.display);
// "2i"

Evaluation Steps

Steps describe how the RPN evaluator transforms the stack.

Each step includes:

  • type: "Operator" or "Function".
  • name: The operator or function name, such as "+", "*", or "sqrt".
  • operands: Values consumed by the operation.
  • result: Value produced by the operation.
  • stackBefore: Stack state before the operation consumed its operands.
  • stackAfter: Stack state after the operation pushed its result.

When normalize: true, operands, result, stackBefore, and stackAfter are normalized too.

engine.evaluate("2 + 3");
// {
//   result: { type: "real", value: 5, display: "5" },
//   steps: [
//     {
//       type: "Operator",
//       name: "+",
//       operands: [
//         { type: "real", value: 2, display: "2" },
//         { type: "real", value: 3, display: "3" }
//       ],
//       result: { type: "real", value: 5, display: "5" },
//       stackBefore: [
//         { type: "real", value: 2, display: "2" },
//         { type: "real", value: 3, display: "3" }
//       ],
//       stackAfter: [
//         { type: "real", value: 5, display: "5" }
//       ]
//     }
//   ]
// }

Variables

The legacy variables API is still supported:

engine.evaluate("2x + 1", { x: 3 });
// {
//   result: { type: "real", value: 7, display: "7" },
//   steps: [...]
// }

The v2 options API is also supported:

engine.evaluate("2x + 1", {
  variables: { x: 3 },
  steps: true
});
// {
//   result: { type: "real", value: 7, display: "7" },
//   steps: [...]
// }

To get only the normalized result with variables:

engine.evaluate("2x + 1", {
  variables: { x: 3 },
  steps: false
});
// { type: "real", value: 7, display: "7" }

Complex Numbers

Complex results are normalized by default:

engine.evaluate("sqrt(-4)");
// {
//   result: { type: "complex", re: 0, im: 2, display: "2i" },
//   steps: [...]
// }

With normalize: false, complex results are returned as raw complex.js Complex instances. If steps is still enabled, raw values also appear inside the evaluation steps:

engine.evaluate("sqrt(-4)", { normalize: false });
// {
//   result: Complex, // complex.js instance
//   steps: [
//     {
//       type: "Function",
//       name: "sqrt",
//       operands: [-4],
//       result: Complex,
//       stackBefore: [-4],
//       stackAfter: [Complex]
//     }
//   ]
// }

When steps are disabled, the raw output is the final value directly. Access .re and .im only after confirming that the returned value is a Complex instance:

import Complex from "complex.js";

const raw = engine.evaluate("sqrt(-4)", {
  steps: false,
  normalize: false
});

if (raw instanceof Complex) {
  console.log(raw.re); // 0
  console.log(raw.im); // 2
}

Common Examples

engine.evaluate("2 + 3 * 4", { steps: false });
// { type: "real", value: 14, display: "14" }

engine.evaluate("(2 + 3) * 4", { steps: false });
// { type: "real", value: 20, display: "20" }

engine.evaluate("4!", { steps: false });
// { type: "real", value: 24, display: "24" }

engine.evaluate("sin(0)", { steps: false });
// { type: "real", value: 0, display: "0" }

Project Structure

polish-engine/
|-- src/
|   |-- polish-engine.ts
|   |-- tokenizer.ts
|   |-- polish-parser.ts
|   |-- polish-evaluator.ts
|   |-- preprocessModule.ts
|   |-- result-normalizer.ts
|   |-- evaluate-options-resolver.ts
|   `-- functionsModule.ts
|-- tests/
|   |-- tokenizer.test.ts
|   |-- parser.test.ts
|   |-- evaluator.test.ts
|   `-- polish-engine.test.ts
|-- package.json
|-- tsconfig.json
|-- vitest.config.ts
|-- LICENSE
`-- README.md

Running Tests

npm test

Run the TypeScript compiler:

npx tsc

Tech Stack

  • TypeScript
  • Vitest
  • complex.js
  • ES Modules

Author

Jonathan Ventura
GitHub: JVenturaDev

License

This project is licensed under the MIT License. See LICENSE for details.