eyeling 1.24.0 → 1.24.2
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 +98 -0
- package/README.md +4 -4
- package/dist/browser/eyeling.browser.js +103 -0
- package/eyeling.js +103 -0
- package/lib/lexer.js +103 -0
- package/package.json +1 -1
- package/see/README.md +13 -11
- package/see/examples/_see.js +33 -2
- package/see/examples/age.js +37 -11
- package/see/examples/annotation.js +37 -11
- package/see/examples/backward.js +37 -11
- package/see/examples/backward_recursion.js +37 -11
- package/see/examples/bayes_diagnosis.js +37 -11
- package/see/examples/bayes_therapy.js +37 -11
- package/see/examples/bmi.js +37 -11
- package/see/examples/builtin_coverage.js +37 -11
- package/see/examples/collection.js +37 -11
- package/see/examples/complex.js +37 -11
- package/see/examples/complex_matrix_stability.js +37 -11
- package/see/examples/composition_of_injective_functions_is_injective.js +37 -11
- package/see/examples/control_system.js +37 -11
- package/see/examples/crypto_builtins_tests.js +37 -11
- package/see/examples/delfour.js +37 -11
- package/see/examples/digital_product_passport.js +37 -11
- package/see/examples/dijkstra.js +37 -11
- package/see/examples/dijkstra_risk_path.js +37 -11
- package/see/examples/doc/age.md +1 -1
- package/see/examples/doc/annotation.md +1 -1
- package/see/examples/doc/backward.md +1 -1
- package/see/examples/doc/backward_recursion.md +1 -1
- package/see/examples/doc/bayes_diagnosis.md +1 -1
- package/see/examples/doc/bayes_therapy.md +1 -1
- package/see/examples/doc/bmi.md +1 -1
- package/see/examples/doc/builtin_coverage.md +1 -1
- package/see/examples/doc/collection.md +1 -1
- package/see/examples/doc/complex.md +1 -1
- package/see/examples/doc/complex_matrix_stability.md +1 -1
- package/see/examples/doc/composition_of_injective_functions_is_injective.md +1 -1
- package/see/examples/doc/control_system.md +1 -1
- package/see/examples/doc/crypto_builtins_tests.md +1 -1
- package/see/examples/doc/delfour.md +1 -1
- package/see/examples/doc/digital_product_passport.md +1 -1
- package/see/examples/doc/dijkstra.md +1 -1
- package/see/examples/doc/dijkstra_risk_path.md +1 -1
- package/see/examples/doc/dog.md +1 -1
- package/see/examples/doc/eco_route_insight.md +1 -1
- package/see/examples/doc/equals.md +1 -1
- package/see/examples/doc/equivalence_classes_overlap_implies_same_class.md +1 -1
- package/see/examples/doc/euler_identity.md +1 -1
- package/see/examples/doc/ev_roundtrip_planner.md +1 -1
- package/see/examples/doc/existential_rule.md +1 -1
- package/see/examples/doc/expression_eval.md +1 -1
- package/see/examples/doc/family_cousins.md +1 -1
- package/see/examples/doc/fastpow.md +1 -1
- package/see/examples/doc/fibonacci.md +1 -1
- package/see/examples/doc/french_cities.md +1 -1
- package/see/examples/doc/fundamental_theorem_arithmetic.md +1 -1
- package/see/examples/doc/genetic_knapsack_selection.md +1 -1
- package/see/examples/doc/goldbach_1000.md +1 -1
- package/see/examples/doc/good_cobbler.md +1 -1
- package/see/examples/doc/gps.md +1 -1
- package/see/examples/doc/gray_code_counter.md +1 -1
- package/see/examples/doc/greatest_lower_bound_uniqueness.md +1 -1
- package/see/examples/doc/group_inverse_uniqueness.md +1 -1
- package/see/examples/doc/hadamard_approx.md +1 -1
- package/see/examples/doc/hanoi.md +1 -1
- package/see/examples/doc/odrl_dpv_risk_ranked.md +1 -1
- package/see/examples/doc/path_discovery.md +1 -1
- package/see/examples/doc/rc_discharge_envelope.md +1 -1
- package/see/examples/doc/rdf_message_flow.md +1 -1
- package/see/examples/doc/rdf_messages.md +1 -1
- package/see/examples/doc/school_placement_audit.md +1 -1
- package/see/examples/doc/smoke_arithmetic.md +1 -1
- package/see/examples/doc/socrates.md +1 -1
- package/see/examples/doc/triple_terms.md +26 -0
- package/see/examples/doc/wind_turbine.md +1 -1
- package/see/examples/doc/witch.md +1 -1
- package/see/examples/dog.js +37 -11
- package/see/examples/eco_route_insight.js +37 -11
- package/see/examples/equals.js +37 -11
- package/see/examples/equivalence_classes_overlap_implies_same_class.js +37 -11
- package/see/examples/euler_identity.js +37 -11
- package/see/examples/ev_roundtrip_planner.js +37 -11
- package/see/examples/existential_rule.js +37 -11
- package/see/examples/expression_eval.js +37 -11
- package/see/examples/family_cousins.js +37 -11
- package/see/examples/fastpow.js +37 -11
- package/see/examples/fibonacci.js +37 -11
- package/see/examples/french_cities.js +37 -11
- package/see/examples/fundamental_theorem_arithmetic.js +37 -11
- package/see/examples/genetic_knapsack_selection.js +37 -11
- package/see/examples/goldbach_1000.js +37 -11
- package/see/examples/good_cobbler.js +37 -11
- package/see/examples/gps.js +37 -11
- package/see/examples/gray_code_counter.js +37 -11
- package/see/examples/greatest_lower_bound_uniqueness.js +37 -11
- package/see/examples/group_inverse_uniqueness.js +37 -11
- package/see/examples/hadamard_approx.js +37 -11
- package/see/examples/hanoi.js +37 -11
- package/see/examples/input/triple_terms.trig +28 -0
- package/see/examples/n3/triple_terms.n3 +23 -0
- package/see/examples/odrl_dpv_risk_ranked.js +37 -11
- package/see/examples/output/age.md +3 -3
- package/see/examples/output/annotation.md +4 -4
- package/see/examples/output/backward.md +3 -3
- package/see/examples/output/backward_recursion.md +3 -3
- package/see/examples/output/bayes_diagnosis.md +1 -1
- package/see/examples/output/bayes_therapy.md +1 -1
- package/see/examples/output/bmi.md +1 -1
- package/see/examples/output/builtin_coverage.md +3 -3
- package/see/examples/output/collection.md +3 -3
- package/see/examples/output/complex.md +4 -4
- package/see/examples/output/complex_matrix_stability.md +1 -1
- package/see/examples/output/composition_of_injective_functions_is_injective.md +3 -3
- package/see/examples/output/control_system.md +3 -3
- package/see/examples/output/crypto_builtins_tests.md +3 -3
- package/see/examples/output/delfour.md +1 -1
- package/see/examples/output/digital_product_passport.md +1 -1
- package/see/examples/output/dijkstra.md +3 -3
- package/see/examples/output/dijkstra_risk_path.md +1 -1
- package/see/examples/output/dog.md +3 -3
- package/see/examples/output/eco_route_insight.md +1 -1
- package/see/examples/output/equals.md +3 -3
- package/see/examples/output/equivalence_classes_overlap_implies_same_class.md +3 -3
- package/see/examples/output/euler_identity.md +3 -3
- package/see/examples/output/ev_roundtrip_planner.md +1 -1
- package/see/examples/output/existential_rule.md +3 -3
- package/see/examples/output/expression_eval.md +3 -3
- package/see/examples/output/family_cousins.md +3 -3
- package/see/examples/output/fastpow.md +1 -1
- package/see/examples/output/fibonacci.md +1 -1
- package/see/examples/output/french_cities.md +3 -3
- package/see/examples/output/fundamental_theorem_arithmetic.md +1 -1
- package/see/examples/output/genetic_knapsack_selection.md +1 -1
- package/see/examples/output/goldbach_1000.md +1 -1
- package/see/examples/output/good_cobbler.md +4 -4
- package/see/examples/output/gps.md +1 -1
- package/see/examples/output/gray_code_counter.md +1 -1
- package/see/examples/output/greatest_lower_bound_uniqueness.md +3 -3
- package/see/examples/output/group_inverse_uniqueness.md +3 -3
- package/see/examples/output/hadamard_approx.md +3 -3
- package/see/examples/output/hanoi.md +3 -3
- package/see/examples/output/odrl_dpv_risk_ranked.md +3 -3
- package/see/examples/output/path_discovery.md +3 -3
- package/see/examples/output/rc_discharge_envelope.md +1 -1
- package/see/examples/output/rdf_message_flow.md +1 -1
- package/see/examples/output/rdf_messages.md +1 -1
- package/see/examples/output/school_placement_audit.md +1 -1
- package/see/examples/output/smoke_arithmetic.md +1 -1
- package/see/examples/output/socrates.md +3 -3
- package/see/examples/output/triple_terms.md +53 -0
- package/see/examples/output/wind_turbine.md +1 -1
- package/see/examples/output/witch.md +3 -3
- package/see/examples/path_discovery.js +37 -11
- package/see/examples/rc_discharge_envelope.js +37 -11
- package/see/examples/rdf_message_flow.js +37 -11
- package/see/examples/rdf_messages.js +37 -11
- package/see/examples/school_placement_audit.js +37 -11
- package/see/examples/smoke_arithmetic.js +37 -11
- package/see/examples/socrates.js +37 -11
- package/see/examples/triple_terms.js +1442 -0
- package/see/examples/wind_turbine.js +37 -11
- package/see/examples/witch.js +37 -11
- package/see/see.js +455 -94
- package/test/api.test.js +20 -0
|
@@ -8,6 +8,7 @@ const crypto = require('crypto');
|
|
|
8
8
|
|
|
9
9
|
function canonical(term) {
|
|
10
10
|
if (term.kind === 'list') return ['list', term.items.map(canonical)];
|
|
11
|
+
if (term.kind === 'triple') return ['triple', canonical(term.s), canonical(term.p), canonical(term.o)];
|
|
11
12
|
if (term.kind === 'formula') return ['formula', term.atoms.map((a) => [canonical(a.s), canonical(a.p), canonical(a.o)])];
|
|
12
13
|
return [term.kind, term.value];
|
|
13
14
|
}
|
|
@@ -17,6 +18,7 @@ function compoundIndexKey() { return Array.from(arguments).map(termIndexKey).joi
|
|
|
17
18
|
function termIsConcrete(t) {
|
|
18
19
|
if (!t || t.kind === 'var') return false;
|
|
19
20
|
if (t.kind === 'list') return t.items.every(termIsConcrete);
|
|
21
|
+
if (t.kind === 'triple') return termIsConcrete(t.s) && termIsConcrete(t.p) && termIsConcrete(t.o);
|
|
20
22
|
if (t.kind === 'formula') return t.atoms.every((a) => termIsConcrete(a.s) && termIsConcrete(a.p) && termIsConcrete(a.o));
|
|
21
23
|
return true;
|
|
22
24
|
}
|
|
@@ -32,6 +34,7 @@ function primitive(t) {
|
|
|
32
34
|
if (t.kind === 'iri') return t.value.replace(/^:/, '');
|
|
33
35
|
if (t.kind === 'blank') return t.value;
|
|
34
36
|
if (t.kind === 'list') return t.items.map(primitive);
|
|
37
|
+
if (t.kind === 'triple') return termToN3(t);
|
|
35
38
|
if (t.kind === 'formula') return termToN3(t);
|
|
36
39
|
return undefined;
|
|
37
40
|
}
|
|
@@ -52,6 +55,7 @@ function termToN3(t) {
|
|
|
52
55
|
if (t.kind === 'var') return '?' + t.value;
|
|
53
56
|
if (t.kind === 'blank') return t.value.startsWith('_:') ? t.value : '_:' + t.value.replace(/^_+/, '');
|
|
54
57
|
if (t.kind === 'list') return '(' + t.items.map(termToN3).join(' ') + ')';
|
|
58
|
+
if (t.kind === 'triple') return '<<( ' + termToN3(t.s) + ' ' + termToN3(t.p) + ' ' + termToN3(t.o) + ' )>>';
|
|
55
59
|
if (t.kind === 'formula') return '{ ' + t.atoms.map(atomToN3).join(' . ') + ' }';
|
|
56
60
|
return String(t.value ?? t);
|
|
57
61
|
}
|
|
@@ -74,6 +78,7 @@ function resolve(term, env, seen = new Set()) {
|
|
|
74
78
|
return resolve(env[term.value], env, seen);
|
|
75
79
|
}
|
|
76
80
|
if (term.kind === 'list') return list(term.items.map((item) => resolve(item, env, seen)));
|
|
81
|
+
if (term.kind === 'triple') return { kind: 'triple', s: resolve(term.s, env), p: resolve(term.p, env), o: resolve(term.o, env) };
|
|
77
82
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: resolve(a.s, env), p: resolve(a.p, env), o: resolve(a.o, env) })) };
|
|
78
83
|
return term;
|
|
79
84
|
}
|
|
@@ -91,6 +96,14 @@ function unify(a, b, env) {
|
|
|
91
96
|
}
|
|
92
97
|
return out;
|
|
93
98
|
}
|
|
99
|
+
if (a.kind === 'triple' || b.kind === 'triple') {
|
|
100
|
+
if (a.kind !== 'triple' || b.kind !== 'triple') return null;
|
|
101
|
+
let out = unify(a.s, b.s, env);
|
|
102
|
+
if (!out) return null;
|
|
103
|
+
out = unify(a.p, b.p, out);
|
|
104
|
+
if (!out) return null;
|
|
105
|
+
return unify(a.o, b.o, out);
|
|
106
|
+
}
|
|
94
107
|
return deepEqual(a, b) ? env : null;
|
|
95
108
|
}
|
|
96
109
|
function bind(pattern, value, env) { return unify(pattern, value, env); }
|
|
@@ -106,6 +119,7 @@ function termIsGround(t, env) {
|
|
|
106
119
|
const r = resolve(t, env);
|
|
107
120
|
if (r.kind === 'var') return false;
|
|
108
121
|
if (r.kind === 'list') return r.items.every((item) => termIsGround(item, env));
|
|
122
|
+
if (r.kind === 'triple') return termIsGround(r.s, env) && termIsGround(r.p, env) && termIsGround(r.o, env);
|
|
109
123
|
if (r.kind === 'formula') return r.atoms.every((atom) => atomIsGround(atom, env));
|
|
110
124
|
return true;
|
|
111
125
|
}
|
|
@@ -665,6 +679,7 @@ function instantiate(term, env, ruleId) {
|
|
|
665
679
|
}
|
|
666
680
|
if (term.kind === 'blank') return blank('_:r' + ruleId + '_' + envSignature(env) + '_' + term.value.replace(/^_/, ''));
|
|
667
681
|
if (term.kind === 'list') return list(term.items.map((item) => instantiate(item, env, ruleId)));
|
|
682
|
+
if (term.kind === 'triple') return { kind: 'triple', s: instantiate(term.s, env, ruleId), p: instantiate(term.p, env, ruleId), o: instantiate(term.o, env, ruleId) };
|
|
668
683
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: instantiate(a.s, env, ruleId), p: instantiate(a.p, env, ruleId), o: instantiate(a.o, env, ruleId) })) };
|
|
669
684
|
return cloneTerm(term);
|
|
670
685
|
}
|
|
@@ -1040,19 +1055,19 @@ function renderStructuredOutput({ title, graph, queries = [], rules = [], initia
|
|
|
1040
1055
|
const lines = [];
|
|
1041
1056
|
lines.push('# ' + title);
|
|
1042
1057
|
lines.push('');
|
|
1043
|
-
lines.push('##
|
|
1058
|
+
lines.push('## Entailment');
|
|
1044
1059
|
if (mode === 'query') {
|
|
1045
1060
|
lines.push('The compiled query selected ' + selected.length + ' fact(s) after the rule closure was computed.');
|
|
1046
1061
|
} else if (mode === 'formula') {
|
|
1047
|
-
lines.push('The derivation produced ' + selected.length + ' formula-valued
|
|
1062
|
+
lines.push('The derivation produced ' + selected.length + ' formula-valued entailment(s).');
|
|
1048
1063
|
} else {
|
|
1049
1064
|
lines.push('The derivation produced ' + derived.length + ' new fact(s) from ' + initialFacts.length + ' stated fact(s).');
|
|
1050
1065
|
}
|
|
1051
|
-
if (keyFact) lines.push('Main
|
|
1066
|
+
if (keyFact) lines.push('Main entailment: **' + factSentence(keyFact) + '**');
|
|
1052
1067
|
const bullets = selected.slice(-6).reverse();
|
|
1053
1068
|
if (bullets.length) {
|
|
1054
1069
|
lines.push('');
|
|
1055
|
-
lines.push('Selected
|
|
1070
|
+
lines.push('Selected entailments:');
|
|
1056
1071
|
for (const fact of bullets) lines.push('- ' + codeFact(fact));
|
|
1057
1072
|
}
|
|
1058
1073
|
lines.push('');
|
|
@@ -1114,15 +1129,15 @@ function dedupeExplanationHeadings(text) {
|
|
|
1114
1129
|
function normalizePublicReport(markdown, title) {
|
|
1115
1130
|
let text = String(markdown || '').trimEnd();
|
|
1116
1131
|
if (!/^\s*#\s+/m.test(text)) text = '# ' + title + '\n\n' + text;
|
|
1117
|
-
if (!/^##\s+
|
|
1118
|
-
text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n##
|
|
1132
|
+
if (!/^##\s+Entailment\s*$/mi.test(text)) {
|
|
1133
|
+
text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n## Entailment\n');
|
|
1119
1134
|
}
|
|
1120
1135
|
if (!/^##\s+Explanation\s*$/mi.test(text)) {
|
|
1121
1136
|
text += '\n\n## Explanation\nNo additional explanation was provided by the generated output.';
|
|
1122
1137
|
}
|
|
1123
1138
|
text = text.replace(/^##\s+([^\n]+?)\s*$/gm, (line, heading) => {
|
|
1124
1139
|
const normalized = heading.trim().toLowerCase();
|
|
1125
|
-
if (normalized === 'insight' || normalized === 'explanation') return '## ' + (normalized === '
|
|
1140
|
+
if (normalized === 'insight' || normalized === 'conclusion' || normalized === 'entailment' || normalized === 'explanation') return '## ' + (normalized === 'explanation' ? 'Explanation' : 'Entailment');
|
|
1126
1141
|
return '**' + heading.trim() + '**';
|
|
1127
1142
|
});
|
|
1128
1143
|
text = dedupeExplanationHeadings(text);
|
|
@@ -1131,13 +1146,13 @@ function normalizePublicReport(markdown, title) {
|
|
|
1131
1146
|
function markdownize(raw, title) {
|
|
1132
1147
|
let text = String(raw || '');
|
|
1133
1148
|
text = text
|
|
1134
|
-
.replace(/===\s*Answer\s*===/g, '##
|
|
1149
|
+
.replace(/===\s*Answer\s*===/g, '## Entailment')
|
|
1135
1150
|
.replace(/===\s*Reason\s+Why\s*===/gi, '## Explanation')
|
|
1136
1151
|
.replace(/===\s*Explanation\s*===/gi, '## Explanation')
|
|
1137
1152
|
.replace(/===\s*([^=]+?)\s*===/g, (_, h) => '**' + h.trim() + '**');
|
|
1138
1153
|
text = text.replace(/^C(\d+)\s+OK\s*-\s*/gm, 'C$1: ');
|
|
1139
1154
|
text = dedupeExplanationHeadings(text);
|
|
1140
|
-
if (!text.trim()) text = '##
|
|
1155
|
+
if (!text.trim()) text = '## Entailment\nNo log:outputString facts were derived.\n\n## Explanation\nThe compiled derivation did not produce authored report text.';
|
|
1141
1156
|
return normalizePublicReport(text, title);
|
|
1142
1157
|
}
|
|
1143
1158
|
function authoredSupportAppendix(graph, queries, rules, initialFacts, trace) {
|
|
@@ -1985,7 +2000,7 @@ const QUERIES = [
|
|
|
1985
2000
|
]
|
|
1986
2001
|
}
|
|
1987
2002
|
];
|
|
1988
|
-
const DOC_MARKDOWN = "# Eco Route Insight\n\nGenerated by `see.js` from a Notation3 source file.\n\nN3-compiled version of the privacy-preserving eco route insight. The JSON\ninput remains the data-input sidecar; this source compiles the local decision\nand signed-envelope explanation.\n\n## Compilation summary\n\n- Example name: `eco_route_insight`\n- Input facts emitted: 33\n- Forward rules compiled: 2\n- Backward predicate rules compiled: 0\n- Fuses compiled: 0\n- Predicate count: 30\n\n## Built-ins used\n\n- `log:outputString`\n- `math:difference`\n- `math:greaterThan`\n- `math:lessThan`\n- `math:notGreaterThan`\n- `string:format`\n\n## Runtime model\n\nThe generated `examples/eco_route_insight.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/eco_route_insight.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/eco-route-insight.trig` or `examples/input/eco_route_insight.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.\n\n## Output model\n\nRunning `node examples/eco_route_insight.js` produces a SEE-style Markdown report with an **
|
|
2003
|
+
const DOC_MARKDOWN = "# Eco Route Insight\n\nGenerated by `see.js` from a Notation3 source file.\n\nN3-compiled version of the privacy-preserving eco route insight. The JSON\ninput remains the data-input sidecar; this source compiles the local decision\nand signed-envelope explanation.\n\n## Compilation summary\n\n- Example name: `eco_route_insight`\n- Input facts emitted: 33\n- Forward rules compiled: 2\n- Backward predicate rules compiled: 0\n- Fuses compiled: 0\n- Predicate count: 30\n\n## Built-ins used\n\n- `log:outputString`\n- `math:difference`\n- `math:greaterThan`\n- `math:lessThan`\n- `math:notGreaterThan`\n- `string:format`\n\n## Runtime model\n\nThe generated `examples/eco_route_insight.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/eco_route_insight.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/eco-route-insight.trig` or `examples/input/eco_route_insight.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.\n\n## Output model\n\nRunning `node examples/eco_route_insight.js` produces a SEE-style Markdown report with an **Entailment** section, an **Explanation** section, and a **Formal TriG Output** section containing the selected derived/query facts.\n";
|
|
1989
2004
|
function seeMetadata(data) { return (data && data.__see) || {}; }
|
|
1990
2005
|
function trustedDerivation(data) { const meta = seeMetadata(data); const facts = data && Array.isArray(data.facts) ? data.facts : []; const expectedFacts = EXPECTED_INPUT_FACTS || Number(meta.InputFacts || 0); if (meta.SourceSHA256 && meta.SourceSHA256 !== "b5af0caa2e473b5957ba8cec647ee4e2edf4cdc2a5c28110e8536c19381b0bd3") throw new Error('input evidence does not match the N3 source compiled into this example'); const result = saturate(facts, RULES); const rawOutput = renderRawOutput(result.graph, QUERIES, RULES, facts); fail('Compiled N3 derivation failed', { 'input evidence metadata is present and matches compiled source': meta.SourceSHA256 === "b5af0caa2e473b5957ba8cec647ee4e2edf4cdc2a5c28110e8536c19381b0bd3", 'input evidence facts were loaded': expectedFacts > 0 ? facts.length === expectedFacts : facts.length >= 0, 'compiled rules were loaded': RULES.length === 2, 'compiled query directives were loaded': QUERIES.length === 1, 'a derivation fixpoint was reached': result.graph.facts.length >= facts.length, 'query or output facts were produced': rawOutput.length > 0 }); return { ...result, rawOutput, inputFacts: facts }; }
|
|
1991
2006
|
function snapshotMarkdown(markdown) { return markdown.split(/\n/).map((line) => line ? line + ' \n' : '\n').join(''); }
|
|
@@ -2019,6 +2034,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
|
|
|
2019
2034
|
}
|
|
2020
2035
|
return out;
|
|
2021
2036
|
}
|
|
2037
|
+
function termHasTripleTerm(term) {
|
|
2038
|
+
if (!term) return false;
|
|
2039
|
+
if (term.kind === 'triple') return true;
|
|
2040
|
+
if (term.kind === 'list') return term.items.some(termHasTripleTerm);
|
|
2041
|
+
if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
|
|
2042
|
+
return false;
|
|
2043
|
+
}
|
|
2044
|
+
function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
|
|
2045
|
+
function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
|
|
2046
|
+
function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
|
|
2022
2047
|
function trigGraphBlock(label, atoms) {
|
|
2023
2048
|
const lines = [label + ' {'];
|
|
2024
2049
|
for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
|
|
@@ -2066,7 +2091,8 @@ function formalOutputToTrig(facts, trig) {
|
|
|
2066
2091
|
const prefixes = prefixLinesFromTrig(trig);
|
|
2067
2092
|
if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
|
|
2068
2093
|
const nl = String.fromCharCode(10);
|
|
2069
|
-
|
|
2094
|
+
const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
|
|
2095
|
+
return version + prefixes.join(nl) + nl + nl + body.join(nl);
|
|
2070
2096
|
}
|
|
2071
2097
|
function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
|
|
2072
2098
|
const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
|
package/see/examples/equals.js
CHANGED
|
@@ -8,6 +8,7 @@ const crypto = require('crypto');
|
|
|
8
8
|
|
|
9
9
|
function canonical(term) {
|
|
10
10
|
if (term.kind === 'list') return ['list', term.items.map(canonical)];
|
|
11
|
+
if (term.kind === 'triple') return ['triple', canonical(term.s), canonical(term.p), canonical(term.o)];
|
|
11
12
|
if (term.kind === 'formula') return ['formula', term.atoms.map((a) => [canonical(a.s), canonical(a.p), canonical(a.o)])];
|
|
12
13
|
return [term.kind, term.value];
|
|
13
14
|
}
|
|
@@ -17,6 +18,7 @@ function compoundIndexKey() { return Array.from(arguments).map(termIndexKey).joi
|
|
|
17
18
|
function termIsConcrete(t) {
|
|
18
19
|
if (!t || t.kind === 'var') return false;
|
|
19
20
|
if (t.kind === 'list') return t.items.every(termIsConcrete);
|
|
21
|
+
if (t.kind === 'triple') return termIsConcrete(t.s) && termIsConcrete(t.p) && termIsConcrete(t.o);
|
|
20
22
|
if (t.kind === 'formula') return t.atoms.every((a) => termIsConcrete(a.s) && termIsConcrete(a.p) && termIsConcrete(a.o));
|
|
21
23
|
return true;
|
|
22
24
|
}
|
|
@@ -32,6 +34,7 @@ function primitive(t) {
|
|
|
32
34
|
if (t.kind === 'iri') return t.value.replace(/^:/, '');
|
|
33
35
|
if (t.kind === 'blank') return t.value;
|
|
34
36
|
if (t.kind === 'list') return t.items.map(primitive);
|
|
37
|
+
if (t.kind === 'triple') return termToN3(t);
|
|
35
38
|
if (t.kind === 'formula') return termToN3(t);
|
|
36
39
|
return undefined;
|
|
37
40
|
}
|
|
@@ -52,6 +55,7 @@ function termToN3(t) {
|
|
|
52
55
|
if (t.kind === 'var') return '?' + t.value;
|
|
53
56
|
if (t.kind === 'blank') return t.value.startsWith('_:') ? t.value : '_:' + t.value.replace(/^_+/, '');
|
|
54
57
|
if (t.kind === 'list') return '(' + t.items.map(termToN3).join(' ') + ')';
|
|
58
|
+
if (t.kind === 'triple') return '<<( ' + termToN3(t.s) + ' ' + termToN3(t.p) + ' ' + termToN3(t.o) + ' )>>';
|
|
55
59
|
if (t.kind === 'formula') return '{ ' + t.atoms.map(atomToN3).join(' . ') + ' }';
|
|
56
60
|
return String(t.value ?? t);
|
|
57
61
|
}
|
|
@@ -74,6 +78,7 @@ function resolve(term, env, seen = new Set()) {
|
|
|
74
78
|
return resolve(env[term.value], env, seen);
|
|
75
79
|
}
|
|
76
80
|
if (term.kind === 'list') return list(term.items.map((item) => resolve(item, env, seen)));
|
|
81
|
+
if (term.kind === 'triple') return { kind: 'triple', s: resolve(term.s, env), p: resolve(term.p, env), o: resolve(term.o, env) };
|
|
77
82
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: resolve(a.s, env), p: resolve(a.p, env), o: resolve(a.o, env) })) };
|
|
78
83
|
return term;
|
|
79
84
|
}
|
|
@@ -91,6 +96,14 @@ function unify(a, b, env) {
|
|
|
91
96
|
}
|
|
92
97
|
return out;
|
|
93
98
|
}
|
|
99
|
+
if (a.kind === 'triple' || b.kind === 'triple') {
|
|
100
|
+
if (a.kind !== 'triple' || b.kind !== 'triple') return null;
|
|
101
|
+
let out = unify(a.s, b.s, env);
|
|
102
|
+
if (!out) return null;
|
|
103
|
+
out = unify(a.p, b.p, out);
|
|
104
|
+
if (!out) return null;
|
|
105
|
+
return unify(a.o, b.o, out);
|
|
106
|
+
}
|
|
94
107
|
return deepEqual(a, b) ? env : null;
|
|
95
108
|
}
|
|
96
109
|
function bind(pattern, value, env) { return unify(pattern, value, env); }
|
|
@@ -106,6 +119,7 @@ function termIsGround(t, env) {
|
|
|
106
119
|
const r = resolve(t, env);
|
|
107
120
|
if (r.kind === 'var') return false;
|
|
108
121
|
if (r.kind === 'list') return r.items.every((item) => termIsGround(item, env));
|
|
122
|
+
if (r.kind === 'triple') return termIsGround(r.s, env) && termIsGround(r.p, env) && termIsGround(r.o, env);
|
|
109
123
|
if (r.kind === 'formula') return r.atoms.every((atom) => atomIsGround(atom, env));
|
|
110
124
|
return true;
|
|
111
125
|
}
|
|
@@ -665,6 +679,7 @@ function instantiate(term, env, ruleId) {
|
|
|
665
679
|
}
|
|
666
680
|
if (term.kind === 'blank') return blank('_:r' + ruleId + '_' + envSignature(env) + '_' + term.value.replace(/^_/, ''));
|
|
667
681
|
if (term.kind === 'list') return list(term.items.map((item) => instantiate(item, env, ruleId)));
|
|
682
|
+
if (term.kind === 'triple') return { kind: 'triple', s: instantiate(term.s, env, ruleId), p: instantiate(term.p, env, ruleId), o: instantiate(term.o, env, ruleId) };
|
|
668
683
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: instantiate(a.s, env, ruleId), p: instantiate(a.p, env, ruleId), o: instantiate(a.o, env, ruleId) })) };
|
|
669
684
|
return cloneTerm(term);
|
|
670
685
|
}
|
|
@@ -1040,19 +1055,19 @@ function renderStructuredOutput({ title, graph, queries = [], rules = [], initia
|
|
|
1040
1055
|
const lines = [];
|
|
1041
1056
|
lines.push('# ' + title);
|
|
1042
1057
|
lines.push('');
|
|
1043
|
-
lines.push('##
|
|
1058
|
+
lines.push('## Entailment');
|
|
1044
1059
|
if (mode === 'query') {
|
|
1045
1060
|
lines.push('The compiled query selected ' + selected.length + ' fact(s) after the rule closure was computed.');
|
|
1046
1061
|
} else if (mode === 'formula') {
|
|
1047
|
-
lines.push('The derivation produced ' + selected.length + ' formula-valued
|
|
1062
|
+
lines.push('The derivation produced ' + selected.length + ' formula-valued entailment(s).');
|
|
1048
1063
|
} else {
|
|
1049
1064
|
lines.push('The derivation produced ' + derived.length + ' new fact(s) from ' + initialFacts.length + ' stated fact(s).');
|
|
1050
1065
|
}
|
|
1051
|
-
if (keyFact) lines.push('Main
|
|
1066
|
+
if (keyFact) lines.push('Main entailment: **' + factSentence(keyFact) + '**');
|
|
1052
1067
|
const bullets = selected.slice(-6).reverse();
|
|
1053
1068
|
if (bullets.length) {
|
|
1054
1069
|
lines.push('');
|
|
1055
|
-
lines.push('Selected
|
|
1070
|
+
lines.push('Selected entailments:');
|
|
1056
1071
|
for (const fact of bullets) lines.push('- ' + codeFact(fact));
|
|
1057
1072
|
}
|
|
1058
1073
|
lines.push('');
|
|
@@ -1114,15 +1129,15 @@ function dedupeExplanationHeadings(text) {
|
|
|
1114
1129
|
function normalizePublicReport(markdown, title) {
|
|
1115
1130
|
let text = String(markdown || '').trimEnd();
|
|
1116
1131
|
if (!/^\s*#\s+/m.test(text)) text = '# ' + title + '\n\n' + text;
|
|
1117
|
-
if (!/^##\s+
|
|
1118
|
-
text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n##
|
|
1132
|
+
if (!/^##\s+Entailment\s*$/mi.test(text)) {
|
|
1133
|
+
text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n## Entailment\n');
|
|
1119
1134
|
}
|
|
1120
1135
|
if (!/^##\s+Explanation\s*$/mi.test(text)) {
|
|
1121
1136
|
text += '\n\n## Explanation\nNo additional explanation was provided by the generated output.';
|
|
1122
1137
|
}
|
|
1123
1138
|
text = text.replace(/^##\s+([^\n]+?)\s*$/gm, (line, heading) => {
|
|
1124
1139
|
const normalized = heading.trim().toLowerCase();
|
|
1125
|
-
if (normalized === 'insight' || normalized === 'explanation') return '## ' + (normalized === '
|
|
1140
|
+
if (normalized === 'insight' || normalized === 'conclusion' || normalized === 'entailment' || normalized === 'explanation') return '## ' + (normalized === 'explanation' ? 'Explanation' : 'Entailment');
|
|
1126
1141
|
return '**' + heading.trim() + '**';
|
|
1127
1142
|
});
|
|
1128
1143
|
text = dedupeExplanationHeadings(text);
|
|
@@ -1131,13 +1146,13 @@ function normalizePublicReport(markdown, title) {
|
|
|
1131
1146
|
function markdownize(raw, title) {
|
|
1132
1147
|
let text = String(raw || '');
|
|
1133
1148
|
text = text
|
|
1134
|
-
.replace(/===\s*Answer\s*===/g, '##
|
|
1149
|
+
.replace(/===\s*Answer\s*===/g, '## Entailment')
|
|
1135
1150
|
.replace(/===\s*Reason\s+Why\s*===/gi, '## Explanation')
|
|
1136
1151
|
.replace(/===\s*Explanation\s*===/gi, '## Explanation')
|
|
1137
1152
|
.replace(/===\s*([^=]+?)\s*===/g, (_, h) => '**' + h.trim() + '**');
|
|
1138
1153
|
text = text.replace(/^C(\d+)\s+OK\s*-\s*/gm, 'C$1: ');
|
|
1139
1154
|
text = dedupeExplanationHeadings(text);
|
|
1140
|
-
if (!text.trim()) text = '##
|
|
1155
|
+
if (!text.trim()) text = '## Entailment\nNo log:outputString facts were derived.\n\n## Explanation\nThe compiled derivation did not produce authored report text.';
|
|
1141
1156
|
return normalizePublicReport(text, title);
|
|
1142
1157
|
}
|
|
1143
1158
|
function authoredSupportAppendix(graph, queries, rules, initialFacts, trace) {
|
|
@@ -1238,7 +1253,7 @@ const RULES = [
|
|
|
1238
1253
|
}
|
|
1239
1254
|
];
|
|
1240
1255
|
const QUERIES = [];
|
|
1241
|
-
const DOC_MARKDOWN = "# Equals test\n\nGenerated by `see.js` from a Notation3 source file.\n\nExample from Patrick Hochstenbach\n\n## Compilation summary\n\n- Example name: `equals`\n- Input facts emitted: 1\n- Forward rules compiled: 1\n- Backward predicate rules compiled: 0\n- Fuses compiled: 0\n- Predicate count: 2\n\n## Built-ins used\n\n- none\n\n## Runtime model\n\nThe generated `examples/equals.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/equals.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/equals.trig` or `examples/input/equals.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.\n\n## Output model\n\nRunning `node examples/equals.js` produces a SEE-style Markdown report with an **
|
|
1256
|
+
const DOC_MARKDOWN = "# Equals test\n\nGenerated by `see.js` from a Notation3 source file.\n\nExample from Patrick Hochstenbach\n\n## Compilation summary\n\n- Example name: `equals`\n- Input facts emitted: 1\n- Forward rules compiled: 1\n- Backward predicate rules compiled: 0\n- Fuses compiled: 0\n- Predicate count: 2\n\n## Built-ins used\n\n- none\n\n## Runtime model\n\nThe generated `examples/equals.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/equals.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/equals.trig` or `examples/input/equals.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.\n\n## Output model\n\nRunning `node examples/equals.js` produces a SEE-style Markdown report with an **Entailment** section, an **Explanation** section, and a **Formal TriG Output** section containing the selected derived/query facts.\n";
|
|
1242
1257
|
function seeMetadata(data) { return (data && data.__see) || {}; }
|
|
1243
1258
|
function trustedDerivation(data) { const meta = seeMetadata(data); const facts = data && Array.isArray(data.facts) ? data.facts : []; const expectedFacts = EXPECTED_INPUT_FACTS || Number(meta.InputFacts || 0); if (meta.SourceSHA256 && meta.SourceSHA256 !== "29dfd303c09b0b2f05c637d6a56b6e3b198053a0732aa179ca3e75265dfb8003") throw new Error('input evidence does not match the N3 source compiled into this example'); const result = saturate(facts, RULES); const rawOutput = renderRawOutput(result.graph, QUERIES, RULES, facts); fail('Compiled N3 derivation failed', { 'input evidence metadata is present and matches compiled source': meta.SourceSHA256 === "29dfd303c09b0b2f05c637d6a56b6e3b198053a0732aa179ca3e75265dfb8003", 'input evidence facts were loaded': expectedFacts > 0 ? facts.length === expectedFacts : facts.length >= 0, 'compiled rules were loaded': RULES.length === 1, 'compiled query directives were loaded': QUERIES.length === 0, 'a derivation fixpoint was reached': result.graph.facts.length >= facts.length, 'query or output facts were produced': rawOutput.length > 0 }); return { ...result, rawOutput, inputFacts: facts }; }
|
|
1244
1259
|
function snapshotMarkdown(markdown) { return markdown.split(/\n/).map((line) => line ? line + ' \n' : '\n').join(''); }
|
|
@@ -1272,6 +1287,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
|
|
|
1272
1287
|
}
|
|
1273
1288
|
return out;
|
|
1274
1289
|
}
|
|
1290
|
+
function termHasTripleTerm(term) {
|
|
1291
|
+
if (!term) return false;
|
|
1292
|
+
if (term.kind === 'triple') return true;
|
|
1293
|
+
if (term.kind === 'list') return term.items.some(termHasTripleTerm);
|
|
1294
|
+
if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
|
|
1295
|
+
return false;
|
|
1296
|
+
}
|
|
1297
|
+
function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
|
|
1298
|
+
function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
|
|
1299
|
+
function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
|
|
1275
1300
|
function trigGraphBlock(label, atoms) {
|
|
1276
1301
|
const lines = [label + ' {'];
|
|
1277
1302
|
for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
|
|
@@ -1319,7 +1344,8 @@ function formalOutputToTrig(facts, trig) {
|
|
|
1319
1344
|
const prefixes = prefixLinesFromTrig(trig);
|
|
1320
1345
|
if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
|
|
1321
1346
|
const nl = String.fromCharCode(10);
|
|
1322
|
-
|
|
1347
|
+
const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
|
|
1348
|
+
return version + prefixes.join(nl) + nl + nl + body.join(nl);
|
|
1323
1349
|
}
|
|
1324
1350
|
function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
|
|
1325
1351
|
const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
|
|
@@ -8,6 +8,7 @@ const crypto = require('crypto');
|
|
|
8
8
|
|
|
9
9
|
function canonical(term) {
|
|
10
10
|
if (term.kind === 'list') return ['list', term.items.map(canonical)];
|
|
11
|
+
if (term.kind === 'triple') return ['triple', canonical(term.s), canonical(term.p), canonical(term.o)];
|
|
11
12
|
if (term.kind === 'formula') return ['formula', term.atoms.map((a) => [canonical(a.s), canonical(a.p), canonical(a.o)])];
|
|
12
13
|
return [term.kind, term.value];
|
|
13
14
|
}
|
|
@@ -17,6 +18,7 @@ function compoundIndexKey() { return Array.from(arguments).map(termIndexKey).joi
|
|
|
17
18
|
function termIsConcrete(t) {
|
|
18
19
|
if (!t || t.kind === 'var') return false;
|
|
19
20
|
if (t.kind === 'list') return t.items.every(termIsConcrete);
|
|
21
|
+
if (t.kind === 'triple') return termIsConcrete(t.s) && termIsConcrete(t.p) && termIsConcrete(t.o);
|
|
20
22
|
if (t.kind === 'formula') return t.atoms.every((a) => termIsConcrete(a.s) && termIsConcrete(a.p) && termIsConcrete(a.o));
|
|
21
23
|
return true;
|
|
22
24
|
}
|
|
@@ -32,6 +34,7 @@ function primitive(t) {
|
|
|
32
34
|
if (t.kind === 'iri') return t.value.replace(/^:/, '');
|
|
33
35
|
if (t.kind === 'blank') return t.value;
|
|
34
36
|
if (t.kind === 'list') return t.items.map(primitive);
|
|
37
|
+
if (t.kind === 'triple') return termToN3(t);
|
|
35
38
|
if (t.kind === 'formula') return termToN3(t);
|
|
36
39
|
return undefined;
|
|
37
40
|
}
|
|
@@ -52,6 +55,7 @@ function termToN3(t) {
|
|
|
52
55
|
if (t.kind === 'var') return '?' + t.value;
|
|
53
56
|
if (t.kind === 'blank') return t.value.startsWith('_:') ? t.value : '_:' + t.value.replace(/^_+/, '');
|
|
54
57
|
if (t.kind === 'list') return '(' + t.items.map(termToN3).join(' ') + ')';
|
|
58
|
+
if (t.kind === 'triple') return '<<( ' + termToN3(t.s) + ' ' + termToN3(t.p) + ' ' + termToN3(t.o) + ' )>>';
|
|
55
59
|
if (t.kind === 'formula') return '{ ' + t.atoms.map(atomToN3).join(' . ') + ' }';
|
|
56
60
|
return String(t.value ?? t);
|
|
57
61
|
}
|
|
@@ -74,6 +78,7 @@ function resolve(term, env, seen = new Set()) {
|
|
|
74
78
|
return resolve(env[term.value], env, seen);
|
|
75
79
|
}
|
|
76
80
|
if (term.kind === 'list') return list(term.items.map((item) => resolve(item, env, seen)));
|
|
81
|
+
if (term.kind === 'triple') return { kind: 'triple', s: resolve(term.s, env), p: resolve(term.p, env), o: resolve(term.o, env) };
|
|
77
82
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: resolve(a.s, env), p: resolve(a.p, env), o: resolve(a.o, env) })) };
|
|
78
83
|
return term;
|
|
79
84
|
}
|
|
@@ -91,6 +96,14 @@ function unify(a, b, env) {
|
|
|
91
96
|
}
|
|
92
97
|
return out;
|
|
93
98
|
}
|
|
99
|
+
if (a.kind === 'triple' || b.kind === 'triple') {
|
|
100
|
+
if (a.kind !== 'triple' || b.kind !== 'triple') return null;
|
|
101
|
+
let out = unify(a.s, b.s, env);
|
|
102
|
+
if (!out) return null;
|
|
103
|
+
out = unify(a.p, b.p, out);
|
|
104
|
+
if (!out) return null;
|
|
105
|
+
return unify(a.o, b.o, out);
|
|
106
|
+
}
|
|
94
107
|
return deepEqual(a, b) ? env : null;
|
|
95
108
|
}
|
|
96
109
|
function bind(pattern, value, env) { return unify(pattern, value, env); }
|
|
@@ -106,6 +119,7 @@ function termIsGround(t, env) {
|
|
|
106
119
|
const r = resolve(t, env);
|
|
107
120
|
if (r.kind === 'var') return false;
|
|
108
121
|
if (r.kind === 'list') return r.items.every((item) => termIsGround(item, env));
|
|
122
|
+
if (r.kind === 'triple') return termIsGround(r.s, env) && termIsGround(r.p, env) && termIsGround(r.o, env);
|
|
109
123
|
if (r.kind === 'formula') return r.atoms.every((atom) => atomIsGround(atom, env));
|
|
110
124
|
return true;
|
|
111
125
|
}
|
|
@@ -665,6 +679,7 @@ function instantiate(term, env, ruleId) {
|
|
|
665
679
|
}
|
|
666
680
|
if (term.kind === 'blank') return blank('_:r' + ruleId + '_' + envSignature(env) + '_' + term.value.replace(/^_/, ''));
|
|
667
681
|
if (term.kind === 'list') return list(term.items.map((item) => instantiate(item, env, ruleId)));
|
|
682
|
+
if (term.kind === 'triple') return { kind: 'triple', s: instantiate(term.s, env, ruleId), p: instantiate(term.p, env, ruleId), o: instantiate(term.o, env, ruleId) };
|
|
668
683
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: instantiate(a.s, env, ruleId), p: instantiate(a.p, env, ruleId), o: instantiate(a.o, env, ruleId) })) };
|
|
669
684
|
return cloneTerm(term);
|
|
670
685
|
}
|
|
@@ -1040,19 +1055,19 @@ function renderStructuredOutput({ title, graph, queries = [], rules = [], initia
|
|
|
1040
1055
|
const lines = [];
|
|
1041
1056
|
lines.push('# ' + title);
|
|
1042
1057
|
lines.push('');
|
|
1043
|
-
lines.push('##
|
|
1058
|
+
lines.push('## Entailment');
|
|
1044
1059
|
if (mode === 'query') {
|
|
1045
1060
|
lines.push('The compiled query selected ' + selected.length + ' fact(s) after the rule closure was computed.');
|
|
1046
1061
|
} else if (mode === 'formula') {
|
|
1047
|
-
lines.push('The derivation produced ' + selected.length + ' formula-valued
|
|
1062
|
+
lines.push('The derivation produced ' + selected.length + ' formula-valued entailment(s).');
|
|
1048
1063
|
} else {
|
|
1049
1064
|
lines.push('The derivation produced ' + derived.length + ' new fact(s) from ' + initialFacts.length + ' stated fact(s).');
|
|
1050
1065
|
}
|
|
1051
|
-
if (keyFact) lines.push('Main
|
|
1066
|
+
if (keyFact) lines.push('Main entailment: **' + factSentence(keyFact) + '**');
|
|
1052
1067
|
const bullets = selected.slice(-6).reverse();
|
|
1053
1068
|
if (bullets.length) {
|
|
1054
1069
|
lines.push('');
|
|
1055
|
-
lines.push('Selected
|
|
1070
|
+
lines.push('Selected entailments:');
|
|
1056
1071
|
for (const fact of bullets) lines.push('- ' + codeFact(fact));
|
|
1057
1072
|
}
|
|
1058
1073
|
lines.push('');
|
|
@@ -1114,15 +1129,15 @@ function dedupeExplanationHeadings(text) {
|
|
|
1114
1129
|
function normalizePublicReport(markdown, title) {
|
|
1115
1130
|
let text = String(markdown || '').trimEnd();
|
|
1116
1131
|
if (!/^\s*#\s+/m.test(text)) text = '# ' + title + '\n\n' + text;
|
|
1117
|
-
if (!/^##\s+
|
|
1118
|
-
text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n##
|
|
1132
|
+
if (!/^##\s+Entailment\s*$/mi.test(text)) {
|
|
1133
|
+
text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n## Entailment\n');
|
|
1119
1134
|
}
|
|
1120
1135
|
if (!/^##\s+Explanation\s*$/mi.test(text)) {
|
|
1121
1136
|
text += '\n\n## Explanation\nNo additional explanation was provided by the generated output.';
|
|
1122
1137
|
}
|
|
1123
1138
|
text = text.replace(/^##\s+([^\n]+?)\s*$/gm, (line, heading) => {
|
|
1124
1139
|
const normalized = heading.trim().toLowerCase();
|
|
1125
|
-
if (normalized === 'insight' || normalized === 'explanation') return '## ' + (normalized === '
|
|
1140
|
+
if (normalized === 'insight' || normalized === 'conclusion' || normalized === 'entailment' || normalized === 'explanation') return '## ' + (normalized === 'explanation' ? 'Explanation' : 'Entailment');
|
|
1126
1141
|
return '**' + heading.trim() + '**';
|
|
1127
1142
|
});
|
|
1128
1143
|
text = dedupeExplanationHeadings(text);
|
|
@@ -1131,13 +1146,13 @@ function normalizePublicReport(markdown, title) {
|
|
|
1131
1146
|
function markdownize(raw, title) {
|
|
1132
1147
|
let text = String(raw || '');
|
|
1133
1148
|
text = text
|
|
1134
|
-
.replace(/===\s*Answer\s*===/g, '##
|
|
1149
|
+
.replace(/===\s*Answer\s*===/g, '## Entailment')
|
|
1135
1150
|
.replace(/===\s*Reason\s+Why\s*===/gi, '## Explanation')
|
|
1136
1151
|
.replace(/===\s*Explanation\s*===/gi, '## Explanation')
|
|
1137
1152
|
.replace(/===\s*([^=]+?)\s*===/g, (_, h) => '**' + h.trim() + '**');
|
|
1138
1153
|
text = text.replace(/^C(\d+)\s+OK\s*-\s*/gm, 'C$1: ');
|
|
1139
1154
|
text = dedupeExplanationHeadings(text);
|
|
1140
|
-
if (!text.trim()) text = '##
|
|
1155
|
+
if (!text.trim()) text = '## Entailment\nNo log:outputString facts were derived.\n\n## Explanation\nThe compiled derivation did not produce authored report text.';
|
|
1141
1156
|
return normalizePublicReport(text, title);
|
|
1142
1157
|
}
|
|
1143
1158
|
function authoredSupportAppendix(graph, queries, rules, initialFacts, trace) {
|
|
@@ -1667,7 +1682,7 @@ const QUERIES = [
|
|
|
1667
1682
|
]
|
|
1668
1683
|
}
|
|
1669
1684
|
];
|
|
1670
|
-
const DOC_MARKDOWN = "# If two equivalence classes share an element, they are the same class.\n\nGenerated by `see.js` from a Notation3 source file.\n\n## Compilation summary\n\n- Example name: `equivalence_classes_overlap_implies_same_class`\n- Input facts emitted: 5\n- Forward rules compiled: 7\n- Backward predicate rules compiled: 0\n- Fuses compiled: 0\n- Predicate count: 7\n\n## Built-ins used\n\n- `log:notEqualTo`\n\n## Runtime model\n\nThe generated `examples/equivalence_classes_overlap_implies_same_class.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/equivalence_classes_overlap_implies_same_class.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/equivalence-classes-overlap-implies-same-class.trig` or `examples/input/equivalence_classes_overlap_implies_same_class.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.\n\n## Output model\n\nRunning `node examples/equivalence_classes_overlap_implies_same_class.js` produces a SEE-style Markdown report with an **
|
|
1685
|
+
const DOC_MARKDOWN = "# If two equivalence classes share an element, they are the same class.\n\nGenerated by `see.js` from a Notation3 source file.\n\n## Compilation summary\n\n- Example name: `equivalence_classes_overlap_implies_same_class`\n- Input facts emitted: 5\n- Forward rules compiled: 7\n- Backward predicate rules compiled: 0\n- Fuses compiled: 0\n- Predicate count: 7\n\n## Built-ins used\n\n- `log:notEqualTo`\n\n## Runtime model\n\nThe generated `examples/equivalence_classes_overlap_implies_same_class.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/equivalence_classes_overlap_implies_same_class.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/equivalence-classes-overlap-implies-same-class.trig` or `examples/input/equivalence_classes_overlap_implies_same_class.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.\n\n## Output model\n\nRunning `node examples/equivalence_classes_overlap_implies_same_class.js` produces a SEE-style Markdown report with an **Entailment** section, an **Explanation** section, and a **Formal TriG Output** section containing the selected derived/query facts.\n";
|
|
1671
1686
|
function seeMetadata(data) { return (data && data.__see) || {}; }
|
|
1672
1687
|
function trustedDerivation(data) { const meta = seeMetadata(data); const facts = data && Array.isArray(data.facts) ? data.facts : []; const expectedFacts = EXPECTED_INPUT_FACTS || Number(meta.InputFacts || 0); if (meta.SourceSHA256 && meta.SourceSHA256 !== "b36ed917f7af601a38f6a1657ce4956a4e38484573c40d0a963e3008cbb46f5f") throw new Error('input evidence does not match the N3 source compiled into this example'); const result = saturate(facts, RULES); const rawOutput = renderRawOutput(result.graph, QUERIES, RULES, facts); fail('Compiled N3 derivation failed', { 'input evidence metadata is present and matches compiled source': meta.SourceSHA256 === "b36ed917f7af601a38f6a1657ce4956a4e38484573c40d0a963e3008cbb46f5f", 'input evidence facts were loaded': expectedFacts > 0 ? facts.length === expectedFacts : facts.length >= 0, 'compiled rules were loaded': RULES.length === 7, 'compiled query directives were loaded': QUERIES.length === 1, 'a derivation fixpoint was reached': result.graph.facts.length >= facts.length, 'query or output facts were produced': rawOutput.length > 0 }); return { ...result, rawOutput, inputFacts: facts }; }
|
|
1673
1688
|
function snapshotMarkdown(markdown) { return markdown.split(/\n/).map((line) => line ? line + ' \n' : '\n').join(''); }
|
|
@@ -1701,6 +1716,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
|
|
|
1701
1716
|
}
|
|
1702
1717
|
return out;
|
|
1703
1718
|
}
|
|
1719
|
+
function termHasTripleTerm(term) {
|
|
1720
|
+
if (!term) return false;
|
|
1721
|
+
if (term.kind === 'triple') return true;
|
|
1722
|
+
if (term.kind === 'list') return term.items.some(termHasTripleTerm);
|
|
1723
|
+
if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
|
|
1724
|
+
return false;
|
|
1725
|
+
}
|
|
1726
|
+
function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
|
|
1727
|
+
function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
|
|
1728
|
+
function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
|
|
1704
1729
|
function trigGraphBlock(label, atoms) {
|
|
1705
1730
|
const lines = [label + ' {'];
|
|
1706
1731
|
for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
|
|
@@ -1748,7 +1773,8 @@ function formalOutputToTrig(facts, trig) {
|
|
|
1748
1773
|
const prefixes = prefixLinesFromTrig(trig);
|
|
1749
1774
|
if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
|
|
1750
1775
|
const nl = String.fromCharCode(10);
|
|
1751
|
-
|
|
1776
|
+
const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
|
|
1777
|
+
return version + prefixes.join(nl) + nl + nl + body.join(nl);
|
|
1752
1778
|
}
|
|
1753
1779
|
function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
|
|
1754
1780
|
const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
|