eyeling 1.19.4 → 1.19.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.
Files changed (40) hide show
  1. package/HANDBOOK.md +48 -89
  2. package/examples/deck/extra.md +169 -0
  3. package/examples/extra/collatz-1000.js +138 -0
  4. package/examples/extra/control-system.js +68 -0
  5. package/examples/extra/deep-taxonomy-100000.js +95 -0
  6. package/examples/extra/delfour.js +110 -0
  7. package/examples/extra/euler-identity.js +41 -0
  8. package/examples/extra/fibonacci.js +81 -0
  9. package/examples/extra/goldbach-1000.js +112 -0
  10. package/examples/extra/gps.js +274 -0
  11. package/examples/extra/kaprekar-6174.js +112 -0
  12. package/examples/extra/matrix-mechanics.js +69 -0
  13. package/examples/extra/odrl-dpv-ehds-risk-ranked.js +255 -0
  14. package/examples/extra/output/collatz-1000.txt +18 -0
  15. package/examples/extra/output/control-system.txt +14 -0
  16. package/examples/extra/output/deep-taxonomy-100000.txt +15 -0
  17. package/examples/extra/output/delfour.txt +20 -0
  18. package/examples/extra/output/euler-identity.txt +12 -0
  19. package/examples/extra/output/fibonacci.txt +21 -0
  20. package/examples/extra/output/goldbach-1000.txt +17 -0
  21. package/examples/extra/output/gps.txt +33 -0
  22. package/examples/extra/output/kaprekar-6174.txt +17 -0
  23. package/examples/extra/output/matrix-mechanics.txt +14 -0
  24. package/examples/extra/output/odrl-dpv-ehds-risk-ranked.txt +48 -0
  25. package/examples/extra/output/path-discovery.txt +28 -0
  26. package/examples/extra/output/pn-junction-tunneling.txt +15 -0
  27. package/examples/extra/output/polynomial.txt +20 -0
  28. package/examples/extra/output/sudoku.txt +47 -0
  29. package/examples/extra/output/transistor-switch.txt +16 -0
  30. package/examples/extra/path-discovery.js +45114 -0
  31. package/examples/extra/pn-junction-tunneling.js +69 -0
  32. package/examples/extra/polynomial.js +181 -0
  33. package/examples/extra/sudoku.js +330 -0
  34. package/examples/extra/transistor-switch.js +93 -0
  35. package/examples/fibonacci.n3 +2 -0
  36. package/examples/output/fibonacci.n3 +1 -0
  37. package/eyeling.js +49 -45
  38. package/lib/engine.js +49 -45
  39. package/package.json +3 -2
  40. package/test/extra.test.js +100 -0
package/eyeling.js CHANGED
@@ -8133,6 +8133,18 @@ function __printTriggeredFuse(rule, prefixes, subst /* optional */, extraNote /*
8133
8133
  }
8134
8134
  }
8135
8135
 
8136
+ function __exitReasoning(code, message /* optional */) {
8137
+ if (typeof process !== 'undefined' && process && typeof process.exit === 'function') {
8138
+ process.exit(code);
8139
+ return;
8140
+ }
8141
+
8142
+ const err = new Error(message || `Process exited with code ${code}`);
8143
+ err.__eyelingExit = true;
8144
+ err.code = code;
8145
+ throw err;
8146
+ }
8147
+
8136
8148
  function forwardChain(facts, forwardRules, backRules, onDerived /* optional */, opts = {}) {
8137
8149
  enterReasoningRun();
8138
8150
  try {
@@ -8272,7 +8284,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8272
8284
  // Allow dynamic fuses: ... => ?X. where ?X becomes false
8273
8285
  if (dynTerm instanceof Literal && dynTerm.value === 'false') {
8274
8286
  __printTriggeredFuse(r, opts && opts.prefixes, s, 'Dynamic head resolved to false.');
8275
- process.exit(2);
8287
+ __exitReasoning(2, 'Inference fuse triggered.');
8276
8288
  }
8277
8289
 
8278
8290
  const dynTriples = __graphTriplesOrTrue(dynTerm);
@@ -8300,10 +8312,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8300
8312
  const objIsGraph = obj instanceof GraphTerm;
8301
8313
  const subjIsTrue = subj instanceof Literal && subj.value === 'true';
8302
8314
  const objIsTrue = obj instanceof Literal && obj.value === 'true';
8315
+ const objIsFalse = obj instanceof Literal && obj.value === 'false';
8303
8316
 
8304
8317
  const isFwRuleTriple =
8305
- isLogImplies(instantiated.p) &&
8306
- ((subjIsGraph && objIsGraph) || (subjIsTrue && objIsGraph) || (subjIsGraph && objIsTrue));
8318
+ isLogImplies(instantiated.p) && (subjIsGraph || subjIsTrue) && (objIsGraph || objIsTrue || objIsFalse);
8307
8319
 
8308
8320
  const isBwRuleTriple =
8309
8321
  isLogImpliedBy(instantiated.p) &&
@@ -8318,47 +8330,39 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8318
8330
  changedHere = true;
8319
8331
  }
8320
8332
 
8321
- // Promote rule-producing triples to live rules, treating literal true as {}.
8322
- const left = __graphTriplesOrTrue(subj);
8323
- const right = __graphTriplesOrTrue(obj);
8324
-
8325
- if (left !== null && right !== null) {
8326
- if (isFwRuleTriple) {
8327
- const [premise, conclusion] = liftBlankRuleVars(left, right);
8328
- const headBlankLabels = collectBlankLabelsInTriples(conclusion);
8329
- const newRule = new Rule(premise, conclusion, true, false, headBlankLabels);
8330
- __prepareForwardRule(newRule);
8331
-
8332
- const key = __ruleKey(
8333
- newRule.isForward,
8334
- newRule.isFuse,
8335
- newRule.premise,
8336
- newRule.conclusion,
8337
- newRule.__dynamicConclusionTerm || null,
8338
- );
8339
- if (!forwardRules.__ruleKeySet.has(key)) {
8340
- forwardRules.__ruleKeySet.add(key);
8341
- forwardRules.push(newRule);
8342
- rulesChanged = true;
8343
- }
8344
- } else if (isBwRuleTriple) {
8345
- const [premise, conclusion] = liftBlankRuleVars(right, left);
8346
- const headBlankLabels = collectBlankLabelsInTriples(conclusion);
8347
- const newRule = new Rule(premise, conclusion, false, false, headBlankLabels);
8348
-
8349
- const key = __ruleKey(
8350
- newRule.isForward,
8351
- newRule.isFuse,
8352
- newRule.premise,
8353
- newRule.conclusion,
8354
- newRule.__dynamicConclusionTerm || null,
8355
- );
8356
- if (!backRules.__ruleKeySet.has(key)) {
8357
- backRules.__ruleKeySet.add(key);
8358
- backRules.push(newRule);
8359
- indexBackRule(backRules, newRule);
8360
- rulesChanged = true;
8361
- }
8333
+ // Promote rule-producing triples to live rules, treating literal true as {}
8334
+ // and literal false as a fuse head.
8335
+ if (isFwRuleTriple) {
8336
+ const newRule = __makeRuleFromTerms(subj, obj, true);
8337
+ __prepareForwardRule(newRule);
8338
+
8339
+ const key = __ruleKey(
8340
+ newRule.isForward,
8341
+ newRule.isFuse,
8342
+ newRule.premise,
8343
+ newRule.conclusion,
8344
+ newRule.__dynamicConclusionTerm || null,
8345
+ );
8346
+ if (!forwardRules.__ruleKeySet.has(key)) {
8347
+ forwardRules.__ruleKeySet.add(key);
8348
+ forwardRules.push(newRule);
8349
+ rulesChanged = true;
8350
+ }
8351
+ } else if (isBwRuleTriple) {
8352
+ const newRule = __makeRuleFromTerms(subj, obj, false);
8353
+
8354
+ const key = __ruleKey(
8355
+ newRule.isForward,
8356
+ newRule.isFuse,
8357
+ newRule.premise,
8358
+ newRule.conclusion,
8359
+ newRule.__dynamicConclusionTerm || null,
8360
+ );
8361
+ if (!backRules.__ruleKeySet.has(key)) {
8362
+ backRules.__ruleKeySet.add(key);
8363
+ backRules.push(newRule);
8364
+ indexBackRule(backRules, newRule);
8365
+ rulesChanged = true;
8362
8366
  }
8363
8367
  }
8364
8368
 
@@ -8441,7 +8445,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8441
8445
  // Inference fuse
8442
8446
  if (r.isFuse && sols.length) {
8443
8447
  __printTriggeredFuse(r, opts && opts.prefixes, sols[0]);
8444
- process.exit(2);
8448
+ __exitReasoning(2, 'Inference fuse triggered.');
8445
8449
  }
8446
8450
 
8447
8451
  for (const s of sols) {
package/lib/engine.js CHANGED
@@ -2651,6 +2651,18 @@ function __printTriggeredFuse(rule, prefixes, subst /* optional */, extraNote /*
2651
2651
  }
2652
2652
  }
2653
2653
 
2654
+ function __exitReasoning(code, message /* optional */) {
2655
+ if (typeof process !== 'undefined' && process && typeof process.exit === 'function') {
2656
+ process.exit(code);
2657
+ return;
2658
+ }
2659
+
2660
+ const err = new Error(message || `Process exited with code ${code}`);
2661
+ err.__eyelingExit = true;
2662
+ err.code = code;
2663
+ throw err;
2664
+ }
2665
+
2654
2666
  function forwardChain(facts, forwardRules, backRules, onDerived /* optional */, opts = {}) {
2655
2667
  enterReasoningRun();
2656
2668
  try {
@@ -2790,7 +2802,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
2790
2802
  // Allow dynamic fuses: ... => ?X. where ?X becomes false
2791
2803
  if (dynTerm instanceof Literal && dynTerm.value === 'false') {
2792
2804
  __printTriggeredFuse(r, opts && opts.prefixes, s, 'Dynamic head resolved to false.');
2793
- process.exit(2);
2805
+ __exitReasoning(2, 'Inference fuse triggered.');
2794
2806
  }
2795
2807
 
2796
2808
  const dynTriples = __graphTriplesOrTrue(dynTerm);
@@ -2818,10 +2830,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
2818
2830
  const objIsGraph = obj instanceof GraphTerm;
2819
2831
  const subjIsTrue = subj instanceof Literal && subj.value === 'true';
2820
2832
  const objIsTrue = obj instanceof Literal && obj.value === 'true';
2833
+ const objIsFalse = obj instanceof Literal && obj.value === 'false';
2821
2834
 
2822
2835
  const isFwRuleTriple =
2823
- isLogImplies(instantiated.p) &&
2824
- ((subjIsGraph && objIsGraph) || (subjIsTrue && objIsGraph) || (subjIsGraph && objIsTrue));
2836
+ isLogImplies(instantiated.p) && (subjIsGraph || subjIsTrue) && (objIsGraph || objIsTrue || objIsFalse);
2825
2837
 
2826
2838
  const isBwRuleTriple =
2827
2839
  isLogImpliedBy(instantiated.p) &&
@@ -2836,47 +2848,39 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
2836
2848
  changedHere = true;
2837
2849
  }
2838
2850
 
2839
- // Promote rule-producing triples to live rules, treating literal true as {}.
2840
- const left = __graphTriplesOrTrue(subj);
2841
- const right = __graphTriplesOrTrue(obj);
2842
-
2843
- if (left !== null && right !== null) {
2844
- if (isFwRuleTriple) {
2845
- const [premise, conclusion] = liftBlankRuleVars(left, right);
2846
- const headBlankLabels = collectBlankLabelsInTriples(conclusion);
2847
- const newRule = new Rule(premise, conclusion, true, false, headBlankLabels);
2848
- __prepareForwardRule(newRule);
2849
-
2850
- const key = __ruleKey(
2851
- newRule.isForward,
2852
- newRule.isFuse,
2853
- newRule.premise,
2854
- newRule.conclusion,
2855
- newRule.__dynamicConclusionTerm || null,
2856
- );
2857
- if (!forwardRules.__ruleKeySet.has(key)) {
2858
- forwardRules.__ruleKeySet.add(key);
2859
- forwardRules.push(newRule);
2860
- rulesChanged = true;
2861
- }
2862
- } else if (isBwRuleTriple) {
2863
- const [premise, conclusion] = liftBlankRuleVars(right, left);
2864
- const headBlankLabels = collectBlankLabelsInTriples(conclusion);
2865
- const newRule = new Rule(premise, conclusion, false, false, headBlankLabels);
2866
-
2867
- const key = __ruleKey(
2868
- newRule.isForward,
2869
- newRule.isFuse,
2870
- newRule.premise,
2871
- newRule.conclusion,
2872
- newRule.__dynamicConclusionTerm || null,
2873
- );
2874
- if (!backRules.__ruleKeySet.has(key)) {
2875
- backRules.__ruleKeySet.add(key);
2876
- backRules.push(newRule);
2877
- indexBackRule(backRules, newRule);
2878
- rulesChanged = true;
2879
- }
2851
+ // Promote rule-producing triples to live rules, treating literal true as {}
2852
+ // and literal false as a fuse head.
2853
+ if (isFwRuleTriple) {
2854
+ const newRule = __makeRuleFromTerms(subj, obj, true);
2855
+ __prepareForwardRule(newRule);
2856
+
2857
+ const key = __ruleKey(
2858
+ newRule.isForward,
2859
+ newRule.isFuse,
2860
+ newRule.premise,
2861
+ newRule.conclusion,
2862
+ newRule.__dynamicConclusionTerm || null,
2863
+ );
2864
+ if (!forwardRules.__ruleKeySet.has(key)) {
2865
+ forwardRules.__ruleKeySet.add(key);
2866
+ forwardRules.push(newRule);
2867
+ rulesChanged = true;
2868
+ }
2869
+ } else if (isBwRuleTriple) {
2870
+ const newRule = __makeRuleFromTerms(subj, obj, false);
2871
+
2872
+ const key = __ruleKey(
2873
+ newRule.isForward,
2874
+ newRule.isFuse,
2875
+ newRule.premise,
2876
+ newRule.conclusion,
2877
+ newRule.__dynamicConclusionTerm || null,
2878
+ );
2879
+ if (!backRules.__ruleKeySet.has(key)) {
2880
+ backRules.__ruleKeySet.add(key);
2881
+ backRules.push(newRule);
2882
+ indexBackRule(backRules, newRule);
2883
+ rulesChanged = true;
2880
2884
  }
2881
2885
  }
2882
2886
 
@@ -2959,7 +2963,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
2959
2963
  // Inference fuse
2960
2964
  if (r.isFuse && sols.length) {
2961
2965
  __printTriggeredFuse(r, opts && opts.prefixes, sols[0]);
2962
- process.exit(2);
2966
+ __exitReasoning(2, 'Inference fuse triggered.');
2963
2967
  }
2964
2968
 
2965
2969
  for (const s of sols) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.19.4",
3
+ "version": "1.19.6",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
@@ -43,10 +43,11 @@
43
43
  "test:builtin-contract": "node test/builtin-contract.test.js",
44
44
  "test:n3gen": "node test/n3gen.test.js",
45
45
  "test:examples": "node test/examples.test.js",
46
+ "test:extra": "node test/extra.test.js",
46
47
  "test:manifest": "node test/manifest.test.js",
47
48
  "test:playground": "node test/playground.test.js",
48
49
  "test:package": "node test/package.test.js",
49
- "test:all": "npm run test:api && npm run test:builtin-contract && npm run test:n3gen && npm run test:examples && npm run test:manifest && npm run test:playground",
50
+ "test:all": "npm run test:api && npm run test:builtin-contract && npm run test:n3gen && npm run test:examples && npm run test:extra && npm run test:manifest && npm run test:playground",
50
51
  "pretest": "npm run build && npm run test:packlist",
51
52
  "test": "npm run test:all",
52
53
  "posttest": "npm run test:package",
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+ const cp = require('node:child_process');
7
+
8
+ const TTY = process.stdout.isTTY;
9
+ const C = TTY
10
+ ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
11
+ : { g: '', r: '', y: '', dim: '', n: '' };
12
+ const msTag = (ms) => `${C.dim}(${ms} ms)${C.n}`;
13
+
14
+ function ok(msg) {
15
+ console.log(`${C.g}OK${C.n} ${msg}`);
16
+ }
17
+ function fail(msg) {
18
+ console.error(`${C.r}FAIL${C.n} ${msg}`);
19
+ }
20
+ function info(msg) {
21
+ console.log(`${C.y}==${C.n} ${msg}`);
22
+ }
23
+
24
+ function main() {
25
+ const suiteStart = Date.now();
26
+ const root = path.resolve(__dirname, '..');
27
+ const extraDir = path.join(root, 'examples', 'extra');
28
+ const outputDir = path.join(extraDir, 'output');
29
+ const nodePath = process.execPath;
30
+
31
+ if (!fs.existsSync(extraDir)) {
32
+ fail(`Cannot find examples/extra directory: ${extraDir}`);
33
+ process.exit(1);
34
+ }
35
+
36
+ fs.mkdirSync(outputDir, { recursive: true });
37
+
38
+ const files = fs
39
+ .readdirSync(extraDir)
40
+ .filter((f) => f.endsWith('.js'))
41
+ .sort((a, b) => a.localeCompare(b));
42
+
43
+ info(`Running ${files.length} extra examples`);
44
+ console.log(`${C.dim}node ${process.version}${C.n}`);
45
+
46
+ if (files.length === 0) {
47
+ ok('No .js files found in examples/extra/');
48
+ process.exit(0);
49
+ }
50
+
51
+ let passed = 0;
52
+ let failed = 0;
53
+ const idxWidth = String(files.length).length;
54
+
55
+ for (let i = 0; i < files.length; i += 1) {
56
+ const idx = String(i + 1).padStart(idxWidth, '0');
57
+ const file = files[i];
58
+ const start = Date.now();
59
+
60
+ const inputPath = path.join(extraDir, file);
61
+ const outputPath = path.join(outputDir, file.replace(/\.js$/i, '.txt'));
62
+
63
+ const r = cp.spawnSync(nodePath, [inputPath], {
64
+ cwd: extraDir,
65
+ encoding: 'utf8',
66
+ maxBuffer: 200 * 1024 * 1024,
67
+ stdio: ['ignore', 'pipe', 'pipe'],
68
+ });
69
+
70
+ const stdout = r.stdout || '';
71
+ fs.writeFileSync(outputPath, stdout, 'utf8');
72
+
73
+ const rc = r.status == null ? 1 : r.status;
74
+ const ms = Date.now() - start;
75
+
76
+ if (rc === 0) {
77
+ ok(`${idx} ${file} -> output/${path.basename(outputPath)} ${msTag(ms)}`);
78
+ passed += 1;
79
+ } else {
80
+ fail(`${idx} ${file} ${msTag(ms)}`);
81
+ fail(`Exit code ${rc}`);
82
+ if (r.stderr) process.stderr.write(r.stderr);
83
+ failed += 1;
84
+ }
85
+ }
86
+
87
+ console.log('');
88
+ const suiteMs = Date.now() - suiteStart;
89
+ info(`Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
90
+
91
+ if (failed === 0) {
92
+ ok(`All extra examples passed (${passed}/${files.length})`);
93
+ process.exit(0);
94
+ }
95
+
96
+ fail(`Some extra examples failed (${passed}/${files.length})`);
97
+ process.exit(2);
98
+ }
99
+
100
+ main();