JSOL

Meet JSON's functional half-brother.


JSOL, a single source of truth for business logic: exactly the same results, across different codebases.

I needed to keep the same business rules running on different targets, and the solutions I found were too complex to integrate into the lightweight pipeline I was already using. So I ended up building my own.

Today, JSOL compiles (even itself) to JavaScript, TypeScript, PHP and Python. It uses a deliberately simple syntax, a set of standard functions designed to remove ambiguities between languages, and contracts to ensure the compiled code produces the same results on every target.

Once I solved my cases, I decided to make it more robust so I could actually trust it. The best robustness standard I could come up with was to keep pushing it until I felt comfortable publishing it, and hopefully help the next person in line. :)


/**
@contract
{ "cases": [
	{ "$nYear": 2000 },
	{ "$nYear": 1900 },
	{ "$nYear": 2023 }
] }
*/

const $bIsLeapYear = function($nYear) {
	if (Math.modX($nYear, 400) === 0) return true;
	if (Math.modX($nYear, 100) === 0) return false;
	return Math.modX($nYear, 4) === 0;
};

JSOL's functions are compiled to every target and tested against the same contract. If any target produces a different result, the contract fails.


NaN

Knowing the risks is good.
Reducing them is better.

When the same business rule has to be maintained across several targets, it's not enough to know that programming languages differ in their implementation and semantics.

  • A change in business logic shouldn't automatically become a new ticket and a separate project for every target. Especially when regulations, tax rules, or other requirements can change with little notice and need to be addressed yesterday.
  • A reliable workflow shouldn't depend on having that particular programmer on call who happens to remember every language-specific edge case.
  • Even with AI in the toolset, some of these differences can slip through: it happened to me, and that's why I started JSOL.

The more often those rules change, the more opportunities there are for a semantic difference to become a real bug.

If you have already been affected by this kind of scenario, you may even consider contributing or supporting the project. The next step for JSOL is to sort out the 140+ semantic differences already documented, making this kind of verification systematic rather than something you'll have to remember and check manually.

Case JS PHP Python
7.5 % 2 1.5 1 1.5
Math.round(2.5) 3 3 2
Math.round(-2.5) -2 -3 -2
"😀".length 2 4 1
String(true) true 1 True
"12abc" as int 12 12 ValueError
"10" < "9" true false true
[10, 1, 2].sort() 1, 10, 2 1, 2, 10 1, 2, 10
"abc".split("") a,b,c a,b,c ValueError
"a" || "b" a true a
min(NaN, 5) NaN 5 nan
[].pop() undefined null IndexError
"" as number 0 0 ValueError

These are documented language behaviors. The challenge isn't knowing that they exist; it's ensuring that they don't become bugs when the business rule changes.

REPL

Is it worth your time?

The first question we developers ask about a new tool is: "Should I bother learning this?". Short answer is: probably not.

Oh. You're still reading. Well, for the long answer:

  • JSOL has strict syntax, zero syntactic sugar, and a learning curve.
  • Writing it –even with an AI's help– will take you longer than writing native code you've already mastered.
  • But JSOL's upfront cost can pay off in iterative maintenance.

The honest way to answer that question is with a model. So the first example below is exactly that: an adoption-economics model written in JSOL, running live in the REPL. And compiled to every target for inspection (less the polyfills).

Play with the inputs: how many targets you maintain, how often your rules change, and get how many iterations until (if) the upfront cost pays for itself. And while you're at it, you're watching JSOL do what it was built to do.

JSOLJSOL Mascot
JSOL's mascot: the Sun of May wearing Jason's mask.
📊 JSOL Spreadsheet REPL — v0.2.97

Function: $aChristmasSundays

// @JSOL v0.2.97

/**
 @description
 Rosetta Code task: https://rosettacode.org/wiki/Day_of_the_week — the
 task asks for every year in a range for which December 25th falls on a
 Sunday. This function solves that literally, calling `$sDayOfWeek` once
 per year in the range; its own contract embeds the task's known-correct
 answer, so the contract doubles as a check against the task statement
 itself, not just against the algorithm.

 Returns every year in `[$nStartYear, $nEndYear]` for which December 25th
 falls on a Sunday.

@param {number} $nStartYear - First year to check, inclusive.
@param {number} $nEndYear - Last year to check, inclusive.
@returns {array<number>} - Years whose Christmas Day is a Sunday.
*/

/**
 @contract
 {
   "cases": [
     { "$nStartYear": 2008, "$nEndYear": 2121 }
   ],
   "expected_for_case_0": [2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095, 2101, 2107, 2112, 2118]
 }
*/

const $aChristmasSundays = function($nStartYear, $nEndYear) {
    const $aYears = [];

    for (let $nYear = $nStartYear; $nYear <= $nEndYear; $nYear = $nYear + 1) {
        const $sWeekday = $sDayOfWeek($nYear, 12, 25);
        if ($sWeekday === "Sunday") {
            Arr.push($aYears, $nYear);
        }
    }

    return $aYears;
};

/**
 @description
 Computes the day of the week for a Gregorian calendar date using
 Zeller's Congruence, a closed-form formula published by Christian
 Zeller in the 1880s. January and February are treated as months 13 and
 14 of the *previous* year, which is why the formula subtracts 1 from
 the year for those two months before anything else: it keeps March as
 the start of the "formula year", avoiding a separate leap-day special
 case inside the arithmetic itself.

 The classic formula ends in "- 2*J"; here it is written as "+ 5*J"
 instead (-2 and +5 are congruent mod 7), so every term in the sum stays
 non-negative.

@param {number} $nYear - Full year (e.g. 2026).
@param {number} $nMonth - Month, 1-12.
@param {number} $nDay - Day of the month.
@returns {string} - Day of the week: "Sunday" through "Saturday".
*/

/**
 @contract
 {
   "cases": [
     { "$nYear": 2026, "$nMonth": 8, "$nDay": 13 },
     { "$nYear": 2000, "$nMonth": 1, "$nDay": 1 }
   ]
 }
*/

const $sDayOfWeek = function($nYear, $nMonth, $nDay) {
    let $nAdjustedMonth = $nMonth;
    let $nAdjustedYear = $nYear;

    if ($nMonth < 3) {
        $nAdjustedMonth = $nMonth + 12;
        $nAdjustedYear = $nYear - 1;
    }

    const $nK = Math.modX($nAdjustedYear, 100);
    const $nJ = Math.floor($nAdjustedYear / 100);

    const $nH = Math.modX(($nDay
        + Math.floor((13 * ($nAdjustedMonth + 1)) / 5)
        + $nK
        + Math.floor($nK / 4)
        + Math.floor($nJ / 4)
        + (5 * $nJ)), 7);

    // $nH: 0=Saturday, 1=Sunday, 2=Monday, ... 6=Friday.
    const $aDayNames = ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];

    return $aDayNames[$nH];
};
// @JSOL v0.2.97

/**
 @description
 Rosetta Code task: https://rosettacode.org/wiki/Day_of_the_week — the
 task asks for every year in a range for which December 25th falls on a
 Sunday. This function solves that literally, calling `$sDayOfWeek` once
 per year in the range; its own contract embeds the task's known-correct
 answer, so the contract doubles as a check against the task statement
 itself, not just against the algorithm.

 Returns every year in `[$nStartYear, $nEndYear]` for which December 25th
 falls on a Sunday.

@param {number} $nStartYear - First year to check, inclusive.
@param {number} $nEndYear - Last year to check, inclusive.
@returns {array<number>} - Years whose Christmas Day is a Sunday.
*/

/**
 @contract
 {
   "cases": [
     { "$nStartYear": 2008, "$nEndYear": 2121 }
   ],
   "expected_for_case_0": [2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095, 2101, 2107, 2112, 2118]
 }
*/

const $aChristmasSundays = function($nStartYear, $nEndYear) {
  const $aYears = [];

    for (let $nYear = $nStartYear; $nYear <= $nEndYear; $nYear = $nYear + 1) {
    const $sWeekday = $sDayOfWeek($nYear, 12, 25);
        if ($sWeekday === "Sunday") {
      $aYears.push( $nYear);
    }
  }
  return $aYears;
};
/**
 @description
 Computes the day of the week for a Gregorian calendar date using
 Zeller's Congruence, a closed-form formula published by Christian
 Zeller in the 1880s. January and February are treated as months 13 and
 14 of the *previous* year, which is why the formula subtracts 1 from
 the year for those two months before anything else: it keeps March as
 the start of the "formula year", avoiding a separate leap-day special
 case inside the arithmetic itself.

 The classic formula ends in "- 2*J"; here it is written as "+ 5*J"
 instead (-2 and +5 are congruent mod 7), so every term in the sum stays
 non-negative.

@param {number} $nYear - Full year (e.g. 2026).
@param {number} $nMonth - Month, 1-12.
@param {number} $nDay - Day of the month.
@returns {string} - Day of the week: "Sunday" through "Saturday".
*/

/**
 @contract
 {
   "cases": [
     { "$nYear": 2026, "$nMonth": 8, "$nDay": 13 },
     { "$nYear": 2000, "$nMonth": 1, "$nDay": 1 }
   ]
 }
*/

const $sDayOfWeek = function($nYear, $nMonth, $nDay) {
  let $nAdjustedMonth = $nMonth;
    let $nAdjustedYear = $nYear;

    if ($nMonth < 3) {
    $nAdjustedMonth = $nMonth + 12;
        $nAdjustedYear = $nYear - 1;
  }
  const $nK = Math["modX"]($nAdjustedYear,  100);
    const $nJ = Math["floor"]($nAdjustedYear / 100);

    const $nH = Math["modX"](($nDay
        + Math["floor"]((13 * ($nAdjustedMonth + 1)) / 5)
        + $nK
        + Math["floor"]($nK / 4)
        + Math["floor"]($nJ / 4)
        + (5 * $nJ)),  7);

    // $nH: 0=Saturday, 1=Sunday, 2=Monday, ... 6=Friday.
    const $aDayNames = ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];

    return $aDayNames[$nH];
};
window['$aChristmasSundays'] = $aChristmasSundays;
<?php
// @JSOL v0.2.97

/**
 @description
 Rosetta Code task: https://rosettacode.org/wiki/Day_of_the_week — the
 task asks for every year in a range for which December 25th falls on a
 Sunday. This function solves that literally, calling `$sDayOfWeek` once
 per year in the range; its own contract embeds the task's known-correct
 answer, so the contract doubles as a check against the task statement
 itself, not just against the algorithm.

 Returns every year in `[$nStartYear, $nEndYear]` for which December 25th
 falls on a Sunday.

@param {number} $nStartYear - First year to check, inclusive.
@param {number} $nEndYear - Last year to check, inclusive.
@returns {array<number>} - Years whose Christmas Day is a Sunday.
*/

/**
 @contract
 {
   "cases": [
     { "$nStartYear": 2008, "$nEndYear": 2121 }
   ],
   "expected_for_case_0": [2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095, 2101, 2107, 2112, 2118]
 }
*/

$aChristmasSundays = function($nStartYear, $nEndYear) use (&$sDayOfWeek) {
  $aYears = [];

    for ($nYear = $nStartYear; $nYear <= $nEndYear; $nYear = $nYear + 1) {
    $sWeekday = $sDayOfWeek($nYear, 12, 25);
        if ($sWeekday === "Sunday") {
      $aYears[] =  $nYear;
    }
  }
  return $aYears;
};
/**
 @description
 Computes the day of the week for a Gregorian calendar date using
 Zeller's Congruence, a closed-form formula published by Christian
 Zeller in the 1880s. January and February are treated as months 13 and
 14 of the *previous* year, which is why the formula subtracts 1 from
 the year for those two months before anything else: it keeps March as
 the start of the "formula year", avoiding a separate leap-day special
 case inside the arithmetic itself.

 The classic formula ends in "- 2*J"; here it is written as "+ 5*J"
 instead (-2 and +5 are congruent mod 7), so every term in the sum stays
 non-negative.

@param {number} $nYear - Full year (e.g. 2026).
@param {number} $nMonth - Month, 1-12.
@param {number} $nDay - Day of the month.
@returns {string} - Day of the week: "Sunday" through "Saturday".
*/

/**
 @contract
 {
   "cases": [
     { "$nYear": 2026, "$nMonth": 8, "$nDay": 13 },
     { "$nYear": 2000, "$nMonth": 1, "$nDay": 1 }
   ]
 }
*/

$sDayOfWeek = function($nYear, $nMonth, $nDay) {
  $nAdjustedMonth = $nMonth;
    $nAdjustedYear = $nYear;

    if ($nMonth < 3) {
    $nAdjustedMonth = $nMonth + 12;
        $nAdjustedYear = $nYear - 1;
  }
  $nK = Math::modX($nAdjustedYear,  100);
    $nJ = floor($nAdjustedYear / 100);

    $nH = Math::modX(($nDay
        + floor((13 * ($nAdjustedMonth + 1)) / 5)
        + $nK
        + floor($nK / 4)
        + floor($nJ / 4)
        + (5 * $nJ)),  7);

    // $nH: 0=Saturday, 1=Sunday, 2=Monday, ... 6=Friday.
    $aDayNames = ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];

    return $aDayNames[$nH];
};
declare var JSOL: any;
declare var Rgx: any;
declare var Str: any;
declare var Arr: any;
declare var Bool: any;
declare var Cast: any;

// @JSOL v0.2.97

/**
 @description
 Rosetta Code task: https://rosettacode.org/wiki/Day_of_the_week — the
 task asks for every year in a range for which December 25th falls on a
 Sunday. This function solves that literally, calling `$sDayOfWeek` once
 per year in the range; its own contract embeds the task's known-correct
 answer, so the contract doubles as a check against the task statement
 itself, not just against the algorithm.

 Returns every year in `[$nStartYear, $nEndYear]` for which December 25th
 falls on a Sunday.

@param {number} $nStartYear - First year to check, inclusive.
@param {number} $nEndYear - Last year to check, inclusive.
@returns {array<number>} - Years whose Christmas Day is a Sunday.
*/

/**
 @contract
 {
   "cases": [
     { "$nStartYear": 2008, "$nEndYear": 2121 }
   ],
   "expected_for_case_0": [2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095, 2101, 2107, 2112, 2118]
 }
*/

const $aChristmasSundays = function($nStartYear: any, $nEndYear: any): any[] {
  const $aYears: any[] = [];

    for (let $nYear = $nStartYear; $nYear <= $nEndYear; $nYear = $nYear + 1) {
    const $sWeekday: string = $sDayOfWeek($nYear, 12, 25);
        if ($sWeekday === "Sunday") {
      $aYears.push( $nYear);
    }
  }
  return $aYears;
};
/**
 @description
 Computes the day of the week for a Gregorian calendar date using
 Zeller's Congruence, a closed-form formula published by Christian
 Zeller in the 1880s. January and February are treated as months 13 and
 14 of the *previous* year, which is why the formula subtracts 1 from
 the year for those two months before anything else: it keeps March as
 the start of the "formula year", avoiding a separate leap-day special
 case inside the arithmetic itself.

 The classic formula ends in "- 2*J"; here it is written as "+ 5*J"
 instead (-2 and +5 are congruent mod 7), so every term in the sum stays
 non-negative.

@param {number} $nYear - Full year (e.g. 2026).
@param {number} $nMonth - Month, 1-12.
@param {number} $nDay - Day of the month.
@returns {string} - Day of the week: "Sunday" through "Saturday".
*/

/**
 @contract
 {
   "cases": [
     { "$nYear": 2026, "$nMonth": 8, "$nDay": 13 },
     { "$nYear": 2000, "$nMonth": 1, "$nDay": 1 }
   ]
 }
*/

const $sDayOfWeek = function($nYear: any, $nMonth: any, $nDay: any): string {
  let $nAdjustedMonth: number = $nMonth;
    let $nAdjustedYear: number = $nYear;

    if ($nMonth < 3) {
    $nAdjustedMonth = $nMonth + 12;
        $nAdjustedYear = $nYear - 1;
  }
  const $nK: number = Math["modX"]($nAdjustedYear,  100);
    const $nJ: number = Math["floor"]($nAdjustedYear / 100);

    const $nH: number = Math["modX"](($nDay
        + Math["floor"]((13 * ($nAdjustedMonth + 1)) / 5)
        + $nK
        + Math["floor"]($nK / 4)
        + Math["floor"]($nJ / 4)
        + (5 * $nJ)),  7);

    // $nH: 0=Saturday, 1=Sunday, 2=Monday, ... 6=Friday.
    const $aDayNames: any[] = ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];

    return $aDayNames[$nH];
};
import math
import functools
from jsol_core import JSOL

# @JSOL v0.2.97

#*
# @description
# Rosetta Code task: https://rosettacode.org/wiki/Day_of_the_week — the
# task asks for every year in a range for which December 25th falls on a
# Sunday. This function solves that literally, calling `$sDayOfWeek` once
# per year in the range; its own contract embeds the task's known-correct
# answer, so the contract doubles as a check against the task statement
# itself, not just against the algorithm.
#
# Returns every year in `[$nStartYear, $nEndYear]` for which December 25th
# falls on a Sunday.
#
#@param {number} $nStartYear - First year to check, inclusive.
#@param {number} $nEndYear - Last year to check, inclusive.
#@returns {array<number>} - Years whose Christmas Day is a Sunday.
#

#*
# @contract
# {
#   "cases": [
#     { "$nStartYear": 2008, "$nEndYear": 2121 }
#   ],
#   "expected_for_case_0": [2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095, 2101, 2107, 2112, 2118]
# }
#

def aChristmasSundays(nStartYear, nEndYear): 

  aYears = [];

  nYear = nStartYear;
  while nYear <= nEndYear: 

    sWeekday = sDayOfWeek(nYear, 12, 25);
    if sWeekday == "Sunday": 

      aYears.append( nYear);


    nYear = nYear + 1;


  return aYears;


#*
# @description
# Computes the day of the week for a Gregorian calendar date using
# Zeller's Congruence, a closed-form formula published by Christian
# Zeller in the 1880s. January and February are treated as months 13 and
# 14 of the *previous* year, which is why the formula subtracts 1 from
# the year for those two months before anything else: it keeps March as
# the start of the "formula year", avoiding a separate leap-day special
# case inside the arithmetic itself.
#
# The classic formula ends in "- 2*J"; here it is written as "+ 5*J"
# instead (-2 and +5 are congruent mod 7), so every term in the sum stays
# non-negative.
#
#@param {number} $nYear - Full year (e.g. 2026).
#@param {number} $nMonth - Month, 1-12.
#@param {number} $nDay - Day of the month.
#@returns {string} - Day of the week: "Sunday" through "Saturday".
#

#*
# @contract
# {
#   "cases": [
#     { "$nYear": 2026, "$nMonth": 8, "$nDay": 13 },
#     { "$nYear": 2000, "$nMonth": 1, "$nDay": 1 }
#   ]
# }
#

def sDayOfWeek(nYear, nMonth, nDay): 

  nAdjustedMonth = nMonth;
  nAdjustedYear = nYear;

  if nMonth < 3: 

    nAdjustedMonth = nMonth + 12;
    nAdjustedYear = nYear - 1;


  nK = JSOL.math_modx(nAdjustedYear,  100);
  nJ = math.floor(nAdjustedYear / 100);

  nH = JSOL.math_modx((nDay
  + math.floor((13 * (nAdjustedMonth + 1)) / 5)
  + nK
  + math.floor(nK / 4)
  + math.floor(nJ / 4)
  + (5 * nJ)),  7);

  # $nH: 0=Saturday, 1=Sunday, 2=Monday, ... 6=Friday.
  aDayNames = ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];

  return aDayNames[nH];


273 OK

Every example is also a test.

To be released, every JSOL build has to pass every example correctly, with exactly the same results across every supported target. A "minor" change that resulted in a test giving out "8" in JavaScript and "8.0" in PHP? Rejected.

The language and compiler are continuosly improved because of what these 70+ examples demand. The CLRS examples are here for the same reason: if JSOL aims to be a language someone can read rather than just compile, it has to survive contact with computer science, not just invoicing.

Strict Validation

Luhn, IBAN, CUIT, ISBN-13. Checksum algorithms that have to produce the exact same result in the browser and on the server: the exact scenario where a divergent implementation means a rejected payment or a failed compliance check.

Finance & Rules

Loan amortization, progressive tax tiers, stacked discounts, shipping rate matrices. Rules that change when the business changes, and that silently produce wrong invoices when the frontend and backend disagree.

Computer Science

Dynamic Programming vs. Greedy (Coin Change), searching algorithms, sorting algorithms, Caesar/Rot13 ciphers, Sieve of Eratosthenes. Executable pseudocode that compiles and runs, not just sits in a textbook.
$sTe

What JSOL is NOT

JSOL is not a full-stack framework, and it's not a general-purpose language. It is an isolated, pure, synchronous calculator. It doesn't touch the DOM, it doesn't make network requests (no fetch or Async), and it doesn't talk to databases.

Every alternative to JSOL (like Haxe or WebAssembly) buys generality or performance at the cost of requiring a toolchain. JSOL buys zero-toolchain portability by aggressively restricting what you can write.


The Honest Tradeoffs (Where JSOL is worse)

JSOL is honestly worse and slower than writing native code on any single target. It is currently validating its core hypotheses on portability first. These are the engine-level restrictions you accept when using it:

Feature Native JS / PHP / Python JSOL Why it was stripped
Developer Speed & Syntax Fast (Syntactic sugar, modern abstractions) Slower (Dumb, ugly, spartan & strictly constrained) To eliminate the need for an AST compiler, JSOL severely restricts grammar to flat, lowest-common-denominator patterns.
Control Flow Async, Promises, Threads Strictly Synchronous (Single-thread blocking) Async control flow has no shared syntax between JS, PHP, and Python.
State & Collections Performance Classes, this, Reference Passing Flat Dicts & Value Semantics (Higher GC pressure) Forbidding classes guarantees simple $O(1)$ property access, but forces copying for immutability across targets, making JSOL slower on large arrays.
Text Parsing Native Regex (PCRE / V8 / RE2) Restricted Regex subset & procedural loops Regex engines differ across runtimes. Complex patterns, lookarounds, and backreferences can cause catastrophic backtracking (ReDoS) in one engine but not another.
Floating Point Precision Native floating-point masking / coercion quirks Strict IEEE-754 across all targets For $n, JSOL enforces strict IEEE-754 behavior across all runtimes for predictable standardization. For exact accounting where IEEE-754 is unsuitable, future specs may incorporate other mature, mathematically proven standards.
!AST

Design Pillars

Four principles shape every rule in the specification.

1. Clarity

A JSOL algorithm has to be readable by the person who owns the business logic, not just by a compiler.

This is also why JSOL doesn't standardize how you structure code (nested functions vs. flat scope, for instance) — that's implementation shape, not business logic.

2. Portability

The same source runs correctly on every proven target.

This is where Deterministic Parity comes from: given identical inputs, every target's output has to match, bit for bit.

3. Performance

The compiled output should be no heavier and no slower than it has to be.

This is where Zero Dead Code comes from: nothing gets shipped that a given file doesn't actually use.

4. Developer Experience

Writing, compiling, and debugging JSOL should be as frictionless as the constraints allow.

This is where the AST-free compiler pipeline and Zero Runtime Dependencies come from.

modX

A sample of JSOL's design tradeoffs


The modulo operation is a good example of JSOL's design.

C-like languages differ in how they calculate this basic operation. When I had to pick one, I realized that the de facto business logic standard isn't a programming language at all, but Excel.

That's where the “X” in modX comes from:

  • Calling it mod would leave the behavior ambiguous: which mod?
  • Math.modX() explicitly means: calculate modulo the way Excel does.

By giving it a distinct name, JSOL makes the decision explicit, and avoids the risk of developers incorrectly expecting the behavior of the language they're used to.

mod(7, 3) = 1 in all languages
mod(-7, 3) = -1 in JS, Java, C#, C, C++, Rust, Go
= 2 in Python, Ruby, Haskell, Lua, R, Dart, Excel
mod(7, -3) = 1 in JS, Java, C#
= -2 in Python, Ruby, Haskell
= ERROR in Zig
mod(7.5, 2) = 1.5 in JS, Python, Excel
= 1 in PHP

Same operation, different results. Only one is what accountants expect.

MIT

This is an open problem, not a finished product

The project maintains a set of EXTENDING documents that map out different ways to push JSOL further: adding new target languages, resolving semantic differences that threaten Deterministic Parity, and extending the type and function system for specific domains such as dates or color science.

These aren't just feature requests. Each one is a different technical problem: from compiler backends and runtime semantics to type design, language interoperability, memory models and closure conversion.

If you're a developer, a researcher, a CS educator or a student and you see a problem here that interests you, there's a place for you to contribute. You don't have to solve the whole thing. A target, a semantic divergence, a domain extension, a test case, or a better solution to an existing problem can all move the project forward.

The goal is simple: solve our problems, and leave fewer problems for the person who comes next.

Fork it. Break it. Build something. Challenge an assumption.