Skip to content

Commit 383f94e

Browse files
smessiejeswr
andauthored
fix: dont accidentally re-bind custom data factories (#467)
* fix: Correctly handle custom data factories by binding functions * chore: only bind on non-native factories * chore: use direct calls rather than binding * chore: fix comment placement --------- Co-authored-by: Jesse Wright <[email protected]>
1 parent 5294eb5 commit 383f94e

File tree

3 files changed

+92
-54
lines changed

3 files changed

+92
-54
lines changed

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/N3Parser.js

Lines changed: 42 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -159,23 +159,23 @@ export default class N3Parser {
159159
const iri = this._resolveIRI(token.value);
160160
if (iri === null)
161161
return this._error('Invalid IRI', token);
162-
value = this._namedNode(iri);
162+
value = this._factory.namedNode(iri);
163163
break;
164164
// Read a prefixed name
165165
case 'type':
166166
case 'prefixed':
167167
const prefix = this._prefixes[token.prefix];
168168
if (prefix === undefined)
169169
return this._error(`Undefined prefix "${token.prefix}:"`, token);
170-
value = this._namedNode(prefix + token.value);
170+
value = this._factory.namedNode(prefix + token.value);
171171
break;
172172
// Read a blank node
173173
case 'blank':
174-
value = this._blankNode(this._prefixes[token.prefix] + token.value);
174+
value = this._factory.blankNode(this._prefixes[token.prefix] + token.value);
175175
break;
176176
// Read a variable
177177
case 'var':
178-
value = this._variable(token.value.substr(1));
178+
value = this._factory.variable(token.value.substr(1));
179179
break;
180180
// Everything else is not an entity
181181
default:
@@ -194,7 +194,7 @@ export default class N3Parser {
194194
case '[':
195195
// Start a new quad with a new blank node as subject
196196
this._saveContext('blank', this._graph,
197-
this._subject = this._blankNode(), null, null);
197+
this._subject = this._factory.blankNode(), null, null);
198198
return this._readBlankNodeHead;
199199
case '(':
200200
// Start a new list
@@ -206,7 +206,7 @@ export default class N3Parser {
206206
if (!this._n3Mode)
207207
return this._error('Unexpected graph', token);
208208
this._saveContext('formula', this._graph,
209-
this._graph = this._blankNode(), null, null);
209+
this._graph = this._factory.blankNode(), null, null);
210210
return this._readSubject;
211211
case '}':
212212
// No subject; the graph in which we are reading is closed instead
@@ -216,14 +216,14 @@ export default class N3Parser {
216216
return this._error('Unexpected "@forSome"', token);
217217
this._subject = null;
218218
this._predicate = this.N3_FORSOME;
219-
this._quantifier = this._blankNode;
219+
this._quantifier = 'blankNode';
220220
return this._readQuantifierList;
221221
case '@forAll':
222222
if (!this._n3Mode)
223223
return this._error('Unexpected "@forAll"', token);
224224
this._subject = null;
225225
this._predicate = this.N3_FORALL;
226-
this._quantifier = this._variable;
226+
this._quantifier = 'variable';
227227
return this._readQuantifierList;
228228
case 'literal':
229229
if (!this._n3Mode)
@@ -234,7 +234,7 @@ export default class N3Parser {
234234
return this._completeSubjectLiteral;
235235
}
236236
else
237-
this._subject = this._literal(token.value, this._namedNode(token.prefix));
237+
this._subject = this._factory.literal(token.value, this._factory.namedNode(token.prefix));
238238

239239
break;
240240
case '<<':
@@ -282,7 +282,7 @@ export default class N3Parser {
282282
if (this._n3Mode) {
283283
// Start a new quad with a new blank node as subject
284284
this._saveContext('blank', this._graph, this._subject,
285-
this._subject = this._blankNode(), null);
285+
this._subject = this._factory.blankNode(), null);
286286
return this._readBlankNodeHead;
287287
}
288288
case 'blank':
@@ -307,12 +307,12 @@ export default class N3Parser {
307307
}
308308
// Pre-datatyped string literal (prefix stores the datatype)
309309
else
310-
this._object = this._literal(token.value, this._namedNode(token.prefix));
310+
this._object = this._factory.literal(token.value, this._factory.namedNode(token.prefix));
311311
break;
312312
case '[':
313313
// Start a new quad with a new blank node as subject
314314
this._saveContext('blank', this._graph, this._subject, this._predicate,
315-
this._subject = this._blankNode());
315+
this._subject = this._factory.blankNode());
316316
return this._readBlankNodeHead;
317317
case '(':
318318
// Start a new list
@@ -324,7 +324,7 @@ export default class N3Parser {
324324
if (!this._n3Mode)
325325
return this._error('Unexpected graph', token);
326326
this._saveContext('formula', this._graph, this._subject, this._predicate,
327-
this._graph = this._blankNode());
327+
this._graph = this._factory.blankNode());
328328
return this._readSubject;
329329
case '<<':
330330
if (!this._supportsRDFStar)
@@ -419,14 +419,14 @@ export default class N3Parser {
419419
case '[':
420420
// Stack the current list quad and start a new quad with a blank node as subject
421421
this._saveContext('blank', this._graph,
422-
list = this._blankNode(), this.RDF_FIRST,
423-
this._subject = item = this._blankNode());
422+
list = this._factory.blankNode(), this.RDF_FIRST,
423+
this._subject = item = this._factory.blankNode());
424424
next = this._readBlankNodeHead;
425425
break;
426426
case '(':
427427
// Stack the current list quad and start a new list
428428
this._saveContext('list', this._graph,
429-
list = this._blankNode(), this.RDF_FIRST, this.RDF_NIL);
429+
list = this._factory.blankNode(), this.RDF_FIRST, this.RDF_NIL);
430430
this._subject = null;
431431
break;
432432
case ')':
@@ -462,7 +462,7 @@ export default class N3Parser {
462462
}
463463
// Pre-datatyped string literal (prefix stores the datatype)
464464
else {
465-
item = this._literal(token.value, this._namedNode(token.prefix));
465+
item = this._factory.literal(token.value, this._factory.namedNode(token.prefix));
466466
next = this._getContextEndReader();
467467
}
468468
break;
@@ -471,7 +471,7 @@ export default class N3Parser {
471471
if (!this._n3Mode)
472472
return this._error('Unexpected graph', token);
473473
this._saveContext('formula', this._graph, this._subject, this._predicate,
474-
this._graph = this._blankNode());
474+
this._graph = this._factory.blankNode());
475475
return this._readSubject;
476476
default:
477477
if ((item = this._readEntity(token)) === undefined)
@@ -480,7 +480,7 @@ export default class N3Parser {
480480

481481
// Create a new blank node if no item head was assigned yet
482482
if (list === null)
483-
this._subject = list = this._blankNode();
483+
this._subject = list = this._factory.blankNode();
484484

485485
// Is this the first element of the list?
486486
if (previousList === null) {
@@ -524,20 +524,20 @@ export default class N3Parser {
524524
// ### `_completeLiteral` completes a literal with an optional datatype or language
525525
_completeLiteral(token) {
526526
// Create a simple string literal by default
527-
let literal = this._literal(this._literalValue);
527+
let literal = this._factory.literal(this._literalValue);
528528

529529
switch (token.type) {
530530
// Create a datatyped literal
531531
case 'type':
532532
case 'typeIRI':
533533
const datatype = this._readEntity(token);
534534
if (datatype === undefined) return; // No datatype means an error occurred
535-
literal = this._literal(this._literalValue, datatype);
535+
literal = this._factory.literal(this._literalValue, datatype);
536536
token = null;
537537
break;
538538
// Create a language-tagged string
539539
case 'langcode':
540-
literal = this._literal(this._literalValue, token.value);
540+
literal = this._factory.literal(this._literalValue, token.value);
541541
token = null;
542542
break;
543543
}
@@ -620,7 +620,7 @@ export default class N3Parser {
620620
return this._error('Unexpected RDF-star syntax', token);
621621
// Continue using the last triple as quoted triple subject for the predicate-object pairs.
622622
const predicate = this._predicate, object = this._object;
623-
this._subject = this._quad(subject, predicate, object, this.DEFAULTGRAPH);
623+
this._subject = this._factory.quad(subject, predicate, object, this.DEFAULTGRAPH);
624624
next = this._readPredicate;
625625
break;
626626
// |} means that the current quoted triple in annotation syntax is finalized.
@@ -721,7 +721,7 @@ export default class N3Parser {
721721
_readNamedGraphBlankLabel(token) {
722722
if (token.type !== ']')
723723
return this._error('Invalid graph label', token);
724-
this._subject = this._blankNode();
724+
this._subject = this._factory.blankNode();
725725
return this._readGraph;
726726
}
727727

@@ -751,17 +751,17 @@ export default class N3Parser {
751751
}
752752
// Without explicit quantifiers, map entities to a quantified entity
753753
if (!this._explicitQuantifiers)
754-
this._quantified[entity.id] = this._quantifier(this._blankNode().value);
754+
this._quantified[entity.id] = this._factory[this._quantifier](this._factory.blankNode().value);
755755
// With explicit quantifiers, output the reified quantifier
756756
else {
757757
// If this is the first item, start a new quantifier list
758758
if (this._subject === null)
759759
this._emit(this._graph || this.DEFAULTGRAPH, this._predicate,
760-
this._subject = this._blankNode(), this.QUANTIFIERS_GRAPH);
760+
this._subject = this._factory.blankNode(), this.QUANTIFIERS_GRAPH);
761761
// Otherwise, continue the previous list
762762
else
763763
this._emit(this._subject, this.RDF_REST,
764-
this._subject = this._blankNode(), this.QUANTIFIERS_GRAPH);
764+
this._subject = this._factory.blankNode(), this.QUANTIFIERS_GRAPH);
765765
// Output the list item
766766
this._emit(this._subject, this.RDF_FIRST, entity, this.QUANTIFIERS_GRAPH);
767767
}
@@ -818,7 +818,7 @@ export default class N3Parser {
818818
// ### `_readForwardPath` reads a '!' path
819819
_readForwardPath(token) {
820820
let subject, predicate;
821-
const object = this._blankNode();
821+
const object = this._factory.blankNode();
822822
// The next token is the predicate
823823
if ((predicate = this._readEntity(token)) === undefined)
824824
return;
@@ -835,7 +835,7 @@ export default class N3Parser {
835835

836836
// ### `_readBackwardPath` reads a '^' path
837837
_readBackwardPath(token) {
838-
const subject = this._blankNode();
838+
const subject = this._factory.blankNode();
839839
let predicate, object;
840840
// The next token is the predicate
841841
if ((predicate = this._readEntity(token)) === undefined)
@@ -867,7 +867,7 @@ export default class N3Parser {
867867
if (token.type !== '>>')
868868
return this._error(`Expected >> but got ${token.type}`, token);
869869
// Read the quad and restore the previous context
870-
const quad = this._quad(this._subject, this._predicate, this._object,
870+
const quad = this._factory.quad(this._subject, this._predicate, this._object,
871871
this._graph || this.DEFAULTGRAPH);
872872
this._restoreContext('<<', token);
873873
// If the triple was the subject, continue by reading the predicate.
@@ -902,7 +902,7 @@ export default class N3Parser {
902902

903903
// ### `_emit` sends a quad through the callback
904904
_emit(subject, predicate, object, graph) {
905-
this._callback(null, this._quad(subject, predicate, object, graph || this.DEFAULTGRAPH));
905+
this._callback(null, this._factory.quad(subject, predicate, object, graph || this.DEFAULTGRAPH));
906906
}
907907

908908
// ### `_error` emits an error message through the callback
@@ -1082,26 +1082,21 @@ function noop() {}
10821082

10831083
// Initializes the parser with the given data factory
10841084
function initDataFactory(parser, factory) {
1085-
// Set factory methods
1086-
const namedNode = factory.namedNode;
1087-
parser._namedNode = namedNode;
1088-
parser._blankNode = factory.blankNode;
1089-
parser._literal = factory.literal;
1090-
parser._variable = factory.variable;
1091-
parser._quad = factory.quad;
1085+
parser._factory = factory;
1086+
10921087
parser.DEFAULTGRAPH = factory.defaultGraph();
10931088

10941089
// Set common named nodes
1095-
parser.RDF_FIRST = namedNode(namespaces.rdf.first);
1096-
parser.RDF_REST = namedNode(namespaces.rdf.rest);
1097-
parser.RDF_NIL = namedNode(namespaces.rdf.nil);
1098-
parser.N3_FORALL = namedNode(namespaces.r.forAll);
1099-
parser.N3_FORSOME = namedNode(namespaces.r.forSome);
1090+
parser.RDF_FIRST = factory.namedNode(namespaces.rdf.first);
1091+
parser.RDF_REST = factory.namedNode(namespaces.rdf.rest);
1092+
parser.RDF_NIL = factory.namedNode(namespaces.rdf.nil);
1093+
parser.N3_FORALL = factory.namedNode(namespaces.r.forAll);
1094+
parser.N3_FORSOME = factory.namedNode(namespaces.r.forSome);
11001095
parser.ABBREVIATIONS = {
1101-
'a': namedNode(namespaces.rdf.type),
1102-
'=': namedNode(namespaces.owl.sameAs),
1103-
'>': namedNode(namespaces.log.implies),
1096+
'a': factory.namedNode(namespaces.rdf.type),
1097+
'=': factory.namedNode(namespaces.owl.sameAs),
1098+
'>': factory.namedNode(namespaces.log.implies),
11041099
};
1105-
parser.QUANTIFIERS_GRAPH = namedNode('urn:n3:quantifiers');
1100+
parser.QUANTIFIERS_GRAPH = factory.namedNode('urn:n3:quantifiers');
11061101
}
11071102
initDataFactory(N3Parser.prototype, N3DataFactory);

test/N3Parser-test.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ beforeEach(() => {
1010
blankId = 0; // reset per-node ID
1111
Parser._resetBlankNodePrefix(); // reset per-parser ID
1212
});
13-
Parser.prototype._blankNode = name => new BlankNode(name || `b${blankId++}`);
13+
Parser.prototype._factory.blankNode = name => new BlankNode(name || `b${blankId++}`);
1414

1515
describe('Parser', () => {
1616
describe('The Parser export', () => {
@@ -2592,6 +2592,49 @@ describe('Parser', () => {
25922592
),
25932593
])).toBe(true);
25942594
});
2595+
2596+
it('should use the internal `this` state', () => {
2597+
const customFactory = {
2598+
blankI: 0,
2599+
...rdfDataModel,
2600+
blankNode: function () {
2601+
return DF.blankNode(`b-custom-${this.blankI++}`);
2602+
},
2603+
};
2604+
2605+
const parser = new Parser({
2606+
baseIRI: BASE_IRI,
2607+
format: 'n3',
2608+
factory: customFactory,
2609+
});
2610+
2611+
const quads = parser.parse(`
2612+
@prefix : <http://example.com/> .
2613+
[ :friend [
2614+
:name "Thomas" ;
2615+
]
2616+
] .
2617+
`);
2618+
2619+
expect(quads.length).toBeGreaterThan(0);
2620+
2621+
// The blank node should be named b-custom-0 and b-custom-1
2622+
const bnode1 = DF.blankNode('b-custom-0');
2623+
const bnode2 = DF.blankNode('b-custom-1');
2624+
2625+
expect(isomorphic(quads, [
2626+
DF.quad(
2627+
bnode1,
2628+
DF.namedNode('http://example.com/friend'),
2629+
bnode2,
2630+
),
2631+
DF.quad(
2632+
bnode2,
2633+
DF.namedNode('http://example.com/name'),
2634+
DF.literal('Thomas'),
2635+
),
2636+
])).toBe(true);
2637+
});
25952638
});
25962639

25972640
describe('A turtle parser instance with external data factory', () => {

0 commit comments

Comments
 (0)