{"id":12670,"library":"whynot","title":"whynot.js Formal Language Matching Framework","description":"whynot.js is a generic, VM-based framework for matching formal languages, drawing inspiration from systems like Russ Cox's regular expression engine. It operates by considering all possible branches of a program in parallel, enabling efficient implementation of various language matching tasks, including regular expressions and XML schemas. A key differentiator is its ability to record program progress through input and grammar, providing detailed feedback on *why* an input might not match a given grammar. The current stable version is 5.0.0, with releases focusing on performance, memory optimization, and module compatibility (ESM/CJS). While there isn't a strict release cadence, updates address bug fixes, dependency bumps, and significant architectural improvements.","status":"active","version":"5.0.0","language":"javascript","source_language":"en","source_url":"https://github.com/bwrrp/whynot.js","tags":["javascript","Language","Matching","Regex","Structure","typescript"],"install":[{"cmd":"npm install whynot","lang":"bash","label":"npm"},{"cmd":"yarn add whynot","lang":"bash","label":"yarn"},{"cmd":"pnpm add whynot","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"ESM is the primary module system since v4.0.0 and further solidified in v5.0.0. CommonJS `require` might fail or load an older UMD bundle depending on environment and bundler configuration.","wrong":"const { VM } = require('whynot');","symbol":"VM","correct":"import { VM } from 'whynot';"},{"note":"Key components like `Program`, `VM`, `Trace`, and `Instruction` are all named exports.","wrong":"import Program from 'whynot'; // Not a default export","symbol":"Program","correct":"import { Program } from 'whynot';"},{"note":"`Instruction` provides static methods for creating VM instructions. It's a namespace-like export.","wrong":"import * as whynot from 'whynot'; const inst = whynot.Instruction.char('a');","symbol":"Instruction","correct":"import { Instruction } from 'whynot';"}],"quickstart":{"code":"import { Program, VM, Instruction, Trace } from 'whynot';\n\n// Define a simple program that matches the sequence 'abc'\nconst program = new Program([\n  Instruction.char('a'),\n  Instruction.char('b'),\n  Instruction.char('c'),\n  Instruction.accept() // Signal successful match\n]);\n\n// Initialize the VM with the program\nconst vm = new VM(program);\n\n// Execute the VM against an input array of characters\nconst input1 = ['a', 'b', 'c'];\nconst trace1: Trace | null = vm.execute(input1);\n\nif (trace1) {\n  console.log(`Input '${input1.join('')}' matched successfully.`);\n  // You can inspect the trace for details on the match path\n  // console.log('Trace records:', trace1.records);\n} else {\n  console.log(`Input '${input1.join('')}' did NOT match.`);\n}\n\nconst input2 = ['a', 'x', 'c'];\nconst trace2: Trace | null = vm.execute(input2);\n\nif (trace2) {\n  console.log(`Input '${input2.join('')}' matched successfully.`);\n} else {\n  console.log(`Input '${input2.join('')}' did NOT match.`);\n}","lang":"typescript","description":"This quickstart demonstrates defining a basic VM program using `Instruction`s, initializing a `VM` with it, and executing an input array to check for a match. It shows both a matching and non-matching scenario."},"warnings":[{"fix":"Ensure your bundler or environment correctly resolves `whynot` or update any direct import paths to the UMD bundle if you are targeting older CJS environments and not using ESM.","message":"Version 5.0.0 renames the UMD module file for older CJS environments to `whynot.umd.js`. While it primarily fixes ESM usage in Node.js, consumers relying on explicit paths for CJS bundles might need updates.","severity":"breaking","affected_versions":">=5.0.0"},{"fix":"Verify your bundler (e.g., Webpack, Rollup) is configured to correctly select the appropriate module entry point. You may need to update `main` or `module` fields in package.json if manually overriding module resolution.","message":"Version 4.0.0 introduced significant changes to bundle filenames, providing `whynot.umd.js` for UMD/CommonJS and `whynot.esm.js` for ES Modules. Most modern bundlers handle this automatically, but older setups or explicit path configurations may break.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"Update all calls to `vm.execute()` to pass an array of input items (e.g., `vm.execute(['a', 'b'])`) instead of a function.","message":"In version 3.0.0, the `VM.execute` method's input argument changed from a generator-like function to a simple array. This was a breaking change aimed at significant performance and memory improvements for large inputs.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Refactor code that accessed `trace.records`. If you need to record progress, ensure your program explicitly uses `Instruction.record` and retrieve trace information through other means if available, or reconsider the design of your program.","message":"The `Trace.records` array was removed in version 3.0.0 to reduce allocations and improve performance. This property is no longer available directly on the `Trace` instance.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Avoid accessing `trace.head`. For recorded paths, rely on explicit `Instruction.record` and check `trace.records` for `null` before iterating.","message":"Version 2.0.0 removed the `head` property from returned `Trace` objects. Additionally, the allocation of the `records` array was made lazy, meaning `records` will be `null` instead of an empty array if no records are recorded for a trace.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"For older browser support, integrate a transpilation step (e.g., Babel with appropriate presets) into your build pipeline to down-level whynot's output to your target ECMAScript version.","message":"Since version 3.0.2, whynot bundles target ES2017 for somewhat modern browsers, reducing bundle size. If you need to support very old browser environments (pre-ES2017), you will need to transpile whynot yourself using tools like Babel.","severity":"gotcha","affected_versions":">=3.0.2"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure your environment supports ESM imports (`import ... from 'whynot';`). If you must use CommonJS, ensure your bundler is configured to pick up `whynot.umd.js` or that Node.js is running in an ESM-compatible context for imported modules.","cause":"This typically occurs after upgrading to v4.x or v5.x when the module resolution system (e.g., Node.js or a bundler) cannot locate the new ESM or UMD bundles, especially if using CommonJS `require()` directly.","error":"Error: Cannot find module 'whynot'"},{"fix":"Change your `vm.execute()` call to pass an array of input items, e.g., `vm.execute(['item1', 'item2'])`.","cause":"This TypeScript error indicates you are passing a function to `VM.execute` where an array is expected. This was a breaking change introduced in v3.0.0.","error":"Argument of type '(...args: any[]) => any' is not assignable to parameter of type 'any[]'."},{"fix":"Always check if `trace.records` is not `null` before attempting to iterate or access its elements, e.g., `if (trace.records) { trace.records.forEach(...) }`.","cause":"Since v2.0.0, the `records` property on a `Trace` object can be `null` if no records were explicitly generated during execution, instead of an empty array.","error":"TypeError: Cannot read properties of null (reading 'forEach') or similar runtime errors when accessing trace.records."},{"fix":"Remove any code that attempts to access `trace.head`. You will need to find alternative ways to determine the start of a trace if that was its purpose.","cause":"The `head` property was removed from `Trace` objects in version 2.0.0.","error":"Property 'head' does not exist on type 'Trace'."}],"ecosystem":"npm"}