eyeling 1.6.21 → 1.6.22

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 (2) hide show
  1. package/eyeling.js +73 -8
  2. package/package.json +1 -1
package/eyeling.js CHANGED
@@ -759,8 +759,9 @@ function lex(inputText) {
759
759
  // ===========================================================================
760
760
 
761
761
  class PrefixEnv {
762
- constructor(map) {
763
- this.map = map || {}; // prefix -> IRI
762
+ constructor(map, baseIri) {
763
+ this.map = map || {}; // prefix -> IRI (including "" for @prefix :)
764
+ this.baseIri = baseIri || ''; // base IRI for resolving <relative>
764
765
  }
765
766
 
766
767
  static newDefault() {
@@ -774,14 +775,18 @@ class PrefixEnv {
774
775
  m['list'] = LIST_NS;
775
776
  m['time'] = TIME_NS;
776
777
  m['genid'] = SKOLEM_NS;
777
- m[''] = '';
778
- return new PrefixEnv(m);
778
+ m[''] = ''; // empty prefix default namespace
779
+ return new PrefixEnv(m, ''); // base IRI starts empty
779
780
  }
780
781
 
781
782
  set(pref, base) {
782
783
  this.map[pref] = base;
783
784
  }
784
785
 
786
+ setBase(baseIri) {
787
+ this.baseIri = baseIri || '';
788
+ }
789
+
785
790
  expandQName(q) {
786
791
  if (q.includes(':')) {
787
792
  const [p, local] = q.split(':', 2);
@@ -1020,7 +1025,7 @@ class Parser {
1020
1025
  const tok2 = this.next();
1021
1026
  let iri;
1022
1027
  if (tok2.typ === 'IriRef') {
1023
- iri = tok2.value || '';
1028
+ iri = resolveIriRef(tok2.value || '', this.prefixes.baseIri || '');
1024
1029
  } else if (tok2.typ === 'Ident') {
1025
1030
  iri = this.prefixes.expandQName(tok2.value || '');
1026
1031
  } else {
@@ -1034,14 +1039,14 @@ class Parser {
1034
1039
  const tok = this.next();
1035
1040
  let iri;
1036
1041
  if (tok.typ === 'IriRef') {
1037
- iri = tok.value || '';
1042
+ iri = resolveIriRef(tok.value || '', this.prefixes.baseIri || '');
1038
1043
  } else if (tok.typ === 'Ident') {
1039
1044
  iri = tok.value || '';
1040
1045
  } else {
1041
1046
  throw new Error(`Expected IRI after @base, got ${tok.toString()}`);
1042
1047
  }
1043
1048
  this.expectDot();
1044
- this.prefixes.set('', iri);
1049
+ this.prefixes.setBase(iri);
1045
1050
  }
1046
1051
 
1047
1052
  parseTerm() {
@@ -1072,7 +1077,7 @@ class Parser {
1072
1077
  }
1073
1078
 
1074
1079
  if (typ === 'IriRef') {
1075
- const base = this.prefixes.map[''] || '';
1080
+ const base = this.prefixes.baseIri || '';
1076
1081
  return internIri(resolveIriRef(val || '', base));
1077
1082
  }
1078
1083
  if (typ === 'Ident') {
@@ -1150,6 +1155,66 @@ class Parser {
1150
1155
  return new Blank(`_:b${this.blankCounter}`);
1151
1156
  }
1152
1157
 
1158
+ // IRI property list: [ id <IRI> predicateObjectList? ]
1159
+ // Lets you embed descriptions of an IRI directly in object position.
1160
+ if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'id') {
1161
+ this.next(); // consume 'id'
1162
+ const iriTerm = this.parseTerm();
1163
+
1164
+ // N3 note: 'id' form is not meant to be used with blank node identifiers.
1165
+ if (iriTerm instanceof Blank && iriTerm.label.startsWith('_:')) {
1166
+ throw new Error("Cannot use 'id' keyword with a blank node identifier inside [...]");
1167
+ }
1168
+
1169
+ // Optional ';' right after the id IRI (tolerated).
1170
+ if (this.peek().typ === 'Semicolon') this.next();
1171
+
1172
+ // Empty IRI property list: [ id :iri ]
1173
+ if (this.peek().typ === 'RBracket') {
1174
+ this.next();
1175
+ return iriTerm;
1176
+ }
1177
+
1178
+ const subj = iriTerm;
1179
+ while (true) {
1180
+ let pred;
1181
+ let invert = false;
1182
+ if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'a') {
1183
+ this.next();
1184
+ pred = internIri(RDF_NS + 'type');
1185
+ } else if (this.peek().typ === 'OpPredInvert') {
1186
+ this.next(); // "<-"
1187
+ pred = this.parseTerm();
1188
+ invert = true;
1189
+ } else {
1190
+ pred = this.parseTerm();
1191
+ }
1192
+
1193
+ const objs = [this.parseTerm()];
1194
+ while (this.peek().typ === 'Comma') {
1195
+ this.next();
1196
+ objs.push(this.parseTerm());
1197
+ }
1198
+
1199
+ for (const o of objs) {
1200
+ this.pendingTriples.push(invert ? new Triple(o, pred, subj) : new Triple(subj, pred, o));
1201
+ }
1202
+
1203
+ if (this.peek().typ === 'Semicolon') {
1204
+ this.next();
1205
+ if (this.peek().typ === 'RBracket') break;
1206
+ continue;
1207
+ }
1208
+ break;
1209
+ }
1210
+
1211
+ if (this.peek().typ !== 'RBracket') {
1212
+ throw new Error(`Expected ']' at end of IRI property list, got ${JSON.stringify(this.peek())}`);
1213
+ }
1214
+ this.next();
1215
+ return iriTerm;
1216
+ }
1217
+
1153
1218
  // [ predicateObjectList ]
1154
1219
  this.blankCounter += 1;
1155
1220
  const id = `_:b${this.blankCounter}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.6.21",
3
+ "version": "1.6.22",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [