eyeling 1.23.4 → 1.23.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/HANDBOOK.md +6 -6
- package/dist/browser/eyeling.browser.js +40 -12
- package/eyeling.js +40 -12
- package/lib/prelude.js +40 -12
- package/package.json +1 -1
- package/test/api.test.js +27 -0
package/HANDBOOK.md
CHANGED
|
@@ -1967,7 +1967,7 @@ It deliberately does **not** expose `loadBuiltinModule(...)`, because loading bu
|
|
|
1967
1967
|
|
|
1968
1968
|
For browser apps, prefer running Eyeling in a **Web Worker** and importing `eyeling/browser` there.
|
|
1969
1969
|
|
|
1970
|
-
### 14.
|
|
1970
|
+
### 14.4 `lib/entry.js`: bundler-friendly exports
|
|
1971
1971
|
|
|
1972
1972
|
`lib/entry.js` exports:
|
|
1973
1973
|
|
|
@@ -1976,14 +1976,14 @@ For browser apps, prefer running Eyeling in a **Web Worker** and importing `eyel
|
|
|
1976
1976
|
|
|
1977
1977
|
`rdfjs` is a small built-in RDF/JS `DataFactory`, so browser / worker code can construct quads without pulling in another package first.
|
|
1978
1978
|
|
|
1979
|
-
### 14.
|
|
1979
|
+
### 14.5 JavaScript API
|
|
1980
1980
|
|
|
1981
1981
|
Eyeling exposes two JavaScript entry styles:
|
|
1982
1982
|
|
|
1983
1983
|
- `reason(...)` from `index.js` when you want the same text output as the CLI
|
|
1984
1984
|
- `reasonStream(...)` / `reasonRdfJs(...)` from the Node bundle or `eyeling/browser` when you want in-process reasoning and structured RDF/JS results
|
|
1985
1985
|
|
|
1986
|
-
#### 14.
|
|
1986
|
+
#### 14.5.1 npm helper: `reason(...)`
|
|
1987
1987
|
|
|
1988
1988
|
The npm `reason(...)` function does something intentionally simple and robust:
|
|
1989
1989
|
|
|
@@ -2036,7 +2036,7 @@ Notes:
|
|
|
2036
2036
|
- By default, the npm helper keeps output machine-friendly (`proofComments: false`).
|
|
2037
2037
|
- Use this path when you want CLI-equivalent behavior inside JavaScript.
|
|
2038
2038
|
|
|
2039
|
-
#### 14.
|
|
2039
|
+
#### 14.5.2 RDF-JS and Eyeling rule-object interoperability
|
|
2040
2040
|
|
|
2041
2041
|
The JavaScript APIs accept four input styles:
|
|
2042
2042
|
|
|
@@ -2114,7 +2114,7 @@ console.log(out);
|
|
|
2114
2114
|
|
|
2115
2115
|
You can also pass a full AST bundle directly, for example `[prefixes, triples, forwardRules, backwardRules]`.
|
|
2116
2116
|
|
|
2117
|
-
#### 14.
|
|
2117
|
+
#### 14.5.3 In-process bundle API: `reasonStream(...)` and `reasonRdfJs(...)`
|
|
2118
2118
|
|
|
2119
2119
|
Use the bundle entry if you want structured results while the engine is running instead of final CLI text after the fact.
|
|
2120
2120
|
|
|
@@ -2153,7 +2153,7 @@ Use these entry points when you need one or more of the following:
|
|
|
2153
2153
|
- derived results consumed as RDF/JS quads
|
|
2154
2154
|
- streaming derived RDF/JS quads during reasoning
|
|
2155
2155
|
|
|
2156
|
-
### 14.
|
|
2156
|
+
### 14.6 Choosing the right entry point
|
|
2157
2157
|
|
|
2158
2158
|
A practical rule of thumb:
|
|
2159
2159
|
|
|
@@ -11158,6 +11158,37 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
|
|
|
11158
11158
|
|
|
11159
11159
|
const __literalPartsCache = new Map(); // lit string -> [lex, dt]
|
|
11160
11160
|
|
|
11161
|
+
function quotedLiteralEndIndex(str) {
|
|
11162
|
+
if (typeof str !== 'string' || str.length < 2) return -1;
|
|
11163
|
+
|
|
11164
|
+
const quote = str[0];
|
|
11165
|
+
if (quote !== '"' && quote !== "'") return -1;
|
|
11166
|
+
|
|
11167
|
+
const delimLen = str.startsWith(quote.repeat(3)) ? 3 : 1;
|
|
11168
|
+
const delim = quote.repeat(delimLen);
|
|
11169
|
+
|
|
11170
|
+
let i = delimLen;
|
|
11171
|
+
while (i < str.length) {
|
|
11172
|
+
// Stored literals may contain escaped quotes, e.g. \"...\"^^xsd:dateTime
|
|
11173
|
+
// inside a plain string. Those must not terminate the outer lexical form.
|
|
11174
|
+
if (str[i] === '\\') {
|
|
11175
|
+
i += 2;
|
|
11176
|
+
continue;
|
|
11177
|
+
}
|
|
11178
|
+
|
|
11179
|
+
if (delimLen === 1) {
|
|
11180
|
+
if (str[i] === quote) return i + 1;
|
|
11181
|
+
i += 1;
|
|
11182
|
+
continue;
|
|
11183
|
+
}
|
|
11184
|
+
|
|
11185
|
+
if (str.startsWith(delim, i)) return i + delimLen;
|
|
11186
|
+
i += 1;
|
|
11187
|
+
}
|
|
11188
|
+
|
|
11189
|
+
return -1;
|
|
11190
|
+
}
|
|
11191
|
+
|
|
11161
11192
|
function literalParts(lit) {
|
|
11162
11193
|
// Avoid caching extremely large literals (notably huge numeric intermediates)
|
|
11163
11194
|
// to prevent unbounded memory growth.
|
|
@@ -11175,24 +11206,21 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
|
|
|
11175
11206
|
let lex = lit;
|
|
11176
11207
|
let dt = null;
|
|
11177
11208
|
|
|
11178
|
-
const
|
|
11179
|
-
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
dt = match[3];
|
|
11209
|
+
const lexEnd = quotedLiteralEndIndex(lit);
|
|
11210
|
+
if (lexEnd > 0 && lit.startsWith('^^', lexEnd)) {
|
|
11211
|
+
lex = lit.slice(0, lexEnd);
|
|
11212
|
+
dt = lit.slice(lexEnd + 2);
|
|
11183
11213
|
if (dt.startsWith('<') && dt.endsWith('>')) {
|
|
11184
11214
|
dt = dt.slice(1, -1);
|
|
11185
11215
|
}
|
|
11186
11216
|
}
|
|
11187
11217
|
|
|
11188
11218
|
// Strip LANGTAG from the lexical form when present.
|
|
11189
|
-
|
|
11190
|
-
|
|
11191
|
-
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
lex = lex.slice(0, lastQuote + 1);
|
|
11195
|
-
}
|
|
11219
|
+
const langLexEnd = quotedLiteralEndIndex(lex);
|
|
11220
|
+
if (langLexEnd > 0 && lex[0] === '"' && langLexEnd < lex.length && lex[langLexEnd] === '@') {
|
|
11221
|
+
const lang = lex.slice(langLexEnd + 1);
|
|
11222
|
+
if (/^[A-Za-z]+(?:-[A-Za-z0-9]+)*$/.test(lang)) {
|
|
11223
|
+
lex = lex.slice(0, langLexEnd);
|
|
11196
11224
|
}
|
|
11197
11225
|
}
|
|
11198
11226
|
|
package/eyeling.js
CHANGED
|
@@ -11130,6 +11130,37 @@ const MAX_LITERAL_PARTS_CACHE_LEN = 1024;
|
|
|
11130
11130
|
|
|
11131
11131
|
const __literalPartsCache = new Map(); // lit string -> [lex, dt]
|
|
11132
11132
|
|
|
11133
|
+
function quotedLiteralEndIndex(str) {
|
|
11134
|
+
if (typeof str !== 'string' || str.length < 2) return -1;
|
|
11135
|
+
|
|
11136
|
+
const quote = str[0];
|
|
11137
|
+
if (quote !== '"' && quote !== "'") return -1;
|
|
11138
|
+
|
|
11139
|
+
const delimLen = str.startsWith(quote.repeat(3)) ? 3 : 1;
|
|
11140
|
+
const delim = quote.repeat(delimLen);
|
|
11141
|
+
|
|
11142
|
+
let i = delimLen;
|
|
11143
|
+
while (i < str.length) {
|
|
11144
|
+
// Stored literals may contain escaped quotes, e.g. \"...\"^^xsd:dateTime
|
|
11145
|
+
// inside a plain string. Those must not terminate the outer lexical form.
|
|
11146
|
+
if (str[i] === '\\') {
|
|
11147
|
+
i += 2;
|
|
11148
|
+
continue;
|
|
11149
|
+
}
|
|
11150
|
+
|
|
11151
|
+
if (delimLen === 1) {
|
|
11152
|
+
if (str[i] === quote) return i + 1;
|
|
11153
|
+
i += 1;
|
|
11154
|
+
continue;
|
|
11155
|
+
}
|
|
11156
|
+
|
|
11157
|
+
if (str.startsWith(delim, i)) return i + delimLen;
|
|
11158
|
+
i += 1;
|
|
11159
|
+
}
|
|
11160
|
+
|
|
11161
|
+
return -1;
|
|
11162
|
+
}
|
|
11163
|
+
|
|
11133
11164
|
function literalParts(lit) {
|
|
11134
11165
|
// Avoid caching extremely large literals (notably huge numeric intermediates)
|
|
11135
11166
|
// to prevent unbounded memory growth.
|
|
@@ -11147,24 +11178,21 @@ function literalParts(lit) {
|
|
|
11147
11178
|
let lex = lit;
|
|
11148
11179
|
let dt = null;
|
|
11149
11180
|
|
|
11150
|
-
const
|
|
11151
|
-
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
dt = match[3];
|
|
11181
|
+
const lexEnd = quotedLiteralEndIndex(lit);
|
|
11182
|
+
if (lexEnd > 0 && lit.startsWith('^^', lexEnd)) {
|
|
11183
|
+
lex = lit.slice(0, lexEnd);
|
|
11184
|
+
dt = lit.slice(lexEnd + 2);
|
|
11155
11185
|
if (dt.startsWith('<') && dt.endsWith('>')) {
|
|
11156
11186
|
dt = dt.slice(1, -1);
|
|
11157
11187
|
}
|
|
11158
11188
|
}
|
|
11159
11189
|
|
|
11160
11190
|
// Strip LANGTAG from the lexical form when present.
|
|
11161
|
-
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
lex = lex.slice(0, lastQuote + 1);
|
|
11167
|
-
}
|
|
11191
|
+
const langLexEnd = quotedLiteralEndIndex(lex);
|
|
11192
|
+
if (langLexEnd > 0 && lex[0] === '"' && langLexEnd < lex.length && lex[langLexEnd] === '@') {
|
|
11193
|
+
const lang = lex.slice(langLexEnd + 1);
|
|
11194
|
+
if (/^[A-Za-z]+(?:-[A-Za-z0-9]+)*$/.test(lang)) {
|
|
11195
|
+
lex = lex.slice(0, langLexEnd);
|
|
11168
11196
|
}
|
|
11169
11197
|
}
|
|
11170
11198
|
|
package/lib/prelude.js
CHANGED
|
@@ -43,6 +43,37 @@ const MAX_LITERAL_PARTS_CACHE_LEN = 1024;
|
|
|
43
43
|
|
|
44
44
|
const __literalPartsCache = new Map(); // lit string -> [lex, dt]
|
|
45
45
|
|
|
46
|
+
function quotedLiteralEndIndex(str) {
|
|
47
|
+
if (typeof str !== 'string' || str.length < 2) return -1;
|
|
48
|
+
|
|
49
|
+
const quote = str[0];
|
|
50
|
+
if (quote !== '"' && quote !== "'") return -1;
|
|
51
|
+
|
|
52
|
+
const delimLen = str.startsWith(quote.repeat(3)) ? 3 : 1;
|
|
53
|
+
const delim = quote.repeat(delimLen);
|
|
54
|
+
|
|
55
|
+
let i = delimLen;
|
|
56
|
+
while (i < str.length) {
|
|
57
|
+
// Stored literals may contain escaped quotes, e.g. \"...\"^^xsd:dateTime
|
|
58
|
+
// inside a plain string. Those must not terminate the outer lexical form.
|
|
59
|
+
if (str[i] === '\\') {
|
|
60
|
+
i += 2;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (delimLen === 1) {
|
|
65
|
+
if (str[i] === quote) return i + 1;
|
|
66
|
+
i += 1;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (str.startsWith(delim, i)) return i + delimLen;
|
|
71
|
+
i += 1;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return -1;
|
|
75
|
+
}
|
|
76
|
+
|
|
46
77
|
function literalParts(lit) {
|
|
47
78
|
// Avoid caching extremely large literals (notably huge numeric intermediates)
|
|
48
79
|
// to prevent unbounded memory growth.
|
|
@@ -60,24 +91,21 @@ function literalParts(lit) {
|
|
|
60
91
|
let lex = lit;
|
|
61
92
|
let dt = null;
|
|
62
93
|
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
dt = match[3];
|
|
94
|
+
const lexEnd = quotedLiteralEndIndex(lit);
|
|
95
|
+
if (lexEnd > 0 && lit.startsWith('^^', lexEnd)) {
|
|
96
|
+
lex = lit.slice(0, lexEnd);
|
|
97
|
+
dt = lit.slice(lexEnd + 2);
|
|
68
98
|
if (dt.startsWith('<') && dt.endsWith('>')) {
|
|
69
99
|
dt = dt.slice(1, -1);
|
|
70
100
|
}
|
|
71
101
|
}
|
|
72
102
|
|
|
73
103
|
// Strip LANGTAG from the lexical form when present.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
lex = lex.slice(0, lastQuote + 1);
|
|
80
|
-
}
|
|
104
|
+
const langLexEnd = quotedLiteralEndIndex(lex);
|
|
105
|
+
if (langLexEnd > 0 && lex[0] === '"' && langLexEnd < lex.length && lex[langLexEnd] === '@') {
|
|
106
|
+
const lang = lex.slice(langLexEnd + 1);
|
|
107
|
+
if (/^[A-Za-z]+(?:-[A-Za-z0-9]+)*$/.test(lang)) {
|
|
108
|
+
lex = lex.slice(0, langLexEnd);
|
|
81
109
|
}
|
|
82
110
|
}
|
|
83
111
|
|
package/package.json
CHANGED
package/test/api.test.js
CHANGED
|
@@ -288,6 +288,33 @@ const cases = [
|
|
|
288
288
|
assert.equal(String(out).trimEnd(), '^^');
|
|
289
289
|
},
|
|
290
290
|
},
|
|
291
|
+
{
|
|
292
|
+
name: '00c quoted string containing typed literal syntax remains plain string',
|
|
293
|
+
opt: { proofComments: false },
|
|
294
|
+
input: `
|
|
295
|
+
@prefix : <http://example.org/>.
|
|
296
|
+
|
|
297
|
+
:s :p """
|
|
298
|
+
@prefix : <http://example.org/>.
|
|
299
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
|
|
300
|
+
|
|
301
|
+
:Let :param \"2023-04-01T18:06:04Z\"^^xsd:dateTime .
|
|
302
|
+
""".
|
|
303
|
+
|
|
304
|
+
{
|
|
305
|
+
:s :p ?O.
|
|
306
|
+
}
|
|
307
|
+
=>
|
|
308
|
+
{
|
|
309
|
+
:test :is ?O.
|
|
310
|
+
}.
|
|
311
|
+
`,
|
|
312
|
+
expect: [/^:test\s+:is\s+/m],
|
|
313
|
+
notExpect: [/\^\^<xsd:dateTime/],
|
|
314
|
+
check(out) {
|
|
315
|
+
assert.match(out, /\\"2023-04-01T18:06:04Z\\"\^\^xsd:dateTime \./);
|
|
316
|
+
},
|
|
317
|
+
},
|
|
291
318
|
{
|
|
292
319
|
name: '01 forward rule: p -> q',
|
|
293
320
|
opt: { proofComments: false },
|