eyeling 1.24.33 → 1.25.0
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/HANDBOOK.md +2 -2
- package/dist/browser/eyeling.browser.js +8 -2
- package/dist/browser/index.mjs +3 -0
- package/examples/fuse.n3 +1 -1
- package/examples/liar.n3 +1 -1
- package/eyeling.js +8 -2
- package/index.d.ts +3 -0
- package/lib/engine.js +7 -2
- package/lib/entry.js +1 -0
- package/package.json +1 -1
- package/test/api.test.js +5 -5
- package/test/package.test.js +2 -2
- package/tools/bundle.js +3 -0
package/HANDBOOK.md
CHANGED
|
@@ -793,7 +793,7 @@ Implementation: deterministic Skolem IDs live in `lib/skolem.js`; the per-firing
|
|
|
793
793
|
A rule whose conclusion is `false` is treated as a hard failure. During forward chaining:
|
|
794
794
|
|
|
795
795
|
- Eyeling proves the premise (it only needs one solution)
|
|
796
|
-
- if the premise is provable, it prints a message and exits with status code
|
|
796
|
+
- if the premise is provable, it prints a message and exits with status code 65 (`EX_DATAERR` in Unix `sysexits.h` terminology)
|
|
797
797
|
|
|
798
798
|
This is Eyeling’s way to express hard consistency checks and detect inconsistencies.
|
|
799
799
|
|
|
@@ -3417,7 +3417,7 @@ So Eyeling is not only implementing the semantics document; it is also defining
|
|
|
3417
3417
|
|
|
3418
3418
|
#### G.2.4 Inference fuses (`=> false`) are an engine-level procedural feature
|
|
3419
3419
|
|
|
3420
|
-
The semantics document discusses `false` in relation to implication and constraints. Eyeling turns `{ ... } => false` into an engine-level hard failure with a visible message and
|
|
3420
|
+
The semantics document discusses `false` in relation to implication and constraints. Eyeling turns `{ ... } => false` into an engine-level hard failure with a visible message and exit status 65 (`EX_DATAERR`). That is a practical tooling feature: it lets a rule act like a checked invariant.
|
|
3421
3421
|
|
|
3422
3422
|
This is very useful in real programs, but it is an operational behavior of the reasoner, not something a model-theoretic semantics “executes.”
|
|
3423
3423
|
|
|
@@ -5517,6 +5517,10 @@ const {
|
|
|
5517
5517
|
copyQuotedGraphMetadata,
|
|
5518
5518
|
} = require('./prelude');
|
|
5519
5519
|
|
|
5520
|
+
// Inference fuses use sysexits.h EX_DATAERR (65): input/rules made a
|
|
5521
|
+
// forbidden condition provable, rather than a generic usage/runtime error.
|
|
5522
|
+
const INFERENCE_FUSE_EXIT_CODE = 65;
|
|
5523
|
+
|
|
5520
5524
|
// In N3/Turtle, rdf:nil is the canonical IRI for the empty RDF list.
|
|
5521
5525
|
// Eyeling represents list literals with ListTerm; ensure rdf:nil unifies with ().
|
|
5522
5526
|
const RDF_NIL_IRI = RDF_NS + 'nil';
|
|
@@ -8445,7 +8449,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8445
8449
|
// Allow dynamic fuses: ... => ?X. where ?X becomes false
|
|
8446
8450
|
if (dynTerm instanceof Literal && dynTerm.value === 'false') {
|
|
8447
8451
|
__printTriggeredFuse(r, opts && opts.prefixes, s, 'Dynamic head resolved to false.');
|
|
8448
|
-
__exitReasoning(
|
|
8452
|
+
__exitReasoning(INFERENCE_FUSE_EXIT_CODE, 'Inference fuse triggered.');
|
|
8449
8453
|
}
|
|
8450
8454
|
|
|
8451
8455
|
const dynTriples = __graphTriplesOrTrue(dynTerm);
|
|
@@ -8606,7 +8610,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8606
8610
|
// Inference fuse
|
|
8607
8611
|
if (r.isFuse && sols.length) {
|
|
8608
8612
|
__printTriggeredFuse(r, opts && opts.prefixes, sols[0]);
|
|
8609
|
-
__exitReasoning(
|
|
8613
|
+
__exitReasoning(INFERENCE_FUSE_EXIT_CODE, 'Inference fuse triggered.');
|
|
8610
8614
|
}
|
|
8611
8615
|
|
|
8612
8616
|
for (const s of sols) {
|
|
@@ -9143,6 +9147,7 @@ module.exports = {
|
|
|
9143
9147
|
registerBuiltinModule,
|
|
9144
9148
|
loadBuiltinModule,
|
|
9145
9149
|
listBuiltinIris,
|
|
9150
|
+
INFERENCE_FUSE_EXIT_CODE,
|
|
9146
9151
|
};
|
|
9147
9152
|
|
|
9148
9153
|
};
|
|
@@ -9170,6 +9175,7 @@ module.exports = {
|
|
|
9170
9175
|
rdfjs: dataFactory,
|
|
9171
9176
|
main: engine.main,
|
|
9172
9177
|
version: engine.version,
|
|
9178
|
+
INFERENCE_FUSE_EXIT_CODE: engine.INFERENCE_FUSE_EXIT_CODE,
|
|
9173
9179
|
|
|
9174
9180
|
// internals for playground.html
|
|
9175
9181
|
lex: engine.lex,
|
package/dist/browser/index.mjs
CHANGED
|
@@ -10,6 +10,8 @@ function getBrowserApi() {
|
|
|
10
10
|
return api;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export const INFERENCE_FUSE_EXIT_CODE = 65;
|
|
14
|
+
|
|
13
15
|
export function reasonStream(input, opts) {
|
|
14
16
|
return getBrowserApi().reasonStream(input, opts);
|
|
15
17
|
}
|
|
@@ -64,6 +66,7 @@ const eyeling = {
|
|
|
64
66
|
get version() {
|
|
65
67
|
return getBrowserApi().version;
|
|
66
68
|
},
|
|
69
|
+
INFERENCE_FUSE_EXIT_CODE,
|
|
67
70
|
reasonStream,
|
|
68
71
|
reasonRdfJs,
|
|
69
72
|
rdfjs,
|
package/examples/fuse.n3
CHANGED
package/examples/liar.n3
CHANGED
package/eyeling.js
CHANGED
|
@@ -5517,6 +5517,10 @@ const {
|
|
|
5517
5517
|
copyQuotedGraphMetadata,
|
|
5518
5518
|
} = require('./prelude');
|
|
5519
5519
|
|
|
5520
|
+
// Inference fuses use sysexits.h EX_DATAERR (65): input/rules made a
|
|
5521
|
+
// forbidden condition provable, rather than a generic usage/runtime error.
|
|
5522
|
+
const INFERENCE_FUSE_EXIT_CODE = 65;
|
|
5523
|
+
|
|
5520
5524
|
// In N3/Turtle, rdf:nil is the canonical IRI for the empty RDF list.
|
|
5521
5525
|
// Eyeling represents list literals with ListTerm; ensure rdf:nil unifies with ().
|
|
5522
5526
|
const RDF_NIL_IRI = RDF_NS + 'nil';
|
|
@@ -8445,7 +8449,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8445
8449
|
// Allow dynamic fuses: ... => ?X. where ?X becomes false
|
|
8446
8450
|
if (dynTerm instanceof Literal && dynTerm.value === 'false') {
|
|
8447
8451
|
__printTriggeredFuse(r, opts && opts.prefixes, s, 'Dynamic head resolved to false.');
|
|
8448
|
-
__exitReasoning(
|
|
8452
|
+
__exitReasoning(INFERENCE_FUSE_EXIT_CODE, 'Inference fuse triggered.');
|
|
8449
8453
|
}
|
|
8450
8454
|
|
|
8451
8455
|
const dynTriples = __graphTriplesOrTrue(dynTerm);
|
|
@@ -8606,7 +8610,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8606
8610
|
// Inference fuse
|
|
8607
8611
|
if (r.isFuse && sols.length) {
|
|
8608
8612
|
__printTriggeredFuse(r, opts && opts.prefixes, sols[0]);
|
|
8609
|
-
__exitReasoning(
|
|
8613
|
+
__exitReasoning(INFERENCE_FUSE_EXIT_CODE, 'Inference fuse triggered.');
|
|
8610
8614
|
}
|
|
8611
8615
|
|
|
8612
8616
|
for (const s of sols) {
|
|
@@ -9143,6 +9147,7 @@ module.exports = {
|
|
|
9143
9147
|
registerBuiltinModule,
|
|
9144
9148
|
loadBuiltinModule,
|
|
9145
9149
|
listBuiltinIris,
|
|
9150
|
+
INFERENCE_FUSE_EXIT_CODE,
|
|
9146
9151
|
};
|
|
9147
9152
|
|
|
9148
9153
|
};
|
|
@@ -9170,6 +9175,7 @@ module.exports = {
|
|
|
9170
9175
|
rdfjs: dataFactory,
|
|
9171
9176
|
main: engine.main,
|
|
9172
9177
|
version: engine.version,
|
|
9178
|
+
INFERENCE_FUSE_EXIT_CODE: engine.INFERENCE_FUSE_EXIT_CODE,
|
|
9173
9179
|
|
|
9174
9180
|
// internals for playground.html
|
|
9175
9181
|
lex: engine.lex,
|
package/index.d.ts
CHANGED
|
@@ -209,6 +209,7 @@ declare module 'eyeling' {
|
|
|
209
209
|
opts?: Omit<ReasonStreamOptions, 'rdfjs' | 'onDerived'>,
|
|
210
210
|
): AsyncIterable<RdfJsQuad>;
|
|
211
211
|
|
|
212
|
+
export const INFERENCE_FUSE_EXIT_CODE: 65;
|
|
212
213
|
export const rdfjs: RdfJsDataFactory;
|
|
213
214
|
export function registerBuiltin(iri: string, handler: BuiltinHandler): BuiltinHandler;
|
|
214
215
|
export function unregisterBuiltin(iri: string): boolean;
|
|
@@ -237,6 +238,7 @@ declare module 'eyeling/browser' {
|
|
|
237
238
|
opts?: Omit<ReasonStreamOptions, 'rdfjs' | 'onDerived'>,
|
|
238
239
|
): AsyncIterable<RdfJsQuad>;
|
|
239
240
|
|
|
241
|
+
export const INFERENCE_FUSE_EXIT_CODE: 65;
|
|
240
242
|
export const rdfjs: RdfJsDataFactory;
|
|
241
243
|
export function registerBuiltin(iri: string, handler: BuiltinHandler): BuiltinHandler;
|
|
242
244
|
export function unregisterBuiltin(iri: string): boolean;
|
|
@@ -247,6 +249,7 @@ declare module 'eyeling/browser' {
|
|
|
247
249
|
readonly version: string;
|
|
248
250
|
reasonStream: typeof reasonStream;
|
|
249
251
|
reasonRdfJs: typeof reasonRdfJs;
|
|
252
|
+
readonly INFERENCE_FUSE_EXIT_CODE: typeof INFERENCE_FUSE_EXIT_CODE;
|
|
250
253
|
rdfjs: typeof rdfjs;
|
|
251
254
|
registerBuiltin: typeof registerBuiltin;
|
|
252
255
|
unregisterBuiltin: typeof unregisterBuiltin;
|
package/lib/engine.js
CHANGED
|
@@ -30,6 +30,10 @@ const {
|
|
|
30
30
|
copyQuotedGraphMetadata,
|
|
31
31
|
} = require('./prelude');
|
|
32
32
|
|
|
33
|
+
// Inference fuses use sysexits.h EX_DATAERR (65): input/rules made a
|
|
34
|
+
// forbidden condition provable, rather than a generic usage/runtime error.
|
|
35
|
+
const INFERENCE_FUSE_EXIT_CODE = 65;
|
|
36
|
+
|
|
33
37
|
// In N3/Turtle, rdf:nil is the canonical IRI for the empty RDF list.
|
|
34
38
|
// Eyeling represents list literals with ListTerm; ensure rdf:nil unifies with ().
|
|
35
39
|
const RDF_NIL_IRI = RDF_NS + 'nil';
|
|
@@ -2958,7 +2962,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
2958
2962
|
// Allow dynamic fuses: ... => ?X. where ?X becomes false
|
|
2959
2963
|
if (dynTerm instanceof Literal && dynTerm.value === 'false') {
|
|
2960
2964
|
__printTriggeredFuse(r, opts && opts.prefixes, s, 'Dynamic head resolved to false.');
|
|
2961
|
-
__exitReasoning(
|
|
2965
|
+
__exitReasoning(INFERENCE_FUSE_EXIT_CODE, 'Inference fuse triggered.');
|
|
2962
2966
|
}
|
|
2963
2967
|
|
|
2964
2968
|
const dynTriples = __graphTriplesOrTrue(dynTerm);
|
|
@@ -3119,7 +3123,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
3119
3123
|
// Inference fuse
|
|
3120
3124
|
if (r.isFuse && sols.length) {
|
|
3121
3125
|
__printTriggeredFuse(r, opts && opts.prefixes, sols[0]);
|
|
3122
|
-
__exitReasoning(
|
|
3126
|
+
__exitReasoning(INFERENCE_FUSE_EXIT_CODE, 'Inference fuse triggered.');
|
|
3123
3127
|
}
|
|
3124
3128
|
|
|
3125
3129
|
for (const s of sols) {
|
|
@@ -3656,4 +3660,5 @@ module.exports = {
|
|
|
3656
3660
|
registerBuiltinModule,
|
|
3657
3661
|
loadBuiltinModule,
|
|
3658
3662
|
listBuiltinIris,
|
|
3663
|
+
INFERENCE_FUSE_EXIT_CODE,
|
|
3659
3664
|
};
|
package/lib/entry.js
CHANGED
package/package.json
CHANGED
package/test/api.test.js
CHANGED
|
@@ -457,13 +457,13 @@ ${U('s')} ${U('p')} ${U('o')}.
|
|
|
457
457
|
notExpect: [/^#/m],
|
|
458
458
|
},
|
|
459
459
|
{
|
|
460
|
-
name: '11 negative entailment: rule derives false (expect exit
|
|
460
|
+
name: '11 negative entailment: rule derives false (expect exit 65 => throws)',
|
|
461
461
|
opt: { proofComments: false },
|
|
462
462
|
input: `
|
|
463
463
|
{ ${U('a')} ${U('p')} ${U('b')}. } => false.
|
|
464
464
|
${U('a')} ${U('p')} ${U('b')}.
|
|
465
465
|
`,
|
|
466
|
-
expectErrorCode:
|
|
466
|
+
expectErrorCode: 65,
|
|
467
467
|
},
|
|
468
468
|
{
|
|
469
469
|
name: '12 invalid syntax should throw (non-zero exit)',
|
|
@@ -987,10 +987,10 @@ ${transitiveClosureN3('sub')}
|
|
|
987
987
|
],
|
|
988
988
|
},
|
|
989
989
|
{
|
|
990
|
-
name: '25 heavier negative entailment: batch + forbidden => false (expect exit
|
|
990
|
+
name: '25 heavier negative entailment: batch + forbidden => false (expect exit 65)',
|
|
991
991
|
opt: { proofComments: false, maxBuffer: 200 * 1024 * 1024 },
|
|
992
992
|
input: negativeEntailmentBatchN3(200),
|
|
993
|
-
expectErrorCode:
|
|
993
|
+
expectErrorCode: 65,
|
|
994
994
|
},
|
|
995
995
|
{
|
|
996
996
|
name: '26 sanity: no rules => no newly derived facts',
|
|
@@ -1108,7 +1108,7 @@ ${U('a')} ${U('p')} ${U('b')}.
|
|
|
1108
1108
|
{ ${U('a')} ${U('p')} ${U('b')}. } => { ${U('a')} ${U('q')} ${U('b')}. }.
|
|
1109
1109
|
{ ${U('a')} ${U('q')} ${U('b')}. } => false.
|
|
1110
1110
|
`,
|
|
1111
|
-
expectErrorCode:
|
|
1111
|
+
expectErrorCode: 65,
|
|
1112
1112
|
},
|
|
1113
1113
|
|
|
1114
1114
|
{
|
package/test/package.test.js
CHANGED
|
@@ -79,12 +79,12 @@ function normalizeMarkdownForCompare(text) {
|
|
|
79
79
|
|
|
80
80
|
// Expectation logic (shared with test/examples.test.js):
|
|
81
81
|
// 1) If file contains: # expect-exit: N -> use N
|
|
82
|
-
// 2) Else, if it contains "=> false" -> expect exit
|
|
82
|
+
// 2) Else, if it contains "=> false" -> expect exit 65
|
|
83
83
|
// 3) Else -> expect exit 0
|
|
84
84
|
function expectedExitCode(n3Text) {
|
|
85
85
|
const m = n3Text.match(/^[ \t]*#[: ]*expect-exit:[ \t]*([0-9]+)\b/m);
|
|
86
86
|
if (m) return parseInt(m[1], 10);
|
|
87
|
-
if (/=>\s*false\b/.test(n3Text)) return
|
|
87
|
+
if (/=>\s*false\b/.test(n3Text)) return 65;
|
|
88
88
|
return 0;
|
|
89
89
|
}
|
|
90
90
|
|
package/tools/bundle.js
CHANGED
|
@@ -229,6 +229,8 @@ function getBrowserApi() {
|
|
|
229
229
|
return api;
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
+
export const INFERENCE_FUSE_EXIT_CODE = 65;
|
|
233
|
+
|
|
232
234
|
export function reasonStream(input, opts) {
|
|
233
235
|
return getBrowserApi().reasonStream(input, opts);
|
|
234
236
|
}
|
|
@@ -283,6 +285,7 @@ const eyeling = {
|
|
|
283
285
|
get version() {
|
|
284
286
|
return getBrowserApi().version;
|
|
285
287
|
},
|
|
288
|
+
INFERENCE_FUSE_EXIT_CODE,
|
|
286
289
|
reasonStream,
|
|
287
290
|
reasonRdfJs,
|
|
288
291
|
rdfjs,
|