eyeling 1.34.5 → 1.34.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -90
- package/bin/eyeling.cjs +4 -56
- package/dist/browser/eyeling.browser.js +0 -1
- package/examples/context-schema-audit.n3 +1 -1
- package/eyeling.js +0 -1
- package/index.d.ts +3 -37
- package/index.js +1 -90
- package/lib/cli.js +0 -1
- package/package.json +3 -12
- package/test/packlist.test.js +0 -2
- package/test/run.js +0 -2
- package/docs/eyelang-guide.md +0 -535
- package/docs/eyelang-language-reference.md +0 -697
- package/examples/eyelang/access-control-policy.pl +0 -52
- package/examples/eyelang/ackermann.pl +0 -46
- package/examples/eyelang/age.pl +0 -28
- package/examples/eyelang/aliases-and-namespaces.pl +0 -22
- package/examples/eyelang/alignment-demo.pl +0 -44
- package/examples/eyelang/allen-interval-calculus.pl +0 -64
- package/examples/eyelang/ancestor.pl +0 -21
- package/examples/eyelang/animal.pl +0 -21
- package/examples/eyelang/annotation.pl +0 -34
- package/examples/eyelang/auroracare.pl +0 -309
- package/examples/eyelang/backward.pl +0 -12
- package/examples/eyelang/basic-monadic.pl +0 -10032
- package/examples/eyelang/bayes-diagnosis.pl +0 -108
- package/examples/eyelang/bayes-therapy.pl +0 -182
- package/examples/eyelang/beam-deflection.pl +0 -50
- package/examples/eyelang/blocks-world-planning.pl +0 -75
- package/examples/eyelang/bmi.pl +0 -232
- package/examples/eyelang/braking-safety-worlds.pl +0 -69
- package/examples/eyelang/buck-converter-design.pl +0 -78
- package/examples/eyelang/cache-performance.pl +0 -54
- package/examples/eyelang/canary-release.pl +0 -49
- package/examples/eyelang/cat-koko.pl +0 -24
- package/examples/eyelang/clinical-trial-screening.pl +0 -92
- package/examples/eyelang/combinatorics-findall-sort.pl +0 -37
- package/examples/eyelang/competitive-enzyme-kinetics.pl +0 -78
- package/examples/eyelang/complex.pl +0 -121
- package/examples/eyelang/composition-of-injective-functions-is-injective.pl +0 -50
- package/examples/eyelang/context-association.pl +0 -53
- package/examples/eyelang/context-schema-audit.pl +0 -46
- package/examples/eyelang/control-system.pl +0 -72
- package/examples/eyelang/cyclic-path.pl +0 -16
- package/examples/eyelang/d3-group.pl +0 -100
- package/examples/eyelang/dairy-energy-balance.pl +0 -65
- package/examples/eyelang/data-negotiation.pl +0 -39
- package/examples/eyelang/deep-taxonomy-10.pl +0 -115
- package/examples/eyelang/deep-taxonomy-100.pl +0 -385
- package/examples/eyelang/deep-taxonomy-1000.pl +0 -3085
- package/examples/eyelang/deep-taxonomy-10000.pl +0 -30094
- package/examples/eyelang/deep-taxonomy-100000.pl +0 -300184
- package/examples/eyelang/delfour.pl +0 -281
- package/examples/eyelang/deontic-logic.pl +0 -52
- package/examples/eyelang/derived-backward-rule.pl +0 -30
- package/examples/eyelang/derived-rule.pl +0 -27
- package/examples/eyelang/diamond-property.pl +0 -38
- package/examples/eyelang/dijkstra-findall-sort.pl +0 -44
- package/examples/eyelang/dijkstra-risk-path.pl +0 -86
- package/examples/eyelang/dijkstra.pl +0 -46
- package/examples/eyelang/dining-philosophers.pl +0 -140
- package/examples/eyelang/dog.pl +0 -25
- package/examples/eyelang/dpv-odrl-purpose-mapping.pl +0 -46
- package/examples/eyelang/drone-corridor-planner.pl +0 -51
- package/examples/eyelang/easter-computus.pl +0 -89
- package/examples/eyelang/electrical-rc-filter.pl +0 -36
- package/examples/eyelang/epidemic-policy.pl +0 -67
- package/examples/eyelang/equivalence-classes-overlap-implies-same-class.pl +0 -27
- package/examples/eyelang/eulerian-path.pl +0 -85
- package/examples/eyelang/ev-range-worlds.pl +0 -82
- package/examples/eyelang/existential-rule.pl +0 -18
- package/examples/eyelang/exoplanet-validation-worlds.pl +0 -88
- package/examples/eyelang/expression-eval.pl +0 -43
- package/examples/eyelang/family-cousins.pl +0 -65
- package/examples/eyelang/fastpow.pl +0 -53
- package/examples/eyelang/fft8-numeric.pl +0 -83
- package/examples/eyelang/fibonacci.pl +0 -53
- package/examples/eyelang/field-nitrogen-balance.pl +0 -70
- package/examples/eyelang/flandor.pl +0 -296
- package/examples/eyelang/floating-point.pl +0 -23
- package/examples/eyelang/four-color-map.pl +0 -127
- package/examples/eyelang/fundamental-theorem-arithmetic.pl +0 -113
- package/examples/eyelang/gd-step-certified.pl +0 -158
- package/examples/eyelang/gdpr-compliance.pl +0 -69
- package/examples/eyelang/good-cobbler.pl +0 -14
- package/examples/eyelang/gps.pl +0 -152
- package/examples/eyelang/graph-reachability.pl +0 -36
- package/examples/eyelang/gray-code-counter.pl +0 -48
- package/examples/eyelang/greatest-lower-bound-uniqueness.pl +0 -28
- package/examples/eyelang/group-inverse-uniqueness.pl +0 -34
- package/examples/eyelang/hamiltonian-path.pl +0 -49
- package/examples/eyelang/hamming-code.pl +0 -105
- package/examples/eyelang/hanoi.pl +0 -20
- package/examples/eyelang/heat-loss.pl +0 -51
- package/examples/eyelang/heron-theorem.pl +0 -36
- package/examples/eyelang/ideal-gas-law.pl +0 -37
- package/examples/eyelang/illegitimate-reasoning.pl +0 -88
- package/examples/eyelang/knowledge-engineering-alignment-flow.pl +0 -40
- package/examples/eyelang/law-of-cosines.pl +0 -31
- package/examples/eyelang/least-squares-regression.pl +0 -81
- package/examples/eyelang/list-collection.pl +0 -33
- package/examples/eyelang/lldm.pl +0 -78
- package/examples/eyelang/manufacturing-quality-control.pl +0 -73
- package/examples/eyelang/microgrid-dispatch.pl +0 -85
- package/examples/eyelang/monkey-bananas.pl +0 -45
- package/examples/eyelang/network-sla.pl +0 -48
- package/examples/eyelang/newton-raphson.pl +0 -49
- package/examples/eyelang/nixon-diamond.pl +0 -37
- package/examples/eyelang/observability-log-correlation.pl +0 -34
- package/examples/eyelang/odrl-dpv-fpv-trust-flow.pl +0 -43
- package/examples/eyelang/odrl-dpv-healthcare-risk-ranked.pl +0 -266
- package/examples/eyelang/odrl-dpv-risk-ranked.pl +0 -320
- package/examples/eyelang/orbital-transfer-design.pl +0 -113
- package/examples/eyelang/output/access-control-policy.pl +0 -2
- package/examples/eyelang/output/ackermann.pl +0 -12
- package/examples/eyelang/output/age.pl +0 -2
- package/examples/eyelang/output/aliases-and-namespaces.pl +0 -5
- package/examples/eyelang/output/alignment-demo.pl +0 -32
- package/examples/eyelang/output/allen-interval-calculus.pl +0 -154
- package/examples/eyelang/output/ancestor.pl +0 -6
- package/examples/eyelang/output/animal.pl +0 -4
- package/examples/eyelang/output/annotation.pl +0 -4
- package/examples/eyelang/output/auroracare.pl +0 -117
- package/examples/eyelang/output/backward.pl +0 -1
- package/examples/eyelang/output/basic-monadic.pl +0 -1518
- package/examples/eyelang/output/bayes-diagnosis.pl +0 -13
- package/examples/eyelang/output/bayes-therapy.pl +0 -23
- package/examples/eyelang/output/beam-deflection.pl +0 -5
- package/examples/eyelang/output/blocks-world-planning.pl +0 -4
- package/examples/eyelang/output/bmi.pl +0 -32
- package/examples/eyelang/output/braking-safety-worlds.pl +0 -18
- package/examples/eyelang/output/buck-converter-design.pl +0 -6
- package/examples/eyelang/output/cache-performance.pl +0 -4
- package/examples/eyelang/output/canary-release.pl +0 -5
- package/examples/eyelang/output/cat-koko.pl +0 -3
- package/examples/eyelang/output/clinical-trial-screening.pl +0 -9
- package/examples/eyelang/output/combinatorics-findall-sort.pl +0 -2
- package/examples/eyelang/output/competitive-enzyme-kinetics.pl +0 -6
- package/examples/eyelang/output/complex.pl +0 -1
- package/examples/eyelang/output/composition-of-injective-functions-is-injective.pl +0 -2
- package/examples/eyelang/output/context-association.pl +0 -3
- package/examples/eyelang/output/context-schema-audit.pl +0 -12
- package/examples/eyelang/output/control-system.pl +0 -6
- package/examples/eyelang/output/cyclic-path.pl +0 -16
- package/examples/eyelang/output/d3-group.pl +0 -2
- package/examples/eyelang/output/dairy-energy-balance.pl +0 -13
- package/examples/eyelang/output/data-negotiation.pl +0 -1
- package/examples/eyelang/output/deep-taxonomy-10.pl +0 -16
- package/examples/eyelang/output/deep-taxonomy-100.pl +0 -16
- package/examples/eyelang/output/deep-taxonomy-1000.pl +0 -16
- package/examples/eyelang/output/deep-taxonomy-10000.pl +0 -16
- package/examples/eyelang/output/deep-taxonomy-100000.pl +0 -16
- package/examples/eyelang/output/delfour.pl +0 -31
- package/examples/eyelang/output/deontic-logic.pl +0 -4
- package/examples/eyelang/output/derived-backward-rule.pl +0 -3
- package/examples/eyelang/output/derived-rule.pl +0 -2
- package/examples/eyelang/output/diamond-property.pl +0 -4
- package/examples/eyelang/output/dijkstra-findall-sort.pl +0 -2
- package/examples/eyelang/output/dijkstra-risk-path.pl +0 -29
- package/examples/eyelang/output/dijkstra.pl +0 -16
- package/examples/eyelang/output/dining-philosophers.pl +0 -350
- package/examples/eyelang/output/dog.pl +0 -1
- package/examples/eyelang/output/dpv-odrl-purpose-mapping.pl +0 -18
- package/examples/eyelang/output/drone-corridor-planner.pl +0 -17
- package/examples/eyelang/output/easter-computus.pl +0 -30
- package/examples/eyelang/output/electrical-rc-filter.pl +0 -3
- package/examples/eyelang/output/epidemic-policy.pl +0 -14
- package/examples/eyelang/output/equivalence-classes-overlap-implies-same-class.pl +0 -18
- package/examples/eyelang/output/eulerian-path.pl +0 -3
- package/examples/eyelang/output/ev-range-worlds.pl +0 -19
- package/examples/eyelang/output/existential-rule.pl +0 -2
- package/examples/eyelang/output/exoplanet-validation-worlds.pl +0 -22
- package/examples/eyelang/output/expression-eval.pl +0 -1
- package/examples/eyelang/output/family-cousins.pl +0 -28
- package/examples/eyelang/output/fastpow.pl +0 -6
- package/examples/eyelang/output/fft8-numeric.pl +0 -4
- package/examples/eyelang/output/fibonacci.pl +0 -6
- package/examples/eyelang/output/field-nitrogen-balance.pl +0 -21
- package/examples/eyelang/output/flandor.pl +0 -43
- package/examples/eyelang/output/floating-point.pl +0 -9
- package/examples/eyelang/output/four-color-map.pl +0 -3
- package/examples/eyelang/output/fundamental-theorem-arithmetic.pl +0 -9
- package/examples/eyelang/output/gd-step-certified.pl +0 -79
- package/examples/eyelang/output/gdpr-compliance.pl +0 -6
- package/examples/eyelang/output/good-cobbler.pl +0 -1
- package/examples/eyelang/output/gps.pl +0 -21
- package/examples/eyelang/output/graph-reachability.pl +0 -3
- package/examples/eyelang/output/gray-code-counter.pl +0 -1
- package/examples/eyelang/output/greatest-lower-bound-uniqueness.pl +0 -2
- package/examples/eyelang/output/group-inverse-uniqueness.pl +0 -2
- package/examples/eyelang/output/hamiltonian-path.pl +0 -121
- package/examples/eyelang/output/hamming-code.pl +0 -6
- package/examples/eyelang/output/hanoi.pl +0 -1
- package/examples/eyelang/output/heat-loss.pl +0 -5
- package/examples/eyelang/output/heron-theorem.pl +0 -4
- package/examples/eyelang/output/ideal-gas-law.pl +0 -3
- package/examples/eyelang/output/illegitimate-reasoning.pl +0 -15
- package/examples/eyelang/output/knowledge-engineering-alignment-flow.pl +0 -17
- package/examples/eyelang/output/law-of-cosines.pl +0 -3
- package/examples/eyelang/output/least-squares-regression.pl +0 -5
- package/examples/eyelang/output/list-collection.pl +0 -3
- package/examples/eyelang/output/lldm.pl +0 -6
- package/examples/eyelang/output/manufacturing-quality-control.pl +0 -6
- package/examples/eyelang/output/microgrid-dispatch.pl +0 -6
- package/examples/eyelang/output/monkey-bananas.pl +0 -5
- package/examples/eyelang/output/network-sla.pl +0 -4
- package/examples/eyelang/output/newton-raphson.pl +0 -3
- package/examples/eyelang/output/nixon-diamond.pl +0 -5
- package/examples/eyelang/output/observability-log-correlation.pl +0 -28
- package/examples/eyelang/output/odrl-dpv-fpv-trust-flow.pl +0 -9
- package/examples/eyelang/output/odrl-dpv-healthcare-risk-ranked.pl +0 -42
- package/examples/eyelang/output/odrl-dpv-risk-ranked.pl +0 -120
- package/examples/eyelang/output/orbital-transfer-design.pl +0 -7
- package/examples/eyelang/output/path-discovery.pl +0 -3
- package/examples/eyelang/output/peano-arithmetic.pl +0 -3
- package/examples/eyelang/output/peasant.pl +0 -10
- package/examples/eyelang/output/pendulum-period.pl +0 -4
- package/examples/eyelang/output/polynomial.pl +0 -14
- package/examples/eyelang/output/proof-contrapositive.pl +0 -3
- package/examples/eyelang/output/quadratic-formula.pl +0 -6
- package/examples/eyelang/output/radioactive-decay.pl +0 -5
- package/examples/eyelang/output/reusable-builtins.pl +0 -5
- package/examples/eyelang/output/riemann-hypothesis.pl +0 -12
- package/examples/eyelang/output/security-incident-correlation.pl +0 -3
- package/examples/eyelang/output/service-impact.pl +0 -11
- package/examples/eyelang/output/sieve.pl +0 -1
- package/examples/eyelang/output/skolem-functions.pl +0 -16
- package/examples/eyelang/output/socket-age.pl +0 -1
- package/examples/eyelang/output/socket-family.pl +0 -3
- package/examples/eyelang/output/socrates.pl +0 -2
- package/examples/eyelang/output/statistics-summary.pl +0 -4
- package/examples/eyelang/output/superdense-coding.pl +0 -6
- package/examples/eyelang/output/term-tools.pl +0 -6
- package/examples/eyelang/output/trust-flow-provenance-threshold.pl +0 -6
- package/examples/eyelang/output/turing.pl +0 -12
- package/examples/eyelang/output/vector-similarity.pl +0 -4
- package/examples/eyelang/output/vulnerability-impact.pl +0 -20
- package/examples/eyelang/output/witch.pl +0 -7
- package/examples/eyelang/output/wolf-goat-cabbage.pl +0 -3
- package/examples/eyelang/output/zebra.pl +0 -3
- package/examples/eyelang/path-discovery.pl +0 -45013
- package/examples/eyelang/peano-arithmetic.pl +0 -31
- package/examples/eyelang/peasant.pl +0 -30
- package/examples/eyelang/pendulum-period.pl +0 -50
- package/examples/eyelang/polynomial.pl +0 -124
- package/examples/eyelang/proof/age.pl +0 -71
- package/examples/eyelang/proof/aliases-and-namespaces.pl +0 -78
- package/examples/eyelang/proof/ancestor.pl +0 -140
- package/examples/eyelang/proof/animal.pl +0 -68
- package/examples/eyelang/proof/annotation.pl +0 -80
- package/examples/eyelang/proof/backward.pl +0 -22
- package/examples/eyelang/proof/cat-koko.pl +0 -86
- package/examples/eyelang/proof/data-negotiation.pl +0 -76
- package/examples/eyelang/proof/derived-rule.pl +0 -43
- package/examples/eyelang/proof/dog.pl +0 -31
- package/examples/eyelang/proof/electrical-rc-filter.pl +0 -105
- package/examples/eyelang/proof/existential-rule.pl +0 -40
- package/examples/eyelang/proof/floating-point.pl +0 -160
- package/examples/eyelang/proof/good-cobbler.pl +0 -16
- package/examples/eyelang/proof/group-inverse-uniqueness.pl +0 -84
- package/examples/eyelang/proof/list-collection.pl +0 -52
- package/examples/eyelang/proof/proof-contrapositive.pl +0 -78
- package/examples/eyelang/proof/socket-age.pl +0 -32
- package/examples/eyelang/proof/socket-family.pl +0 -59
- package/examples/eyelang/proof/socrates.pl +0 -38
- package/examples/eyelang/proof-contrapositive.pl +0 -27
- package/examples/eyelang/quadratic-formula.pl +0 -54
- package/examples/eyelang/radioactive-decay.pl +0 -56
- package/examples/eyelang/reusable-builtins.pl +0 -32
- package/examples/eyelang/riemann-hypothesis.pl +0 -110
- package/examples/eyelang/security-incident-correlation.pl +0 -69
- package/examples/eyelang/service-impact.pl +0 -41
- package/examples/eyelang/sieve.pl +0 -20
- package/examples/eyelang/skolem-functions.pl +0 -52
- package/examples/eyelang/socket-age.pl +0 -39
- package/examples/eyelang/socket-family.pl +0 -28
- package/examples/eyelang/socrates.pl +0 -19
- package/examples/eyelang/statistics-summary.pl +0 -54
- package/examples/eyelang/superdense-coding.pl +0 -84
- package/examples/eyelang/term-tools.pl +0 -23
- package/examples/eyelang/trust-flow-provenance-threshold.pl +0 -40
- package/examples/eyelang/turing.pl +0 -67
- package/examples/eyelang/vector-similarity.pl +0 -56
- package/examples/eyelang/vulnerability-impact.pl +0 -70
- package/examples/eyelang/witch.pl +0 -38
- package/examples/eyelang/wolf-goat-cabbage.pl +0 -56
- package/examples/eyelang/zebra.pl +0 -44
- package/eyelang.d.ts +0 -80
- package/lib/eyelang/bin.js +0 -7
- package/lib/eyelang/builtins/aggregation.js +0 -81
- package/lib/eyelang/builtins/arithmetic.js +0 -208
- package/lib/eyelang/builtins/context.js +0 -42
- package/lib/eyelang/builtins/control.js +0 -34
- package/lib/eyelang/builtins/core.js +0 -78
- package/lib/eyelang/builtins/lists.js +0 -283
- package/lib/eyelang/builtins/registry.js +0 -48
- package/lib/eyelang/builtins/strings.js +0 -234
- package/lib/eyelang/builtins/terms.js +0 -66
- package/lib/eyelang/cli.js +0 -180
- package/lib/eyelang/explain.js +0 -324
- package/lib/eyelang/hash.js +0 -294
- package/lib/eyelang/index.js +0 -47
- package/lib/eyelang/package.json +0 -3
- package/lib/eyelang/parser.js +0 -428
- package/lib/eyelang/program.js +0 -237
- package/lib/eyelang/solver.js +0 -237
- package/lib/eyelang/term.js +0 -328
package/lib/eyelang/solver.js
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
// Depth-first eyelang solver with builtin dispatch, memoization, and guarded recursion handling.
|
|
2
|
-
// Most semantic decisions still flow through unification; optimizations only select candidates earlier.
|
|
3
|
-
import { COMPOUND, Env, flattenConjunction, freshTerm, termToString, unify, variantTerms } from './term.js';
|
|
4
|
-
import { createDefaultRegistry } from './builtins/registry.js';
|
|
5
|
-
import { selectClauseCandidates } from './program.js';
|
|
6
|
-
|
|
7
|
-
let freshCounter = 0;
|
|
8
|
-
|
|
9
|
-
export function nextFreshId() {
|
|
10
|
-
return ++freshCounter;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class Solver {
|
|
14
|
-
constructor(program, options = {}) {
|
|
15
|
-
this.program = program;
|
|
16
|
-
this.registry = options.registry ?? createDefaultRegistry();
|
|
17
|
-
this.maxDepth = options.maxDepth ?? 100000;
|
|
18
|
-
this.solutionLimit = options.solutionLimit ?? 10000000;
|
|
19
|
-
this.solutionsSeen = 0;
|
|
20
|
-
this.active = [];
|
|
21
|
-
this.memo = new Map();
|
|
22
|
-
this.stats = {
|
|
23
|
-
completed_goal_lists: 0,
|
|
24
|
-
solve_goals_calls: 0,
|
|
25
|
-
solve_one_goal_calls: 0,
|
|
26
|
-
unify_calls: 0,
|
|
27
|
-
max_depth: 0,
|
|
28
|
-
max_goal_count: 0,
|
|
29
|
-
deterministic_builtin_successes: 0,
|
|
30
|
-
deterministic_builtin_failures: 0,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
cloneForInnerGoal(solutionLimit = this.solutionLimit) {
|
|
35
|
-
const solver = new Solver(this.program, { registry: this.registry, maxDepth: this.maxDepth, solutionLimit });
|
|
36
|
-
solver.memo = this.memo;
|
|
37
|
-
return solver;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
*solve(goals, env = new Env(), depth = 0) {
|
|
41
|
-
this.stats.solve_goals_calls++;
|
|
42
|
-
this.stats.max_depth = Math.max(this.stats.max_depth, depth);
|
|
43
|
-
this.stats.max_goal_count = Math.max(this.stats.max_goal_count, goals.length);
|
|
44
|
-
if (depth > this.maxDepth || this.solutionsSeen >= this.solutionLimit) return;
|
|
45
|
-
if (!Array.isArray(goals)) goals = [goals];
|
|
46
|
-
|
|
47
|
-
if (goals.length === 0) {
|
|
48
|
-
this.solutionsSeen++;
|
|
49
|
-
this.stats.completed_goal_lists++;
|
|
50
|
-
yield env;
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// eyelang normally solves left-to-right, but ready deterministic builtins can
|
|
55
|
-
// be run early as pure filters. This gives large pruning wins without
|
|
56
|
-
// reordering user predicates or nondeterministic generators.
|
|
57
|
-
const selectedIndex = selectReadyDeterministicBuiltin(goals, env, this.registry);
|
|
58
|
-
const goal = goals[selectedIndex];
|
|
59
|
-
const rest = selectedIndex === 0 ? goals.slice(1) : [...goals.slice(0, selectedIndex), ...goals.slice(selectedIndex + 1)];
|
|
60
|
-
if (goal.type === COMPOUND && goal.name === ',' && goal.arity === 2) {
|
|
61
|
-
yield* this.solve([...flattenConjunction(goal), ...rest], env, depth + 1);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const def = goal.type === COMPOUND ? this.registry.get(goal.name, goal.arity) : null;
|
|
66
|
-
if (def && builtinIsReadyOrAuthoritative(def, this, goal, env)) {
|
|
67
|
-
let produced = false;
|
|
68
|
-
for (const next of def.handler({ solver: this, goal, env })) {
|
|
69
|
-
produced = true;
|
|
70
|
-
yield* this.solve(rest, next, depth + 1);
|
|
71
|
-
if (this.solutionsSeen >= this.solutionLimit) return;
|
|
72
|
-
}
|
|
73
|
-
if (def.deterministic) {
|
|
74
|
-
if (produced) this.stats.deterministic_builtin_successes++;
|
|
75
|
-
else this.stats.deterministic_builtin_failures++;
|
|
76
|
-
}
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
yield* this.solveUserGoal(goal, rest, env, depth);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
activeVariant(goal, env) {
|
|
84
|
-
return this.active.some((entry) => variantTerms(goal, env, entry.goal, entry.env));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
*solveUserGoal(goal, rest, env, depth) {
|
|
88
|
-
this.stats.solve_one_goal_calls++;
|
|
89
|
-
if (depth > this.maxDepth || this.solutionsSeen >= this.solutionLimit) return;
|
|
90
|
-
if (goal.type !== COMPOUND) return;
|
|
91
|
-
const group = this.program.findGroup(goal.name, goal.arity);
|
|
92
|
-
if (!group) return;
|
|
93
|
-
if (group.memoized) {
|
|
94
|
-
yield* this.solveMemoizedGoal(group, goal, rest, env, depth);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
yield* this.solveUserGoalUncached(group, goal, rest, env, depth);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
*solveMemoizedGoal(group, goal, rest, env, depth) {
|
|
101
|
-
// Memoization only pays off when at least one argument is bound. A fully
|
|
102
|
-
// open call would require storing the entire relation and can mask useful
|
|
103
|
-
// goal-ordering behavior, so it falls back to the normal path.
|
|
104
|
-
const key = memoKey(goal, env);
|
|
105
|
-
if (!key.hasBound) {
|
|
106
|
-
yield* this.solveUserGoalUncached(group, goal, rest, env, depth);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
const mapKey = `${goal.name}/${goal.arity}:${key.text}`;
|
|
110
|
-
let entry = this.memo.get(mapKey);
|
|
111
|
-
if (!entry) {
|
|
112
|
-
entry = { computing: false, complete: false, answers: [] };
|
|
113
|
-
this.memo.set(mapKey, entry);
|
|
114
|
-
}
|
|
115
|
-
if (!entry.complete && !entry.computing) {
|
|
116
|
-
entry.computing = true;
|
|
117
|
-
const collector = this.cloneForInnerGoal();
|
|
118
|
-
for (const answerEnv of collector.solveUserGoalUncached(group, goal, [], env.clone(), depth)) {
|
|
119
|
-
entry.answers.push(goal.args.map((arg) => importResolved(arg, answerEnv)));
|
|
120
|
-
}
|
|
121
|
-
entry.computing = false;
|
|
122
|
-
entry.complete = true;
|
|
123
|
-
}
|
|
124
|
-
if (!entry.complete && entry.computing) {
|
|
125
|
-
yield* this.solveUserGoalUncached(group, goal, rest, env, depth);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
for (const answerArgs of entry.answers) {
|
|
129
|
-
const next = env.clone();
|
|
130
|
-
let ok = true;
|
|
131
|
-
for (let i = 0; i < goal.arity; i++) {
|
|
132
|
-
this.stats.unify_calls++;
|
|
133
|
-
if (!unify(goal.args[i], answerArgs[i], next)) { ok = false; break; }
|
|
134
|
-
}
|
|
135
|
-
if (ok) yield* this.solve(rest, next, depth + 1);
|
|
136
|
-
if (this.solutionsSeen >= this.solutionLimit) return;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
*solveUserGoalUncached(group, goal, rest, env, depth) {
|
|
141
|
-
if (this.activeVariant(goal, env)) return;
|
|
142
|
-
// Program indexes provide candidate clauses, but every candidate is still
|
|
143
|
-
// freshened and unified below. The index is a performance hint, not a
|
|
144
|
-
// semantic shortcut.
|
|
145
|
-
const candidates = selectClauseCandidates(group, goal, env);
|
|
146
|
-
for (const pass of [candidates.primary, candidates.fallback]) {
|
|
147
|
-
for (const clause of pass) {
|
|
148
|
-
if (headCannotMatch(goal, clause.head, env)) continue;
|
|
149
|
-
const id = nextFreshId();
|
|
150
|
-
const freshHead = freshTerm(clause.head, id);
|
|
151
|
-
const freshBody = clause.body.map((term) => freshTerm(term, id));
|
|
152
|
-
const next = env.clone();
|
|
153
|
-
this.stats.unify_calls++;
|
|
154
|
-
if (!unify(goal, freshHead, next)) continue;
|
|
155
|
-
if (freshBody.length === 0) {
|
|
156
|
-
yield* this.solve(rest, next, depth + 1);
|
|
157
|
-
} else {
|
|
158
|
-
yield* this.solveRuleBodyThenRest(goal, env, freshBody, rest, next, depth);
|
|
159
|
-
}
|
|
160
|
-
if (this.solutionsSeen >= this.solutionLimit) return;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
*solveRuleBodyThenRest(goal, goalEnv, body, rest, env, depth) {
|
|
165
|
-
// Match the C engine's active-call lifetime: the active guard protects
|
|
166
|
-
// expansion of the current rule body, but it must be released before
|
|
167
|
-
// the caller's remaining goals are solved. Keeping the goal active
|
|
168
|
-
// through rest goals over-prunes valid transitive/recursive derivations.
|
|
169
|
-
this.active.push({ goal, env: goalEnv });
|
|
170
|
-
for (const bodyEnv of this.solve(body, env, depth + 1)) {
|
|
171
|
-
if (this.solutionsSeen > 0) this.solutionsSeen--;
|
|
172
|
-
this.active.pop();
|
|
173
|
-
yield* this.solve(rest, bodyEnv, depth + 1);
|
|
174
|
-
this.active.push({ goal, env: goalEnv });
|
|
175
|
-
if (this.solutionsSeen >= this.solutionLimit) break;
|
|
176
|
-
}
|
|
177
|
-
this.active.pop();
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
function builtinIsReadyOrAuthoritative(def, solver, goal, env) {
|
|
184
|
-
if (typeof def.shouldUse === 'function' && !def.shouldUse({ solver, goal, env })) return false;
|
|
185
|
-
if (typeof def.ready !== 'function') return true;
|
|
186
|
-
if (def.ready(goal, env)) return true;
|
|
187
|
-
return !def.fallbackWhenNotReady;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function selectReadyDeterministicBuiltin(goals, env, registry) {
|
|
191
|
-
for (let i = 0; i < goals.length; i++) {
|
|
192
|
-
const goal = goals[i];
|
|
193
|
-
if (goal.type !== COMPOUND) continue;
|
|
194
|
-
const def = registry.get(goal.name, goal.arity);
|
|
195
|
-
if (!def?.deterministic || typeof def.ready !== 'function') continue;
|
|
196
|
-
if (typeof def.shouldUse === 'function') continue;
|
|
197
|
-
if (def.ready(goal, env)) return i;
|
|
198
|
-
}
|
|
199
|
-
return 0;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function headCannotMatch(goal, head, env) {
|
|
203
|
-
if (goal.type !== COMPOUND || head.type !== COMPOUND) return false;
|
|
204
|
-
if (goal.name !== head.name || goal.arity !== head.arity) return true;
|
|
205
|
-
for (let i = 0; i < goal.arity; i++) {
|
|
206
|
-
const a = goal.args[i];
|
|
207
|
-
const b = head.args[i];
|
|
208
|
-
// Keep this only as a cheap scalar rejection. unify() remains authoritative.
|
|
209
|
-
const da = derefForLocal(a, env);
|
|
210
|
-
if (da.args?.length === 0 && ['atom', 'string', 'number'].includes(da.type) && ['atom', 'string', 'number'].includes(b.type) && da.name !== b.name) return true;
|
|
211
|
-
}
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function derefForLocal(term, env) {
|
|
216
|
-
let current = term;
|
|
217
|
-
while (current.type === 'var' && env.has(current.name)) current = env.get(current.name);
|
|
218
|
-
return current;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function memoKey(goal, env) {
|
|
222
|
-
let hasBound = false;
|
|
223
|
-
const parts = goal.args.map((arg) => {
|
|
224
|
-
const value = derefForLocal(arg, env);
|
|
225
|
-
if (value.type !== 'var') hasBound = true;
|
|
226
|
-
return value.type === 'var' ? '_' : termToString(value, env, true);
|
|
227
|
-
});
|
|
228
|
-
return { hasBound, text: parts.join('|') };
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function importResolved(term, env) {
|
|
232
|
-
const { copyResolved } = termModuleCache;
|
|
233
|
-
return copyResolved(term, env);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Avoid circular import surprises in older Node loaders.
|
|
237
|
-
import * as termModuleCache from './term.js';
|
package/lib/eyelang/term.js
DELETED
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
// Term model, environments, unification, readback, and ordering helpers.
|
|
2
|
-
// This file is intentionally dependency-free because nearly every other module imports it.
|
|
3
|
-
export const VAR = 'var';
|
|
4
|
-
export const ATOM = 'atom';
|
|
5
|
-
export const STRING = 'string';
|
|
6
|
-
export const NUMBER = 'number';
|
|
7
|
-
export const COMPOUND = 'compound';
|
|
8
|
-
|
|
9
|
-
export class Term {
|
|
10
|
-
constructor(type, name, args = []) {
|
|
11
|
-
this.type = type;
|
|
12
|
-
this.name = String(name ?? '');
|
|
13
|
-
this.args = args;
|
|
14
|
-
}
|
|
15
|
-
get arity() {
|
|
16
|
-
return this.args.length;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const variable = (name) => new Term(VAR, name);
|
|
21
|
-
export const atom = (name) => new Term(ATOM, name);
|
|
22
|
-
export const stringTerm = (value) => new Term(STRING, value);
|
|
23
|
-
export const numberTerm = (value) => new Term(NUMBER, value);
|
|
24
|
-
export const compound = (name, args = []) => new Term(COMPOUND, name, args);
|
|
25
|
-
export const emptyList = () => atom('[]');
|
|
26
|
-
export const cons = (head, tail) => compound('.', [head, tail]);
|
|
27
|
-
|
|
28
|
-
export class Env {
|
|
29
|
-
constructor(bindings) {
|
|
30
|
-
this.bindings = bindings ? new Map(bindings) : new Map();
|
|
31
|
-
}
|
|
32
|
-
clone() {
|
|
33
|
-
return new Env(this.bindings);
|
|
34
|
-
}
|
|
35
|
-
has(name) {
|
|
36
|
-
return this.bindings.has(name);
|
|
37
|
-
}
|
|
38
|
-
get(name) {
|
|
39
|
-
return this.bindings.get(name);
|
|
40
|
-
}
|
|
41
|
-
bind(name, term) {
|
|
42
|
-
this.bindings.set(name, term);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function deref(term, env) {
|
|
47
|
-
// Follow variable bindings until a non-variable term is reached. The seen set
|
|
48
|
-
// protects readback from accidental cycles in partially constructed terms.
|
|
49
|
-
let current = term;
|
|
50
|
-
const seen = new Set();
|
|
51
|
-
while (current?.type === VAR && env?.has(current.name)) {
|
|
52
|
-
if (seen.has(current.name)) break;
|
|
53
|
-
seen.add(current.name);
|
|
54
|
-
current = env.get(current.name);
|
|
55
|
-
}
|
|
56
|
-
return current;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function isScalar(term) {
|
|
60
|
-
return term && (term.type === ATOM || term.type === STRING || term.type === NUMBER);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function isEmptyList(term) {
|
|
64
|
-
return term?.type === ATOM && term.name === '[]';
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function isCons(term) {
|
|
68
|
-
return term?.type === COMPOUND && term.name === '.' && term.arity === 2;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function isConjunction(term) {
|
|
72
|
-
return term?.type === COMPOUND && term.name === ',' && term.arity === 2;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function unify(left, right, env) {
|
|
76
|
-
// Iterative unification avoids deep JavaScript recursion on long lists or
|
|
77
|
-
// deeply nested compounds. Bindings are written into the supplied Env.
|
|
78
|
-
const stack = [[left, right]];
|
|
79
|
-
while (stack.length) {
|
|
80
|
-
let [a, b] = stack.pop();
|
|
81
|
-
a = deref(a, env);
|
|
82
|
-
b = deref(b, env);
|
|
83
|
-
|
|
84
|
-
if (a.type === VAR && b.type === VAR && a.name === b.name) continue;
|
|
85
|
-
if (a.type === VAR) {
|
|
86
|
-
env.bind(a.name, b);
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
if (b.type === VAR) {
|
|
90
|
-
env.bind(b.name, a);
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (a.type !== b.type) {
|
|
95
|
-
if (isScalar(a) && isScalar(b) && a.name === b.name) continue;
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (isScalar(a)) {
|
|
100
|
-
if (a.name !== b.name) return false;
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (a.type === COMPOUND) {
|
|
105
|
-
if (a.name !== b.name || a.arity !== b.arity) return false;
|
|
106
|
-
for (let i = a.arity - 1; i >= 0; i--) stack.push([a.args[i], b.args[i]]);
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export function cloneTerm(term) {
|
|
116
|
-
return new Term(term.type, term.name, term.args.map(cloneTerm));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export function freshTerm(term, suffix) {
|
|
120
|
-
if (term.type === VAR) return variable(`${term.name}#${suffix}`);
|
|
121
|
-
return new Term(term.type, term.name, term.args.map((arg) => freshTerm(arg, suffix)));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function copyResolved(term, env) {
|
|
125
|
-
const resolved = deref(term, env);
|
|
126
|
-
if (resolved.type === VAR) return variable(resolved.name);
|
|
127
|
-
return new Term(resolved.type, resolved.name, resolved.args.map((arg) => copyResolved(arg, env)));
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function termIsGround(term, env = new Env()) {
|
|
131
|
-
const resolved = deref(term, env);
|
|
132
|
-
if (resolved.type === VAR) return false;
|
|
133
|
-
return resolved.args.every((arg) => termIsGround(arg, env));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const graphicAtomChars = new Set('#$&*+-/<=>?@^~\\'.split(''));
|
|
137
|
-
|
|
138
|
-
function atomNeedsQuotes(name) {
|
|
139
|
-
if (!name) return true;
|
|
140
|
-
if (name === '[]') return false;
|
|
141
|
-
if (/^[a-z][A-Za-z0-9_]*$/.test(name)) return false;
|
|
142
|
-
for (const ch of name) if (!graphicAtomChars.has(ch)) return true;
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function quoteAtom(name) {
|
|
147
|
-
let out = "'";
|
|
148
|
-
for (const ch of name) {
|
|
149
|
-
if (ch === "'") out += "''";
|
|
150
|
-
else if (ch === '\\') out += '\\\\';
|
|
151
|
-
else if (ch === '\n') out += '\\n';
|
|
152
|
-
else if (ch === '\t') out += '\\t';
|
|
153
|
-
else out += ch;
|
|
154
|
-
}
|
|
155
|
-
return out + "'";
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function writeAtom(name) {
|
|
159
|
-
return atomNeedsQuotes(name) ? quoteAtom(name) : name;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function writeString(value, quoteStrings) {
|
|
163
|
-
if (!quoteStrings) return value;
|
|
164
|
-
let out = '"';
|
|
165
|
-
for (const ch of value) {
|
|
166
|
-
if (ch === '"' || ch === '\\') out += `\\${ch}`;
|
|
167
|
-
else if (ch === '\n') out += '\\n';
|
|
168
|
-
else out += ch;
|
|
169
|
-
}
|
|
170
|
-
return out + '"';
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function writeList(term, env) {
|
|
174
|
-
const parts = [];
|
|
175
|
-
let cursor = term;
|
|
176
|
-
while (true) {
|
|
177
|
-
cursor = deref(cursor, env);
|
|
178
|
-
if (isEmptyList(cursor)) return `[${parts.join(', ')}]`;
|
|
179
|
-
if (!isCons(cursor)) {
|
|
180
|
-
if (parts.length) return `[${parts.join(', ')} | ${termToString(cursor, env, true)}]`;
|
|
181
|
-
return `[${termToString(cursor, env, true)}]`;
|
|
182
|
-
}
|
|
183
|
-
parts.push(termToString(cursor.args[0], env, true));
|
|
184
|
-
cursor = cursor.args[1];
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export function termToString(term, env = new Env(), quoteStrings = true) {
|
|
189
|
-
const resolved = deref(term, env);
|
|
190
|
-
if (resolved.type === VAR) return resolved.name;
|
|
191
|
-
if (isCons(resolved)) return writeList(resolved, env);
|
|
192
|
-
if (resolved.type === STRING) return writeString(resolved.name, quoteStrings);
|
|
193
|
-
if (resolved.type === ATOM) return writeAtom(resolved.name);
|
|
194
|
-
if (resolved.type === NUMBER) return resolved.name;
|
|
195
|
-
if (isConjunction(resolved)) {
|
|
196
|
-
const parts = [];
|
|
197
|
-
let cursor = resolved;
|
|
198
|
-
while (true) {
|
|
199
|
-
cursor = deref(cursor, env);
|
|
200
|
-
if (isConjunction(cursor)) {
|
|
201
|
-
parts.push(termToString(cursor.args[0], env, true));
|
|
202
|
-
cursor = cursor.args[1];
|
|
203
|
-
} else {
|
|
204
|
-
parts.push(termToString(cursor, env, true));
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return `(${parts.join(', ')})`;
|
|
209
|
-
}
|
|
210
|
-
return `${writeAtom(resolved.name)}(${resolved.args.map((arg) => termToString(arg, env, true)).join(', ')})`;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export function lexicalValue(term, env) {
|
|
214
|
-
const resolved = deref(term, env);
|
|
215
|
-
if (resolved.type === VAR) return null;
|
|
216
|
-
if (resolved.type === ATOM || resolved.type === STRING || resolved.type === NUMBER) return resolved.name;
|
|
217
|
-
return termToString(resolved, env, true);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export function properListItems(list, env) {
|
|
221
|
-
const items = [];
|
|
222
|
-
let cursor = deref(list, env);
|
|
223
|
-
while (isCons(cursor)) {
|
|
224
|
-
items.push(cursor.args[0]);
|
|
225
|
-
cursor = deref(cursor.args[1], env);
|
|
226
|
-
}
|
|
227
|
-
if (!isEmptyList(cursor)) return null;
|
|
228
|
-
return items;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export function listFromItems(items, start = 0, end = items.length, tail = emptyList()) {
|
|
232
|
-
let result = tail;
|
|
233
|
-
for (let i = end - 1; i >= start; i--) result = cons(items[i], result);
|
|
234
|
-
return result;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
export function flattenConjunction(goal) {
|
|
238
|
-
const out = [];
|
|
239
|
-
const stack = [goal];
|
|
240
|
-
while (stack.length) {
|
|
241
|
-
const current = stack.pop();
|
|
242
|
-
if (isConjunction(current)) {
|
|
243
|
-
stack.push(current.args[1], current.args[0]);
|
|
244
|
-
} else {
|
|
245
|
-
out.push(current);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return out;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
export function termSignature(term) {
|
|
252
|
-
return term?.type === COMPOUND ? `${term.name}/${term.arity}` : null;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
export function variantTerms(left, leftEnv, right, rightEnv, pairs = new Map(), reverse = new Map()) {
|
|
256
|
-
left = deref(left, leftEnv);
|
|
257
|
-
right = deref(right, rightEnv);
|
|
258
|
-
if (left.type === VAR || right.type === VAR) {
|
|
259
|
-
if (left.type !== VAR || right.type !== VAR) return false;
|
|
260
|
-
if (pairs.has(left.name) || reverse.has(right.name)) {
|
|
261
|
-
return pairs.get(left.name) === right.name && reverse.get(right.name) === left.name;
|
|
262
|
-
}
|
|
263
|
-
pairs.set(left.name, right.name);
|
|
264
|
-
reverse.set(right.name, left.name);
|
|
265
|
-
return true;
|
|
266
|
-
}
|
|
267
|
-
if (left.type !== right.type || left.name !== right.name || left.arity !== right.arity) return false;
|
|
268
|
-
for (let i = 0; i < left.arity; i++) {
|
|
269
|
-
if (!variantTerms(left.args[i], leftEnv, right.args[i], rightEnv, pairs, reverse)) return false;
|
|
270
|
-
}
|
|
271
|
-
return true;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export function compareTerms(left, right) {
|
|
275
|
-
const rank = (term) => ({ [VAR]: 0, [NUMBER]: 1, [ATOM]: 2, [STRING]: 3, [COMPOUND]: 4 })[term.type];
|
|
276
|
-
left = deref(left, new Env());
|
|
277
|
-
right = deref(right, new Env());
|
|
278
|
-
const lr = rank(left);
|
|
279
|
-
const rr = rank(right);
|
|
280
|
-
if (lr !== rr) return lr < rr ? -1 : 1;
|
|
281
|
-
if (left.type === NUMBER) return compareNumberText(left.name, right.name);
|
|
282
|
-
if (left.type === VAR || left.type === ATOM || left.type === STRING) return left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
|
|
283
|
-
if (left.name !== right.name) return left.name < right.name ? -1 : 1;
|
|
284
|
-
if (left.arity !== right.arity) return left.arity < right.arity ? -1 : 1;
|
|
285
|
-
for (let i = 0; i < left.arity; i++) {
|
|
286
|
-
const cmp = compareTerms(left.args[i], right.args[i]);
|
|
287
|
-
if (cmp) return cmp;
|
|
288
|
-
}
|
|
289
|
-
return 0;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
export function isDecimalInteger(text) {
|
|
293
|
-
return /^-?\d+$/.test(text ?? '');
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export function compareIntegerText(left, right) {
|
|
297
|
-
const a = BigInt(left);
|
|
298
|
-
const b = BigInt(right);
|
|
299
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export function parseFiniteNumber(text) {
|
|
303
|
-
if (text == null || text === '') return null;
|
|
304
|
-
if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(text)) return null;
|
|
305
|
-
const n = Number(text);
|
|
306
|
-
return Number.isFinite(n) ? n : null;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
export function numberTextFromDouble(value) {
|
|
310
|
-
if (!Number.isFinite(value)) return null;
|
|
311
|
-
if (Object.is(value, -0)) value = 0;
|
|
312
|
-
let text = Number(value).toPrecision(17);
|
|
313
|
-
if (text.includes('e') || text.includes('E')) {
|
|
314
|
-
text = text.replace(/(\.\d*?[1-9])0+(e[+-]?\d+)$/i, '$1$2').replace(/\.0+(e[+-]?\d+)$/i, '$1');
|
|
315
|
-
} else if (text.includes('.')) {
|
|
316
|
-
text = text.replace(/0+$/, '').replace(/\.$/, '');
|
|
317
|
-
}
|
|
318
|
-
if (!/[.eE]/.test(text)) text += '.0';
|
|
319
|
-
return text;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
export function compareNumberText(left, right) {
|
|
323
|
-
if (isDecimalInteger(left) && isDecimalInteger(right)) return compareIntegerText(left, right);
|
|
324
|
-
const a = parseFiniteNumber(left);
|
|
325
|
-
const b = parseFiniteNumber(right);
|
|
326
|
-
if (a != null && b != null) return a < b ? -1 : a > b ? 1 : 0;
|
|
327
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
328
|
-
}
|