+108
| # AGENTS.md — yopl | ||
| > `yopl` is an ES6 mini-library implementing a Prolog-style logic solver. It supports rule-based | ||
| > logic programming with both callback and generator drivers. Its only runtime dependency is | ||
| > [`deep6`](https://www.npmjs.com/package/deep6), itself a zero-dependency library that provides | ||
| > the unification engine. | ||
| ## AI Documentation | ||
| - **Architecture:** [ARCHITECTURE.md](./ARCHITECTURE.md) — module map, dependency graph, algorithm details | ||
| - **Quick API:** [llms.txt](./llms.txt) — concise API reference for LLMs | ||
| - **Full API:** [llms-full.txt](./llms-full.txt) — complete API reference with examples | ||
| - **Codebase Quick Ref:** [CODEBASE.md](./CODEBASE.md) — one-liner, entry points, key patterns | ||
| - **Usage:** [README.md](./README.md) — installation and examples | ||
| For detailed usage docs see the [wiki](https://github.com/uhop/yopl/wiki). | ||
| ## Setup | ||
| ```bash | ||
| git clone git@github.com:uhop/yopl.git | ||
| cd yopl | ||
| npm install | ||
| ``` | ||
| ## Commands | ||
| - **Install:** `npm install` | ||
| - **Test:** `npm test` | ||
| - **Debug:** `npm run debug` — run tests with Node inspector | ||
| - **Type check:** `npm run ts-check` — TypeScript type checking | ||
| - **Lint:** `npm run lint` — Prettier check | ||
| - **Lint fix:** `npm run lint:fix` — Prettier write | ||
| ## Project structure | ||
| ``` | ||
| yopl/ | ||
| ├── package.json # Package config | ||
| ├── src/ # ES6 source code | ||
| │ ├── solve.js # Solver core / main entry | ||
| │ ├── solvers/ # Alternative driver styles | ||
| │ │ ├── gen.js # Synchronous generator-based solver | ||
| │ │ ├── async.js # Async callback-based solver | ||
| │ │ └── asyncGen.js # Async generator-based solver | ||
| │ └── rules/ # Built-in rule library | ||
| │ ├── logic.js # Logical connectives | ||
| │ ├── comp.js # Comparison rules | ||
| │ ├── math.js # Arithmetic rules | ||
| │ ├── bits.js # Bitwise rules | ||
| │ └── system.js # System / utility rules | ||
| ├── tests/ # Test files (grouped test-*.js, dispatched by tests.js) | ||
| └── .github/ # CI workflows, funding, dependabot | ||
| ``` | ||
| ## Code style | ||
| - **ES6 modules** (`"type": "module"` in package.json). | ||
| - **Single runtime dependency.** Only `deep6` is allowed in `dependencies`. | ||
| - **Prettier** for formatting (see `.prettierrc`): 2-space indent, single quotes, semicolons required, no trailing commas. | ||
| ## Critical rules | ||
| - **ES6 modules.** Use `import`/`export` syntax in source. | ||
| - **Single runtime dependency.** Do not add packages to `dependencies` other than `deep6`. | ||
| - **Do not modify or delete test expectations** without understanding why they changed. | ||
| - **Do not add or remove comments** unless explicitly asked. | ||
| - **Keep `src/` in sync.** Run `npm test` and `npm run lint:fix` after changes. | ||
| ## Architecture | ||
| - **Solver core** (`src/solve.js`) — the synchronous callback-style solver and the main public | ||
| entry. Drives unification (via `deep6`) over a rule database using an explicit goal stack. | ||
| - **Solver drivers** (`src/solvers/`) — alternative execution strategies sharing the same proof | ||
| loop: | ||
| - `gen.js` — synchronous generator yielding one `Env` per solution. | ||
| - `async.js` — async callback-based driver for `await`-bearing predicates. | ||
| - `asyncGen.js` — async generator combining the two. | ||
| - **Rule library** (`src/rules/`) — built-in predicates: | ||
| - `system.js` — helpers (`head`, `term`, `list`, `listHead`, `rest`) and control predicates | ||
| (`call`, `cut`, `fail`, `halt`, `isBound`, `not`, `true`, `eq`, `once`, `map`, `filter`, | ||
| `foldl`, `foldr`, …). | ||
| - `comp.js` — comparisons (`lt`/`le`/`gt`/`ge`, `nz`). | ||
| - `math.js` — arithmetic (`add`/`sub`/`mul`/`div`/`neg`). | ||
| - `bits.js` — bitwise (`bitAnd`/`bitOr`/`bitXor`/`bitNot`). | ||
| - `logic.js` — boolean logic (`logicalAnd`/`logicalOr`/`logicalXor`/`logicalNot`). | ||
| Per-module documentation lives in the [wiki](https://github.com/uhop/yopl/wiki). | ||
| ## Writing tests | ||
| - Tests live in `tests/test-*.js`, dispatched via `tests/tests.js`. | ||
| - The test harness (`tests/harness.js`) is the same lightweight one used by `deep6`. Each test | ||
| module exports a default array of named test functions; the dispatcher concatenates them and | ||
| runs them with `runAllTests`. | ||
| - Use the `submit` / `TEST` helpers from `harness.js` to record assertions: | ||
| `eval(TEST('unify(result, [1, 2, 3])'));`. | ||
| - Shared list/timeout helpers live in `tests/helpers.js`. | ||
| - Run with `npm test`. The CommonJS interop smoke test lives at `tests/test-cjs.cjs` and is run | ||
| manually with `node tests/test-cjs.cjs`. | ||
| - TypeScript typings are exercised separately by `tests/test-types.ts` via `npm run ts-check`. | ||
| ## When reading the codebase | ||
| - Start with this file ([AGENTS.md](./AGENTS.md)) for rules and constraints. | ||
| - Consult [ARCHITECTURE.md](./ARCHITECTURE.md) for module relationships. | ||
| - `src/solve.js` is the core — read it first. | ||
| - Run `npm test` and `npm run lint:fix` after any changes. |
+116
| # Architecture | ||
| `yopl` is an ES6 mini-library implementing a Prolog-style logic solver. It supports rule-based | ||
| logic programming with multiple driver styles. Unification is delegated to | ||
| [`deep6`](https://www.npmjs.com/package/deep6) — yopl's only runtime dependency, itself a | ||
| zero-dependency library. | ||
| ## Project layout | ||
| ``` | ||
| package.json # Package config | ||
| src/ # ES6 source code | ||
| ├── solve.js # Solver core / main entry | ||
| ├── solvers/ # Alternative driver styles | ||
| │ ├── gen.js # Synchronous generator-based solver | ||
| │ ├── async.js # Async callback-based solver | ||
| │ └── asyncGen.js # Async generator-based solver | ||
| └── rules/ # Built-in rule library | ||
| ├── logic.js # Logical connectives | ||
| ├── comp.js # Comparison rules | ||
| ├── math.js # Arithmetic rules | ||
| ├── bits.js # Bitwise rules | ||
| └── system.js # System / utility rules | ||
| tests/ # Tests grouped in test-*.js, dispatched by tests.js | ||
| .github/ # CI workflows, funding, dependabot | ||
| ``` | ||
| ## Core concepts | ||
| ### Goals, terms, rules | ||
| A **goal** is one of: | ||
| - a string — the name of a rule with no arguments; | ||
| - a structured term `{name, args}` — invoke `name` with the given argument vector; | ||
| - a JavaScript function `(env, goals, stack) => boolean | GoalFrame` — an inline predicate | ||
| evaluated directly by the solver. | ||
| A **rule** is a function (or array of functions for a disjunction) that receives a fresh batch | ||
| of logical variables and returns an array of terms. The first element is the rule's _head_ | ||
| (`{args: [...]}`); the rest are body goals evaluated in order. | ||
| A **rule database** is a `{name: rule}` object. Spread the built-in rule libraries into your own | ||
| database to compose them: `{...systemRules, ...mathRules, myRule: …}`. | ||
| ### Solver core | ||
| `src/solve.js` is the heart of yopl. It evaluates goals against a rule database, delegating | ||
| unification to `deep6`. The proof loop is non-recursive — it maintains an explicit stack of | ||
| frames so deep proofs do not blow the JS call stack — and tracks alternatives via choice-point | ||
| frames so that backtracking is just popping the stack and reverting the environment. | ||
| The synchronous callback-style `solve` is the main entry point. The `src/solvers/` modules wrap | ||
| the same proof loop in alternative drivers that yield (or `await`) per solution. | ||
| ### Drivers | ||
| The `src/solvers/` modules wrap the core in alternative execution styles: | ||
| - **`gen.js`** — synchronous generator yielding solutions on demand. | ||
| - **`async.js`** — async callback-based driver for I/O-bound rules. | ||
| - **`asyncGen.js`** — async generator that combines both. | ||
| ### Built-in rules | ||
| `src/rules/` provides a small standard library of predicates: | ||
| - **`logic.js`** — logical connectives (and/or/not, if-then-else, etc.). | ||
| - **`comp.js`** — comparison and ordering predicates. | ||
| - **`math.js`** — arithmetic predicates. | ||
| - **`bits.js`** — bitwise predicates. | ||
| - **`system.js`** — system/utility predicates. | ||
| ## Module dependency graph | ||
| ``` | ||
| src/solve.js ── deep6/unify.js (unify, Env, variable) | ||
| src/solvers/gen.js ── deep6/unify.js | ||
| src/solvers/async.js ── deep6/unify.js | ||
| src/solvers/asyncGen.js ── deep6/unify.js | ||
| src/rules/system.js ── deep6/env.js (_, isVariable) | ||
| src/rules/comp.js ── deep6/env.js (_), src/rules/system.js | ||
| src/rules/math.js ── deep6/env.js (_), src/rules/system.js | ||
| src/rules/bits.js ── deep6/env.js (_), src/rules/system.js | ||
| src/rules/logic.js ── deep6/env.js (_), src/rules/system.js | ||
| ``` | ||
| The solver drivers are independent — none of them imports the others, and none of them depends | ||
| on the rule library. The rule modules depend only on `deep6` and on `system.js` for shared | ||
| helpers. | ||
| ## Import paths | ||
| ```js | ||
| // Main solver | ||
| import solve from 'yopl'; | ||
| // Drivers | ||
| import solveGen from 'yopl/solvers/gen.js'; | ||
| import solveAsync from 'yopl/solvers/async.js'; | ||
| import solveAsyncGen from 'yopl/solvers/asyncGen.js'; | ||
| // Rules | ||
| import logic from 'yopl/rules/logic.js'; | ||
| import comp from 'yopl/rules/comp.js'; | ||
| import math from 'yopl/rules/math.js'; | ||
| import bits from 'yopl/rules/bits.js'; | ||
| import system from 'yopl/rules/system.js'; | ||
| ``` | ||
| ## Testing | ||
| - **Run all:** `npm test` | ||
| - **Single dispatcher:** `node tests/tests.js` | ||
| - **Debug:** `npm run debug` (Node inspector) |
+158
| # yopl — Full API Reference | ||
| `yopl` is an ES6 mini-library implementing a Prolog-style logic solver in JavaScript. It | ||
| provides rule-based logic programming with multiple driver styles and a built-in rule library. | ||
| Unification is delegated to [`deep6`](https://www.npmjs.com/package/deep6) — yopl's only | ||
| runtime dependency, itself a zero-dependency library. | ||
| ## Concepts | ||
| - **Goal** — one of: a string (rule name with no args), a structured term `{name, args}`, or | ||
| an inline JS function `(env, goals, stack) => boolean | GoalFrame`. | ||
| - **Rule** — a function (or array of functions for a disjunction) that receives a fresh batch | ||
| of logical variables and returns an array of terms. The first element is the rule head | ||
| (`{args: [...]}`); the rest are body goals. | ||
| - **Rule database** — a `{name: rule}` object. Spread the built-in libraries to compose them. | ||
| ## Solvers | ||
| ### `yopl` — `src/solve.js` | ||
| ```js | ||
| import solve from 'yopl'; | ||
| solve(rules, name, args, env => { /* per solution */ }); | ||
| ``` | ||
| Synchronous callback-style solver. Main public entry point. | ||
| ### `yopl/solvers/gen.js` | ||
| ```js | ||
| import gen from 'yopl/solvers/gen.js'; | ||
| for (const env of gen(rules, name, args)) { /* … */ } | ||
| ``` | ||
| Synchronous generator. Supports lazy enumeration — break out of the loop to stop after the | ||
| first match. | ||
| ### `yopl/solvers/async.js` | ||
| ```js | ||
| import asyncSolve from 'yopl/solvers/async.js'; | ||
| await asyncSolve(rules, name, args, async env => { /* … */ }); | ||
| ``` | ||
| Async callback solver — for predicates that need to `await`. | ||
| ### `yopl/solvers/asyncGen.js` | ||
| ```js | ||
| import asyncGen from 'yopl/solvers/asyncGen.js'; | ||
| for await (const env of asyncGen(rules, name, args)) { /* … */ } | ||
| ``` | ||
| Async generator solver. | ||
| ## Rule libraries | ||
| ### `yopl/rules/system.js` | ||
| Helpers and control predicates. | ||
| **Term builders:** | ||
| - `head(...args)` → `{args}`. Build a rule head. | ||
| - `term(name, ...args)` → `{name, args}`. Build a structured goal term. | ||
| - `list(...args)` → cons-list. Wrap the last arg with `rest()` to splice an explicit tail. | ||
| - `listHead(...args)` → cons-list whose last argument is the tail directly. Requires ≥ 2 args. | ||
| - `rest(value)` → `Tail` marker. | ||
| **Inline goal builders:** | ||
| - `fail` — always fails. | ||
| - `halt` — aborts the proof search by clearing the driver stack. | ||
| - `cut(sys)` — Prolog-style cut. Pass the trailing `...sys` rest-args from the rule definition. | ||
| - `call(X)` — meta-call. `X` may be a string, a structured term, or a bound variable. | ||
| - `isBound(...vars)` — succeeds when every supplied variable is bound. | ||
| **`rules` object** — spreadable rule database: | ||
| - *Type tests:* `isVar`, `isNonVar`, `isNumber`, `isString`, `isNull`, `isUndefined`, `isArray`. | ||
| - *Equality:* `eq` (alias `unify`), `notEq` (alias `notUnifiable`). | ||
| - *Control:* `call`, `not`, `isUnifiable`, `conjunction`, `disjunction`, `true`, `once`. | ||
| - *Extended logic:* `counterExample`, `implies`. | ||
| - *Higher-order:* `map`, `filter`, `foldl`, `foldr`, `compose`, `converse`. | ||
| ### `yopl/rules/comp.js` | ||
| Comparisons. `lt`, `le`, `gt`, `ge` operate on bound, comparable values (number or string) | ||
| of the same type. `nz` succeeds when its argument is non-zero. | ||
| ### `yopl/rules/math.js` | ||
| Arithmetic, all reversible: `add`, `sub`, `mul`, `div`, `neg`. Each rule defines a relation | ||
| between its arguments, and any one missing operand can be solved for. | ||
| ### `yopl/rules/bits.js` | ||
| Bitwise: `bitAnd`, `bitOr` (forward only), `bitXor`, `bitNot` (reversible). | ||
| ### `yopl/rules/logic.js` | ||
| Boolean logic: `logicalAnd`, `logicalOr`, `logicalXor`, `logicalNot`. Arguments are coerced | ||
| with `!!`. | ||
| ## Examples | ||
| ### List membership | ||
| ```js | ||
| import {variable} from 'deep6/env.js'; | ||
| import assemble from 'deep6/traverse/assemble.js'; | ||
| import solve from 'yopl'; | ||
| const rules = { | ||
| member: [ | ||
| (V, X) => [{args: [{value: V, next: X}, V]}], | ||
| (V, X) => [{args: [{next: X}, V]}, {name: 'member', args: [X, V]}] | ||
| ] | ||
| }; | ||
| const list = {value: 1, next: {value: 2, next: {value: 3, next: null}}}; | ||
| const X = variable('X'); | ||
| solve(rules, 'member', [list, X], env => console.log(assemble(X, env))); | ||
| // 1, 2, 3 | ||
| ``` | ||
| ### Append | ||
| ```js | ||
| import gen from 'yopl/solvers/gen.js'; | ||
| import {variable} from 'deep6/env.js'; | ||
| import assemble from 'deep6/traverse/assemble.js'; | ||
| const rules = { | ||
| append: [ | ||
| Y => [{args: [null, Y, Y]}], | ||
| (X, Y, Z, V) => [ | ||
| {args: [{value: V, next: X}, Y, {value: V, next: Z}]}, | ||
| {name: 'append', args: [X, Y, Z]} | ||
| ] | ||
| ] | ||
| }; | ||
| const X = variable('X'), Y = variable('Y'); | ||
| const target = {value: 1, next: {value: 2, next: {value: 3, next: null}}}; | ||
| for (const env of gen(rules, 'append', [X, Y, target])) { | ||
| console.log(assemble(X, env), '++', assemble(Y, env)); | ||
| } | ||
| ``` | ||
| ## Conventions | ||
| - ES6 modules; `"type": "module"`. | ||
| - Single runtime dependency: `deep6`. | ||
| - 2-space indent, single quotes, semicolons (Prettier). | ||
| - Pure ESM source. CommonJS consumers can use Node's built-in dynamic `import()` — | ||
| see `tests/test-cjs.cjs` for a smoke test. |
+30
| # yopl — Quick API Reference | ||
| `yopl` is an ES6 mini-library implementing a Prolog-style logic solver. Unification is delegated | ||
| to `deep6`, yopl's only runtime dependency (a zero-dependency library). | ||
| ## Entry points | ||
| - `yopl` (`src/solve.js`) — synchronous callback-style solver. `solve(rules, name, args, callback)`. | ||
| - `yopl/solvers/gen.js` — synchronous generator solver. `gen(rules, name, args)` → `Generator<Env>`. | ||
| - `yopl/solvers/async.js` — async callback solver. `asyncSolve(rules, name, args, async callback)`. | ||
| - `yopl/solvers/asyncGen.js` — async generator solver. `asyncGen(rules, name, args)` → `AsyncGenerator<Env>`. | ||
| ## Rule libraries | ||
| All rule modules export a spreadable `rules` object: | ||
| - `yopl/rules/system.js` — helpers (`head`, `term`, `list`, `listHead`, `rest`) and control predicates (`call`, `cut`, `fail`, `halt`, `isBound`). The `rules` export adds `eq`/`unify`, `notEq`/`notUnifiable`, `not`, `true`, `once`, `conjunction`, `disjunction`, `isVar`/`isNonVar`/`isNumber`/`isString`/`isNull`/`isUndefined`/`isArray`, `counterExample`, `implies`, and the higher-order predicates `map`, `filter`, `foldl`, `foldr`, `compose`, `converse`. | ||
| - `yopl/rules/comp.js` — `lt`, `le`, `gt`, `ge`, `nz`. | ||
| - `yopl/rules/math.js` — `add`, `sub`, `mul`, `div`, `neg` (each reversible). | ||
| - `yopl/rules/bits.js` — `bitAnd`, `bitOr`, `bitXor`, `bitNot`. | ||
| - `yopl/rules/logic.js` — `logicalAnd`, `logicalOr`, `logicalXor`, `logicalNot`. | ||
| ## Conventions | ||
| - ES6 modules; `"type": "module"`. | ||
| - Single runtime dependency: `deep6`. | ||
| - Use `assemble(variable, env)` from `deep6/traverse/assemble.js` to extract solution values. | ||
| - A rule body returns an array of terms whose first element is the head and whose remaining | ||
| elements are body goals. A goal is a string, a `{name, args}` term, or an inline | ||
| `(env, goals, stack) => boolean | GoalFrame` function. |
| --- | ||
| name: use-yopl | ||
| description: Use the yopl Prolog-style logic engine in a JavaScript/TypeScript project. Use when adding rule-based search, pattern matching with extraction, constraint solving, type inference, planning, or expert-system style logic to a project that depends on `yopl`. | ||
| --- | ||
| # Use yopl | ||
| `yopl` is an embeddable Prolog-style logic engine for JavaScript. It gives you | ||
| declarative rules, unification (via `deep6`), backtracking search, and four | ||
| solver drivers (sync/async × callback/generator). It is ES-modules only and has | ||
| a single runtime dependency (`deep6`). | ||
| When in doubt, the canonical references are | ||
| `node_modules/yopl/llms.txt` (concise) and `node_modules/yopl/llms-full.txt` | ||
| (complete with examples). Read them before guessing. | ||
| ## When to reach for yopl | ||
| Pick yopl when the problem is naturally expressed as _rules + queries_: | ||
| - Pattern matching where you also need to **extract** parts of the input. | ||
| - Multi-solution search with backtracking (planners, constraint puzzles, layout). | ||
| - Type inference, dataflow analysis, simple theorem proving. | ||
| - Policy / authorization rules. | ||
| - Anything you'd otherwise hand-roll as nested `if`/`for` over a small relational model. | ||
| Do **not** reach for yopl for plain validation, simple lookups, or anything a | ||
| single SQL query / array filter handles cleanly. | ||
| ## Installation | ||
| ```bash | ||
| npm install yopl | ||
| ``` | ||
| `yopl` requires Node 18+ (or modern Deno / Bun) and `"type": "module"` (or | ||
| `.mjs` files). | ||
| ## Imports | ||
| ```js | ||
| // Default sync callback solver | ||
| import solve from 'yopl'; | ||
| // Alternative drivers | ||
| import gen from 'yopl/solvers/gen.js'; // sync generator → yields Env per solution | ||
| import asyncSolve from 'yopl/solvers/async.js'; // async callback (await-able predicates) | ||
| import asyncGen from 'yopl/solvers/asyncGen.js'; // async generator | ||
| // Built-in rule libraries (each exports a spreadable `rules` object) | ||
| import {rules as systemRules} from 'yopl/rules/system.js'; | ||
| import {rules as compRules} from 'yopl/rules/comp.js'; | ||
| import {rules as mathRules} from 'yopl/rules/math.js'; | ||
| import {rules as bitsRules} from 'yopl/rules/bits.js'; | ||
| import {rules as logicRules} from 'yopl/rules/logic.js'; | ||
| // Variables and value extraction come from deep6 | ||
| import {variable as v} from 'deep6/unify.js'; | ||
| import assemble from 'deep6/traverse/assemble.js'; | ||
| ``` | ||
| ## Defining rules | ||
| A _rule database_ is a plain object keyed by `'name/arity'`. Each entry is one | ||
| clause function (or an array of clause functions, tried in order): | ||
| ```js | ||
| const rules = { | ||
| // member(X, [X | _]). | ||
| // member(X, [_ | T]) :- member(X, T). | ||
| 'member/2': [(V, X) => [{args: [{value: V, next: X}, V]}], (V, X, T) => [{args: [{value: V, next: X}, T]}, {name: 'member/2', args: [X, T]}]] | ||
| }; | ||
| ``` | ||
| A clause is a function whose parameters become **fresh logical variables** for | ||
| that invocation. It returns an array of _terms_: | ||
| - The **first** term is the rule head: `{args: [...]}`. | ||
| - The remaining terms are body goals: either `{name: 'foo/N', args: [...]}` to | ||
| call another rule, or an inline guard `env => boolean` for native checks. | ||
| ## Composing the rule database | ||
| Combine your own rules with the built-ins via spread: | ||
| ```js | ||
| import {rules as systemRules} from 'yopl/rules/system.js'; | ||
| import {rules as compRules} from 'yopl/rules/comp.js'; | ||
| import {rules as mathRules} from 'yopl/rules/math.js'; | ||
| const rules = { | ||
| ...systemRules, | ||
| ...compRules, | ||
| ...mathRules, | ||
| 'positive/1': X => [{args: [X]}, {name: 'gt/2', args: [X, 0]}], | ||
| 'square/2': (X, Y) => [{args: [X, Y]}, {name: 'mul/3', args: [X, X, Y]}] | ||
| }; | ||
| ``` | ||
| ## Running queries | ||
| ### Sync, callback (default `solve`) | ||
| ```js | ||
| import solve from 'yopl'; | ||
| import {variable as v} from 'deep6/unify.js'; | ||
| import assemble from 'deep6/traverse/assemble.js'; | ||
| const X = v('X'); | ||
| const results = []; | ||
| solve(rules, 'square/2', [5, X], env => { | ||
| results.push(assemble(X, env)); | ||
| }); | ||
| // results === [25] | ||
| ``` | ||
| The callback is invoked **once per solution**. Return `false` from it to stop | ||
| the search early. | ||
| ### Sync generator | ||
| ```js | ||
| import gen from 'yopl/solvers/gen.js'; | ||
| const X = v('X'); | ||
| for (const env of gen(rules, 'member/2', [X, list])) { | ||
| console.log(assemble(X, env)); | ||
| } | ||
| ``` | ||
| ### Async (callback or generator) | ||
| Use `yopl/solvers/async.js` or `yopl/solvers/asyncGen.js` when any rule body | ||
| needs `await` (database lookups, HTTP, fs, etc.). | ||
| ```js | ||
| import asyncGen from 'yopl/solvers/asyncGen.js'; | ||
| for await (const env of asyncGen(rules, 'lookup/2', [key, X])) { | ||
| // ... | ||
| } | ||
| ``` | ||
| ## Extracting values | ||
| Variables are not "results" by themselves — they're placeholders. After a | ||
| successful solve, call `assemble(variable, env)` from | ||
| `deep6/traverse/assemble.js` to walk the bindings and produce a plain value | ||
| (deeply, including nested terms and lists). | ||
| ## Common patterns | ||
| - **List membership / search:** use `member/2` (built-in via your own clauses | ||
| or via `system.js` helpers like `list`, `listHead`, `rest`). | ||
| - **Higher-order:** `system.js` provides `map`, `filter`, `foldl`, `foldr`, | ||
| `compose`, `converse` — all expressed as logic rules. | ||
| - **Negation as failure:** `not`, `counterExample`. | ||
| - **Reversible arithmetic:** `add`, `sub`, `mul`, `div`, `neg` from `math.js` | ||
| work in any direction as long as enough arguments are bound. | ||
| - **Cut / control:** `cut`, `once`, `fail`, `halt`, `call` from `system.js`. | ||
| - **Type tests:** `isVar`, `isNonVar`, `isNumber`, `isString`, `isArray`, etc. | ||
| ## Pitfalls | ||
| - **Arity matters.** `'foo/2'` is _only_ called when the goal arity is 2. | ||
| Mismatched arity silently produces no solutions. | ||
| - **Guards on unbound variables.** An inline guard `env => X.get(env) > 0` | ||
| will throw or behave wrongly if `X` isn't bound yet. Gate it: | ||
| `env => X.isBound(env) && X.get(env) > 0`. | ||
| - **Recursion needs a base case.** Otherwise the solver backtracks forever or | ||
| blows the stack. | ||
| - **Don't share captured variables across clauses.** Each clause function gets | ||
| its own fresh variables via its parameters. | ||
| - **Async drivers require async consumption.** Don't call `asyncSolve` | ||
| fire-and-forget — `await` it (or iterate `asyncGen` with `for await`). | ||
| ## Picking a driver | ||
| | Need | Driver | | ||
| | ----------------------------------------- | -------------------------- | | ||
| | Simple, sync, one callback per solution | `yopl` (default `solve`) | | ||
| | Sync, want to pull solutions lazily | `yopl/solvers/gen.js` | | ||
| | Any rule body needs `await` | `yopl/solvers/async.js` | | ||
| | Async + want lazy `for await` consumption | `yopl/solvers/asyncGen.js` | | ||
| ## Where to look next | ||
| - `node_modules/yopl/llms.txt` — concise API reference. | ||
| - `node_modules/yopl/llms-full.txt` — full API reference with examples. | ||
| - `node_modules/yopl/AGENTS.md` — rules, conventions, architecture quick ref. | ||
| - [Wiki](https://github.com/uhop/yopl/wiki) — per-module documentation, including each built-in rule. |
| // Type definitions for yopl — bitwise rules. | ||
| import type {Rules} from '../solve.js'; | ||
| /** | ||
| * Bitwise rule library: `bitAnd`, `bitOr`, `bitXor`, `bitNot`. | ||
| * | ||
| * `bitXor` and `bitNot` are reversible (any one missing operand can be | ||
| * solved for). `bitAnd` and `bitOr` are forward-only. | ||
| */ | ||
| export declare const rules: Rules; |
| // Type definitions for yopl — comparison rules. | ||
| import type {Rules} from '../solve.js'; | ||
| /** | ||
| * Comparison rule library: `lt`, `le`, `gt`, `ge`, and `nz`. | ||
| * | ||
| * Each comparison succeeds when both operands are bound, of the same | ||
| * comparable type (number or string), and stand in the requested | ||
| * relation. `nz` succeeds for any non-zero value. | ||
| */ | ||
| export declare const rules: Rules; |
| // Type definitions for yopl — boolean logic rules. | ||
| import type {Rules} from '../solve.js'; | ||
| /** | ||
| * Boolean logic rule library: `logicalAnd`, `logicalOr`, `logicalXor`, | ||
| * `logicalNot`. Each defines a relation among its boolean arguments | ||
| * that may be solved in any direction with sufficient bindings. | ||
| */ | ||
| export declare const rules: Rules; |
| // Type definitions for yopl — arithmetic rules. | ||
| import type {Rules} from '../solve.js'; | ||
| /** | ||
| * Arithmetic rule library: `add`, `sub`, `mul`, `div`, `neg`. | ||
| * | ||
| * Each rule defines a relation X⊕Y=Z and is reversible: any two of | ||
| * the three arguments may be bound, and the rule will solve for the | ||
| * third. | ||
| */ | ||
| export declare const rules: Rules; |
| // Type definitions for yopl — system rules and helpers. | ||
| import type {Variable} from 'deep6/env.js'; | ||
| import type {GoalFn, Rules, TermObject} from '../solve.js'; | ||
| /** Goal that always fails (used to force backtracking). */ | ||
| export declare const fail: GoalFn; | ||
| /** Goal that aborts the entire proof search. */ | ||
| export declare const halt: GoalFn; | ||
| /** | ||
| * Build a Prolog-style cut. Pass the rest of the rule's variables | ||
| * (`...sys`) so cut can locate the choice point to commit to. | ||
| * | ||
| * @param sys The trailing rest-args bound by the rule definition. | ||
| */ | ||
| export declare const cut: (sys: ReadonlyArray<Variable>) => GoalFn; | ||
| /** | ||
| * Meta-call: evaluate `X` as a goal at proof time. | ||
| * | ||
| * @param X A goal name (string), a structured term, or a variable | ||
| * bound to either of the above. | ||
| */ | ||
| export declare const call: (X: string | TermObject | Variable) => GoalFn; | ||
| /** | ||
| * Goal that succeeds when every supplied variable is bound in the | ||
| * current environment. | ||
| */ | ||
| export declare const isBound: (...args: Variable[]) => GoalFn; | ||
| /** | ||
| * Build a rule head: `head(a, b, c)` → `{args: [a, b, c]}`. | ||
| */ | ||
| export declare const head: (...args: unknown[]) => TermObject; | ||
| /** | ||
| * Build a structured goal term: `term('foo', 1, 2)` → | ||
| * `{name: 'foo', args: [1, 2]}`. | ||
| */ | ||
| export declare const term: (name: string, ...args: unknown[]) => TermObject; | ||
| /** Tail wrapper used by `list` to mark the rest position. */ | ||
| export declare class Tail { | ||
| value: unknown; | ||
| constructor(value: unknown); | ||
| } | ||
| /** Mark `list`'s final argument as the explicit list tail. */ | ||
| export declare const rest: (list: unknown) => Tail; | ||
| /** | ||
| * Build a yopl cons-list from positional arguments. The last argument | ||
| * may be wrapped in `rest()` to supply an explicit tail; otherwise the | ||
| * tail defaults to `null`. | ||
| * | ||
| * @throws Error if `rest()` is used in a non-final position. | ||
| */ | ||
| export declare const list: (...args: unknown[]) => unknown; | ||
| /** | ||
| * Like `list` but the *last* argument is treated as the tail directly | ||
| * (no `rest()` wrapper). Requires at least 2 arguments. | ||
| * | ||
| * @throws Error if called with fewer than 2 arguments. | ||
| */ | ||
| export declare const listHead: (...args: unknown[]) => unknown; | ||
| /** The system rule library. */ | ||
| export declare const rules: Rules; |
| // Type definitions for yopl — solver core. | ||
| // Generated by hand from src/solve.js. | ||
| import type {Env, Variable} from 'deep6/env.js'; | ||
| /** | ||
| * A goal: either a structured term, a bare goal name, or an inline JS | ||
| * function executed during proof search. | ||
| */ | ||
| export type Goal = string | TermObject | GoalFn; | ||
| /** | ||
| * Structured goal term. `name` is the rule name (omitted for the rule | ||
| * head, where only `args` matters). `args` is the argument vector that | ||
| * unifies against the head of a rule. | ||
| */ | ||
| export interface TermObject { | ||
| name?: string; | ||
| args?: readonly unknown[]; | ||
| } | ||
| /** | ||
| * Inline goal function executed by the solver. | ||
| * | ||
| * Receives the current environment, the current goal frame, and the | ||
| * driver stack. Return values: | ||
| * - `true` — succeed and advance to the next goal | ||
| * - `false` — fail and trigger backtracking | ||
| * - a goal frame — replace the current continuation | ||
| */ | ||
| export type GoalFn = (env: Env, goals: GoalFrame, stack: ReadonlyArray<unknown>) => boolean | GoalFrame | void; | ||
| /** Linked list of goal frames maintained by the prover. */ | ||
| export interface GoalFrame { | ||
| terms: ReadonlyArray<Goal>; | ||
| index: number; | ||
| next: GoalFrame | null; | ||
| } | ||
| /** | ||
| * A rule body factory. Receives a fresh batch of logical variables and | ||
| * returns the rule's terms; the first element is the head and the rest | ||
| * are body goals. | ||
| */ | ||
| export type RuleBody = (...vars: Variable[]) => ReadonlyArray<Goal>; | ||
| /** A single rule body or a disjunction (an array of bodies). */ | ||
| export type Rule = RuleBody | ReadonlyArray<RuleBody>; | ||
| /** Rule database — maps a rule name to its definition. */ | ||
| export type Rules = Record<string, Rule>; | ||
| /** Callback invoked once per solution discovered by the solver. */ | ||
| export type SolveCallback = (env: Env) => void; | ||
| /** | ||
| * Run the callback-style solver against `rules`, attempting to satisfy | ||
| * the goal named `name` with the supplied `args`. The callback fires | ||
| * once for every solution found. | ||
| * | ||
| * @param rules Rule database. | ||
| * @param name Initial goal name. | ||
| * @param args Argument vector for the initial goal. | ||
| * @param callback Invoked for every solution. | ||
| */ | ||
| declare function solve(rules: Rules, name: string, args: ReadonlyArray<unknown>, callback: SolveCallback): void; | ||
| export default solve; |
| // Type definitions for yopl — asynchronous callback-based solver. | ||
| import type {Env} from 'deep6/env.js'; | ||
| import type {Rules} from '../solve.js'; | ||
| /** Async callback invoked once per solution. */ | ||
| export type AsyncSolveCallback = (env: Env) => void | Promise<void>; | ||
| /** | ||
| * Async callback-style solver. Identical in shape to the synchronous | ||
| * `solve`, except the per-solution callback may return a promise that | ||
| * the solver awaits before backtracking. | ||
| * | ||
| * @param rules Rule database. | ||
| * @param name Initial goal name. | ||
| * @param args Argument vector for the initial goal. | ||
| * @param callback Async callback invoked for every solution. | ||
| */ | ||
| declare function solve(rules: Rules, name: string, args: ReadonlyArray<unknown>, callback: AsyncSolveCallback): Promise<void>; | ||
| export default solve; |
| // Type definitions for yopl — asynchronous generator-based solver. | ||
| import type {Env} from 'deep6/env.js'; | ||
| import type {Rules} from '../solve.js'; | ||
| /** | ||
| * Async generator solver. Yields the live `Env` for each solution. | ||
| * Useful when goals contain `await`-bearing inline functions. | ||
| * | ||
| * @param rules Rule database. | ||
| * @param name Initial goal name. | ||
| * @param args Argument vector for the initial goal. | ||
| */ | ||
| declare function generate(rules: Rules, name: string, args: ReadonlyArray<unknown>): AsyncGenerator<Env, void, void>; | ||
| export default generate; |
| // Type definitions for yopl — synchronous generator-based solver. | ||
| import type {Env} from 'deep6/env.js'; | ||
| import type {Rules} from '../solve.js'; | ||
| /** | ||
| * Synchronous generator solver. Yields the live `Env` for each solution | ||
| * found; consumers typically call `assemble(variable, env)` from deep6 | ||
| * to extract bindings before requesting the next solution. | ||
| * | ||
| * @param rules Rule database. | ||
| * @param name Initial goal name. | ||
| * @param args Argument vector for the initial goal. | ||
| */ | ||
| declare function generate(rules: Rules, name: string, args: ReadonlyArray<unknown>): Generator<Env, void, void>; | ||
| export default generate; |
+1
-1
| The "New" BSD License: | ||
| ********************** | ||
| Copyright (c) 2005-2020, Eugene Lazutkin | ||
| Copyright (c) 2005-2026, Eugene Lazutkin | ||
| All rights reserved. | ||
@@ -6,0 +6,0 @@ |
+48
-60
| { | ||
| "name": "yopl", | ||
| "version": "1.1.4", | ||
| "description": "No dependency mini-library: unification, deep equivalence, deep cloning, logical solver.", | ||
| "version": "1.2.0", | ||
| "description": "Embeddable Prolog-style logic engine for JavaScript: declarative rules, unification, backtracking search, and four solver drivers (sync/async × callback/generator). Useful for pattern matching with extraction, constraint search, type inference, planners, expert systems, and policy checks. ESM, single runtime dependency (deep6).", | ||
| "type": "module", | ||
| "module": "src/solve.js", | ||
| "main": "cjs/solve.js", | ||
| "main": "src/solve.js", | ||
| "types": "src/solve.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "require": "./cjs/solve.js", | ||
| "default": "./src/solve.js" | ||
| }, | ||
| "./*": { | ||
| "require": "./cjs/*", | ||
| "default": "./src/*" | ||
| }, | ||
| "./solve.js": { | ||
| "require": "./cjs/solve.js", | ||
| "default": "./src/solve.js" | ||
| }, | ||
| "./solvers/async.js": { | ||
| "require": "./cjs/solvers/async.js", | ||
| "default": "./src/solvers/async.js" | ||
| }, | ||
| "./solvers/asyncGen.js": { | ||
| "require": "./cjs/solvers/asyncGen.js", | ||
| "default": "./src/solvers/asyncGen.js" | ||
| }, | ||
| "./solvers/gen.js": { | ||
| "require": "./cjs/solvers/gen.js", | ||
| "default": "./src/solvers/gen.js" | ||
| }, | ||
| "./cjs": "./cjs/solve.js", | ||
| "./cjs/*": "./cjs/*", | ||
| "./cjs/solve.js": "./cjs/solve.js", | ||
| "./cjs/solvers/async.js": "./cjs/solvers/async.js", | ||
| "./cjs/solvers/asyncGen.js": "./cjs/solvers/asyncGen.js", | ||
| "./cjs/solvers/gen.js": "./cjs/solvers/gen.js" | ||
| ".": "./src/solve.js", | ||
| "./*": "./src/*" | ||
| }, | ||
| "scripts": { | ||
| "test": "node tests/tests.js", | ||
| "test:bun": "bun run tests/tests.js", | ||
| "test:deno": "deno run --allow-read tests/tests.js", | ||
| "debug": "node --inspect-brk tests/tests.js", | ||
| "prepareDist": "node scripts/prepareDist.js", | ||
| "babel": "babel src --out-dir cjs", | ||
| "build": "npm run prepareDist && npm run babel", | ||
| "prepublishOnly": "npm run build" | ||
| "ts-check": "tsc --noEmit", | ||
| "lint": "prettier --check .", | ||
| "lint:fix": "prettier --write ." | ||
| }, | ||
@@ -53,32 +26,47 @@ "repository": { | ||
| "keywords": [ | ||
| "logic programming", | ||
| "solver", | ||
| "logic solver" | ||
| "prolog", | ||
| "logic-programming", | ||
| "logic-solver", | ||
| "unification", | ||
| "unify", | ||
| "backtracking", | ||
| "rule-engine", | ||
| "rules", | ||
| "declarative", | ||
| "relational", | ||
| "constraint-solver", | ||
| "pattern-matching", | ||
| "inference", | ||
| "type-inference", | ||
| "expert-system", | ||
| "planner", | ||
| "embeddable", | ||
| "esm", | ||
| "typescript", | ||
| "deep6" | ||
| ], | ||
| "author": "Eugene Lazutkin <eugene.lazutkin@gmail.com> (https://www.lazutkin.com/)", | ||
| "funding": "https://github.com/sponsors/uhop", | ||
| "license": "BSD-3-Clause", | ||
| "devDependencies": { | ||
| "@babel/cli": "^7.24.7", | ||
| "@babel/core": "^7.24.7", | ||
| "@babel/preset-env": "^7.24.7" | ||
| "bugs": { | ||
| "url": "https://github.com/uhop/yopl/issues" | ||
| }, | ||
| "homepage": "https://github.com/uhop/yopl#readme", | ||
| "llms": "https://raw.githubusercontent.com/uhop/yopl/master/llms.txt", | ||
| "llmsFull": "https://raw.githubusercontent.com/uhop/yopl/master/llms-full.txt", | ||
| "files": [ | ||
| "/src", | ||
| "/cjs" | ||
| "src", | ||
| "skills", | ||
| "llms.txt", | ||
| "llms-full.txt", | ||
| "AGENTS.md", | ||
| "ARCHITECTURE.md" | ||
| ], | ||
| "babel": { | ||
| "presets": [ | ||
| [ | ||
| "@babel/preset-env", | ||
| { | ||
| "targets": { | ||
| "node": "current" | ||
| } | ||
| } | ||
| ] | ||
| ] | ||
| "dependencies": { | ||
| "deep6": "^1.2.1" | ||
| }, | ||
| "dependencies": { | ||
| "deep6": "^1.1.4" | ||
| "devDependencies": { | ||
| "prettier": "^3.8.1", | ||
| "typescript": "^6.0.2" | ||
| } | ||
| } |
+86
-17
| # yopl [![NPM version][npm-image]][npm-url] | ||
| [npm-image]: https://img.shields.io/npm/v/yopl.svg | ||
| [npm-url]: https://npmjs.org/package/yopl | ||
| [npm-image]: https://img.shields.io/npm/v/yopl.svg | ||
| [npm-url]: https://npmjs.org/package/yopl | ||
| `yopl`: an ES6 mini-library that provides: | ||
| `yopl` is an ES6 mini-library that implements a Prolog-style logic solver in JavaScript. It provides: | ||
| * Logic solvers. | ||
| * Rule-based, logic programming style. | ||
| * Both callback and generator styles are supported. | ||
| - A small core solver with multiple driver styles: callback, generator, async callback, async generator. | ||
| - A built-in rule library: helpers and control predicates, comparisons, arithmetic, bitwise, and boolean logic. | ||
| It has only one dependency: [deep6](https://www.npmjs.com/package/deep6), which is a no-dependency library itself. | ||
| Its only runtime dependency is [`deep6`](https://www.npmjs.com/package/deep6), itself a zero-dependency library that provides the unification engine. | ||
| # Introduction | ||
| ## What it does and when to use it | ||
| TBD | ||
| `yopl` lets you describe a problem as a set of _rules_ over JavaScript values and ask the solver to find values that satisfy them. You write declarative rules; the engine handles search, unification, and backtracking. You stay inside JavaScript — there is no embedded DSL to parse, no separate Prolog runtime, and rules can call back into plain JS (sync or async) whenever a piece of logic is easier to express that way. | ||
| It is useful when a problem is awkward to express as straight-line code but natural to express as constraints or relations: | ||
| - Pattern matching and shape validation against deeply nested data, where you also want to _extract_ values during the match. | ||
| - Searching configurations, dependency graphs, or rule sets for combinations that satisfy several conditions at once. | ||
| - Type-inference-like or tag-propagation passes over an AST or IR. | ||
| - Small expert systems, planners, permission/policy checks, and "find me an X such that Y" queries embedded inside a larger JS app. | ||
| - Test fixtures and property-style checks that need to enumerate all values matching a spec. | ||
| If you only need single-direction pattern matching, a regex or a destructuring assignment is simpler. Reach for `yopl` when you need _bidirectional_ matching (unification), backtracking across alternative rules, or enumeration of all solutions — and you want all of that without leaving your JavaScript codebase. | ||
| ## Installation | ||
@@ -24,10 +33,70 @@ | ||
| ## Release History | ||
| ## Quick start | ||
| - 1.1.4 *updated dependencies.* | ||
| - 1.1.3 *updated dependencies.* | ||
| - 1.1.2 *updated dependencies.* | ||
| - 1.1.1 *updated dependencies.* | ||
| - 1.1.0 *[deep6](https://npmjs.org/package/deep6) was extracted from this package, and now used as a dependency.* | ||
| - 1.0.1 *added the exports statement.* | ||
| - 1.0.0 *the first 1.0 release.* | ||
| ```js | ||
| import {variable} from 'deep6/env.js'; | ||
| import assemble from 'deep6/traverse/assemble.js'; | ||
| import solve from 'yopl'; | ||
| const rules = { | ||
| member: [(V, X) => [{args: [{value: V, next: X}, V]}], (V, X) => [{args: [{next: X}, V]}, {name: 'member', args: [X, V]}]] | ||
| }; | ||
| const list = {value: 1, next: {value: 2, next: {value: 3, next: null}}}; | ||
| const X = variable('X'); | ||
| solve(rules, 'member', [list, X], env => { | ||
| console.log('X =', assemble(X, env)); | ||
| }); | ||
| // X = 1 | ||
| // X = 2 | ||
| // X = 3 | ||
| ``` | ||
| ## Modules | ||
| | Module | Purpose | | ||
| | -------------------------- | ------------------------------------------------------------------------------- | | ||
| | `yopl` (`src/solve.js`) | Synchronous callback solver — main entry point. | | ||
| | `yopl/solvers/gen.js` | Synchronous generator solver. | | ||
| | `yopl/solvers/async.js` | Async callback solver. | | ||
| | `yopl/solvers/asyncGen.js` | Async generator solver. | | ||
| | `yopl/rules/system.js` | Helpers + control predicates (`head`, `term`, `list`, `cut`, `call`, `not`, …). | | ||
| | `yopl/rules/comp.js` | Comparisons: `lt`, `le`, `gt`, `ge`, `nz`. | | ||
| | `yopl/rules/math.js` | Arithmetic: `add`, `sub`, `mul`, `div`, `neg`. | | ||
| | `yopl/rules/bits.js` | Bitwise: `bitAnd`, `bitOr`, `bitXor`, `bitNot`. | | ||
| | `yopl/rules/logic.js` | Boolean logic: `logicalAnd`, `logicalOr`, `logicalXor`, `logicalNot`. | | ||
| Per-module documentation lives in the [wiki](https://github.com/uhop/yopl/wiki). | ||
| ## CommonJS | ||
| `yopl` ships as ESM only. CommonJS consumers can use Node's built-in dynamic `import()`: | ||
| ```js | ||
| const {default: solve} = await import('yopl'); | ||
| ``` | ||
| A full CJS interop demo lives in `tests/test-cjs.cjs` (run it with `node tests/test-cjs.cjs`). | ||
| ## Development | ||
| ```bash | ||
| git clone git@github.com:uhop/yopl.git | ||
| cd yopl | ||
| npm install | ||
| npm test | ||
| ``` | ||
| See [CONTRIBUTING.md](./CONTRIBUTING.md) for the development workflow and [AGENTS.md](./AGENTS.md) for AI-agent rules. | ||
| ## Release history | ||
| - 1.2.0 — removed CJS build, restructured tests, added TypeScript typings, simplified list creation, bug fixes and performance improvements, expanded docs and wiki. | ||
| - 1.1.4 — updated dependencies. | ||
| - 1.1.3 — updated dependencies. | ||
| - 1.1.2 — updated dependencies. | ||
| - 1.1.1 — updated dependencies. | ||
| - 1.1.0 — [deep6](https://npmjs.org/package/deep6) was extracted from this package and is now a dependency. | ||
| - 1.0.1 — added the `exports` statement. | ||
| - 1.0.0 — first 1.0 release. |
+10
-11
@@ -1,2 +0,2 @@ | ||
| import {_} from 'deep6/env'; | ||
| import {_} from 'deep6/env.js'; | ||
| import {head, cut} from './system.js'; | ||
@@ -19,3 +19,3 @@ | ||
| if (y !== _ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| const z = Z.get(env); | ||
| if (z !== _ && typeof z != 'number') return false; | ||
@@ -52,3 +52,3 @@ if (x === _ || y === _ || z === _) return true; | ||
| if (y !== _ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| const z = Z.get(env); | ||
| if (z !== _ && typeof z != 'number') return false; | ||
@@ -75,3 +75,3 @@ if (x === _ || y === _ || z === _) return true; | ||
| head(X, Y, Z), | ||
| (env, stack) => { | ||
| (env, goals, stack) => { | ||
| const isX = X.isBound(env), | ||
@@ -82,3 +82,3 @@ isY = Y.isBound(env), | ||
| if (count < 2) return false; | ||
| cut(sys)(env, stack); | ||
| cut(sys)(env, goals, stack); | ||
| if (count == 3) { | ||
@@ -89,3 +89,3 @@ const x = X.get(env); | ||
| if (y !== _ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| const z = Z.get(env); | ||
| if (z !== _ && typeof z != 'number') return false; | ||
@@ -124,8 +124,8 @@ if (x === _ || y === _ || z === _) return true; | ||
| head(X, Y), | ||
| (env, stack) => { | ||
| (env, goals, stack) => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env); | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0); | ||
| isY = Y.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0); | ||
| if (count < 1) return false; | ||
| cut(sys)(env, stack); | ||
| cut(sys)(env, goals, stack); | ||
| if (count == 2) { | ||
@@ -136,3 +136,2 @@ const x = X.get(env); | ||
| if (y !== _ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (x === _ || y === _) return true; | ||
@@ -139,0 +138,0 @@ return x === ~y; |
@@ -1,2 +0,2 @@ | ||
| import {_} from 'deep6/env'; | ||
| import {_} from 'deep6/env.js'; | ||
| import {head, cut, fail, isBound} from './system.js'; | ||
@@ -3,0 +3,0 @@ |
@@ -1,2 +0,2 @@ | ||
| import {_} from 'deep6/env'; | ||
| import {_} from 'deep6/env.js'; | ||
| import {head} from './system.js'; | ||
@@ -17,3 +17,3 @@ | ||
| y = Y.get(env), | ||
| z = X.get(env); | ||
| z = Z.get(env); | ||
| if (x === _ || y === _ || z === _) return true; | ||
@@ -46,3 +46,3 @@ return !(x && y) === !z; | ||
| y = Y.get(env), | ||
| z = X.get(env); | ||
| z = Z.get(env); | ||
| if (x === _ || y === _ || z === _) return true; | ||
@@ -75,3 +75,3 @@ return !(x || y) === !z; | ||
| y = Y.get(env), | ||
| z = X.get(env); | ||
| z = Z.get(env); | ||
| if (x === _ || y === _ || z === _) return true; | ||
@@ -104,4 +104,4 @@ return (!!x ^ !!y) === !!z; | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env); | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0); | ||
| isY = Y.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0); | ||
| if (count < 1) return false; | ||
@@ -108,0 +108,0 @@ if (count == 2) { |
+17
-18
@@ -1,2 +0,2 @@ | ||
| import {_} from 'deep6/env'; | ||
| import {_} from 'deep6/env.js'; | ||
| import {head, cut} from './system.js'; | ||
@@ -9,3 +9,3 @@ | ||
| head(X, Y, Z), | ||
| (env, stack) => { | ||
| (env, goals, stack) => { | ||
| const isX = X.isBound(env), | ||
@@ -16,3 +16,3 @@ isY = Y.isBound(env), | ||
| if (count < 2) return false; | ||
| cut(sys)(env, stack); | ||
| cut(sys)(env, goals, stack); | ||
| if (count == 3) { | ||
@@ -23,3 +23,3 @@ const x = X.get(env); | ||
| if (y !== _ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| const z = Z.get(env); | ||
| if (z !== _ && typeof z != 'number') return false; | ||
@@ -57,3 +57,3 @@ if (x === _ || y === _ || z === _) return true; | ||
| head(X, Y, Z), | ||
| (env, stack) => { | ||
| (env, goals, stack) => { | ||
| const isX = X.isBound(env), | ||
@@ -64,3 +64,3 @@ isY = Y.isBound(env), | ||
| if (count < 2) return false; | ||
| cut(sys)(env, stack); | ||
| cut(sys)(env, goals, stack); | ||
| if (count == 3) { | ||
@@ -71,3 +71,3 @@ const x = X.get(env); | ||
| if (y !== _ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| const z = Z.get(env); | ||
| if (z !== _ && typeof z != 'number') return false; | ||
@@ -105,3 +105,3 @@ if (x === _ || y === _ || z === _) return true; | ||
| head(X, Y, Z), | ||
| (env, stack) => { | ||
| (env, goals, stack) => { | ||
| const isX = X.isBound(env), | ||
@@ -112,3 +112,3 @@ isY = Y.isBound(env), | ||
| if (count < 2) return false; | ||
| cut(sys)(env, stack); | ||
| cut(sys)(env, goals, stack); | ||
| if (count == 3) { | ||
@@ -119,3 +119,3 @@ const x = X.get(env); | ||
| if (y !== _ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| const z = Z.get(env); | ||
| if (z !== _ && typeof z != 'number') return false; | ||
@@ -155,3 +155,3 @@ if (x === _ || y === _ || z === _) return true; | ||
| head(X, Y, Z), | ||
| (env, stack) => { | ||
| (env, goals, stack) => { | ||
| const isX = X.isBound(env), | ||
@@ -162,3 +162,3 @@ isY = Y.isBound(env), | ||
| if (count < 2) return false; | ||
| cut(sys)(env, stack); | ||
| cut(sys)(env, goals, stack); | ||
| if (count == 3) { | ||
@@ -169,3 +169,3 @@ const x = X.get(env); | ||
| if (y !== _ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| const z = Z.get(env); | ||
| if (z !== _ && typeof z != 'number') return false; | ||
@@ -204,8 +204,8 @@ if (x === _ || y === _ || z === _) return true; | ||
| head(X, Y), | ||
| (env, stack) => { | ||
| (env, goals, stack) => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env); | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0); | ||
| isY = Y.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0); | ||
| if (count < 1) return false; | ||
| cut(sys)(env, stack); | ||
| cut(sys)(env, goals, stack); | ||
| if (count == 2) { | ||
@@ -216,3 +216,2 @@ const x = X.get(env); | ||
| if (y !== _ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (x === _ || y === _) return true; | ||
@@ -219,0 +218,0 @@ return x === -y; |
@@ -22,3 +22,3 @@ import {_, isVariable} from 'deep6/env.js'; | ||
| args; | ||
| // TODO: add processing of arrays of goals | ||
| // TODO: add processing of arrays of goals | ||
| if (isVariable(X)) { | ||
@@ -46,3 +46,6 @@ if (!X.isBound(env)) return false; | ||
| export const isBound = (...args) => env => args.every(V => isVariable(V) && V.isBound(env)); | ||
| export const isBound = | ||
| (...args) => | ||
| env => | ||
| args.every(V => isVariable(V) && V.isBound(env)); | ||
@@ -98,4 +101,4 @@ export const head = (...args) => ({args}); | ||
| // equality | ||
| eq: X => head(X, X), | ||
| notEq: [(X, ...sys) => [head(X, X), cut(sys), fail], [_]], | ||
| eq: X => [head(X, X)], | ||
| notEq: [(X, ...sys) => [head(X, X), cut(sys), fail], () => [head(_, _)]], | ||
| // unify is eq | ||
@@ -105,3 +108,3 @@ | ||
| call: X => [head(X), call(X)], | ||
| not: [(X, ...sys) => [head(X), call(X), cut(sys), fail], () => [head()]], | ||
| not: [(X, ...sys) => [head(X), call(X), cut(sys), fail], () => [head(_)]], | ||
| isUnifiable: (X, Y) => [head(X, Y), term('not', term('not', term('eq', [X, Y])))], | ||
@@ -129,3 +132,3 @@ // notUnifiable is notEq | ||
| ], | ||
| foldl: [A => [head(_, A, null, A)], (F, A, X, Xt, O, B) => [head(F, A, listHead(X, Xt), Yt), call(term(F, A, X, B)), term('foldl', F, B, Xt, O)]], | ||
| foldl: [A => [head(_, A, null, A)], (F, A, X, Xt, O, B) => [head(F, A, listHead(X, Xt), O), call(term(F, A, X, B)), term('foldl', F, B, Xt, O)]], | ||
| foldr: [A => [head(_, A, null, A)], (F, A, X, Xt, O, T) => [head(F, A, listHead(X, Xt), O), term('foldr', F, A, Xt, T), call(term(F, X, T, O))]], | ||
@@ -132,0 +135,0 @@ compose: (F, G, X, O, T) => [head(F, G, X, O), call(term(G, X, T)), call(term(F, T, O))], |
+13
-9
@@ -5,7 +5,10 @@ import unify, {Env, variable} from 'deep6/unify.js'; | ||
| const generateVariables = count => { | ||
| const t = []; | ||
| for (let i = 0; i < count; ++i) t.push(counter++); | ||
| return t.map(name => variable(Symbol(name))); | ||
| const vars = new Array(count); | ||
| for (let i = 0; i < count; ++i) vars[i] = variable(Symbol(counter++)); | ||
| return vars; | ||
| }; | ||
| const POP = {command: 1}; | ||
| const NO_ARGS = Object.freeze([]); | ||
| const prove = (rules, goals, env) => { | ||
@@ -25,5 +28,5 @@ const stack = [{goals}]; | ||
| env.push(); | ||
| if (unify(terms[0].args || [], frame.args, env)) { | ||
| if (unify(terms[0].args || NO_ARGS, frame.args, env)) { | ||
| const newGoals = {terms, index: 1, next: frame.goals}; | ||
| stack.push(frame, {command: 1}, {goals: newGoals}); | ||
| stack.push(frame, POP, {goals: newGoals}); | ||
| env.bindVal(vars[vars.length - 1].name, frame); | ||
@@ -46,4 +49,4 @@ continue main; | ||
| if (newGoals || newGoals === null) { | ||
| (newGoals && !newGoals.terms) && (newGoals = goals); | ||
| stack.push({command: 1}, {goals: newGoals}); | ||
| newGoals && !newGoals.terms && (newGoals = goals); | ||
| stack.push(POP, {goals: newGoals}); | ||
| continue main; | ||
@@ -59,4 +62,5 @@ } | ||
| let ruleList = rules[goal.name]; | ||
| !Array.isArray(ruleList) && (ruleList = [ruleList]); | ||
| stack.push({command: 2, ruleList, index: 0, goals, args: goal.args || []}); | ||
| if (ruleList == null) continue main; | ||
| if (!Array.isArray(ruleList)) ruleList = [ruleList]; | ||
| stack.push({command: 2, ruleList, index: 0, goals, args: goal.args || NO_ARGS}); | ||
| } | ||
@@ -63,0 +67,0 @@ }; |
+14
-10
@@ -5,7 +5,10 @@ import unify, {Env, variable} from 'deep6/unify.js'; | ||
| const generateVariables = count => { | ||
| const t = []; | ||
| for (let i = 0; i < count; ++i) t.push(counter++); | ||
| return t.map(name => variable(Symbol(name))); | ||
| const vars = new Array(count); | ||
| for (let i = 0; i < count; ++i) vars[i] = variable(Symbol(counter++)); | ||
| return vars; | ||
| }; | ||
| const POP = {command: 1}; | ||
| const NO_ARGS = Object.freeze([]); | ||
| const prove = async (rules, goals, env) => { | ||
@@ -25,5 +28,5 @@ const stack = [{goals}]; | ||
| env.push(); | ||
| if (unify(terms[0].args || [], frame.args, env)) { | ||
| if (unify(terms[0].args || NO_ARGS, frame.args, env)) { | ||
| const newGoals = {terms, index: 1, next: frame.goals}; | ||
| stack.push(frame, {command: 1}, {goals: newGoals}); | ||
| stack.push(frame, POP, {goals: newGoals}); | ||
| env.bindVal(vars[vars.length - 1].name, frame); | ||
@@ -46,4 +49,4 @@ continue main; | ||
| if (newGoals || newGoals === null) { | ||
| (newGoals && !newGoals.terms) && (newGoals = goals); | ||
| stack.push({command: 1}, {goals: newGoals}); | ||
| newGoals && !newGoals.terms && (newGoals = goals); | ||
| stack.push(POP, {goals: newGoals}); | ||
| continue main; | ||
@@ -59,4 +62,5 @@ } | ||
| let ruleList = rules[goal.name]; | ||
| !Array.isArray(ruleList) && (ruleList = [ruleList]); | ||
| stack.push({command: 2, ruleList, index: 0, goals, args: goal.args || []}); | ||
| if (ruleList == null) continue main; | ||
| if (!Array.isArray(ruleList)) ruleList = [ruleList]; | ||
| stack.push({command: 2, ruleList, index: 0, goals, args: goal.args || NO_ARGS}); | ||
| } | ||
@@ -69,5 +73,5 @@ }; | ||
| const goals = {terms: [{name, args}, async env => (await callback(env), false)], index: 0, next: null}; | ||
| prove(rules, goals, env); | ||
| await prove(rules, goals, env); | ||
| }; | ||
| export default solve; |
+16
-12
@@ -5,7 +5,10 @@ import unify, {Env, variable} from 'deep6/unify.js'; | ||
| const generateVariables = count => { | ||
| const t = []; | ||
| for (let i = 0; i < count; ++i) t.push(counter++); | ||
| return t.map(name => variable(Symbol(name))); | ||
| const vars = new Array(count); | ||
| for (let i = 0; i < count; ++i) vars[i] = variable(Symbol(counter++)); | ||
| return vars; | ||
| }; | ||
| const POP = {command: 1}; | ||
| const NO_ARGS = Object.freeze([]); | ||
| async function* prove(rules, goals, env) { | ||
@@ -25,5 +28,5 @@ const stack = [{goals}]; | ||
| env.push(); | ||
| if (unify(terms[0].args || [], frame.args, env)) { | ||
| if (unify(terms[0].args || NO_ARGS, frame.args, env)) { | ||
| const newGoals = {terms, index: 1, next: frame.goals}; | ||
| stack.push(frame, {command: 1}, {goals: newGoals}); | ||
| stack.push(frame, POP, {goals: newGoals}); | ||
| env.bindVal(vars[vars.length - 1].name, frame); | ||
@@ -40,3 +43,3 @@ continue main; | ||
| } | ||
| if (!goals){ | ||
| if (!goals) { | ||
| yield env; | ||
@@ -50,4 +53,4 @@ continue main; | ||
| if (newGoals || newGoals === null) { | ||
| (newGoals && !newGoals.terms) && (newGoals = goals); | ||
| stack.push({command: 1}, {goals: newGoals}); | ||
| newGoals && !newGoals.terms && (newGoals = goals); | ||
| stack.push(POP, {goals: newGoals}); | ||
| continue main; | ||
@@ -63,6 +66,7 @@ } | ||
| let ruleList = rules[goal.name]; | ||
| !Array.isArray(ruleList) && (ruleList = [ruleList]); | ||
| stack.push({command: 2, ruleList, index: 0, goals, args: goal.args || []}); | ||
| if (ruleList == null) continue main; | ||
| if (!Array.isArray(ruleList)) ruleList = [ruleList]; | ||
| stack.push({command: 2, ruleList, index: 0, goals, args: goal.args || NO_ARGS}); | ||
| } | ||
| }; | ||
| } | ||
@@ -74,4 +78,4 @@ async function* generate(rules, name, args) { | ||
| yield* prove(rules, goals, env); | ||
| }; | ||
| } | ||
| export default generate; |
+16
-12
@@ -5,7 +5,10 @@ import unify, {Env, variable} from 'deep6/unify.js'; | ||
| const generateVariables = count => { | ||
| const t = []; | ||
| for (let i = 0; i < count; ++i) t.push(counter++); | ||
| return t.map(name => variable(Symbol(name))); | ||
| const vars = new Array(count); | ||
| for (let i = 0; i < count; ++i) vars[i] = variable(Symbol(counter++)); | ||
| return vars; | ||
| }; | ||
| const POP = {command: 1}; | ||
| const NO_ARGS = Object.freeze([]); | ||
| function* prove(rules, goals, env) { | ||
@@ -25,5 +28,5 @@ const stack = [{goals}]; | ||
| env.push(); | ||
| if (unify(terms[0].args || [], frame.args, env)) { | ||
| if (unify(terms[0].args || NO_ARGS, frame.args, env)) { | ||
| const newGoals = {terms, index: 1, next: frame.goals}; | ||
| stack.push(frame, {command: 1}, {goals: newGoals}); | ||
| stack.push(frame, POP, {goals: newGoals}); | ||
| env.bindVal(vars[vars.length - 1].name, frame); | ||
@@ -40,3 +43,3 @@ continue main; | ||
| } | ||
| if (!goals){ | ||
| if (!goals) { | ||
| yield env; | ||
@@ -50,4 +53,4 @@ continue main; | ||
| if (newGoals || newGoals === null) { | ||
| (newGoals && !newGoals.terms) && (newGoals = goals); | ||
| stack.push({command: 1}, {goals: newGoals}); | ||
| newGoals && !newGoals.terms && (newGoals = goals); | ||
| stack.push(POP, {goals: newGoals}); | ||
| continue main; | ||
@@ -63,6 +66,7 @@ } | ||
| let ruleList = rules[goal.name]; | ||
| !Array.isArray(ruleList) && (ruleList = [ruleList]); | ||
| stack.push({command: 2, ruleList, index: 0, goals, args: goal.args || []}); | ||
| if (ruleList == null) continue main; | ||
| if (!Array.isArray(ruleList)) ruleList = [ruleList]; | ||
| stack.push({command: 2, ruleList, index: 0, goals, args: goal.args || NO_ARGS}); | ||
| } | ||
| }; | ||
| } | ||
@@ -74,4 +78,4 @@ function* generate(rules, name, args) { | ||
| yield* prove(rules, goals, env); | ||
| }; | ||
| } | ||
| export default generate; |
| {"type":"commonjs"} |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.rules = void 0; | ||
| var _env = require("deep6/env"); | ||
| var _system = require("./system.js"); | ||
| const rules = exports.rules = { | ||
| // bitwise operations | ||
| bitAnd: (X, Y, Z) => [(0, _system.head)(X, Y, Z), env => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env), | ||
| isZ = Z.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0) + (isZ ? 1 : 0); | ||
| if (count < 2) return false; | ||
| if (count == 3) { | ||
| const x = X.get(env); | ||
| if (x !== _env._ && typeof x != 'number') return false; | ||
| const y = Y.get(env); | ||
| if (y !== _env._ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (z !== _env._ && typeof z != 'number') return false; | ||
| if (x === _env._ || y === _env._ || z === _env._) return true; | ||
| return (x & y) === z; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (typeof x != 'number') return false; | ||
| if (isY) { | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| env.bindVal(Z.name, x & y); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| return false; | ||
| }], | ||
| bitOr: (X, Y, Z) => [(0, _system.head)(X, Y, Z), env => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env), | ||
| isZ = Z.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0) + (isZ ? 1 : 0); | ||
| if (count < 2) return false; | ||
| if (count == 3) { | ||
| const x = X.get(env); | ||
| if (x !== _env._ && typeof x != 'number') return false; | ||
| const y = Y.get(env); | ||
| if (y !== _env._ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (z !== _env._ && typeof z != 'number') return false; | ||
| if (x === _env._ || y === _env._ || z === _env._) return true; | ||
| return (x | y) === z; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (typeof x != 'number') return false; | ||
| if (isY) { | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| env.bindVal(Z.name, x | y); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| return false; | ||
| }], | ||
| bitXor: [(X, Y, Z, ...sys) => [(0, _system.head)(X, Y, Z), (env, stack) => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env), | ||
| isZ = Z.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0) + (isZ ? 1 : 0); | ||
| if (count < 2) return false; | ||
| (0, _system.cut)(sys)(env, stack); | ||
| if (count == 3) { | ||
| const x = X.get(env); | ||
| if (x !== _env._ && typeof x != 'number') return false; | ||
| const y = Y.get(env); | ||
| if (y !== _env._ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (z !== _env._ && typeof z != 'number') return false; | ||
| if (x === _env._ || y === _env._ || z === _env._) return true; | ||
| return (x ^ y) === z; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (typeof x != 'number') return false; | ||
| if (isY) { | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| env.bindVal(Z.name, x ^ y); | ||
| return true; | ||
| } | ||
| const z = Z.get(env); | ||
| if (typeof z != 'number') return false; | ||
| env.bindVal(Y.name, x ^ z); | ||
| return true; | ||
| } | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| const z = Z.get(env); | ||
| if (typeof z != 'number') return false; | ||
| env.bindVal(X.name, y ^ z); | ||
| return true; | ||
| }], Y => [(0, _system.head)(0, Y, Y)], X => [(0, _system.head)(X, 0, X)], X => [(0, _system.head)(X, X, 0)]], | ||
| bitNot: [(X, Y, ...sys) => [(0, _system.head)(X, Y), (env, stack) => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env); | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0); | ||
| if (count < 1) return false; | ||
| (0, _system.cut)(sys)(env, stack); | ||
| if (count == 2) { | ||
| const x = X.get(env); | ||
| if (x !== _env._ && typeof x != 'number') return false; | ||
| const y = Y.get(env); | ||
| if (y !== _env._ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (x === _env._ || y === _env._) return true; | ||
| return x === ~y; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (typeof x != 'number') return false; | ||
| env.bindVal(Y.name, ~x); | ||
| return true; | ||
| } | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| env.bindVal(X.name, ~y); | ||
| return true; | ||
| }], () => [(0, _system.head)(0, 0)]] | ||
| }; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.rules = void 0; | ||
| var _env = require("deep6/env"); | ||
| var _system = require("./system.js"); | ||
| const comparable = { | ||
| string: 1, | ||
| number: 1 | ||
| }; | ||
| const rules = exports.rules = { | ||
| // comparisons | ||
| lt: (X, Y) => [(0, _system.head)(X, Y), (0, _system.isBound)(X, Y), env => { | ||
| const x = X.get(env), | ||
| y = Y.get(env); | ||
| if (x === _env._) return y === _env._ || comparable[typeof y] === 1; | ||
| if (y === _env._) return comparable[typeof x] === 1; | ||
| return typeof x == typeof y && comparable[typeof x] === 1 && x < y; | ||
| }], | ||
| le: (X, Y) => [(0, _system.head)(X, Y), (0, _system.isBound)(X, Y), env => { | ||
| const x = X.get(env), | ||
| y = Y.get(env); | ||
| if (x === _env._) return y === _env._ || comparable[typeof y] === 1; | ||
| if (y === _env._) return comparable[typeof x] === 1; | ||
| return typeof x == typeof y && comparable[typeof x] === 1 && x <= y; | ||
| }], | ||
| gt: (X, Y) => [(0, _system.head)(X, Y), (0, _system.isBound)(X, Y), env => { | ||
| const x = X.get(env), | ||
| y = Y.get(env); | ||
| if (x === _env._) return y === _env._ || comparable[typeof y] === 1; | ||
| if (y === _env._) return comparable[typeof x] === 1; | ||
| return typeof x == typeof y && comparable[typeof x] === 1 && x > y; | ||
| }], | ||
| ge: (X, Y) => [(0, _system.head)(X, Y), (0, _system.isBound)(X, Y), env => { | ||
| const x = X.get(env), | ||
| y = Y.get(env); | ||
| if (x === _env._) return y === _env._ || comparable[typeof y] === 1; | ||
| if (y === _env._) return comparable[typeof x] === 1; | ||
| return typeof x == typeof y && comparable[typeof x] === 1 && x >= y; | ||
| }], | ||
| // miscellaneous | ||
| nz: [(...sys) => [(0, _system.head)(0), (0, _system.cut)(sys), _system.fail], (...sys) => [(0, _system.head)(_env._), (0, _system.cut)(sys)]] | ||
| }; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.rules = void 0; | ||
| var _env = require("deep6/env"); | ||
| var _system = require("./system.js"); | ||
| const rules = exports.rules = { | ||
| // logical operations | ||
| logicalAnd: (X, Y, Z) => [(0, _system.head)(X, Y, Z), env => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env), | ||
| isZ = Z.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0) + (isZ ? 1 : 0); | ||
| if (count < 2) return false; | ||
| if (count == 3) { | ||
| const x = X.get(env), | ||
| y = Y.get(env), | ||
| z = X.get(env); | ||
| if (x === _env._ || y === _env._ || z === _env._) return true; | ||
| return !(x && y) === !z; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (isY) { | ||
| const y = Y.get(env); | ||
| if (x === _env._ || y === _env._) return true; | ||
| env.bindVal(Z.name, !!(x && y)); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| return false; | ||
| }], | ||
| logicalOr: (X, Y, Z) => [(0, _system.head)(X, Y, Z), env => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env), | ||
| isZ = Z.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0) + (isZ ? 1 : 0); | ||
| if (count < 2) return false; | ||
| if (count == 3) { | ||
| const x = X.get(env), | ||
| y = Y.get(env), | ||
| z = X.get(env); | ||
| if (x === _env._ || y === _env._ || z === _env._) return true; | ||
| return !(x || y) === !z; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (isY) { | ||
| const y = Y.get(env); | ||
| if (x === _env._ || y === _env._) return true; | ||
| env.bindVal(Z.name, !!(x || y)); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| return false; | ||
| }], | ||
| logicalXor: (X, Y, Z) => [(0, _system.head)(X, Y, Z), env => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env), | ||
| isZ = Z.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0) + (isZ ? 1 : 0); | ||
| if (count < 2) return false; | ||
| if (count == 3) { | ||
| const x = X.get(env), | ||
| y = Y.get(env), | ||
| z = X.get(env); | ||
| if (x === _env._ || y === _env._ || z === _env._) return true; | ||
| return (!!x ^ !!y) === !!z; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (isY) { | ||
| const y = Y.get(env); | ||
| if (x === _env._ || y === _env._) return true; | ||
| env.bindVal(Z.name, !!(!!x ^ !!y)); | ||
| return true; | ||
| } | ||
| const z = Z.get(env); | ||
| if (x === _env._ || z === _env._) return true; | ||
| env.bindVal(Y.name, !!(!!x ^ !!z)); | ||
| return true; | ||
| } | ||
| const y = Y.get(env), | ||
| z = Z.get(env); | ||
| if (y === _env._ || z === _env._) return true; | ||
| env.bindVal(X.name, !!(!!y ^ !!z)); | ||
| return true; | ||
| }], | ||
| logicalNot: (X, Y) => [(0, _system.head)(X, Y), env => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env); | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0); | ||
| if (count < 1) return false; | ||
| if (count == 2) { | ||
| const x = X.get(env), | ||
| y = Y.get(env); | ||
| if (x === _env._ || y === _env._) return true; | ||
| return !x === !!y; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (x === _env._) return true; | ||
| env.bindVal(Y.name, !x); | ||
| return true; | ||
| } | ||
| const y = Y.get(env); | ||
| if (y === _env._) return true; | ||
| env.bindVal(X.name, !y); | ||
| return true; | ||
| }] | ||
| }; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.rules = void 0; | ||
| var _env = require("deep6/env"); | ||
| var _system = require("./system.js"); | ||
| const rules = exports.rules = { | ||
| // arithmetics | ||
| add: [(X, Y, Z, ...sys) => [(0, _system.head)(X, Y, Z), (env, stack) => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env), | ||
| isZ = Z.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0) + (isZ ? 1 : 0); | ||
| if (count < 2) return false; | ||
| (0, _system.cut)(sys)(env, stack); | ||
| if (count == 3) { | ||
| const x = X.get(env); | ||
| if (x !== _env._ && typeof x != 'number') return false; | ||
| const y = Y.get(env); | ||
| if (y !== _env._ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (z !== _env._ && typeof z != 'number') return false; | ||
| if (x === _env._ || y === _env._ || z === _env._) return true; | ||
| return x + y === z; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (typeof x != 'number') return false; | ||
| if (isY) { | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| env.bindVal(Z.name, x + y); | ||
| return true; | ||
| } | ||
| const z = Z.get(env); | ||
| if (typeof z != 'number') return false; | ||
| env.bindVal(Y.name, z - x); | ||
| return true; | ||
| } | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| const z = Z.get(env); | ||
| if (typeof z != 'number') return false; | ||
| env.bindVal(X.name, z - y); | ||
| return true; | ||
| }], Y => [(0, _system.head)(0, Y, Y)], X => [(0, _system.head)(X, 0, X)]], | ||
| sub: [(X, Y, Z, ...sys) => [(0, _system.head)(X, Y, Z), (env, stack) => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env), | ||
| isZ = Z.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0) + (isZ ? 1 : 0); | ||
| if (count < 2) return false; | ||
| (0, _system.cut)(sys)(env, stack); | ||
| if (count == 3) { | ||
| const x = X.get(env); | ||
| if (x !== _env._ && typeof x != 'number') return false; | ||
| const y = Y.get(env); | ||
| if (y !== _env._ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (z !== _env._ && typeof z != 'number') return false; | ||
| if (x === _env._ || y === _env._ || z === _env._) return true; | ||
| return x - y === z; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (typeof x != 'number') return false; | ||
| if (isY) { | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| env.bindVal(Z.name, x - y); | ||
| return true; | ||
| } | ||
| const z = Z.get(env); | ||
| if (typeof z != 'number') return false; | ||
| env.bindVal(Y.name, x - z); | ||
| return true; | ||
| } | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| const z = Z.get(env); | ||
| if (typeof z != 'number') return false; | ||
| env.bindVal(X.name, y + z); | ||
| return true; | ||
| }], X => [(0, _system.head)(X, 0, X)], X => [(0, _system.head)(X, X, 0)]], | ||
| mul: [(X, Y, Z, ...sys) => [(0, _system.head)(X, Y, Z), (env, stack) => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env), | ||
| isZ = Z.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0) + (isZ ? 1 : 0); | ||
| if (count < 2) return false; | ||
| (0, _system.cut)(sys)(env, stack); | ||
| if (count == 3) { | ||
| const x = X.get(env); | ||
| if (x !== _env._ && typeof x != 'number') return false; | ||
| const y = Y.get(env); | ||
| if (y !== _env._ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (z !== _env._ && typeof z != 'number') return false; | ||
| if (x === _env._ || y === _env._ || z === _env._) return true; | ||
| return x * y === z; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (typeof x != 'number') return false; | ||
| if (isY) { | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| env.bindVal(Z.name, x * y); | ||
| return true; | ||
| } | ||
| const z = Z.get(env); | ||
| if (typeof z != 'number') return false; | ||
| env.bindVal(Y.name, z / x); | ||
| return true; | ||
| } | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| const z = Z.get(env); | ||
| if (typeof z != 'number') return false; | ||
| env.bindVal(X.name, z / y); | ||
| return true; | ||
| }], () => [(0, _system.head)(0, _env._, 0)], () => [(0, _system.head)(_env._, 0, 0)], X => [(0, _system.head)(1, X, X)], X => [(0, _system.head)(X, 1, X)]], | ||
| div: [(X, Y, Z, ...sys) => [(0, _system.head)(X, Y, Z), (env, stack) => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env), | ||
| isZ = Z.isBound(env), | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0) + (isZ ? 1 : 0); | ||
| if (count < 2) return false; | ||
| (0, _system.cut)(sys)(env, stack); | ||
| if (count == 3) { | ||
| const x = X.get(env); | ||
| if (x !== _env._ && typeof x != 'number') return false; | ||
| const y = Y.get(env); | ||
| if (y !== _env._ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (z !== _env._ && typeof z != 'number') return false; | ||
| if (x === _env._ || y === _env._ || z === _env._) return true; | ||
| return x / y === z; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (typeof x != 'number') return false; | ||
| if (isY) { | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| env.bindVal(Z.name, x / y); | ||
| return true; | ||
| } | ||
| const z = Z.get(env); | ||
| if (typeof z != 'number') return false; | ||
| env.bindVal(Y.name, x / z); | ||
| return true; | ||
| } | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| const z = Z.get(env); | ||
| if (typeof z != 'number') return false; | ||
| env.bindVal(X.name, y * z); | ||
| return true; | ||
| }], () => [(0, _system.head)(0, _env._, 0)], X => [(0, _system.head)(X, X, 1)], X => [(0, _system.head)(X, 1, X)]], | ||
| neg: [(X, Y, ...sys) => [(0, _system.head)(X, Y), (env, stack) => { | ||
| const isX = X.isBound(env), | ||
| isY = Y.isBound(env); | ||
| count = (isX ? 1 : 0) + (isY ? 1 : 0); | ||
| if (count < 1) return false; | ||
| (0, _system.cut)(sys)(env, stack); | ||
| if (count == 2) { | ||
| const x = X.get(env); | ||
| if (x !== _env._ && typeof x != 'number') return false; | ||
| const y = Y.get(env); | ||
| if (y !== _env._ && typeof y != 'number') return false; | ||
| const z = X.get(env); | ||
| if (x === _env._ || y === _env._) return true; | ||
| return x === -y; | ||
| } | ||
| if (isX) { | ||
| const x = X.get(env); | ||
| if (typeof x != 'number') return false; | ||
| env.bindVal(Y.name, -x); | ||
| return true; | ||
| } | ||
| const y = Y.get(env); | ||
| if (typeof y != 'number') return false; | ||
| env.bindVal(X.name, -y); | ||
| return true; | ||
| }], () => [(0, _system.head)(0, 0)]] | ||
| }; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.term = exports.rules = exports.rest = exports.listHead = exports.list = exports.isBound = exports.head = exports.halt = exports.fail = exports.cut = exports.call = void 0; | ||
| var _env = require("deep6/env.js"); | ||
| // utilities | ||
| const fail = () => false; | ||
| exports.fail = fail; | ||
| const halt = (env, goals, stack) => (stack.splice(0), false); | ||
| exports.halt = halt; | ||
| const cut = sys => (env, goals, stack) => { | ||
| const lastFrame = sys[0].get(env); | ||
| for (let i = stack.length - 1; i >= 0; --i) { | ||
| const frame = stack[i]; | ||
| if (frame.command === 2) { | ||
| frame.index = Infinity; | ||
| } | ||
| if (frame === lastFrame) break; | ||
| } | ||
| return true; | ||
| }; | ||
| exports.cut = cut; | ||
| const call = X => (env, goals) => { | ||
| let term = X, | ||
| name, | ||
| args; | ||
| // TODO: add processing of arrays of goals | ||
| if ((0, _env.isVariable)(X)) { | ||
| if (!X.isBound(env)) return false; | ||
| term = X.get(env); | ||
| } | ||
| if (typeof term == 'string') { | ||
| name = term; | ||
| } else { | ||
| if (!term || typeof term != 'object') return false; | ||
| name = term.name; | ||
| args = term.args; | ||
| if ((0, _env.isVariable)(name)) { | ||
| name = name.get(env); | ||
| } | ||
| if (typeof name != 'string') return false; | ||
| if ((0, _env.isVariable)(args)) { | ||
| args = args.get(env); | ||
| } | ||
| if (args && !Array.isArray(args)) return false; | ||
| } | ||
| return { | ||
| terms: [{ | ||
| name, | ||
| args | ||
| }], | ||
| index: 0, | ||
| next: goals | ||
| }; | ||
| }; | ||
| exports.call = call; | ||
| const isBound = (...args) => env => args.every(V => (0, _env.isVariable)(V) && V.isBound(env)); | ||
| exports.isBound = isBound; | ||
| const head = (...args) => ({ | ||
| args | ||
| }); | ||
| exports.head = head; | ||
| const term = (name, ...args) => ({ | ||
| name, | ||
| args | ||
| }); | ||
| exports.term = term; | ||
| class Tail { | ||
| constructor(value) { | ||
| this.value = value; | ||
| } | ||
| } | ||
| const rest = list => new Tail(list); | ||
| exports.rest = rest; | ||
| const list = (...args) => { | ||
| if (!args.length) return null; | ||
| let list = null, | ||
| startFrom = args.length - 1; | ||
| const last = args[startFrom]; | ||
| if (last instanceof Tail) { | ||
| --startFrom; | ||
| list = last.value; | ||
| } | ||
| for (let i = startFrom; i >= 0; --i) { | ||
| const value = args[i]; | ||
| if (value instanceof Tail) throw new Error('list cannot contain a tail argument in the middle'); | ||
| list = { | ||
| value, | ||
| next: list | ||
| }; | ||
| } | ||
| return list; | ||
| }; | ||
| exports.list = list; | ||
| const listHead = (...args) => { | ||
| if (args.length < 2) throw new Error('list constructor cannot have less then 2 elements'); | ||
| let startFrom = args.length - 1, | ||
| list = args[startFrom]; | ||
| for (let i = startFrom - 1; i >= 0; --i) { | ||
| list = { | ||
| value: args[i], | ||
| next: list | ||
| }; | ||
| } | ||
| return list; | ||
| }; | ||
| // rules | ||
| exports.listHead = listHead; | ||
| const rules = exports.rules = { | ||
| // types | ||
| isVar: X => [head(X), env => !X.isBound(env)], | ||
| isNonVar: X => [head(X), env => X.isBound(env)], | ||
| isNumber: X => [head(X), env => X.isBound(env) && typeof X.get(env) == 'number'], | ||
| isString: X => [head(X), env => X.isBound(env) && typeof X.get(env) == 'string'], | ||
| isNull: X => [head(X), env => X.isBound(env) && X.get(env) === null], | ||
| isUndefined: X => [head(X), env => X.isBound(env) && X.get(env) === undefined], | ||
| isArray: X => [head(X), env => X.isBound(env) && Array.isArray(X.get(env))], | ||
| // equality | ||
| eq: X => head(X, X), | ||
| notEq: [(X, ...sys) => [head(X, X), cut(sys), fail], [_env._]], | ||
| // unify is eq | ||
| // control predicates | ||
| call: X => [head(X), call(X)], | ||
| not: [(X, ...sys) => [head(X), call(X), cut(sys), fail], () => [head()]], | ||
| isUnifiable: (X, Y) => [head(X, Y), term('not', term('not', term('eq', [X, Y])))], | ||
| // notUnifiable is notEq | ||
| conjunction: [() => [head(null)], (X, Xt) => [head(listHead(X, Xt)), call(X), term('conjunction', Xt)]], | ||
| disjunction: [X => [head(listHead(X, _env._)), call(X)], Xt => [head(listHead(_env._, Xt)), term('disjunction', Xt)]], | ||
| true: () => [head()], | ||
| once: (X, ...sys) => [head(X), call(X), cut(sys)], | ||
| // meta predicates | ||
| // apply, applyp | ||
| // extended logic | ||
| counterExample: (A, B) => [head(A, B), call(A), term('not', B)], | ||
| implies: (A, B) => [head(A, B), term('not', term('counterExample', A, B))], | ||
| // second-order logic | ||
| // map, filter, foldl, foldr, compose, converse | ||
| map: [() => [head(_env._, null, null)], (F, X, Xt, Y, Yt) => [head(F, listHead(X, Xt), listHead(Y, Yt)), call(term(F, X, Y)), term('map', Xt, Yt)]], | ||
| filter: [() => [head(_env._, null, null)], (P, X, Xt, Yt) => [head(P, listHead(X, Xt), listHead(X, Yt)), term(P, X), term('filter', Xt, Yt)], (P, X, Xt, Yt) => [head(P, listHead(X, Xt), listHead(X, Yt)), term('not', term(P, X)), term('filter', Xt, Yt)]], | ||
| foldl: [A => [head(_env._, A, null, A)], (F, A, X, Xt, O, B) => [head(F, A, listHead(X, Xt), Yt), call(term(F, A, X, B)), term('foldl', F, B, Xt, O)]], | ||
| foldr: [A => [head(_env._, A, null, A)], (F, A, X, Xt, O, T) => [head(F, A, listHead(X, Xt), O), term('foldr', F, A, Xt, T), call(term(F, X, T, O))]], | ||
| compose: (F, G, X, O, T) => [head(F, G, X, O), call(term(G, X, T)), call(term(F, T, O))], | ||
| converse: (F, X, Y, O) => [head(F, X, Y, O), call(term(F, Y, X, O))] | ||
| }; | ||
| rules.unify = rules.eq; | ||
| rules.notUnifiable = rules.notEq; |
-101
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.default = void 0; | ||
| var _unify = _interopRequireWildcard(require("deep6/unify.js")); | ||
| function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } | ||
| function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } | ||
| let counter = 0; | ||
| const generateVariables = count => { | ||
| const t = []; | ||
| for (let i = 0; i < count; ++i) t.push(counter++); | ||
| return t.map(name => (0, _unify.variable)(Symbol(name))); | ||
| }; | ||
| const prove = (rules, goals, env) => { | ||
| const stack = [{ | ||
| goals | ||
| }]; | ||
| main: while (stack.length) { | ||
| const frame = stack.pop(); | ||
| if (frame.command) { | ||
| if (frame.command === 1) { | ||
| env.pop(); | ||
| continue main; | ||
| } | ||
| while (frame.index < frame.ruleList.length) { | ||
| const rule = frame.ruleList[frame.index++], | ||
| vars = generateVariables(rule.length + 1), | ||
| terms = (typeof rule == 'function' ? rule : rule.goals)(...vars); | ||
| env.push(); | ||
| if ((0, _unify.default)(terms[0].args || [], frame.args, env)) { | ||
| const newGoals = { | ||
| terms, | ||
| index: 1, | ||
| next: frame.goals | ||
| }; | ||
| stack.push(frame, { | ||
| command: 1 | ||
| }, { | ||
| goals: newGoals | ||
| }); | ||
| env.bindVal(vars[vars.length - 1].name, frame); | ||
| continue main; | ||
| } | ||
| env.pop(); | ||
| } | ||
| continue main; | ||
| } | ||
| let goals = frame.goals; | ||
| while (goals && goals.index >= goals.terms.length) { | ||
| goals = goals.next; | ||
| } | ||
| if (!goals) continue main; | ||
| let goal = goals.terms[goals.index++]; | ||
| if (typeof goal == 'function') { | ||
| env.push(); | ||
| let newGoals = goal(env, goals, stack); | ||
| if (newGoals || newGoals === null) { | ||
| newGoals && !newGoals.terms && (newGoals = goals); | ||
| stack.push({ | ||
| command: 1 | ||
| }, { | ||
| goals: newGoals | ||
| }); | ||
| continue main; | ||
| } | ||
| --goals.index; | ||
| env.pop(); | ||
| continue main; | ||
| } | ||
| if (typeof goal == 'string') { | ||
| goal = { | ||
| name: goal | ||
| }; | ||
| } | ||
| let ruleList = rules[goal.name]; | ||
| !Array.isArray(ruleList) && (ruleList = [ruleList]); | ||
| stack.push({ | ||
| command: 2, | ||
| ruleList, | ||
| index: 0, | ||
| goals, | ||
| args: goal.args || [] | ||
| }); | ||
| } | ||
| }; | ||
| const solve = (rules, name, args, callback) => { | ||
| const env = new _unify.Env(); | ||
| env.openObjects = true; | ||
| const goals = { | ||
| terms: [{ | ||
| name, | ||
| args | ||
| }, env => (callback(env), false)], | ||
| index: 0, | ||
| next: null | ||
| }; | ||
| prove(rules, goals, env); | ||
| }; | ||
| var _default = exports.default = solve; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.default = void 0; | ||
| var _unify = _interopRequireWildcard(require("deep6/unify.js")); | ||
| function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } | ||
| function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } | ||
| let counter = 0; | ||
| const generateVariables = count => { | ||
| const t = []; | ||
| for (let i = 0; i < count; ++i) t.push(counter++); | ||
| return t.map(name => (0, _unify.variable)(Symbol(name))); | ||
| }; | ||
| const prove = async (rules, goals, env) => { | ||
| const stack = [{ | ||
| goals | ||
| }]; | ||
| main: while (stack.length) { | ||
| const frame = stack.pop(); | ||
| if (frame.command) { | ||
| if (frame.command === 1) { | ||
| env.pop(); | ||
| continue main; | ||
| } | ||
| while (frame.index < frame.ruleList.length) { | ||
| const rule = frame.ruleList[frame.index++], | ||
| vars = generateVariables(rule.length + 1), | ||
| terms = (typeof rule == 'function' ? rule : rule.goals)(...vars); | ||
| env.push(); | ||
| if ((0, _unify.default)(terms[0].args || [], frame.args, env)) { | ||
| const newGoals = { | ||
| terms, | ||
| index: 1, | ||
| next: frame.goals | ||
| }; | ||
| stack.push(frame, { | ||
| command: 1 | ||
| }, { | ||
| goals: newGoals | ||
| }); | ||
| env.bindVal(vars[vars.length - 1].name, frame); | ||
| continue main; | ||
| } | ||
| env.pop(); | ||
| } | ||
| continue main; | ||
| } | ||
| let goals = frame.goals; | ||
| while (goals && goals.index >= goals.terms.length) { | ||
| goals = goals.next; | ||
| } | ||
| if (!goals) continue main; | ||
| let goal = goals.terms[goals.index++]; | ||
| if (typeof goal == 'function') { | ||
| env.push(); | ||
| let newGoals = await goal(env, goals, stack); | ||
| if (newGoals || newGoals === null) { | ||
| newGoals && !newGoals.terms && (newGoals = goals); | ||
| stack.push({ | ||
| command: 1 | ||
| }, { | ||
| goals: newGoals | ||
| }); | ||
| continue main; | ||
| } | ||
| --goals.index; | ||
| env.pop(); | ||
| continue main; | ||
| } | ||
| if (typeof goal == 'string') { | ||
| goal = { | ||
| name: goal | ||
| }; | ||
| } | ||
| let ruleList = rules[goal.name]; | ||
| !Array.isArray(ruleList) && (ruleList = [ruleList]); | ||
| stack.push({ | ||
| command: 2, | ||
| ruleList, | ||
| index: 0, | ||
| goals, | ||
| args: goal.args || [] | ||
| }); | ||
| } | ||
| }; | ||
| const solve = async (rules, name, args, callback) => { | ||
| const env = new _unify.Env(); | ||
| env.openObjects = true; | ||
| const goals = { | ||
| terms: [{ | ||
| name, | ||
| args | ||
| }, async env => (await callback(env), false)], | ||
| index: 0, | ||
| next: null | ||
| }; | ||
| prove(rules, goals, env); | ||
| }; | ||
| var _default = exports.default = solve; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.default = void 0; | ||
| var _unify = _interopRequireWildcard(require("deep6/unify.js")); | ||
| function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } | ||
| function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } | ||
| let counter = 0; | ||
| const generateVariables = count => { | ||
| const t = []; | ||
| for (let i = 0; i < count; ++i) t.push(counter++); | ||
| return t.map(name => (0, _unify.variable)(Symbol(name))); | ||
| }; | ||
| async function* prove(rules, goals, env) { | ||
| const stack = [{ | ||
| goals | ||
| }]; | ||
| main: while (stack.length) { | ||
| const frame = stack.pop(); | ||
| if (frame.command) { | ||
| if (frame.command === 1) { | ||
| env.pop(); | ||
| continue main; | ||
| } | ||
| while (frame.index < frame.ruleList.length) { | ||
| const rule = frame.ruleList[frame.index++], | ||
| vars = generateVariables(rule.length + 1), | ||
| terms = (typeof rule == 'function' ? rule : rule.goals)(...vars); | ||
| env.push(); | ||
| if ((0, _unify.default)(terms[0].args || [], frame.args, env)) { | ||
| const newGoals = { | ||
| terms, | ||
| index: 1, | ||
| next: frame.goals | ||
| }; | ||
| stack.push(frame, { | ||
| command: 1 | ||
| }, { | ||
| goals: newGoals | ||
| }); | ||
| env.bindVal(vars[vars.length - 1].name, frame); | ||
| continue main; | ||
| } | ||
| env.pop(); | ||
| } | ||
| continue main; | ||
| } | ||
| let goals = frame.goals; | ||
| while (goals && goals.index >= goals.terms.length) { | ||
| goals = goals.next; | ||
| } | ||
| if (!goals) { | ||
| yield env; | ||
| continue main; | ||
| } | ||
| let goal = goals.terms[goals.index++]; | ||
| if (typeof goal == 'function') { | ||
| env.push(); | ||
| let newGoals = await goal(env, goals, stack); | ||
| if (newGoals || newGoals === null) { | ||
| newGoals && !newGoals.terms && (newGoals = goals); | ||
| stack.push({ | ||
| command: 1 | ||
| }, { | ||
| goals: newGoals | ||
| }); | ||
| continue main; | ||
| } | ||
| --goals.index; | ||
| env.pop(); | ||
| continue main; | ||
| } | ||
| if (typeof goal == 'string') { | ||
| goal = { | ||
| name: goal | ||
| }; | ||
| } | ||
| let ruleList = rules[goal.name]; | ||
| !Array.isArray(ruleList) && (ruleList = [ruleList]); | ||
| stack.push({ | ||
| command: 2, | ||
| ruleList, | ||
| index: 0, | ||
| goals, | ||
| args: goal.args || [] | ||
| }); | ||
| } | ||
| } | ||
| ; | ||
| async function* generate(rules, name, args) { | ||
| const env = new _unify.Env(); | ||
| env.openObjects = true; | ||
| const goals = { | ||
| terms: [{ | ||
| name, | ||
| args | ||
| }], | ||
| index: 0, | ||
| next: null | ||
| }; | ||
| yield* prove(rules, goals, env); | ||
| } | ||
| ; | ||
| var _default = exports.default = generate; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.default = void 0; | ||
| var _unify = _interopRequireWildcard(require("deep6/unify.js")); | ||
| function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } | ||
| function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } | ||
| let counter = 0; | ||
| const generateVariables = count => { | ||
| const t = []; | ||
| for (let i = 0; i < count; ++i) t.push(counter++); | ||
| return t.map(name => (0, _unify.variable)(Symbol(name))); | ||
| }; | ||
| function* prove(rules, goals, env) { | ||
| const stack = [{ | ||
| goals | ||
| }]; | ||
| main: while (stack.length) { | ||
| const frame = stack.pop(); | ||
| if (frame.command) { | ||
| if (frame.command === 1) { | ||
| env.pop(); | ||
| continue main; | ||
| } | ||
| while (frame.index < frame.ruleList.length) { | ||
| const rule = frame.ruleList[frame.index++], | ||
| vars = generateVariables(rule.length + 1), | ||
| terms = (typeof rule == 'function' ? rule : rule.goals)(...vars); | ||
| env.push(); | ||
| if ((0, _unify.default)(terms[0].args || [], frame.args, env)) { | ||
| const newGoals = { | ||
| terms, | ||
| index: 1, | ||
| next: frame.goals | ||
| }; | ||
| stack.push(frame, { | ||
| command: 1 | ||
| }, { | ||
| goals: newGoals | ||
| }); | ||
| env.bindVal(vars[vars.length - 1].name, frame); | ||
| continue main; | ||
| } | ||
| env.pop(); | ||
| } | ||
| continue main; | ||
| } | ||
| let goals = frame.goals; | ||
| while (goals && goals.index >= goals.terms.length) { | ||
| goals = goals.next; | ||
| } | ||
| if (!goals) { | ||
| yield env; | ||
| continue main; | ||
| } | ||
| let goal = goals.terms[goals.index++]; | ||
| if (typeof goal == 'function') { | ||
| env.push(); | ||
| let newGoals = goal(env, goals, stack); | ||
| if (newGoals || newGoals === null) { | ||
| newGoals && !newGoals.terms && (newGoals = goals); | ||
| stack.push({ | ||
| command: 1 | ||
| }, { | ||
| goals: newGoals | ||
| }); | ||
| continue main; | ||
| } | ||
| --goals.index; | ||
| env.pop(); | ||
| continue main; | ||
| } | ||
| if (typeof goal == 'string') { | ||
| goal = { | ||
| name: goal | ||
| }; | ||
| } | ||
| let ruleList = rules[goal.name]; | ||
| !Array.isArray(ruleList) && (ruleList = [ruleList]); | ||
| stack.push({ | ||
| command: 2, | ||
| ruleList, | ||
| index: 0, | ||
| goals, | ||
| args: goal.args || [] | ||
| }); | ||
| } | ||
| } | ||
| ; | ||
| function* generate(rules, name, args) { | ||
| const env = new _unify.Env(); | ||
| env.openObjects = true; | ||
| const goals = { | ||
| terms: [{ | ||
| name, | ||
| args | ||
| }], | ||
| index: 0, | ||
| next: null | ||
| }; | ||
| yield* prove(rules, goals, env); | ||
| } | ||
| ; | ||
| var _default = exports.default = generate; |
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
No website
QualityPackage does not have a website.
69178
2.06%2
-33.33%26
18.18%0
-100%0
-100%102
209.09%1130
-42.05%Updated