eyeling 1.29.2 → 1.30.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/README.md +153 -0
- package/dist/browser/eyeling.browser.js +711 -21
- package/examples/input/rdf-surfaces-all-values-from-reverse.ttl +17 -0
- package/examples/input/rdf-surfaces-all-values-from.ttl +13 -0
- package/examples/input/rdf-surfaces-ancestor.ttl +20 -0
- package/examples/input/rdf-surfaces-city.ttl +11 -0
- package/examples/input/rdf-surfaces-domain.ttl +11 -0
- package/examples/input/rdf-surfaces-multi-premise.ttl +13 -0
- package/examples/input/rdf-surfaces-owl-all-values-from-codex.ttl +35 -0
- package/examples/input/rdf-surfaces-property-chain.ttl +13 -0
- package/examples/input/rdf-surfaces-range.ttl +11 -0
- package/examples/input/rdf-surfaces-rdfs-range-codex.ttl +18 -0
- package/examples/input/rdf-surfaces-rdfs-subclass-codex.ttl +18 -0
- package/examples/output/rdf-surfaces-all-values-from-reverse.n3 +3 -0
- package/examples/output/rdf-surfaces-all-values-from.n3 +3 -0
- package/examples/output/rdf-surfaces-ancestor.n3 +5 -0
- package/examples/output/rdf-surfaces-city.n3 +3 -0
- package/examples/output/rdf-surfaces-domain.n3 +3 -0
- package/examples/output/rdf-surfaces-multi-premise.n3 +3 -0
- package/examples/output/rdf-surfaces-owl-all-values-from-codex.n3 +6 -0
- package/examples/output/rdf-surfaces-property-chain.n3 +3 -0
- package/examples/output/rdf-surfaces-range.n3 +3 -0
- package/examples/output/rdf-surfaces-rdfs-range-codex.n3 +3 -0
- package/examples/output/rdf-surfaces-rdfs-subclass-codex.n3 +3 -0
- package/examples/rdf-surfaces-all-values-from-reverse.n3 +6 -0
- package/examples/rdf-surfaces-all-values-from.n3 +6 -0
- package/examples/rdf-surfaces-ancestor.n3 +6 -0
- package/examples/rdf-surfaces-city.n3 +6 -0
- package/examples/rdf-surfaces-domain.n3 +6 -0
- package/examples/rdf-surfaces-multi-premise.n3 +6 -0
- package/examples/rdf-surfaces-owl-all-values-from-codex.n3 +10 -0
- package/examples/rdf-surfaces-property-chain.n3 +6 -0
- package/examples/rdf-surfaces-range.n3 +6 -0
- package/examples/rdf-surfaces-rdfs-range-codex.n3 +6 -0
- package/examples/rdf-surfaces-rdfs-subclass-codex.n3 +6 -0
- package/eyeling.js +711 -21
- package/index.d.ts +4 -0
- package/index.js +2 -1
- package/lib/builtins.js +128 -3
- package/lib/cli.js +11 -4
- package/lib/engine.js +15 -7
- package/lib/lexer.js +4 -0
- package/lib/multisource.js +5 -3
- package/lib/prelude.js +18 -0
- package/lib/rdf_surfaces.js +524 -0
- package/lib/rules.js +3 -4
- package/package.json +8 -5
- package/test/builtins.test.js +36 -0
- package/test/examples.test.js +37 -5
- package/test/rdf_surfaces.test.js +204 -0
package/test/examples.test.js
CHANGED
|
@@ -152,6 +152,27 @@ function resolveExampleTrigInput(root, inputFile) {
|
|
|
152
152
|
return { abs, rel };
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
function resolveExampleRdfSurfaceInput(root, inputFile) {
|
|
156
|
+
const stem = path.basename(inputFile, path.extname(inputFile));
|
|
157
|
+
const rel = path.join('input', `${stem}.ttl`);
|
|
158
|
+
const abs = path.join(root, 'examples', rel);
|
|
159
|
+
if (!fs.existsSync(abs)) return null;
|
|
160
|
+
const text = fs.readFileSync(abs, 'utf8');
|
|
161
|
+
if (!text.includes('%not[')) return null;
|
|
162
|
+
return { abs, rel };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
function exampleOptionFlags(sourceText) {
|
|
167
|
+
const m = String(sourceText || '').match(/^[ \t]*#\s*eyeling-options:\s*(.*?)\s*$/m);
|
|
168
|
+
if (!m) return [];
|
|
169
|
+
return m[1].trim().split(/\s+/).filter(Boolean);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function addUniqueFlag(args, flag) {
|
|
173
|
+
if (!args.includes(flag)) args.push(flag);
|
|
174
|
+
}
|
|
175
|
+
|
|
155
176
|
function resolveExampleBuiltinPath(root, inputFile) {
|
|
156
177
|
const stem = path.basename(inputFile, path.extname(inputFile));
|
|
157
178
|
const rel = path.join('examples', 'builtin', `${stem}.js`);
|
|
@@ -160,10 +181,13 @@ function resolveExampleBuiltinPath(root, inputFile) {
|
|
|
160
181
|
return { abs, rel };
|
|
161
182
|
}
|
|
162
183
|
|
|
163
|
-
function runExampleToFile({ root, examplesDir, eyelingJsPath, nodePath, file, generatedPath, proof = false }) {
|
|
184
|
+
function runExampleToFile({ root, examplesDir, eyelingJsPath, nodePath, file, generatedPath, proof = false, sourceText = '' }) {
|
|
164
185
|
const builtin = resolveExampleBuiltinPath(root, file);
|
|
165
186
|
const trigInput = resolveExampleTrigInput(root, file);
|
|
166
|
-
const
|
|
187
|
+
const rdfSurfaceInput = resolveExampleRdfSurfaceInput(root, file);
|
|
188
|
+
const optionFlags = exampleOptionFlags(sourceText);
|
|
189
|
+
const rdfSurfacesMode = !!rdfSurfaceInput || optionFlags.includes('--rdf-surfaces');
|
|
190
|
+
const rdfMode = !!trigInput || rdfSurfacesMode || optionFlags.includes('--rdf') || optionFlags.includes('-r');
|
|
167
191
|
const modeFlag = proof ? '-p' : '-d';
|
|
168
192
|
const outFd = fs.openSync(generatedPath, 'w');
|
|
169
193
|
|
|
@@ -171,7 +195,12 @@ function runExampleToFile({ root, examplesDir, eyelingJsPath, nodePath, file, ge
|
|
|
171
195
|
if (builtin) {
|
|
172
196
|
const args = [eyelingJsPath, modeFlag];
|
|
173
197
|
if (rdfMode) args.push('-r');
|
|
174
|
-
|
|
198
|
+
for (const flag of optionFlags) addUniqueFlag(args, flag);
|
|
199
|
+
if (rdfSurfacesMode) addUniqueFlag(args, '--rdf-surfaces');
|
|
200
|
+
args.push('--builtin');
|
|
201
|
+
args.push(builtin.rel);
|
|
202
|
+
if (rdfSurfaceInput) args.push(path.join('examples', rdfSurfaceInput.rel));
|
|
203
|
+
args.push(path.join('examples', file));
|
|
175
204
|
if (trigInput) args.push(path.join('examples', trigInput.rel));
|
|
176
205
|
return cp.spawnSync(nodePath, args, {
|
|
177
206
|
cwd: root,
|
|
@@ -183,6 +212,9 @@ function runExampleToFile({ root, examplesDir, eyelingJsPath, nodePath, file, ge
|
|
|
183
212
|
|
|
184
213
|
const args = [eyelingJsPath, modeFlag];
|
|
185
214
|
if (rdfMode) args.push('-r');
|
|
215
|
+
for (const flag of optionFlags) addUniqueFlag(args, flag);
|
|
216
|
+
if (rdfSurfacesMode) addUniqueFlag(args, '--rdf-surfaces');
|
|
217
|
+
if (rdfSurfaceInput) args.push(rdfSurfaceInput.rel);
|
|
186
218
|
args.push(file);
|
|
187
219
|
if (trigInput) args.push(trigInput.rel);
|
|
188
220
|
return cp.spawnSync(nodePath, args, {
|
|
@@ -290,7 +322,7 @@ function main() {
|
|
|
290
322
|
// node eyeling.js --builtin examples/builtin/foo.js examples/foo.n3
|
|
291
323
|
// A matching examples/input/<stem>.trig sidecar is external RDF/TriG
|
|
292
324
|
// evidence for this example, so include it and run in -r mode automatically.
|
|
293
|
-
const r = runExampleToFile({ root, examplesDir, eyelingJsPath, nodePath, file, generatedPath });
|
|
325
|
+
const r = runExampleToFile({ root, examplesDir, eyelingJsPath, nodePath, file, generatedPath, sourceText: n3Text });
|
|
294
326
|
|
|
295
327
|
const rc = r.status == null ? 1 : r.status;
|
|
296
328
|
|
|
@@ -358,7 +390,7 @@ function main() {
|
|
|
358
390
|
const expectedRc = expectedExitCode(n3Text);
|
|
359
391
|
const tmpDir = mkTmpDir();
|
|
360
392
|
const generatedPath = path.join(tmpDir, 'generated.n3');
|
|
361
|
-
const r = runExampleToFile({ root, examplesDir, eyelingJsPath, nodePath, file, generatedPath, proof: true });
|
|
393
|
+
const r = runExampleToFile({ root, examplesDir, eyelingJsPath, nodePath, file, generatedPath, proof: true, sourceText: n3Text });
|
|
362
394
|
const rc = r.status == null ? 1 : r.status;
|
|
363
395
|
const ms = Date.now() - start;
|
|
364
396
|
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const cp = require('node:child_process');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
|
|
8
|
+
const { reason } = require('../index.js');
|
|
9
|
+
const { C, failResult, pass } = require('./report');
|
|
10
|
+
|
|
11
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
12
|
+
const EYELING = path.join(ROOT, 'eyeling.js');
|
|
13
|
+
|
|
14
|
+
function runCli(input, args = ['--rdf-surfaces']) {
|
|
15
|
+
return cp.spawnSync(process.execPath, [EYELING, ...args, '-'], {
|
|
16
|
+
input,
|
|
17
|
+
encoding: 'utf8',
|
|
18
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function runExample(name) {
|
|
23
|
+
return cp.spawnSync(
|
|
24
|
+
process.execPath,
|
|
25
|
+
[EYELING, '--rdf-surfaces', path.join(ROOT, 'examples', 'input', `${name}.ttl`), path.join(ROOT, 'examples', `${name}.n3`)],
|
|
26
|
+
{ encoding: 'utf8', maxBuffer: 20 * 1024 * 1024 },
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const inlineCases = [
|
|
31
|
+
{
|
|
32
|
+
name: 'slide32-style surface derives subclass instance',
|
|
33
|
+
input: `
|
|
34
|
+
@prefix ex: <http://example.org/> .
|
|
35
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
36
|
+
|
|
37
|
+
ex:Brussels a ex:City .
|
|
38
|
+
|
|
39
|
+
%not[ _:x
|
|
40
|
+
_:x a ex:City .
|
|
41
|
+
%not[
|
|
42
|
+
_:x a ex:HumanCommunity .
|
|
43
|
+
%]
|
|
44
|
+
%]
|
|
45
|
+
|
|
46
|
+
{ ?s a ex:HumanCommunity . } log:query { ?s a ex:HumanCommunity . } .
|
|
47
|
+
`,
|
|
48
|
+
expect: '@prefix ex: <http://example.org/> .\n\nex:Brussels a ex:HumanCommunity .',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'slide33 range-style surface derives object type',
|
|
52
|
+
input: `
|
|
53
|
+
@prefix ex: <http://example.org/> .
|
|
54
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
55
|
+
|
|
56
|
+
ex:alice ex:parent ex:bob .
|
|
57
|
+
|
|
58
|
+
%not[ _:x _:y
|
|
59
|
+
_:x ex:parent _:y .
|
|
60
|
+
%not[
|
|
61
|
+
_:y a ex:Person .
|
|
62
|
+
%]
|
|
63
|
+
%]
|
|
64
|
+
|
|
65
|
+
{ ?s a ex:Person . } log:query { ?s a ex:Person . } .
|
|
66
|
+
`,
|
|
67
|
+
expect: '@prefix ex: <http://example.org/> .\n\nex:bob a ex:Person .',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'slide33 allValuesFrom forward surface derives filler type',
|
|
71
|
+
input: `
|
|
72
|
+
@prefix ex: <http://example.org/> .
|
|
73
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
74
|
+
|
|
75
|
+
ex:box a ex:AllowedContainer .
|
|
76
|
+
ex:box ex:contains ex:item42 .
|
|
77
|
+
|
|
78
|
+
%not[ _:x _:y
|
|
79
|
+
_:x a ex:AllowedContainer .
|
|
80
|
+
_:x ex:contains _:y .
|
|
81
|
+
%not[
|
|
82
|
+
_:y a ex:AllowedItem .
|
|
83
|
+
%]
|
|
84
|
+
%]
|
|
85
|
+
|
|
86
|
+
{ ?s a ex:AllowedItem . } log:query { ?s a ex:AllowedItem . } .
|
|
87
|
+
`,
|
|
88
|
+
expect: '@prefix ex: <http://example.org/> .\n\nex:item42 a ex:AllowedItem .',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'slide33 allValuesFrom reverse surface derives restricted class',
|
|
92
|
+
input: `
|
|
93
|
+
@prefix ex: <http://example.org/> .
|
|
94
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
95
|
+
|
|
96
|
+
ex:box ex:contains ex:item42 .
|
|
97
|
+
ex:item42 a ex:AllowedItem .
|
|
98
|
+
|
|
99
|
+
%not[ _:x
|
|
100
|
+
%not[ _:y
|
|
101
|
+
_:x ex:contains _:y .
|
|
102
|
+
%not[
|
|
103
|
+
_:y a ex:AllowedItem .
|
|
104
|
+
%]
|
|
105
|
+
%]
|
|
106
|
+
%not[
|
|
107
|
+
_:x a ex:AllowedContainer .
|
|
108
|
+
%]
|
|
109
|
+
%]
|
|
110
|
+
|
|
111
|
+
{ ?s a ex:AllowedContainer . } log:query { ?s a ex:AllowedContainer . } .
|
|
112
|
+
`,
|
|
113
|
+
expect: '@prefix ex: <http://example.org/> .\n\nex:box a ex:AllowedContainer .',
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
const exampleCases = [
|
|
118
|
+
['rdf-surfaces-city', '@prefix ex: <http://example.org/> .\n\nex:Brussels a ex:HumanCommunity .'],
|
|
119
|
+
['rdf-surfaces-range', '@prefix ex: <http://example.org/> .\n\nex:bob a ex:Person .'],
|
|
120
|
+
['rdf-surfaces-domain', '@prefix ex: <http://example.org/> .\n\nex:alice a ex:Member .'],
|
|
121
|
+
['rdf-surfaces-property-chain', '@prefix ex: <http://example.org/> .\n\nex:alice ex:grandparent ex:carol .'],
|
|
122
|
+
[
|
|
123
|
+
'rdf-surfaces-ancestor',
|
|
124
|
+
'@prefix ex: <http://example.org/> .\n\nex:ann ex:ancestor ex:bob .\nex:bob ex:ancestor ex:cat .\nex:ann ex:ancestor ex:cat .',
|
|
125
|
+
],
|
|
126
|
+
['rdf-surfaces-multi-premise', '@prefix ex: <http://example.org/> .\n\nex:case123 a ex:PriorityCase .'],
|
|
127
|
+
['rdf-surfaces-all-values-from', '@prefix ex: <http://example.org/> .\n\nex:item42 a ex:AllowedItem .'],
|
|
128
|
+
['rdf-surfaces-all-values-from-reverse', '@prefix ex: <http://example.org/> .\n\nex:box a ex:AllowedContainer .'],
|
|
129
|
+
['rdf-surfaces-rdfs-range-codex', '@prefix ex: <http://example.org/> .\n\nex:bob a ex:Person .'],
|
|
130
|
+
['rdf-surfaces-rdfs-subclass-codex', '@prefix ex: <http://example.org/> .\n\nex:Brussels a ex:HumanCommunity .'],
|
|
131
|
+
[
|
|
132
|
+
'rdf-surfaces-owl-all-values-from-codex',
|
|
133
|
+
'@prefix ex: <http://example.org/> .\n\nex:item43 a ex:AllowedItem .\nex:item42 a ex:AllowedItem .\nex:box a ex:AllowedContainer .\nex:crate a ex:AllowedContainer .',
|
|
134
|
+
],
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
let seq = 0;
|
|
138
|
+
let failed = 0;
|
|
139
|
+
|
|
140
|
+
for (const tc of inlineCases) {
|
|
141
|
+
const n = ++seq;
|
|
142
|
+
const t0 = Date.now();
|
|
143
|
+
try {
|
|
144
|
+
const r = runCli(tc.input);
|
|
145
|
+
assert.equal(r.status, 0, r.stderr || r.stdout);
|
|
146
|
+
assert.equal(r.stdout.trim(), tc.expect);
|
|
147
|
+
pass(n, tc.name, Date.now() - t0);
|
|
148
|
+
} catch (e) {
|
|
149
|
+
failed++;
|
|
150
|
+
failResult(n, tc.name, Date.now() - t0);
|
|
151
|
+
console.error(`${C.dim}${e && e.stack ? e.stack : String(e)}${C.n}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
for (const [name, expect] of exampleCases) {
|
|
156
|
+
const n = ++seq;
|
|
157
|
+
const t0 = Date.now();
|
|
158
|
+
try {
|
|
159
|
+
const r = runExample(name);
|
|
160
|
+
assert.equal(r.status, 0, r.stderr || r.stdout);
|
|
161
|
+
assert.equal(r.stdout.trim(), expect);
|
|
162
|
+
pass(n, `example split input/query ${name}`, Date.now() - t0);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
failed++;
|
|
165
|
+
failResult(n, `example split input/query ${name}`, Date.now() - t0);
|
|
166
|
+
console.error(`${C.dim}${e && e.stack ? e.stack : String(e)}${C.n}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
{
|
|
171
|
+
const n = ++seq;
|
|
172
|
+
const t0 = Date.now();
|
|
173
|
+
try {
|
|
174
|
+
const out = reason({ rdfSurfaces: true }, inlineCases[0].input).trim();
|
|
175
|
+
assert.equal(out, inlineCases[0].expect);
|
|
176
|
+
pass(n, 'API rdfSurfaces option implies RDF compatibility', Date.now() - t0);
|
|
177
|
+
} catch (e) {
|
|
178
|
+
failed++;
|
|
179
|
+
failResult(n, 'API rdfSurfaces option implies RDF compatibility', Date.now() - t0);
|
|
180
|
+
console.error(`${C.dim}${e && e.stack ? e.stack : String(e)}${C.n}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
{
|
|
185
|
+
const n = ++seq;
|
|
186
|
+
const t0 = Date.now();
|
|
187
|
+
try {
|
|
188
|
+
const r = runCli(`
|
|
189
|
+
@prefix ex: <http://example.org/> .
|
|
190
|
+
ex:bad a ex:Impossible .
|
|
191
|
+
%not[ _:x
|
|
192
|
+
_:x a ex:Impossible .
|
|
193
|
+
%]
|
|
194
|
+
`);
|
|
195
|
+
assert.equal(r.status, 65, r.stderr || r.stdout);
|
|
196
|
+
pass(n, 'top-level negative surface without child is an inference fuse', Date.now() - t0);
|
|
197
|
+
} catch (e) {
|
|
198
|
+
failed++;
|
|
199
|
+
failResult(n, 'top-level negative surface without child is an inference fuse', Date.now() - t0);
|
|
200
|
+
console.error(`${C.dim}${e && e.stack ? e.stack : String(e)}${C.n}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (failed) process.exit(1);
|