eyeling 1.28.1 → 1.28.2

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.
@@ -11140,11 +11140,114 @@ function normalizeRdfCompatibility(inputText) {
11140
11140
  return text.slice(at, j);
11141
11141
  }
11142
11142
 
11143
+ function readBalancedTermAt(text, at) {
11144
+ const open = text[at];
11145
+ const matching = { '{': '}', '[': ']', '(': ')' };
11146
+ if (!matching[open]) return null;
11147
+
11148
+ const stack = [matching[open]];
11149
+ let j = at + 1;
11150
+ while (j < text.length) {
11151
+ const ch = text[j];
11152
+ if (ch === '"' || ch === "'") {
11153
+ const str = readStringAt(text, j);
11154
+ j = str.end;
11155
+ continue;
11156
+ }
11157
+ if (ch === '<' && !text.startsWith('<<', j)) {
11158
+ const iri = readIriAt(text, j);
11159
+ j = iri.end;
11160
+ continue;
11161
+ }
11162
+ if (ch === '#') {
11163
+ while (j < text.length && text[j] !== '\n' && text[j] !== '\r') j += 1;
11164
+ continue;
11165
+ }
11166
+ if (matching[ch]) {
11167
+ stack.push(matching[ch]);
11168
+ j += 1;
11169
+ continue;
11170
+ }
11171
+ if (ch === stack[stack.length - 1]) {
11172
+ stack.pop();
11173
+ j += 1;
11174
+ if (stack.length === 0) return { text: text.slice(at, j), end: j };
11175
+ continue;
11176
+ }
11177
+ j += 1;
11178
+ }
11179
+
11180
+ throw new N3SyntaxError(`Unterminated term inside RDF 1.2 triple term, expected ${stack[stack.length - 1]}`);
11181
+ }
11182
+
11183
+ function readRdfTripleTermComponent(text, at) {
11184
+ const j = skipWsAndComments(text, at);
11185
+ if (j >= text.length) return null;
11186
+ const ch = text[j];
11187
+
11188
+ if (ch === '<') return readIriAt(text, j);
11189
+
11190
+ if (ch === '"' || ch === "'") {
11191
+ const str = readStringAt(text, j);
11192
+ let end = str.end;
11193
+ let termText = str.text;
11194
+ if (text.startsWith('^^', end)) {
11195
+ const datatype = readRdfTripleTermComponent(text, end + 2);
11196
+ if (datatype) {
11197
+ termText += '^^' + datatype.text;
11198
+ end = datatype.end;
11199
+ }
11200
+ } else if (text[end] === '@') {
11201
+ let k = end + 1;
11202
+ if (/[A-Za-z]/.test(text[k] || '')) {
11203
+ while (k < text.length && /[A-Za-z0-9-]/.test(text[k])) k += 1;
11204
+ termText += text.slice(end, k);
11205
+ end = k;
11206
+ }
11207
+ }
11208
+ return { text: termText, end };
11209
+ }
11210
+
11211
+ if (ch === '{' || ch === '[' || ch === '(') return readBalancedTermAt(text, j);
11212
+
11213
+ let k = j;
11214
+ while (k < text.length && !/\s/.test(text[k]) && !'{}[](),;'.includes(text[k])) k += 1;
11215
+ if (k === j) return null;
11216
+ const value = text.slice(j, k);
11217
+ if (!value || value.startsWith('@')) return null;
11218
+ return { text: value, end: k };
11219
+ }
11220
+
11221
+ function validateSingleRdfTripleTerm(rawTriple) {
11222
+ const triple = rawTriple.trim();
11223
+ if (!triple) throw new N3SyntaxError('RDF 1.2 triple term must contain exactly one subject, predicate, and object');
11224
+
11225
+ let pos = 0;
11226
+ for (const label of ['subject', 'predicate', 'object']) {
11227
+ const term = readRdfTripleTermComponent(triple, pos);
11228
+ if (!term) throw new N3SyntaxError(`RDF 1.2 triple term is missing a ${label}`);
11229
+ pos = term.end;
11230
+ }
11231
+
11232
+ const rest = skipWsAndComments(triple, pos);
11233
+ if (rest >= triple.length) return;
11234
+
11235
+ const found = triple[rest];
11236
+ if (found === ',') {
11237
+ throw new N3SyntaxError("RDF 1.2 triple terms must contain exactly one object; object lists using ',' are not valid inside <<( ... )>>");
11238
+ }
11239
+ if (found === ';') {
11240
+ throw new N3SyntaxError("RDF 1.2 triple terms must contain exactly one predicate-object pair; ';' is not valid inside <<( ... )>>");
11241
+ }
11242
+ throw new N3SyntaxError(`RDF 1.2 triple term must contain exactly one subject, predicate, and object; unexpected ${JSON.stringify(triple.slice(rest, rest + 20))}`);
11243
+ }
11244
+
11143
11245
  function graphTermFromTripleBody(rawBody, parenthesized) {
11144
11246
  let body = rawBody.trim();
11145
11247
  if (parenthesized && body.startsWith('(') && body.endsWith(')')) body = body.slice(1, -1).trim();
11146
11248
  const split = splitTopLevelReifier(body);
11147
11249
  const triple = split.triple;
11250
+ validateSingleRdfTripleTerm(triple);
11148
11251
  const graph = '{ ' + triple + ' }';
11149
11252
  if (split.reifier) {
11150
11253
  const reifier = firstTerm(split.reifier);
package/eyeling.js CHANGED
@@ -11140,11 +11140,114 @@ function normalizeRdfCompatibility(inputText) {
11140
11140
  return text.slice(at, j);
11141
11141
  }
11142
11142
 
11143
+ function readBalancedTermAt(text, at) {
11144
+ const open = text[at];
11145
+ const matching = { '{': '}', '[': ']', '(': ')' };
11146
+ if (!matching[open]) return null;
11147
+
11148
+ const stack = [matching[open]];
11149
+ let j = at + 1;
11150
+ while (j < text.length) {
11151
+ const ch = text[j];
11152
+ if (ch === '"' || ch === "'") {
11153
+ const str = readStringAt(text, j);
11154
+ j = str.end;
11155
+ continue;
11156
+ }
11157
+ if (ch === '<' && !text.startsWith('<<', j)) {
11158
+ const iri = readIriAt(text, j);
11159
+ j = iri.end;
11160
+ continue;
11161
+ }
11162
+ if (ch === '#') {
11163
+ while (j < text.length && text[j] !== '\n' && text[j] !== '\r') j += 1;
11164
+ continue;
11165
+ }
11166
+ if (matching[ch]) {
11167
+ stack.push(matching[ch]);
11168
+ j += 1;
11169
+ continue;
11170
+ }
11171
+ if (ch === stack[stack.length - 1]) {
11172
+ stack.pop();
11173
+ j += 1;
11174
+ if (stack.length === 0) return { text: text.slice(at, j), end: j };
11175
+ continue;
11176
+ }
11177
+ j += 1;
11178
+ }
11179
+
11180
+ throw new N3SyntaxError(`Unterminated term inside RDF 1.2 triple term, expected ${stack[stack.length - 1]}`);
11181
+ }
11182
+
11183
+ function readRdfTripleTermComponent(text, at) {
11184
+ const j = skipWsAndComments(text, at);
11185
+ if (j >= text.length) return null;
11186
+ const ch = text[j];
11187
+
11188
+ if (ch === '<') return readIriAt(text, j);
11189
+
11190
+ if (ch === '"' || ch === "'") {
11191
+ const str = readStringAt(text, j);
11192
+ let end = str.end;
11193
+ let termText = str.text;
11194
+ if (text.startsWith('^^', end)) {
11195
+ const datatype = readRdfTripleTermComponent(text, end + 2);
11196
+ if (datatype) {
11197
+ termText += '^^' + datatype.text;
11198
+ end = datatype.end;
11199
+ }
11200
+ } else if (text[end] === '@') {
11201
+ let k = end + 1;
11202
+ if (/[A-Za-z]/.test(text[k] || '')) {
11203
+ while (k < text.length && /[A-Za-z0-9-]/.test(text[k])) k += 1;
11204
+ termText += text.slice(end, k);
11205
+ end = k;
11206
+ }
11207
+ }
11208
+ return { text: termText, end };
11209
+ }
11210
+
11211
+ if (ch === '{' || ch === '[' || ch === '(') return readBalancedTermAt(text, j);
11212
+
11213
+ let k = j;
11214
+ while (k < text.length && !/\s/.test(text[k]) && !'{}[](),;'.includes(text[k])) k += 1;
11215
+ if (k === j) return null;
11216
+ const value = text.slice(j, k);
11217
+ if (!value || value.startsWith('@')) return null;
11218
+ return { text: value, end: k };
11219
+ }
11220
+
11221
+ function validateSingleRdfTripleTerm(rawTriple) {
11222
+ const triple = rawTriple.trim();
11223
+ if (!triple) throw new N3SyntaxError('RDF 1.2 triple term must contain exactly one subject, predicate, and object');
11224
+
11225
+ let pos = 0;
11226
+ for (const label of ['subject', 'predicate', 'object']) {
11227
+ const term = readRdfTripleTermComponent(triple, pos);
11228
+ if (!term) throw new N3SyntaxError(`RDF 1.2 triple term is missing a ${label}`);
11229
+ pos = term.end;
11230
+ }
11231
+
11232
+ const rest = skipWsAndComments(triple, pos);
11233
+ if (rest >= triple.length) return;
11234
+
11235
+ const found = triple[rest];
11236
+ if (found === ',') {
11237
+ throw new N3SyntaxError("RDF 1.2 triple terms must contain exactly one object; object lists using ',' are not valid inside <<( ... )>>");
11238
+ }
11239
+ if (found === ';') {
11240
+ throw new N3SyntaxError("RDF 1.2 triple terms must contain exactly one predicate-object pair; ';' is not valid inside <<( ... )>>");
11241
+ }
11242
+ throw new N3SyntaxError(`RDF 1.2 triple term must contain exactly one subject, predicate, and object; unexpected ${JSON.stringify(triple.slice(rest, rest + 20))}`);
11243
+ }
11244
+
11143
11245
  function graphTermFromTripleBody(rawBody, parenthesized) {
11144
11246
  let body = rawBody.trim();
11145
11247
  if (parenthesized && body.startsWith('(') && body.endsWith(')')) body = body.slice(1, -1).trim();
11146
11248
  const split = splitTopLevelReifier(body);
11147
11249
  const triple = split.triple;
11250
+ validateSingleRdfTripleTerm(triple);
11148
11251
  const graph = '{ ' + triple + ' }';
11149
11252
  if (split.reifier) {
11150
11253
  const reifier = firstTerm(split.reifier);
package/lib/lexer.js CHANGED
@@ -515,11 +515,114 @@ function normalizeRdfCompatibility(inputText) {
515
515
  return text.slice(at, j);
516
516
  }
517
517
 
518
+ function readBalancedTermAt(text, at) {
519
+ const open = text[at];
520
+ const matching = { '{': '}', '[': ']', '(': ')' };
521
+ if (!matching[open]) return null;
522
+
523
+ const stack = [matching[open]];
524
+ let j = at + 1;
525
+ while (j < text.length) {
526
+ const ch = text[j];
527
+ if (ch === '"' || ch === "'") {
528
+ const str = readStringAt(text, j);
529
+ j = str.end;
530
+ continue;
531
+ }
532
+ if (ch === '<' && !text.startsWith('<<', j)) {
533
+ const iri = readIriAt(text, j);
534
+ j = iri.end;
535
+ continue;
536
+ }
537
+ if (ch === '#') {
538
+ while (j < text.length && text[j] !== '\n' && text[j] !== '\r') j += 1;
539
+ continue;
540
+ }
541
+ if (matching[ch]) {
542
+ stack.push(matching[ch]);
543
+ j += 1;
544
+ continue;
545
+ }
546
+ if (ch === stack[stack.length - 1]) {
547
+ stack.pop();
548
+ j += 1;
549
+ if (stack.length === 0) return { text: text.slice(at, j), end: j };
550
+ continue;
551
+ }
552
+ j += 1;
553
+ }
554
+
555
+ throw new N3SyntaxError(`Unterminated term inside RDF 1.2 triple term, expected ${stack[stack.length - 1]}`);
556
+ }
557
+
558
+ function readRdfTripleTermComponent(text, at) {
559
+ const j = skipWsAndComments(text, at);
560
+ if (j >= text.length) return null;
561
+ const ch = text[j];
562
+
563
+ if (ch === '<') return readIriAt(text, j);
564
+
565
+ if (ch === '"' || ch === "'") {
566
+ const str = readStringAt(text, j);
567
+ let end = str.end;
568
+ let termText = str.text;
569
+ if (text.startsWith('^^', end)) {
570
+ const datatype = readRdfTripleTermComponent(text, end + 2);
571
+ if (datatype) {
572
+ termText += '^^' + datatype.text;
573
+ end = datatype.end;
574
+ }
575
+ } else if (text[end] === '@') {
576
+ let k = end + 1;
577
+ if (/[A-Za-z]/.test(text[k] || '')) {
578
+ while (k < text.length && /[A-Za-z0-9-]/.test(text[k])) k += 1;
579
+ termText += text.slice(end, k);
580
+ end = k;
581
+ }
582
+ }
583
+ return { text: termText, end };
584
+ }
585
+
586
+ if (ch === '{' || ch === '[' || ch === '(') return readBalancedTermAt(text, j);
587
+
588
+ let k = j;
589
+ while (k < text.length && !/\s/.test(text[k]) && !'{}[](),;'.includes(text[k])) k += 1;
590
+ if (k === j) return null;
591
+ const value = text.slice(j, k);
592
+ if (!value || value.startsWith('@')) return null;
593
+ return { text: value, end: k };
594
+ }
595
+
596
+ function validateSingleRdfTripleTerm(rawTriple) {
597
+ const triple = rawTriple.trim();
598
+ if (!triple) throw new N3SyntaxError('RDF 1.2 triple term must contain exactly one subject, predicate, and object');
599
+
600
+ let pos = 0;
601
+ for (const label of ['subject', 'predicate', 'object']) {
602
+ const term = readRdfTripleTermComponent(triple, pos);
603
+ if (!term) throw new N3SyntaxError(`RDF 1.2 triple term is missing a ${label}`);
604
+ pos = term.end;
605
+ }
606
+
607
+ const rest = skipWsAndComments(triple, pos);
608
+ if (rest >= triple.length) return;
609
+
610
+ const found = triple[rest];
611
+ if (found === ',') {
612
+ throw new N3SyntaxError("RDF 1.2 triple terms must contain exactly one object; object lists using ',' are not valid inside <<( ... )>>");
613
+ }
614
+ if (found === ';') {
615
+ throw new N3SyntaxError("RDF 1.2 triple terms must contain exactly one predicate-object pair; ';' is not valid inside <<( ... )>>");
616
+ }
617
+ throw new N3SyntaxError(`RDF 1.2 triple term must contain exactly one subject, predicate, and object; unexpected ${JSON.stringify(triple.slice(rest, rest + 20))}`);
618
+ }
619
+
518
620
  function graphTermFromTripleBody(rawBody, parenthesized) {
519
621
  let body = rawBody.trim();
520
622
  if (parenthesized && body.startsWith('(') && body.endsWith(')')) body = body.slice(1, -1).trim();
521
623
  const split = splitTopLevelReifier(body);
522
624
  const triple = split.triple;
625
+ validateSingleRdfTripleTerm(triple);
523
626
  const graph = '{ ' + triple + ' }';
524
627
  if (split.reifier) {
525
628
  const reifier = firstTerm(split.reifier);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.28.1",
3
+ "version": "1.28.2",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
package/test/api.test.js CHANGED
@@ -2916,6 +2916,22 @@ _:b a ex:Person ; ex:name "B" .
2916
2916
  expect: [/:observation\s+:entails\s+<<\(\s+:sensor\s+:needs\s+:inspection\s*\)>>\s*\./m],
2917
2917
  },
2918
2918
 
2919
+ {
2920
+ name: 'RDF mode rejects object lists inside RDF 1.2 triple terms',
2921
+ opt: { proofComments: false, rdf: true },
2922
+ input: `VERSION "1.2"
2923
+ @prefix : <http://example.org/ns#> .
2924
+ @prefix coll: <http://example.org/collection#> .
2925
+
2926
+ :climateDataset coll:contains
2927
+ <<( :asiaDataset coll:contains
2928
+ <<( :China :avgTemp :18C )>>,
2929
+ <<( :Thailand :avgTemp :20C )>>
2930
+ )>> .
2931
+ `,
2932
+ expectError: true,
2933
+ },
2934
+
2919
2935
  {
2920
2936
  name: 'RDF mode accepts PREFIX and reified triple terms with reifiers',
2921
2937
  opt: { proofComments: false, rdf: true },