eyeling 1.24.24 → 1.24.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/browser/eyeling.browser.js +258 -17
- package/examples/input/rdf-message-flow.trig +87 -59
- package/examples/input/rdf-message-microgrid.trig +89 -0
- package/examples/input/rdf-messages.trig +61 -40
- package/examples/output/rdf-message-flow.md +2 -2
- package/examples/output/rdf-message-microgrid.md +14 -0
- package/examples/output/rdf-messages.md +4 -2
- package/examples/rdf-message-flow.n3 +84 -69
- package/examples/rdf-message-microgrid.n3 +141 -0
- package/examples/rdf-messages.n3 +72 -52
- package/eyeling.js +258 -17
- package/lib/builtins.js +106 -10
- package/lib/engine.js +4 -1
- package/lib/parser.js +14 -0
- package/lib/prelude.js +134 -6
- package/package.json +1 -1
- package/test/api.test.js +137 -0
package/lib/parser.js
CHANGED
|
@@ -465,6 +465,20 @@ class Parser {
|
|
|
465
465
|
if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'a') {
|
|
466
466
|
this.next();
|
|
467
467
|
pred = internIri(RDF_NS + 'type');
|
|
468
|
+
} else if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'has') {
|
|
469
|
+
// N3 syntactic sugar is also valid in predicate-object lists,
|
|
470
|
+
// including blank node property lists: [ has :p :o ] means _:b :p :o.
|
|
471
|
+
this.next();
|
|
472
|
+
pred = this.parseTerm();
|
|
473
|
+
} else if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'is') {
|
|
474
|
+
// N3 syntactic sugar: [ is :p of :s ] means :s :p _:b.
|
|
475
|
+
this.next();
|
|
476
|
+
pred = this.parseTerm();
|
|
477
|
+
if (!(this.peek().typ === 'Ident' && (this.peek().value || '') === 'of')) {
|
|
478
|
+
this.fail(`Expected 'of' after 'is <expr>', got ${this.peek().toString()}`);
|
|
479
|
+
}
|
|
480
|
+
this.next();
|
|
481
|
+
invert = true;
|
|
468
482
|
} else if (this.peek().typ === 'OpPredInvert') {
|
|
469
483
|
this.next();
|
|
470
484
|
pred = this.parseTerm();
|
package/lib/prelude.js
CHANGED
|
@@ -24,14 +24,142 @@ const STRING_NS = 'http://www.w3.org/2000/10/swap/string#';
|
|
|
24
24
|
const SKOLEM_NS = 'https://eyereasoner.github.io/.well-known/genid/';
|
|
25
25
|
const RDF_JSON_DT = RDF_NS + 'JSON';
|
|
26
26
|
|
|
27
|
+
function parseUriReferenceForResolution(uri) {
|
|
28
|
+
// RFC 3986 Appendix B-style component parser, with the scheme tightened to
|
|
29
|
+
// the RFC scheme grammar. Capturing delimiter presence matters: `?` with an
|
|
30
|
+
// empty query is defined, while no `?` means undefined.
|
|
31
|
+
const m = /^(([A-Za-z][A-Za-z0-9+.-]*):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/u.exec(String(uri));
|
|
32
|
+
if (!m) return null;
|
|
33
|
+
return {
|
|
34
|
+
scheme: m[2] !== undefined ? m[2] : undefined,
|
|
35
|
+
authority: m[4] !== undefined ? m[4] : undefined,
|
|
36
|
+
path: m[5] || '',
|
|
37
|
+
query: m[6] !== undefined ? (m[7] || '') : undefined,
|
|
38
|
+
fragment: m[8] !== undefined ? (m[9] || '') : undefined,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function recomposeUriReference(parts) {
|
|
43
|
+
let out = '';
|
|
44
|
+
if (parts.scheme !== undefined) out += `${parts.scheme}:`;
|
|
45
|
+
if (parts.authority !== undefined) out += `//${parts.authority}`;
|
|
46
|
+
out += parts.path || '';
|
|
47
|
+
if (parts.query !== undefined) out += `?${parts.query}`;
|
|
48
|
+
if (parts.fragment !== undefined) out += `#${parts.fragment}`;
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function removeLastPathSegment(path) {
|
|
53
|
+
if (!path) return '';
|
|
54
|
+
const i = path.lastIndexOf('/');
|
|
55
|
+
if (i < 0) return '';
|
|
56
|
+
if (i === 0) return '';
|
|
57
|
+
return path.slice(0, i);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function removeDotSegments(path) {
|
|
61
|
+
// RFC 3986 section 5.2.4. This deliberately avoids WHATWG URL parsing so
|
|
62
|
+
// Eyeling preserves IRI spelling (for example, it does not add a trailing
|
|
63
|
+
// slash to `http://example.org`) while still normalizing `.` and `..` path
|
|
64
|
+
// segments as required by section 5.2.2.
|
|
65
|
+
let input = String(path || '');
|
|
66
|
+
let output = '';
|
|
67
|
+
|
|
68
|
+
while (input.length > 0) {
|
|
69
|
+
if (input.startsWith('../')) {
|
|
70
|
+
input = input.slice(3);
|
|
71
|
+
} else if (input.startsWith('./')) {
|
|
72
|
+
input = input.slice(2);
|
|
73
|
+
} else if (input.startsWith('/./')) {
|
|
74
|
+
input = `/${input.slice(3)}`;
|
|
75
|
+
} else if (input === '/.') {
|
|
76
|
+
input = '/';
|
|
77
|
+
} else if (input.startsWith('/../')) {
|
|
78
|
+
input = `/${input.slice(4)}`;
|
|
79
|
+
output = removeLastPathSegment(output);
|
|
80
|
+
} else if (input === '/..') {
|
|
81
|
+
input = '/';
|
|
82
|
+
output = removeLastPathSegment(output);
|
|
83
|
+
} else if (input === '.' || input === '..') {
|
|
84
|
+
input = '';
|
|
85
|
+
} else {
|
|
86
|
+
let segmentEnd;
|
|
87
|
+
if (input[0] === '/') {
|
|
88
|
+
segmentEnd = input.indexOf('/', 1);
|
|
89
|
+
} else {
|
|
90
|
+
segmentEnd = input.indexOf('/');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (segmentEnd < 0) {
|
|
94
|
+
output += input;
|
|
95
|
+
input = '';
|
|
96
|
+
} else {
|
|
97
|
+
output += input.slice(0, segmentEnd);
|
|
98
|
+
input = input.slice(segmentEnd);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return output;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function mergePaths(base, refPath) {
|
|
107
|
+
if (base.authority !== undefined && base.path === '') {
|
|
108
|
+
return `/${refPath}`;
|
|
109
|
+
}
|
|
110
|
+
const i = base.path.lastIndexOf('/');
|
|
111
|
+
if (i < 0) return refPath;
|
|
112
|
+
return `${base.path.slice(0, i + 1)}${refPath}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
27
115
|
function resolveIriRef(ref, base) {
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
116
|
+
const r = parseUriReferenceForResolution(ref);
|
|
117
|
+
if (!r) return ref;
|
|
118
|
+
|
|
119
|
+
const baseParts = base ? parseUriReferenceForResolution(base) : null;
|
|
120
|
+
|
|
121
|
+
// Absolute references do not need a base, but RFC 3986 section 5.2.2 still
|
|
122
|
+
// applies remove_dot_segments(R.path) when R.scheme is defined.
|
|
123
|
+
if (r.scheme !== undefined) {
|
|
124
|
+
return recomposeUriReference({
|
|
125
|
+
scheme: r.scheme,
|
|
126
|
+
authority: r.authority,
|
|
127
|
+
path: removeDotSegments(r.path),
|
|
128
|
+
query: r.query,
|
|
129
|
+
fragment: r.fragment,
|
|
130
|
+
});
|
|
34
131
|
}
|
|
132
|
+
|
|
133
|
+
// Without a usable base, preserve relative references as written.
|
|
134
|
+
if (!baseParts || baseParts.scheme === undefined) return ref;
|
|
135
|
+
|
|
136
|
+
const t = {
|
|
137
|
+
scheme: baseParts.scheme,
|
|
138
|
+
authority: undefined,
|
|
139
|
+
path: '',
|
|
140
|
+
query: undefined,
|
|
141
|
+
fragment: r.fragment,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
if (r.authority !== undefined) {
|
|
145
|
+
t.authority = r.authority;
|
|
146
|
+
t.path = removeDotSegments(r.path);
|
|
147
|
+
t.query = r.query;
|
|
148
|
+
} else if (r.path === '') {
|
|
149
|
+
t.authority = baseParts.authority;
|
|
150
|
+
t.path = baseParts.path;
|
|
151
|
+
t.query = r.query !== undefined ? r.query : baseParts.query;
|
|
152
|
+
} else {
|
|
153
|
+
t.authority = baseParts.authority;
|
|
154
|
+
if (r.path.startsWith('/')) {
|
|
155
|
+
t.path = removeDotSegments(r.path);
|
|
156
|
+
} else {
|
|
157
|
+
t.path = removeDotSegments(mergePaths(baseParts, r.path));
|
|
158
|
+
}
|
|
159
|
+
t.query = r.query;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return recomposeUriReference(t);
|
|
35
163
|
}
|
|
36
164
|
|
|
37
165
|
// -----------------------------------------------------------------------------
|
package/package.json
CHANGED
package/test/api.test.js
CHANGED
|
@@ -1269,6 +1269,38 @@ _:l2 rdf:rest rdf:nil.
|
|
|
1269
1269
|
// Newer eyeling.js features
|
|
1270
1270
|
// -------------------------
|
|
1271
1271
|
|
|
1272
|
+
{
|
|
1273
|
+
name: '50b regression: rdf:first/rest with variable subject binds existing N3 list term',
|
|
1274
|
+
opt: { proofComments: false },
|
|
1275
|
+
input: `@prefix : <http://example.org/> .
|
|
1276
|
+
|
|
1277
|
+
(1) <http://a.example/p> <http://a.example/o> .
|
|
1278
|
+
|
|
1279
|
+
{
|
|
1280
|
+
_:el1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> 1 .
|
|
1281
|
+
_:el1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
|
|
1282
|
+
_:el1 <http://a.example/p> <http://a.example/o> .
|
|
1283
|
+
}
|
|
1284
|
+
=>
|
|
1285
|
+
{
|
|
1286
|
+
:result :has :success-literal-25.
|
|
1287
|
+
}.
|
|
1288
|
+
|
|
1289
|
+
{} => {
|
|
1290
|
+
:test :contains :success-literal-25.
|
|
1291
|
+
}.
|
|
1292
|
+
|
|
1293
|
+
{
|
|
1294
|
+
:result :has :success-literal-25.
|
|
1295
|
+
}
|
|
1296
|
+
=>
|
|
1297
|
+
{
|
|
1298
|
+
:test :is true.
|
|
1299
|
+
}.
|
|
1300
|
+
`,
|
|
1301
|
+
expect: [/:result\s+:has\s+:success-literal-25\s*\./, /:test\s+:is\s+true\s*\./],
|
|
1302
|
+
},
|
|
1303
|
+
|
|
1272
1304
|
{
|
|
1273
1305
|
name: '51 automatic output rendering: prints log:outputString values ordered by key (subject)',
|
|
1274
1306
|
opt: ['-n'],
|
|
@@ -1396,6 +1428,111 @@ res:CITY_Chañaral rdfs:label "Chañaral".
|
|
|
1396
1428
|
expect: [/:result\s+:has\s+:success-literal-24\s*\./, /:test\s+:is\s+true\s*\./],
|
|
1397
1429
|
},
|
|
1398
1430
|
|
|
1431
|
+
{
|
|
1432
|
+
name: '52d regression: @base and @prefix remapping resolve relative IRIs incrementally',
|
|
1433
|
+
opt: { proofComments: false },
|
|
1434
|
+
input: `# Reference: https://www.w3.org/TR/turtle/#sec-iri-references
|
|
1435
|
+
@base <http://foo/bar/> .
|
|
1436
|
+
<a1> <b1> <c1> .
|
|
1437
|
+
@base <http://example.org/ns/> .
|
|
1438
|
+
<a2> <http://example.org/ns/b2> <c2> .
|
|
1439
|
+
@base <foo/> .
|
|
1440
|
+
<a3> <b3> <c3> .
|
|
1441
|
+
@prefix : <bar#> .
|
|
1442
|
+
:a4 :b4 :c4 .
|
|
1443
|
+
@prefix : <http://example.org/ns2#> .
|
|
1444
|
+
:a5 :b5 :c5 .
|
|
1445
|
+
|
|
1446
|
+
{
|
|
1447
|
+
<http://foo/bar/a1> <http://foo/bar/b1> <http://foo/bar/c1> .
|
|
1448
|
+
<http://example.org/ns/a2> <http://example.org/ns/b2> <http://example.org/ns/c2> .
|
|
1449
|
+
<http://example.org/ns/foo/a3> <http://example.org/ns/foo/b3> <http://example.org/ns/foo/c3> .
|
|
1450
|
+
<http://example.org/ns/foo/bar#a4> <http://example.org/ns/foo/bar#b4> <http://example.org/ns/foo/bar#c4> .
|
|
1451
|
+
<http://example.org/ns2#a5> <http://example.org/ns2#b5> <http://example.org/ns2#c5> .
|
|
1452
|
+
}
|
|
1453
|
+
=>
|
|
1454
|
+
{
|
|
1455
|
+
:result :has :success-literal-28.
|
|
1456
|
+
}.
|
|
1457
|
+
|
|
1458
|
+
{} => {
|
|
1459
|
+
:test :contains :success-literal-28.
|
|
1460
|
+
}.
|
|
1461
|
+
|
|
1462
|
+
{
|
|
1463
|
+
:result :has :success-literal-28.
|
|
1464
|
+
}
|
|
1465
|
+
=>
|
|
1466
|
+
{
|
|
1467
|
+
:test :is true.
|
|
1468
|
+
}.
|
|
1469
|
+
`,
|
|
1470
|
+
expect: [/:result\s+:has\s+:success-literal-28\s*\./, /:test\s+:is\s+true\s*\./],
|
|
1471
|
+
},
|
|
1472
|
+
|
|
1473
|
+
{
|
|
1474
|
+
name: '52e regression: is/of works in blank node property lists with list values',
|
|
1475
|
+
opt: { proofComments: false },
|
|
1476
|
+
input: `@prefix : <http://example.org/> .
|
|
1477
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
1478
|
+
|
|
1479
|
+
:thing :prop (1 2 3).
|
|
1480
|
+
|
|
1481
|
+
{
|
|
1482
|
+
(1 2 3) is :prop of :thing.
|
|
1483
|
+
[ is :prop of :thing ].
|
|
1484
|
+
}
|
|
1485
|
+
=>
|
|
1486
|
+
{
|
|
1487
|
+
:result :has :success-literal-29.
|
|
1488
|
+
}.
|
|
1489
|
+
|
|
1490
|
+
{} => {
|
|
1491
|
+
:test :contains :success-literal-29.
|
|
1492
|
+
}.
|
|
1493
|
+
|
|
1494
|
+
{
|
|
1495
|
+
:result :has :success-literal-29.
|
|
1496
|
+
}
|
|
1497
|
+
=>
|
|
1498
|
+
{
|
|
1499
|
+
:test :is true.
|
|
1500
|
+
}.
|
|
1501
|
+
`,
|
|
1502
|
+
expect: [/:result\s+:has\s+:success-literal-29\s*\./, /:test\s+:is\s+true\s*\./],
|
|
1503
|
+
},
|
|
1504
|
+
|
|
1505
|
+
{
|
|
1506
|
+
name: '52f regression: has works with list values in statement predicate position',
|
|
1507
|
+
opt: { proofComments: false },
|
|
1508
|
+
input: `@prefix : <http://example.org/> .
|
|
1509
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
1510
|
+
|
|
1511
|
+
:thing :prop (1 2 3).
|
|
1512
|
+
|
|
1513
|
+
{
|
|
1514
|
+
:thing has :prop (1 2 3).
|
|
1515
|
+
}
|
|
1516
|
+
=>
|
|
1517
|
+
{
|
|
1518
|
+
:result :has :success-literal-30.
|
|
1519
|
+
}.
|
|
1520
|
+
|
|
1521
|
+
{} => {
|
|
1522
|
+
:test :contains :success-literal-30.
|
|
1523
|
+
}.
|
|
1524
|
+
|
|
1525
|
+
{
|
|
1526
|
+
:result :has :success-literal-30.
|
|
1527
|
+
}
|
|
1528
|
+
=>
|
|
1529
|
+
{
|
|
1530
|
+
:test :is true.
|
|
1531
|
+
}.
|
|
1532
|
+
`,
|
|
1533
|
+
expect: [/:result\s+:has\s+:success-literal-30\s*\./, /:test\s+:is\s+true\s*\./],
|
|
1534
|
+
},
|
|
1535
|
+
|
|
1399
1536
|
{
|
|
1400
1537
|
name: '53 --stream: prints prefixes used in input (not just derived output) before streaming triples',
|
|
1401
1538
|
opt: ['--stream', '-n'],
|