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 🙏

© 2025 – Pkg Stats / Ryan Hefner

arith2js-calculator

v1.0.0

Published

Transpiler for calculator expressions with complex numbers, scope analysis and variable support

Readme

Open in Codespaces

arith2js Calculator with Scope Analysis

This project implements a transpiler that converts calculator expressions into executable JavaScript code. It's an extension of the previous arithmetic calculator that now supports complex numbers, min/max operators, and scope analysis features including variable declarations, assignments, and print operations.

New Features: Scope Introduction

The calculator has been extended to support:

  1. Variable Declarations and Assignments

    • Variables are automatically declared on first assignment
    • Variables must be used only after declaration
    • Variable names are prefixed with $ to avoid conflicts with JavaScript keywords
  2. Comma-Separated Expressions

    • Support for multiple expressions separated by commas
    • Evaluates each expression in sequence
  3. Print Function

    • Added print() functionality to output results
    • Properly handles complex number output formatting
  4. Scope Analysis

    • Detects and reports usage of undeclared variables
    • Provides line and column information for errors
    • Ensures variables are declared before use

Bug Fixes in This Version

Complex Number Operation Handling

The major bug fixed in this version involved the handling of complex number operations. The issue was that the code was directly calling complex number methods (like sub, mul, div) on JavaScript primitive numbers, which don't have these methods.

For example, code was being generated like this:

(4 - 2).sub(Complex(1))  // Error: 2.sub is not a function

The fix involved modifying the buildBinaryExpression function in ast-build.js to ensure that all operands are properly wrapped in Complex objects before calling these methods:

// Fixed code generation for complex operations
Complex(4 - 2).sub(Complex(1))  // Correct: Wraps the result in a Complex object

Key changes included:

  1. Updated operator handling condition:

    // Only use regular binary expressions if both operands are simple numbers 
    // AND operation is not arithmetic
    if (isSimpleNumericNode(left) && isSimpleNumericNode(right) && 
        ['+', '-', '*', '/'].indexOf(op) === -1) {
      // Use regular binary expression
    }
  2. Added special handling for binary expressions:

    else if (left.type === "BinaryExpression") {
      // If left is a binary expression, wrap it in Complex()
      leftNode = {
        type: "CallExpression",
        callee: {
          type: "Identifier",
          name: "Complex"
        },
        arguments: [{ /* binary expression */ }],
      };
    }
  3. Enhanced unary expression handling: The negation operator also needed similar fixes to properly wrap arguments in Complex objects.

These changes ensure that complex number methods are always called on Complex objects, not on primitive JavaScript numbers.

Variable Assignment Semantics

In this implementation, variables use reference semantics when assigned complex numbers:

  • When assigning a complex number to a variable, the variable holds a reference to the object
  • This approach is faster and consumes less memory
  • It's consistent with JavaScript's default behavior for objects

Implementation Details

The transpiler treats variable assignments as references to objects, following JavaScript's native behavior for complex types:

// Example of reference semantics in the generated code
$a = Complex("2").add(Complex("3i"));
$b = $a; // $b now references the same object as $a
$b = Complex("9"); // $b now references a completely different object

When executed, each variable maintains its own identity:

print($b); // 9
print($a); // 2 + 3i

Scope Analysis Implementation

The scope analyzer is implemented in scope.js using the ast-types library for AST traversal and manipulation. It performs the following key functions:

1. Variable Declaration and Usage Tracking

Three key data structures are built during AST traversal:

let assigned = new Map();           // Maps variable names to their first assignment
let used = new Map();               // Tracks all variable usages
let usedBeforeDeclaration = new Map(); // Tracks variables used before declaration

2. AST Traversal

The analyzer traverses the AST using the visitor pattern:

  • Assignment Expression Visitor: Detects variable declarations by tracking the first assignment to each variable

    visitAssignmentExpression(path) {
      if (node.left.type === "Identifier" && name.startsWith("$")) {
        // Register the first assignment as a declaration
        assigned.set(name, node);
      }
    }
  • Identifier Visitor: Monitors variable usages and validates declarations

    visitIdentifier(path) {
      // Skip if not a variable usage
      if (!name.startsWith("$")) return;
        
      // Check if the variable has been declared
      if (!assigned.has(name)) {
        // Record usage before declaration error
        usedBeforeDeclaration.set(name, [...]);
      }
    }
  • Program Visitor: Injects let declarations at the program start

    visitProgram(path) {
      // Generate variable declarations
      let declared = Array.from(assigned.keys())
        .map(name => variableDeclarator(identifier(name)));
          
      // Insert at program start
      path.get('body').unshift(variableDeclaration("let", declared));
    }

3. Error Reporting

When variables are used before declaration, the system generates clear error messages with precise location information:

Bad usage of variable "b" at line 2 column 3

This implementation enables proper variable scoping, ensuring that variables are declared before use while maintaining JavaScript's reference semantics for objects.

Usage Examples

Variable Assignment and Print

$ node bin/calc2js.mjs --run "a = 4+i, b = 2-2i, print(a*b)"
{ re: 10, im: -6 }

Error Detection (Undeclared Variables)

$ node bin/calc2js.mjs "a = 4+d+i, b = 2-2i, print(c)"
Not declared variable 'd' at line 1 column 7
Not declared variable 'c' at line 3 column 7

Usage Before Declaration

$ node bin/calc2js.mjs "a = 2, a-b, b = 3"
Bad usage of variable "b" at line 2 column 3

Architecture

The transpiler follows a standard compiler architecture with these components:

  1. Lexer (lexer.l): Defines tokens (numbers, identifiers, operators)
  2. Parser (grammar.jison): Defines grammar rules for expressions
  3. AST Building (ast-build.js): Constructs JavaScript AST from parsed expressions
  4. Scope Analysis (scope.js): Analyzes variable declarations and usages
  5. Support Library (support-lib.js): Provides utilities for complex numbers
  6. Transpiler (transpile.js): Coordinates the transpilation process
  7. CLI (bin/calc2js.mjs): Provides user interface to the transpiler

Transpilation Process

  1. Read the input file
  2. Parse the input to generate an AST
  3. Analyze scope to detect variable declarations and usages
  4. Check for undeclared or improperly used variables
  5. Identify support library dependencies
  6. Generate JavaScript code from the AST
  7. Run or write the generated code to an output file

Command Line Options

Usage: calc2js [options] [command] <filename>

Transpile a Calc program to JavaScript

Options:
  -V, --version                   output the version number
  -r, --run                       run the generated code
  -o, --output <filename>         file in which to write the output
  -v, --verbose                   show the generated code
  -a, --ast                       show the AST
  -s, --scope                     shows the scope map
  -D, --definitions               shows the variable definitions
  -h, --help                      display help for command

Commands:
  transpile [options] <filename>  Transpiles the input to JavaScript
  run [options] <filename>        Transpiles and run the resulting JS code

Installation

From npm (as a package)

npm install arith2js-calculator

From Source

git clone https://github.com/ULL-ESIT-PL-2425/arith2js-adrian-grassin-luis-alu0101349480.git
cd arith2js-adrian-grassin-luis-alu0101349480
npm install

Testing and Documentation

Run tests:

npm test

Generate documentation and coverage:

npm run docs

View documentation:

npm run dev

Publishing the Package

To publish this package to npm:

  1. Update the package name and version in package.json
  2. Create an npm account if you don't have one
  3. Login to npm:
    npm login
  4. Publish the package:
    npm publish

License

ISC