eyeling 1.7.3 → 1.7.5
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/LICENSE.md +1 -1
- package/README.md +1 -1
- package/examples/output/reaching-out.n3 +12 -0
- package/examples/reaching-out.n3 +19 -0
- package/eyeling.js +213 -6
- package/package.json +1 -1
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
### Copyright 2021-
|
|
3
|
+
### Copyright 2021-2026 Jos De Roo, KNoWS office of IDLab, Ghent University - imec
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -215,7 +215,7 @@ Commonly used N3/Turtle features:
|
|
|
215
215
|
|
|
216
216
|
- **crypto**: `crypto:md5` `crypto:sha` `crypto:sha256` `crypto:sha512`
|
|
217
217
|
- **list**: `list:append` `list:first` `list:firstRest` `list:in` `list:iterate` `list:last` `list:length` `list:map` `list:member` `list:memberAt` `list:notMember` `list:remove` `list:rest` `list:reverse` `list:sort`
|
|
218
|
-
- **log**: `log:collectAllIn` `log:equalTo` `log:forAllIn` `log:impliedBy` `log:implies` `log:notEqualTo` `log:notIncludes` `log:skolem` `log:uri`
|
|
218
|
+
- **log**: `log:collectAllIn` `log:content` `log:equalTo` `log:forAllIn` `log:impliedBy` `log:implies` `log:notEqualTo` `log:notIncludes` `log:semantics` `log:skolem` `log:uri`
|
|
219
219
|
- **math**: `math:absoluteValue` `math:acos` `math:asin` `math:atan` `math:cos` `math:cosh` `math:degrees` `math:difference` `math:equalTo` `math:exponentiation` `math:greaterThan` `math:integerQuotient` `math:lessThan` `math:negation` `math:notEqualTo` `math:notGreaterThan` `math:notLessThan` `math:product` `math:quotient` `math:remainder` `math:rounded` `math:sin` `math:sinh` `math:sum` `math:tan` `math:tanh`
|
|
220
220
|
- **string**: `string:concatenation` `string:contains` `string:containsIgnoringCase` `string:endsWith` `string:equalIgnoringCase` `string:format` `string:greaterThan` `string:jsonPointer` `string:lessThan` `string:matches` `string:notEqualIgnoringCase` `string:notGreaterThan` `string:notLessThan` `string:notMatches` `string:replace` `string:scrape` `string:startsWith`
|
|
221
221
|
- **time**: `time:localTime`
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
@prefix : <http://example.org/> .
|
|
2
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
3
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
4
|
+
|
|
5
|
+
:gotContent :is "# Schema for test data\n#\n# This is only \n@prefix daml: <http://www.daml.org/2001/03/daml+oil#> .\n@prefix mech: <#> .\n\n@prefix : <#> .\n\n:includes a daml:TransitiveProperty .\n\n:partOf a daml:TransitiveProperty; daml:inverseOf :includes .\n\n:dependsOn a daml:TransitiveProperty ;\n daml:hasSubProperty :includes . # Real name of subproperty?\n\n\n\n\n"^^xsd:string .
|
|
6
|
+
:gotFormula :is {
|
|
7
|
+
<https://www.w3.org/2000/10/swap/test/s1.n3#includes> a <http://www.daml.org/2001/03/daml+oil#TransitiveProperty> .
|
|
8
|
+
<https://www.w3.org/2000/10/swap/test/s1.n3#partOf> a <http://www.daml.org/2001/03/daml+oil#TransitiveProperty> .
|
|
9
|
+
<https://www.w3.org/2000/10/swap/test/s1.n3#partOf> <http://www.daml.org/2001/03/daml+oil#inverseOf> <https://www.w3.org/2000/10/swap/test/s1.n3#includes> .
|
|
10
|
+
<https://www.w3.org/2000/10/swap/test/s1.n3#dependsOn> a <http://www.daml.org/2001/03/daml+oil#TransitiveProperty> .
|
|
11
|
+
<https://www.w3.org/2000/10/swap/test/s1.n3#dependsOn> <http://www.daml.org/2001/03/daml+oil#hasSubProperty> <https://www.w3.org/2000/10/swap/test/s1.n3#includes> .
|
|
12
|
+
} .
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# =============================================
|
|
2
|
+
# Reaching out onto the Web
|
|
3
|
+
# See https://www.w3.org/2000/10/swap/doc/Reach
|
|
4
|
+
# =============================================
|
|
5
|
+
|
|
6
|
+
@prefix : <http://example.org/>.
|
|
7
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
<https://www.w3.org/2000/10/swap/test/s1.n3>
|
|
11
|
+
log:content ?txt ;
|
|
12
|
+
log:semantics ?f .
|
|
13
|
+
}
|
|
14
|
+
=>
|
|
15
|
+
{
|
|
16
|
+
:gotContent :is ?txt .
|
|
17
|
+
:gotFormula :is ?f .
|
|
18
|
+
}.
|
|
19
|
+
|
package/eyeling.js
CHANGED
|
@@ -82,6 +82,159 @@ const __parseNumericInfoCache = new Map(); // lit string -> info|null
|
|
|
82
82
|
// Cache for string:jsonPointer: jsonText -> { parsed: any|null, ptrCache: Map<string, Term|null> }
|
|
83
83
|
const jsonPointerCache = new Map();
|
|
84
84
|
|
|
85
|
+
// -----------------------------------------------------------------------------
|
|
86
|
+
// log:content / log:semantics support (basic, synchronous)
|
|
87
|
+
// -----------------------------------------------------------------------------
|
|
88
|
+
// Cache dereferenced resources within a single run.
|
|
89
|
+
// Key is the dereferenced document IRI *without* fragment.
|
|
90
|
+
const __logContentCache = new Map(); // iri -> string | null (null means fetch/read failed)
|
|
91
|
+
const __logSemanticsCache = new Map(); // iri -> GraphTerm | null (null means parse failed)
|
|
92
|
+
|
|
93
|
+
function __stripFragment(iri) {
|
|
94
|
+
const i = iri.indexOf('#');
|
|
95
|
+
return i >= 0 ? iri.slice(0, i) : iri;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function __isHttpIri(iri) {
|
|
99
|
+
return typeof iri === 'string' && (iri.startsWith('http://') || iri.startsWith('https://'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function __isFileIri(iri) {
|
|
103
|
+
return typeof iri === 'string' && iri.startsWith('file://');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function __fileIriToPath(fileIri) {
|
|
107
|
+
// Basic file:// URI decoding. Handles file:///abs/path and file://localhost/abs/path.
|
|
108
|
+
try {
|
|
109
|
+
const u = new URL(fileIri);
|
|
110
|
+
return decodeURIComponent(u.pathname);
|
|
111
|
+
} catch {
|
|
112
|
+
return decodeURIComponent(fileIri.replace(/^file:\/\//, ''));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function __readFileText(pathOrFileIri) {
|
|
117
|
+
const fs = require('fs');
|
|
118
|
+
let path = pathOrFileIri;
|
|
119
|
+
if (__isFileIri(pathOrFileIri)) path = __fileIriToPath(pathOrFileIri);
|
|
120
|
+
try {
|
|
121
|
+
return fs.readFileSync(path, { encoding: 'utf8' });
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function __fetchHttpTextViaSubprocess(url) {
|
|
128
|
+
const cp = require('child_process');
|
|
129
|
+
// Use a subprocess so this code remains synchronous without rewriting the whole reasoner to async.
|
|
130
|
+
const script = `
|
|
131
|
+
const url = process.argv[1];
|
|
132
|
+
const maxRedirects = 10;
|
|
133
|
+
function get(u, n) {
|
|
134
|
+
if (n > maxRedirects) { console.error('Too many redirects'); process.exit(3); }
|
|
135
|
+
let mod;
|
|
136
|
+
if (u.startsWith('https://')) mod = require('https');
|
|
137
|
+
else if (u.startsWith('http://')) mod = require('http');
|
|
138
|
+
else { console.error('Not http(s)'); process.exit(2); }
|
|
139
|
+
|
|
140
|
+
const { URL } = require('url');
|
|
141
|
+
const uu = new URL(u);
|
|
142
|
+
const opts = {
|
|
143
|
+
protocol: uu.protocol,
|
|
144
|
+
hostname: uu.hostname,
|
|
145
|
+
port: uu.port || undefined,
|
|
146
|
+
path: uu.pathname + uu.search,
|
|
147
|
+
headers: {
|
|
148
|
+
'accept': 'text/n3, text/turtle, application/n-triples, application/n-quads, text/plain;q=0.1, */*;q=0.01',
|
|
149
|
+
'user-agent': 'eyeling-log-builtins'
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const req = mod.request(opts, (res) => {
|
|
153
|
+
const sc = res.statusCode || 0;
|
|
154
|
+
if (sc >= 300 && sc < 400 && res.headers && res.headers.location) {
|
|
155
|
+
const next = new URL(res.headers.location, u).toString();
|
|
156
|
+
res.resume();
|
|
157
|
+
return get(next, n + 1);
|
|
158
|
+
}
|
|
159
|
+
if (sc < 200 || sc >= 300) {
|
|
160
|
+
res.resume();
|
|
161
|
+
console.error('HTTP status ' + sc);
|
|
162
|
+
process.exit(4);
|
|
163
|
+
}
|
|
164
|
+
res.setEncoding('utf8');
|
|
165
|
+
let data = '';
|
|
166
|
+
res.on('data', (c) => { data += c; });
|
|
167
|
+
res.on('end', () => { process.stdout.write(data); });
|
|
168
|
+
});
|
|
169
|
+
req.on('error', (e) => { console.error(e && e.message ? e.message : String(e)); process.exit(5); });
|
|
170
|
+
req.end();
|
|
171
|
+
}
|
|
172
|
+
get(url, 0);
|
|
173
|
+
`;
|
|
174
|
+
const r = cp.spawnSync(process.execPath, ['-e', script, url], {
|
|
175
|
+
encoding: 'utf8',
|
|
176
|
+
maxBuffer: 32 * 1024 * 1024
|
|
177
|
+
});
|
|
178
|
+
if (r.status !== 0) return null;
|
|
179
|
+
return r.stdout;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function __derefTextSync(iriNoFrag) {
|
|
183
|
+
if (__logContentCache.has(iriNoFrag)) return __logContentCache.get(iriNoFrag);
|
|
184
|
+
|
|
185
|
+
let text = null;
|
|
186
|
+
if (__isHttpIri(iriNoFrag)) {
|
|
187
|
+
text = __fetchHttpTextViaSubprocess(iriNoFrag);
|
|
188
|
+
} else {
|
|
189
|
+
// Treat any non-http(s) IRI as a local path (including file://), for basic usability.
|
|
190
|
+
text = __readFileText(iriNoFrag);
|
|
191
|
+
}
|
|
192
|
+
__logContentCache.set(iriNoFrag, text);
|
|
193
|
+
return text;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function __parseSemanticsToFormula(text, baseIri) {
|
|
197
|
+
const toks = lex(text);
|
|
198
|
+
const parser = new Parser(toks);
|
|
199
|
+
if (typeof baseIri === 'string' && baseIri) parser.prefixes.setBase(baseIri);
|
|
200
|
+
|
|
201
|
+
const [_prefixes, triples, frules, brules] = parser.parseDocument();
|
|
202
|
+
|
|
203
|
+
const all = triples.slice();
|
|
204
|
+
const impliesPred = internIri(LOG_NS + 'implies');
|
|
205
|
+
const impliedByPred = internIri(LOG_NS + 'impliedBy');
|
|
206
|
+
|
|
207
|
+
// Represent top-level => / <= rules as triples between formula terms,
|
|
208
|
+
// so the returned formula can include them.
|
|
209
|
+
for (const r of frules) {
|
|
210
|
+
all.push(new Triple(new GraphTerm(r.premise), impliesPred, new GraphTerm(r.conclusion)));
|
|
211
|
+
}
|
|
212
|
+
for (const r of brules) {
|
|
213
|
+
all.push(new Triple(new GraphTerm(r.conclusion), impliedByPred, new GraphTerm(r.premise)));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return new GraphTerm(all);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function __derefSemanticsSync(iriNoFrag) {
|
|
220
|
+
if (__logSemanticsCache.has(iriNoFrag)) return __logSemanticsCache.get(iriNoFrag);
|
|
221
|
+
|
|
222
|
+
const text = __derefTextSync(iriNoFrag);
|
|
223
|
+
if (typeof text !== 'string') {
|
|
224
|
+
__logSemanticsCache.set(iriNoFrag, null);
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
const formula = __parseSemanticsToFormula(text, iriNoFrag);
|
|
229
|
+
__logSemanticsCache.set(iriNoFrag, formula);
|
|
230
|
+
return formula;
|
|
231
|
+
} catch {
|
|
232
|
+
__logSemanticsCache.set(iriNoFrag, null);
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
|
|
85
238
|
// Controls whether human-readable proof comments are printed.
|
|
86
239
|
let proofCommentsEnabled = false;
|
|
87
240
|
// Super restricted mode: disable *all* builtins except => / <= (log:implies / log:impliedBy)
|
|
@@ -4569,19 +4722,26 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4569
4722
|
const inputList = inputTerm.elems;
|
|
4570
4723
|
if (!(predTerm instanceof Iri)) return [];
|
|
4571
4724
|
const pred = internIri(predTerm.value);
|
|
4572
|
-
|
|
4725
|
+
|
|
4726
|
+
// Allow mapping *any* predicate (not just builtins).
|
|
4727
|
+
// Semantics: for each input element `el`, collect *all* solutions of `el pred ?y`
|
|
4728
|
+
// (facts, rules, and builtins), in order, and concatenate them into the output list.
|
|
4729
|
+
// If an element has no solutions, it contributes nothing.
|
|
4573
4730
|
if (!inputList.every((e) => isGroundTerm(e))) return [];
|
|
4574
4731
|
|
|
4575
4732
|
const results = [];
|
|
4576
4733
|
for (const el of inputList) {
|
|
4577
4734
|
const yvar = new Var('_mapY');
|
|
4578
4735
|
const goal2 = new Triple(el, pred, yvar);
|
|
4579
|
-
const sols =
|
|
4580
|
-
|
|
4581
|
-
const
|
|
4582
|
-
|
|
4583
|
-
|
|
4736
|
+
const sols = proveGoals([goal2], subst, facts, backRules, depth + 1, [], varGen);
|
|
4737
|
+
|
|
4738
|
+
for (const sol of sols) {
|
|
4739
|
+
const yval = applySubstTerm(yvar, sol);
|
|
4740
|
+
if (yval instanceof Var) continue;
|
|
4741
|
+
results.push(yval);
|
|
4742
|
+
}
|
|
4584
4743
|
}
|
|
4744
|
+
|
|
4585
4745
|
const outList = new ListTerm(results);
|
|
4586
4746
|
const s2 = unifyTerm(g.o, outList, subst);
|
|
4587
4747
|
return s2 !== null ? [s2] : [];
|
|
@@ -4690,6 +4850,53 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4690
4850
|
return s2 !== null ? [s2] : [];
|
|
4691
4851
|
}
|
|
4692
4852
|
|
|
4853
|
+
// log:content
|
|
4854
|
+
// Schema: $s+ log:content $o?
|
|
4855
|
+
// Dereferences $s and returns the online resource as an xsd:string.
|
|
4856
|
+
if (pv === LOG_NS + 'content') {
|
|
4857
|
+
const iri = iriValue(g.s);
|
|
4858
|
+
if (iri === null) return [];
|
|
4859
|
+
const docIri = __stripFragment(iri);
|
|
4860
|
+
|
|
4861
|
+
const text = __derefTextSync(docIri);
|
|
4862
|
+
if (typeof text !== 'string') return [];
|
|
4863
|
+
|
|
4864
|
+
const lit = internLiteral(`${JSON.stringify(text)}^^<${XSD_NS}string>`);
|
|
4865
|
+
|
|
4866
|
+
if (g.o instanceof Var) {
|
|
4867
|
+
const s2 = { ...subst };
|
|
4868
|
+
s2[g.o.name] = lit;
|
|
4869
|
+
return [s2];
|
|
4870
|
+
}
|
|
4871
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
4872
|
+
|
|
4873
|
+
const s2 = unifyTerm(g.o, lit, subst);
|
|
4874
|
+
return s2 !== null ? [s2] : [];
|
|
4875
|
+
}
|
|
4876
|
+
|
|
4877
|
+
// log:semantics
|
|
4878
|
+
// Schema: $s+ log:semantics $o?
|
|
4879
|
+
// Dereferences $s, parses the retrieved resource, and returns it as a formula.
|
|
4880
|
+
if (pv === LOG_NS + 'semantics') {
|
|
4881
|
+
const iri = iriValue(g.s);
|
|
4882
|
+
if (iri === null) return [];
|
|
4883
|
+
const docIri = __stripFragment(iri);
|
|
4884
|
+
|
|
4885
|
+
const formula = __derefSemanticsSync(docIri);
|
|
4886
|
+
if (!(formula instanceof GraphTerm)) return [];
|
|
4887
|
+
|
|
4888
|
+
if (g.o instanceof Var) {
|
|
4889
|
+
const s2 = { ...subst };
|
|
4890
|
+
s2[g.o.name] = formula;
|
|
4891
|
+
return [s2];
|
|
4892
|
+
}
|
|
4893
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
4894
|
+
|
|
4895
|
+
const s2 = unifyTerm(g.o, formula, subst);
|
|
4896
|
+
return s2 !== null ? [s2] : [];
|
|
4897
|
+
}
|
|
4898
|
+
|
|
4899
|
+
|
|
4693
4900
|
// log:dtlit
|
|
4694
4901
|
// Schema: ( $s.1? $s.2? )? log:dtlit $o?
|
|
4695
4902
|
// true iff $o is a datatyped literal with string value $s.1 and datatype IRI $s.2
|