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.
Files changed (50) hide show
  1. package/README.md +153 -0
  2. package/dist/browser/eyeling.browser.js +711 -21
  3. package/examples/input/rdf-surfaces-all-values-from-reverse.ttl +17 -0
  4. package/examples/input/rdf-surfaces-all-values-from.ttl +13 -0
  5. package/examples/input/rdf-surfaces-ancestor.ttl +20 -0
  6. package/examples/input/rdf-surfaces-city.ttl +11 -0
  7. package/examples/input/rdf-surfaces-domain.ttl +11 -0
  8. package/examples/input/rdf-surfaces-multi-premise.ttl +13 -0
  9. package/examples/input/rdf-surfaces-owl-all-values-from-codex.ttl +35 -0
  10. package/examples/input/rdf-surfaces-property-chain.ttl +13 -0
  11. package/examples/input/rdf-surfaces-range.ttl +11 -0
  12. package/examples/input/rdf-surfaces-rdfs-range-codex.ttl +18 -0
  13. package/examples/input/rdf-surfaces-rdfs-subclass-codex.ttl +18 -0
  14. package/examples/output/rdf-surfaces-all-values-from-reverse.n3 +3 -0
  15. package/examples/output/rdf-surfaces-all-values-from.n3 +3 -0
  16. package/examples/output/rdf-surfaces-ancestor.n3 +5 -0
  17. package/examples/output/rdf-surfaces-city.n3 +3 -0
  18. package/examples/output/rdf-surfaces-domain.n3 +3 -0
  19. package/examples/output/rdf-surfaces-multi-premise.n3 +3 -0
  20. package/examples/output/rdf-surfaces-owl-all-values-from-codex.n3 +6 -0
  21. package/examples/output/rdf-surfaces-property-chain.n3 +3 -0
  22. package/examples/output/rdf-surfaces-range.n3 +3 -0
  23. package/examples/output/rdf-surfaces-rdfs-range-codex.n3 +3 -0
  24. package/examples/output/rdf-surfaces-rdfs-subclass-codex.n3 +3 -0
  25. package/examples/rdf-surfaces-all-values-from-reverse.n3 +6 -0
  26. package/examples/rdf-surfaces-all-values-from.n3 +6 -0
  27. package/examples/rdf-surfaces-ancestor.n3 +6 -0
  28. package/examples/rdf-surfaces-city.n3 +6 -0
  29. package/examples/rdf-surfaces-domain.n3 +6 -0
  30. package/examples/rdf-surfaces-multi-premise.n3 +6 -0
  31. package/examples/rdf-surfaces-owl-all-values-from-codex.n3 +10 -0
  32. package/examples/rdf-surfaces-property-chain.n3 +6 -0
  33. package/examples/rdf-surfaces-range.n3 +6 -0
  34. package/examples/rdf-surfaces-rdfs-range-codex.n3 +6 -0
  35. package/examples/rdf-surfaces-rdfs-subclass-codex.n3 +6 -0
  36. package/eyeling.js +711 -21
  37. package/index.d.ts +4 -0
  38. package/index.js +2 -1
  39. package/lib/builtins.js +128 -3
  40. package/lib/cli.js +11 -4
  41. package/lib/engine.js +15 -7
  42. package/lib/lexer.js +4 -0
  43. package/lib/multisource.js +5 -3
  44. package/lib/prelude.js +18 -0
  45. package/lib/rdf_surfaces.js +524 -0
  46. package/lib/rules.js +3 -4
  47. package/package.json +8 -5
  48. package/test/builtins.test.js +36 -0
  49. package/test/examples.test.js +37 -5
  50. package/test/rdf_surfaces.test.js +204 -0
@@ -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 rdfMode = !!trigInput;
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
- args.push('--builtin', builtin.rel, path.join('examples', file));
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);