From 2f7d6e859e46645b7b54c37529d9e43f3c03d2ff Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 23 Nov 2022 13:57:38 +1100 Subject: [PATCH 01/29] chore: add nested list test --- test/N3Parser-test.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 80cb2466..b3b8d8c7 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -476,6 +476,16 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b0_y'], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + it('should parse a nested list', + shouldParse(' ( ( ) ).', + ['a', 'b', '_:b0'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'c'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'] + )); + + it('should parse statements with a nested empty list', shouldParse(' ( ()).', ['a', 'b', '_:b0'], From 37ab09c3d27550b505ef3ac6e61eaf378bc3810c Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 23 Nov 2022 14:48:43 +1100 Subject: [PATCH 02/29] fix: support quoted triples in list --- src/N3Parser.js | 17 +++++ test/N3Parser-test.js | 165 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 172 insertions(+), 10 deletions(-) diff --git a/src/N3Parser.js b/src/N3Parser.js index 1a7d7449..2045cd7d 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -473,6 +473,16 @@ export default class N3Parser { this._saveContext('formula', this._graph, this._subject, this._predicate, this._graph = this._blankNode()); return this._readSubject; + case '<<': + if (!this._supportsRDFStar) + return this._error('Unexpected RDF* syntax', token); + + this._saveContext('<<', this._graph, this._subject, null, null); + return this._readSubject; + case '>>': + item = this._graph; + this._graph = null; + break; default: if ((item = this._readEntity(token)) === undefined) return; @@ -854,6 +864,13 @@ export default class N3Parser { const quad = this._quad(this._subject, this._predicate, this._object, this._graph || this.DEFAULTGRAPH); this._restoreContext('<<', token); + + // If the triple is in a list then return to reading the remaining elements + if (this._contextStack.length > 0 && this._contextStack[this._contextStack.length - 1].type === 'list') { + this._graph = quad; + return this._readListItem(token); + } + // If the triple was the subject, continue by reading the predicate. if (this._subject === null) { this._subject = quad; diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index b3b8d8c7..60935bff 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -478,13 +478,11 @@ describe('Parser', () => { it('should parse a nested list', shouldParse(' ( ( ) ).', - ['a', 'b', '_:b0'], - ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], - ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], - ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'c'], - ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'] - )); - + ['a', 'b', '_:b0'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'c'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); it('should parse statements with a nested empty list', shouldParse(' ( ()).', @@ -504,13 +502,152 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'y'], ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a blank node', + it('should parse statements with a list containing a quoted triple', + shouldParse(' ( << >> ) .', + ['a', 'b', '_:b0'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a quoted triple and iri after', + shouldParse(' ( << >> ) .', + ['a', 'b', '_:b0'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'f'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a quoted triple and iri before', + shouldParse(' ( << >> ) .', + ['a', 'b', '_:b0'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'f'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a 2 quoted triples', + shouldParse(' ( << >> << >> ) .', + ['a', 'b', '_:b0'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c1', 'd1', 'e1']], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + + it('should parse statements with a list containing a 3 quoted triples', + shouldParse(' ( << >> << >> << >> ) .', + ['a', 'b', '_:b0'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c1', 'd1', 'e1']], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b2'], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c2', 'd2', 'e2']], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a 1 quoted triple and 2 iris', + shouldParse(' ( << >> ) .', + ['a', 'b', '_:b0'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'h'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b2'], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'i'], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a 1 quoted triple between 2 iris', + shouldParse(' ( << >> ) .', + ['a', 'b', '_:b0'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'h'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b2'], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'i'], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + + it('should parse a nested list containing 1 quoted triple', + shouldParse(' ( ( << >> ) ).', + ['a', 'b', '_:b0'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a quoted triple with list as subject', + shouldParse('( << >> ) .', + ['_:b0', 'a', 'b'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a quoted triple and iri after with list as subject', + shouldParse('( << >> ) .', + ['_:b0', 'a', 'b'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'f'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a quoted triple and iri before with list as subject', + shouldParse('( << >> ) .', + ['_:b0', 'a', 'b'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'f'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a 2 quoted triples with list as subject', + shouldParse('( << >> << >> ) .', + ['_:b0', 'a', 'b'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c1', 'd1', 'e1']], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + + it('should parse statements with a list containing a 3 quoted triples with list as subject', + shouldParse('( << >> << >> << >> ) .', + ['_:b0', 'a', 'b'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c1', 'd1', 'e1']], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b2'], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c2', 'd2', 'e2']], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a 1 quoted triple and 2 iris with list as subject', + shouldParse('( << >> ) .', + ['_:b0', 'a', 'b'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'h'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b2'], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'i'], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a 1 quoted triple between 2 iris with list as subject', + shouldParse('( << >> ) .', + ['_:b0', 'a', 'b'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'h'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b2'], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'i'], + ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse a nested list containing 1 quoted triple with list as subject', + shouldParse('( ( << >> ) ) .', + ['_:b0', 'a', 'b'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], + ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], + ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); + + it('should parse statements with a list containing a blank node with list as subject', shouldParse('([]) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing multiple blank nodes', + it('should parse statements with a list containing multiple blank nodes with list as subject', shouldParse('([] [ ]) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], @@ -519,7 +656,7 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], ['_:b3', 'x', 'y'])); - it('should parse statements with a blank node containing a list', + it('should parse statements with a blank node containing a list with list as subject', shouldParse('[ ()] .', ['_:b0', 'c', 'd'], ['_:b0', 'a', '_:b1'], @@ -1235,6 +1372,14 @@ describe('Parser', () => { it('should not parse RDF* in the object position', shouldNotParse(parser, ' < >>.', 'Unexpected RDF* syntax on line 1.')); + + it('should not parse RDF* in the object list', + shouldNotParse(parser, ' ( < >> ).', + 'Unexpected RDF* syntax on line 1.')); + + it('should not parse RDF* in the subject list', + shouldNotParse(parser, '( < >> ) .', + 'Unexpected RDF* syntax on line 1.')); }); describe('A Parser instance for the TurtleStar format', () => { From f837b8d46bd5f64b7001b02fccefc5d57dc76ccd Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 23 Nov 2022 15:13:24 +1100 Subject: [PATCH 03/29] breaking: drop support for quads in quoted triples as they are forbidden in the rdf-star spec --- src/N3Parser.js | 15 ++------------- test/N3Parser-test.js | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/src/N3Parser.js b/src/N3Parser.js index 2045cd7d..1e10dbb4 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -845,21 +845,10 @@ export default class N3Parser { return this._readPath; } - // ### `_readRDFStarTailOrGraph` reads the graph of a nested RDF* quad or the end of a nested RDF* triple - _readRDFStarTailOrGraph(token) { - if (token.type !== '>>') { - // An entity means this is a quad (only allowed if not already inside a graph) - if (this._supportsQuads && this._graph === null && (this._graph = this._readEntity(token)) !== undefined) - return this._readRDFStarTail; - return this._error(`Expected >> to follow "${this._object.id}"`, token); - } - return this._readRDFStarTail(token); - } - // ### `_readRDFStarTail` reads the end of a nested RDF* triple _readRDFStarTail(token) { if (token.type !== '>>') - return this._error(`Expected >> but got ${token.type}`, token); + return this._error(`Expected >> to follow "${this._object.id}" but got ${token.type}`, token); // Read the quad and restore the previous context const quad = this._quad(this._subject, this._predicate, this._object, this._graph || this.DEFAULTGRAPH); @@ -897,7 +886,7 @@ export default class N3Parser { case 'formula': return this._readFormulaTail; case '<<': - return this._readRDFStarTailOrGraph; + return this._readRDFStarTail; } } diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 60935bff..a85d653e 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -933,6 +933,10 @@ describe('Parser', () => { shouldParse(' .', ['a', 'b', 'c', 'g'])); + it('should not parse a quad in a quoted triple', + shouldNotParse('<< >> .', + 'Expected >> to follow "http://example.org/c" but got IRI on line 1.')); + it('should parse a quad with 4 prefixed names', shouldParse('@prefix p: .\np:a p:b p:c p:g.', ['p#a', 'p#b', 'p#c', 'p#g'])); @@ -1124,12 +1128,12 @@ describe('Parser', () => { it('should not parse nested RDF* statements that are partially closed', shouldNotParse(' <<<< >> .', - 'Expected entity but got . on line 1.' + 'Expected >> to follow "http://example.org/g" but got . on line 1.' )); it('should not parse partially closed nested RDF* statements', shouldNotParse(' <<<< >>.', - 'Expected >> but got IRI on line 1.' + 'Expected >> to follow "http://example.org/c" but got IRI on line 1.' )); it('should not parse nested RDF* statements with too many closing tags', @@ -1167,13 +1171,9 @@ describe('Parser', () => { 'Unexpected . on line 1.' )); - it('should parse an RDF* quad', - shouldParse('<< >> .', - [['a', 'b', 'c', 'd'], 'a', 'b'])); - it('should not parse a malformed RDF* quad', shouldNotParse('<< >> .', - 'Expected >> but got IRI on line 1.')); + 'Expected >> to follow "http://example.org/c" but got IRI on line 1.')); it('should parse statements with a shared RDF* subject', shouldParse('<< >> ;\n .', @@ -1392,7 +1392,7 @@ describe('Parser', () => { it('should not parse nested quads', shouldNotParse(parser, '<<_:a _:b >> "c" .', - 'Expected >> to follow "_:b0_b" on line 1.')); + 'Expected >> to follow "_:b0_b" but got IRI on line 1.')); }); describe('A Parser instance for the TriG format', () => { @@ -1452,7 +1452,7 @@ describe('Parser', () => { it('should not parse nested quads', shouldNotParse(parser, '<<_:a _:b >> "c" .', - 'Expected >> to follow "_:b0_b" on line 1.')); + 'Expected >> to follow "_:b0_b" but got IRI on line 1.')); }); describe('A Parser instance for the N-Triples format', () => { @@ -1527,7 +1527,7 @@ describe('Parser', () => { it('should not parse nested quads', shouldNotParse(parser, '<<_:a _:b >> "c" .', - 'Expected >> to follow "_:b0_b" on line 1.')); + 'Expected >> to follow "_:b0_b" but got IRI on line 1.')); }); describe('A Parser instance for the N-Quads format', () => { @@ -1941,7 +1941,7 @@ describe('Parser', () => { it('should not parse nested quads', shouldNotParse(parser, '<<_:a _:b >> "c" .', - 'Expected >> to follow "_:.b" on line 1.')); + 'Expected >> to follow "_:.b" but got IRI on line 1.')); }); describe('A Parser instance for the N3 format with the explicitQuantifiers option', () => { From da945e9bb992b6bad3f4859adb193d8972d9a891 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 23 Nov 2022 16:32:40 +1100 Subject: [PATCH 04/29] feat: support annotated triples --- src/N3Lexer.js | 10 ++++++- src/N3Parser.js | 27 ++++++++++++++++++ test/N3Lexer-test.js | 46 ++++++++++++++++++++++++++++++ test/N3Parser-test.js | 65 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 146 insertions(+), 2 deletions(-) diff --git a/src/N3Lexer.js b/src/N3Lexer.js index eeb36bdd..19ceca32 100644 --- a/src/N3Lexer.js +++ b/src/N3Lexer.js @@ -302,12 +302,20 @@ export default class N3Lexer { case ')': case '{': case '}': + if (input.length > 1 && input[1] === '|') { + type = '{|', matchLength = 2; + break; + } if (!this._lineMode) { matchLength = 1; type = firstChar; } break; - + case '|': + if (input.length > 1 && input[1] === '}') { + type = '|}', matchLength = 2; + break; + } default: inconclusive = true; } diff --git a/src/N3Parser.js b/src/N3Parser.js index 1e10dbb4..4af2750b 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -624,6 +624,19 @@ export default class N3Parser { case ',': next = this._readObject; break; + case '{|': + if (!this._supportsRDFStar) + return this._error('Unexpected RDF* syntax', token); + + // TODO: Have error handling behavior here + // TODO: See if we can just emit and then save null context + this._saveContext('{|', this._graph, this._subject, this._predicate, this._object); + + // Note - we always use the default graph for the quoted triple component + this._subject = this._quad(this._subject, this._predicate, this._object, this.DEFAULTGRAPH); + this._predicate = null; + this._object = null; + return this._readPredicate; default: // An entity means this is a quad (only allowed if not already inside a graph) if (this._supportsQuads && this._graph === null && (graph = this._readEntity(token)) !== undefined) { @@ -872,6 +885,18 @@ export default class N3Parser { } } + // ### `_readRDFStarTail` reads the end of a nested RDF* triple + _readAnnotatedTail(token) { + this._emit(this._subject, this._predicate, this._object, this._graph); + // if (this._subject && this._predicate && this._object) { + // this._emit(this._subject, this._predicate, this._object, this._graph); + // } + if (token.type !== '|}') + return this._readPredicate; + this._restoreContext('{|', token); + return this._getContextEndReader(); + } + // ### `_getContextEndReader` gets the next reader function at the end of a context _getContextEndReader() { const contextStack = this._contextStack; @@ -887,6 +912,8 @@ export default class N3Parser { return this._readFormulaTail; case '<<': return this._readRDFStarTail; + case '{|': + return this._readAnnotatedTail; } } diff --git a/test/N3Lexer-test.js b/test/N3Lexer-test.js index 03902d3f..8034f596 100644 --- a/test/N3Lexer-test.js +++ b/test/N3Lexer-test.js @@ -869,6 +869,52 @@ describe('Lexer', () => { shouldNotTokenize('<< \n\t \n\t> .', 'Unexpected ">" on line 3.')); + it('should tokenize an RDF-star annotated statement', + shouldTokenize(' {| |}', + { type: 'IRI', value: 'a', line: 1 }, + { type: 'IRI', value: 'b', line: 1 }, + { type: 'IRI', value: 'c', line: 1 }, + { type: '{|', line: 1 }, + { type: 'IRI', value: 'd', line: 1 }, + { type: 'IRI', value: 'e', line: 1 }, + { type: '|}', line: 1 }, + { type: 'eof', line: 1 })); + + it('should tokenize an RDF-star annotated statement with multiple annotations', + shouldTokenize(' {| ; |}', + { type: 'IRI', value: 'a', line: 1 }, + { type: 'IRI', value: 'b', line: 1 }, + { type: 'IRI', value: 'c', line: 1 }, + { type: '{|', line: 1 }, + { type: 'IRI', value: 'd', line: 1 }, + { type: 'IRI', value: 'e', line: 1 }, + { type: ';', line: 1 }, + { type: 'IRI', value: 'f', line: 1 }, + { type: 'IRI', value: 'g', line: 1 }, + { type: '|}', line: 1 }, + { type: 'eof', line: 1 })); + + it('should tokenize an RDF-star annotated statement with multiple annotations, one containing a blank node', + shouldTokenize(' {| [ "f" ]; |}', + { type: 'IRI', value: 'a', line: 1 }, + { type: 'IRI', value: 'b', line: 1 }, + { type: 'IRI', value: 'c', line: 1 }, + { type: '{|', line: 1 }, + { type: 'IRI', value: 'd', line: 1 }, + { type: '[', value: '', line: 1 }, + { type: 'IRI', value: 'e', line: 1 }, + { type: 'literal', value: 'f', line: 1 }, + { type: ']', value: '', line: 1 }, + { type: ';', line: 1 }, + { type: 'IRI', value: 'f', line: 1 }, + { type: 'IRI', value: 'g', line: 1 }, + { type: '|}', line: 1 }, + { type: 'eof', line: 1 })); + + it('should not tokenize an annotated statement that is not closed', + shouldNotTokenize(' {| [ "f" ]; |', + 'Unexpected "|" on line 1.')); + it('should tokenize a split RDF* statement with IRIs', shouldTokenize(streamOf('<', '< \n\t \n\t>> .'), { type: '<<', line: 1 }, diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index a85d653e..5b236b36 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -1189,6 +1189,65 @@ describe('Parser', () => { shouldParse(' .\n<< >> .', ['a', 'b', 'c', 'g'], [['a', 'b', 'c'], 'd', 'e'])); + + it('should parse an explicit triple with reified annotation', + shouldParse(' {| |} .', + ['a', 'b', 'c'], + [['a', 'b', 'c'], 'd', 'e'])); + + const q = ['http://example.com/ns#s', 'http://example.com/ns#p', + ['http://example.com/ns#a', 'http://example.com/ns#b', 'http://example.com/ns#c']]; + + it('should parse an explicit triple with reified annotation containing prefixed iris', + shouldParse('PREFIX : \n :s :p <<:a :b :c>> {| :q :z |} .', + q, [q, 'http://example.com/ns#q', 'http://example.com/ns#z'])); + + it('should parse an explicit triple with 2 reified annotations', + shouldParse(' {| ; |} .', + ['a', 'b', 'c'], + [['a', 'b', 'c'], 'd', 'e'], + [['a', 'b', 'c'], 'f', 'g'])); + + // TODO: See if this is required by the spec tests + // it('should parse an explicit triple with reified annotation containing punctuation', + // shouldParse(' {| . |} .', + // ['a', 'b', 'c'], + // [['a', 'b', 'c'], 'd', 'e'])); + + it('should parse an explicit triple with reified annotation in a named graph', + shouldParse(' { {| |} . }', + ['a', 'b', 'c', 'G'], + [['a', 'b', 'c'], 'd', 'e', 'G'])); + + it('should parse an explicit triple with 2 reified annotations in a named graph', + shouldParse(' { {| ; |} . }', + ['a', 'b', 'c', 'G'], + [['a', 'b', 'c'], 'd', 'e', 'G'], + [['a', 'b', 'c'], 'f', 'g', 'G'])); + + it('should not parse an annotated object in list', + shouldNotParse(' ( {| |} )', + 'Expected entity but got {| on line 1.')); + + it('should not parse an annotated statement in list', + shouldNotParse(' ( {| |} )', + 'Expected entity but got {| on line 1.')); + + it('should not parse fourth term in quoted triple', + shouldNotParse('<< >>

', + 'Expected >> to follow "http://example.org/c" but got IRI on line 1.')); + + it('should not parse fourth term in quoted triple object', + shouldNotParse('

<< >>', + 'Expected >> to follow "http://example.org/c" but got IRI on line 1.')); + + it('should not parse quoted triple as predicate', + shouldNotParse('

<< >> ', + 'Expected entity but got << on line 1.')); + + it('should not parse quoted quad as predicate', + shouldNotParse('

<< >> ', + 'Expected entity but got << on line 1.')); }); describe('An Parser instance without document IRI', () => { @@ -1369,6 +1428,10 @@ describe('Parser', () => { shouldNotParse(parser, '<< >> .', 'Unexpected RDF* syntax on line 1.')); + it('should not parse annotated statement', + shouldNotParse(parser, ' {| |} .', + 'Unexpected RDF* syntax on line 1.')); + it('should not parse RDF* in the object position', shouldNotParse(parser, ' < >>.', 'Unexpected RDF* syntax on line 1.')); @@ -2494,7 +2557,7 @@ function mapToQuad(item) { function toSortedJSON(triples) { triples = triples.map(t => { return JSON.stringify([ - t.subject.toJSON(), t.predicate.toJSON(), t.object.toJSON(), t.graph.toJSON(), + t.subject && t.subject.toJSON(), t.predicate && t.predicate.toJSON(), t.object && t.object.toJSON(), t.graph && t.graph.toJSON(), ]); }); triples.sort(); From 624e3e1e1dfb47b684e60feca5505ecb8b2211bf Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 23 Nov 2022 17:26:54 +1100 Subject: [PATCH 05/29] chore: error on quoted compound bnodes --- package-lock.json | 530 ++++++++++++++---------------------------- package.json | 10 +- src/N3Parser.js | 13 +- test/N3Parser-test.js | 30 +++ 4 files changed, 224 insertions(+), 359 deletions(-) diff --git a/package-lock.json b/package-lock.json index e3ff8bbe..c2f10e65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "mocha": "^8.0.0", "nyc": "^14.1.1", "pre-commit": "^1.2.2", - "rdf-test-suite": "^1.19.2", + "rdf-test-suite": "^1.20.0", "streamify-string": "^1.0.1", "uglify-js": "^3.14.3" }, @@ -1735,9 +1735,9 @@ "optional": true }, "node_modules/@rdfjs/types": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.0.1.tgz", - "integrity": "sha512-YxVkH0XrCNG3MWeZxfg596GFe+oorTVusmNxRP6ZHTsGczZ8AGvG3UchRNkg3Fy4MyysI7vBAA5YZbESL+VmHQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.1.0.tgz", + "integrity": "sha512-5zm8bN2/CC634dTcn/0AhTRLaQRjXDZs3QfcAsQKNturHT7XVWcKy/8p3P5gXl+YkZTAmy7T5M/LyiT/jbkENw==", "dev": true, "dependencies": { "@types/node": "*" @@ -1753,25 +1753,15 @@ } }, "node_modules/@types/json-stable-stringify": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz", - "integrity": "sha512-q9Q6+eUEGwQkv4Sbst3J4PNgDOvpuVuKj79Hl/qnmBMEIPzB5QoFRUtjcgcg2xNUZyYUGXBk5wYIBKHt0A+Mxw==", + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz", + "integrity": "sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw==", "dev": true }, - "node_modules/@types/log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-uW/AGf/41aZ1c1dhZ3s063Ii2OqT8EQooZu3t4VCRyR3dqyA2Bg46BcKyZpnWTY7wzm6cayq4jzylnruu4KqSA==", - "deprecated": "This is a stub types definition. log-symbols provides its own type definitions, so you do not need this installed.", - "dev": true, - "dependencies": { - "log-symbols": "*" - } - }, "node_modules/@types/minimist": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", - "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true }, "node_modules/@types/n3": { @@ -1785,25 +1775,31 @@ } }, "node_modules/@types/node": { - "version": "18.6.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.3.tgz", - "integrity": "sha512-6qKpDtoaYLM+5+AFChLhHermMQxc3TOEFIDzrZLPRGHPrLEwqFkkT5Kx3ju05g6X7uDPazz3jHbKPX0KzCjntg==", + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", "dev": true }, "node_modules/@types/readable-stream": { - "version": "2.3.14", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.14.tgz", - "integrity": "sha512-8jQ5Mp7bsDJEnW/69i6nAaQMoLwAVJVc7ZRAVTrdh/o6XueQsX38TEvKuYyoQj76/mg7WdlRfMrtl9pDLCJWsg==", + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz", + "integrity": "sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==", "dev": true, "dependencies": { "@types/node": "*", - "safe-buffer": "*" + "safe-buffer": "~5.1.1" } }, + "node_modules/@types/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/@types/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-dqYdvN7Sbw8QT/0Ci5rhjE4/iCMJEM0Y9rHpCu+gGXD9Lwbz28t6HI2yegsB6BoV1sShRMU6lAmAcgRjmFy7LA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==", "dev": true, "dependencies": { "@types/node": "*" @@ -3999,9 +3995,9 @@ } }, "node_modules/http-link-header": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.0.5.tgz", - "integrity": "sha512-msKrMbv/xHzhdOD4sstbEr+NbGqpv8ZtZliiCeByGENJo1jK1GZ/81zHF9HpWtEH5ihovPpdqHXniwZapJCKEA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.1.0.tgz", + "integrity": "sha512-pj6N1yxOz/ANO8HHsWGg/OoIL1kmRYvQnXQ7PIRpgp+15AnEsRH8fmIJE6D1OdWG2Bov+BJHVla1fFXxg1JbbA==", "dev": true, "engines": { "node": ">=6.0.0" @@ -4466,18 +4462,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-weakref": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", @@ -4689,12 +4673,15 @@ "dev": true }, "node_modules/json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz", + "integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==", "dev": true, "dependencies": { - "jsonify": "~0.0.0" + "jsonify": "^0.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/json-stable-stringify-without-jsonify": { @@ -4731,18 +4718,18 @@ } }, "node_modules/jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", "dev": true, - "engines": { - "node": "*" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/jsonld-context-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jsonld-context-parser/-/jsonld-context-parser-2.2.0.tgz", - "integrity": "sha512-h4ykp8iUOV4Xm6MgS1zVrytyw/dNVgOeofMCcD/5mHPng3i49qAsaomLT0BOXqYas7lwITVT5c6NZIRVMdXfVQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/jsonld-context-parser/-/jsonld-context-parser-2.2.2.tgz", + "integrity": "sha512-3VWIg/4NCMTXP6NsI6O93spFTd4qIOucKEmD8I+Exhxk9ZUVrnkLp2G4f0toR5jVleZkiiB9YGPS+yT1wwMqnQ==", "dev": true, "dependencies": { "@types/http-link-header": "^1.0.1", @@ -4757,9 +4744,9 @@ } }, "node_modules/jsonld-streaming-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonld-streaming-parser/-/jsonld-streaming-parser-3.0.0.tgz", - "integrity": "sha512-n+IW+gTIw2UeXWXdN0ZlPY4DvKANUCrV0HOagXOsDUCvkO/SiDcYOZn2hrDkBGKm7yD5sefvvG3d/FxbeepbuA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonld-streaming-parser/-/jsonld-streaming-parser-3.0.1.tgz", + "integrity": "sha512-zSJlEgrKypQDk/85R+xkudeCZo6vmnvJuCPvcjk2BzHPLzv1yqiwoKQDyFzfgfgCHM0p7YCJBZl0liT9RMUZJw==", "dev": true, "dependencies": { "@rdfjs/types": "*", @@ -5616,30 +5603,16 @@ "dev": true }, "node_modules/n3": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/n3/-/n3-1.16.2.tgz", - "integrity": "sha512-5vYa2HuNEJ+a26FEs4FGgfFLgaPOODaZpJlc7FS0eUjDumc4uK0cvx216PjKXBkLzmAsSqGgQPwqztcLLvwDsw==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/n3/-/n3-1.16.3.tgz", + "integrity": "sha512-9caLSZuMW1kdlPxEN4ka6E4E8a5QKoZ2emxpW+zHMofI+Bo92nJhN//wNub15S5T9I4c6saEqdGEu+YXJqMZVA==", "dev": true, "dependencies": { "queue-microtask": "^1.1.2", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/n3/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">=12.0" } }, "node_modules/nanoid": { @@ -6209,7 +6182,7 @@ "node_modules/promise-polyfill": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-1.1.6.tgz", - "integrity": "sha1-zQTv9G9clcOn0EVZHXm14+AfEtc=", + "integrity": "sha512-7rrONfyLkDEc7OJ5QBkqa4KI4EBhCd340xRuIUPGCfu13znS+vx+VDdrT9ODAJHlXm7w4lbxN3DRjyv58EuzDg==", "dev": true }, "node_modules/pseudomap": { @@ -6314,9 +6287,9 @@ } }, "node_modules/rdf-isomorphic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rdf-isomorphic/-/rdf-isomorphic-1.3.0.tgz", - "integrity": "sha512-3BRwUwCNHHR8//bqmVH+knTFVbVfkp7CWyQk7qPHHA8JriXBYxrab21OomjJx/2KF21w8bWz344mgNYEaQABYQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rdf-isomorphic/-/rdf-isomorphic-1.3.1.tgz", + "integrity": "sha512-6uIhsXTVp2AtO6f41PdnRV5xZsa0zVZQDTBdn0br+DZuFf5M/YD+T6m8hKDUnALI6nFL/IujTMLgEs20MlNidQ==", "dev": true, "dependencies": { "@rdfjs/types": "*", @@ -6335,9 +6308,9 @@ } }, "node_modules/rdf-literal": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rdf-literal/-/rdf-literal-1.3.0.tgz", - "integrity": "sha512-5u5L4kPYNZANie5AE4gCXqwpNO/p9E/nUcDurk05XAOJT/pt9rQlDk6+BX7j3dNSee3h9GS4xlLoWxQDj7sXtg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rdf-literal/-/rdf-literal-1.3.1.tgz", + "integrity": "sha512-+o/PGOfJchyay9Rjrvi/oveRJACnt2WFO3LhEvtPlsRD1tFmwVUCMU+s33FtQprMo+z1ohFrv/yfEQ6Eym4KgQ==", "dev": true, "dependencies": { "@rdfjs/types": "*", @@ -6345,9 +6318,9 @@ } }, "node_modules/rdf-object": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/rdf-object/-/rdf-object-1.13.1.tgz", - "integrity": "sha512-Sgq+GbsqdPsMYh+d4OZ4C9brXlzqa9MvfVHG4pkuT9p7o+AX39nqjTWE/8HVaXjjOZBIDe8T54WWTMWphu3BpA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/rdf-object/-/rdf-object-1.13.2.tgz", + "integrity": "sha512-DVLDCbxPOkhd/k43j9wcLU7CXe/gdldBBomMV3RyZ1G9E2zPa2FFNFijzMGgRGNY1OEyGmhBxw2eiJjUC7GVNw==", "dev": true, "dependencies": { "@rdfjs/types": "*", @@ -6379,9 +6352,9 @@ } }, "node_modules/rdf-terms": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/rdf-terms/-/rdf-terms-1.9.0.tgz", - "integrity": "sha512-FGMPOIpr6vEN8gWd/dVuPpcE/7k+u4Ufqi8FvM5lczjhduT1MN9Shmrw50fWCpHFVE4n0T3lV0qti1PCaHxAfg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/rdf-terms/-/rdf-terms-1.9.1.tgz", + "integrity": "sha512-GrE8CbQSvuVEFRCywMu6VOgV1AFE6X+nFYcAhEc5pwYKI13bUvz4voiVufQiy3V8rzQKu21Sgl+dS2qcJavy7w==", "dev": true, "dependencies": { "@rdfjs/types": "*", @@ -6390,14 +6363,13 @@ } }, "node_modules/rdf-test-suite": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/rdf-test-suite/-/rdf-test-suite-1.19.2.tgz", - "integrity": "sha512-Qvbf05SfcNcvwFzroBVSVf51zS6R74GaQmX43UwXKNxVWMoDyZlgXWLfznDtTJW2HfahnFkTsyosxrliN1zZ1Q==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/rdf-test-suite/-/rdf-test-suite-1.20.0.tgz", + "integrity": "sha512-5p26dxVT3Hxr0nV3fEmGHm/NYHr7i8R4zp1hijCNHVrwV22LhAGtYWqYUtRUe00HO0J0kUr+Hpxh3X/OejCLxA==", "dev": true, "dependencies": { "@rdfjs/types": "*", "@types/json-stable-stringify": "^1.0.32", - "@types/log-symbols": "^3.0.0", "@types/minimist": "^1.2.0", "@types/n3": "^1.10.3", "@types/sax": "^1.0.1", @@ -6428,70 +6400,12 @@ "rdf-test-suite": "bin/Runner.js" } }, - "node_modules/rdf-test-suite/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/rdf-test-suite/node_modules/arrayify-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arrayify-stream/-/arrayify-stream-2.0.0.tgz", - "integrity": "sha512-Z2NRtxpWQIz3NRA2bEZOziIungBH+fpsFFEolc5u8uVRheYitvsDNvejlfyh/hjZ9VyS9Ba62oY0zc5oa6Wu7g==", - "dev": true - }, - "node_modules/rdf-test-suite/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/rdf-test-suite/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/rdf-test-suite/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "resolved": "https://registry.npmjs.org/arrayify-stream/-/arrayify-stream-2.0.1.tgz", + "integrity": "sha512-z8fB6PtmnewQpFB53piS2d1KlUi3BPMICH2h7leCOUXpQcwvZ4GbHHSpdKoUrgLMR6b4Qan/uDe1St3Ao3yIHg==", "dev": true }, - "node_modules/rdf-test-suite/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/rdf-test-suite/node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -6504,38 +6418,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rdf-test-suite/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rdf-test-suite/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/rdfxml-streaming-parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/rdfxml-streaming-parser/-/rdfxml-streaming-parser-2.1.0.tgz", - "integrity": "sha512-G2kYXekAy7TUE5G6PAI5/Y/5ugqwFkA+305dcqbnRqqnK+a5gq2ubLGGmxJIIIEFbcFoUZ5UfQRHvqZdsWC8xQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/rdfxml-streaming-parser/-/rdfxml-streaming-parser-2.2.1.tgz", + "integrity": "sha512-1r7aXfSRCLkBYXGcko/GpSZdHxXKvYaeUi2ulEbB7cLvACD7DNoAA/uW6dsETEhgmsEipJZI7NLqBl2whOse8Q==", "dev": true, "dependencies": { "@rdfjs/types": "*", @@ -6544,7 +6430,8 @@ "rdf-data-factory": "^1.1.0", "readable-stream": "^4.0.0", "relative-to-absolute-iri": "^1.0.0", - "saxes": "^6.0.0" + "saxes": "^6.0.0", + "validate-iri": "^1.0.0" } }, "node_modules/rdfxml-streaming-parser/node_modules/buffer": { @@ -6777,9 +6664,9 @@ } }, "node_modules/relative-to-absolute-iri": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/relative-to-absolute-iri/-/relative-to-absolute-iri-1.0.6.tgz", - "integrity": "sha512-Xw5/Zx6iWSCMJUXwXVOjySjH8Xli4hVFL9QQFvkl1qEmFBG94J+QUI9emnoctOCD3285f1jNV+QWV9eDYwIdfQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/relative-to-absolute-iri/-/relative-to-absolute-iri-1.0.7.tgz", + "integrity": "sha512-Xjyl4HmIzg2jzK/Un2gELqbcE8Fxy85A/aLSHE6PE/3+OGsFwmKVA1vRyGaz6vLWSqLDMHA+5rjD/xbibSQN1Q==", "dev": true }, "node_modules/release-zalgo": { @@ -7102,9 +6989,9 @@ } }, "node_modules/sparqljson-parse": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sparqljson-parse/-/sparqljson-parse-2.1.0.tgz", - "integrity": "sha512-JKyoDNDR9xrJwR6x6N41UWfER6kfeirE9BRBHdSFSfQF3eF3pxpuUTcJ6QGwHQ4wC/JsQdG/4OGmzkuk1B+8gg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/sparqljson-parse/-/sparqljson-parse-2.1.2.tgz", + "integrity": "sha512-RqPeyy+RYQMeqgEsKPTY+ME5ZNXcgXJzg1v0o+tROiTntS9CwUW8mAY3wsx6seSvW3LVyNDEtsqOxnAokoGXOA==", "dev": true, "dependencies": { "@rdfjs/types": "*", @@ -7140,9 +7027,9 @@ } }, "node_modules/sparqlxml-parse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/sparqlxml-parse/-/sparqlxml-parse-2.0.1.tgz", - "integrity": "sha512-7HZMm0l9a+NQW6mEHzur+KEXA2/gpLYsyiq9yMLKa7Us8yfUJG/+fbHmuBoN7yE0t41SfCXtq4EU/nDjFSGudw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sparqlxml-parse/-/sparqlxml-parse-2.0.2.tgz", + "integrity": "sha512-Iqso0WSTCSuMUYoX2pOEJxteCq9U+7AkOqwlFcvFG1S1aM87xWrp28njQOIiyIrL7Y8CkVXBZG1ec+DhZYUNXA==", "dev": true, "dependencies": { "@rdfjs/types": "*", @@ -7868,6 +7755,12 @@ "uuid": "bin/uuid" } }, + "node_modules/validate-iri": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/validate-iri/-/validate-iri-1.0.1.tgz", + "integrity": "sha512-gLXi7351CoyVVQw8XE5sgpYawRKatxE7kj/xmCxXOZS1kMdtcqC0ILIqLuVEVnAUQSL/evOGG3eQ+8VgbdnstA==", + "dev": true + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -9383,9 +9276,9 @@ "optional": true }, "@rdfjs/types": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.0.1.tgz", - "integrity": "sha512-YxVkH0XrCNG3MWeZxfg596GFe+oorTVusmNxRP6ZHTsGczZ8AGvG3UchRNkg3Fy4MyysI7vBAA5YZbESL+VmHQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.1.0.tgz", + "integrity": "sha512-5zm8bN2/CC634dTcn/0AhTRLaQRjXDZs3QfcAsQKNturHT7XVWcKy/8p3P5gXl+YkZTAmy7T5M/LyiT/jbkENw==", "dev": true, "requires": { "@types/node": "*" @@ -9401,24 +9294,15 @@ } }, "@types/json-stable-stringify": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz", - "integrity": "sha512-q9Q6+eUEGwQkv4Sbst3J4PNgDOvpuVuKj79Hl/qnmBMEIPzB5QoFRUtjcgcg2xNUZyYUGXBk5wYIBKHt0A+Mxw==", + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz", + "integrity": "sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw==", "dev": true }, - "@types/log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-uW/AGf/41aZ1c1dhZ3s063Ii2OqT8EQooZu3t4VCRyR3dqyA2Bg46BcKyZpnWTY7wzm6cayq4jzylnruu4KqSA==", - "dev": true, - "requires": { - "log-symbols": "*" - } - }, "@types/minimist": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", - "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true }, "@types/n3": { @@ -9432,25 +9316,33 @@ } }, "@types/node": { - "version": "18.6.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.3.tgz", - "integrity": "sha512-6qKpDtoaYLM+5+AFChLhHermMQxc3TOEFIDzrZLPRGHPrLEwqFkkT5Kx3ju05g6X7uDPazz3jHbKPX0KzCjntg==", + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", "dev": true }, "@types/readable-stream": { - "version": "2.3.14", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.14.tgz", - "integrity": "sha512-8jQ5Mp7bsDJEnW/69i6nAaQMoLwAVJVc7ZRAVTrdh/o6XueQsX38TEvKuYyoQj76/mg7WdlRfMrtl9pDLCJWsg==", + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz", + "integrity": "sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==", "dev": true, "requires": { "@types/node": "*", - "safe-buffer": "*" + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "@types/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-dqYdvN7Sbw8QT/0Ci5rhjE4/iCMJEM0Y9rHpCu+gGXD9Lwbz28t6HI2yegsB6BoV1sShRMU6lAmAcgRjmFy7LA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==", "dev": true, "requires": { "@types/node": "*" @@ -11283,9 +11175,9 @@ "dev": true }, "http-link-header": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.0.5.tgz", - "integrity": "sha512-msKrMbv/xHzhdOD4sstbEr+NbGqpv8ZtZliiCeByGENJo1jK1GZ/81zHF9HpWtEH5ihovPpdqHXniwZapJCKEA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.1.0.tgz", + "integrity": "sha512-pj6N1yxOz/ANO8HHsWGg/OoIL1kmRYvQnXQ7PIRpgp+15AnEsRH8fmIJE6D1OdWG2Bov+BJHVla1fFXxg1JbbA==", "dev": true }, "https-browserify": { @@ -11606,12 +11498,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, "is-weakref": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", @@ -11779,12 +11665,12 @@ "dev": true }, "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz", + "integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==", "dev": true, "requires": { - "jsonify": "~0.0.0" + "jsonify": "^0.0.1" } }, "json-stable-stringify-without-jsonify": { @@ -11813,15 +11699,15 @@ } }, "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", "dev": true }, "jsonld-context-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jsonld-context-parser/-/jsonld-context-parser-2.2.0.tgz", - "integrity": "sha512-h4ykp8iUOV4Xm6MgS1zVrytyw/dNVgOeofMCcD/5mHPng3i49qAsaomLT0BOXqYas7lwITVT5c6NZIRVMdXfVQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/jsonld-context-parser/-/jsonld-context-parser-2.2.2.tgz", + "integrity": "sha512-3VWIg/4NCMTXP6NsI6O93spFTd4qIOucKEmD8I+Exhxk9ZUVrnkLp2G4f0toR5jVleZkiiB9YGPS+yT1wwMqnQ==", "dev": true, "requires": { "@types/http-link-header": "^1.0.1", @@ -11833,9 +11719,9 @@ } }, "jsonld-streaming-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonld-streaming-parser/-/jsonld-streaming-parser-3.0.0.tgz", - "integrity": "sha512-n+IW+gTIw2UeXWXdN0ZlPY4DvKANUCrV0HOagXOsDUCvkO/SiDcYOZn2hrDkBGKm7yD5sefvvG3d/FxbeepbuA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonld-streaming-parser/-/jsonld-streaming-parser-3.0.1.tgz", + "integrity": "sha512-zSJlEgrKypQDk/85R+xkudeCZo6vmnvJuCPvcjk2BzHPLzv1yqiwoKQDyFzfgfgCHM0p7YCJBZl0liT9RMUZJw==", "dev": true, "requires": { "@rdfjs/types": "*", @@ -12493,26 +12379,13 @@ "dev": true }, "n3": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/n3/-/n3-1.16.2.tgz", - "integrity": "sha512-5vYa2HuNEJ+a26FEs4FGgfFLgaPOODaZpJlc7FS0eUjDumc4uK0cvx216PjKXBkLzmAsSqGgQPwqztcLLvwDsw==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/n3/-/n3-1.16.3.tgz", + "integrity": "sha512-9caLSZuMW1kdlPxEN4ka6E4E8a5QKoZ2emxpW+zHMofI+Bo92nJhN//wNub15S5T9I4c6saEqdGEu+YXJqMZVA==", "dev": true, "requires": { "queue-microtask": "^1.1.2", - "readable-stream": "^3.6.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } + "readable-stream": "^4.0.0" } }, "nanoid": { @@ -12954,7 +12827,7 @@ "promise-polyfill": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-1.1.6.tgz", - "integrity": "sha1-zQTv9G9clcOn0EVZHXm14+AfEtc=", + "integrity": "sha512-7rrONfyLkDEc7OJ5QBkqa4KI4EBhCd340xRuIUPGCfu13znS+vx+VDdrT9ODAJHlXm7w4lbxN3DRjyv58EuzDg==", "dev": true }, "pseudomap": { @@ -13037,9 +12910,9 @@ } }, "rdf-isomorphic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rdf-isomorphic/-/rdf-isomorphic-1.3.0.tgz", - "integrity": "sha512-3BRwUwCNHHR8//bqmVH+knTFVbVfkp7CWyQk7qPHHA8JriXBYxrab21OomjJx/2KF21w8bWz344mgNYEaQABYQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rdf-isomorphic/-/rdf-isomorphic-1.3.1.tgz", + "integrity": "sha512-6uIhsXTVp2AtO6f41PdnRV5xZsa0zVZQDTBdn0br+DZuFf5M/YD+T6m8hKDUnALI6nFL/IujTMLgEs20MlNidQ==", "dev": true, "requires": { "@rdfjs/types": "*", @@ -13058,9 +12931,9 @@ } }, "rdf-literal": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rdf-literal/-/rdf-literal-1.3.0.tgz", - "integrity": "sha512-5u5L4kPYNZANie5AE4gCXqwpNO/p9E/nUcDurk05XAOJT/pt9rQlDk6+BX7j3dNSee3h9GS4xlLoWxQDj7sXtg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rdf-literal/-/rdf-literal-1.3.1.tgz", + "integrity": "sha512-+o/PGOfJchyay9Rjrvi/oveRJACnt2WFO3LhEvtPlsRD1tFmwVUCMU+s33FtQprMo+z1ohFrv/yfEQ6Eym4KgQ==", "dev": true, "requires": { "@rdfjs/types": "*", @@ -13068,9 +12941,9 @@ } }, "rdf-object": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/rdf-object/-/rdf-object-1.13.1.tgz", - "integrity": "sha512-Sgq+GbsqdPsMYh+d4OZ4C9brXlzqa9MvfVHG4pkuT9p7o+AX39nqjTWE/8HVaXjjOZBIDe8T54WWTMWphu3BpA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/rdf-object/-/rdf-object-1.13.2.tgz", + "integrity": "sha512-DVLDCbxPOkhd/k43j9wcLU7CXe/gdldBBomMV3RyZ1G9E2zPa2FFNFijzMGgRGNY1OEyGmhBxw2eiJjUC7GVNw==", "dev": true, "requires": { "@rdfjs/types": "*", @@ -13102,9 +12975,9 @@ } }, "rdf-terms": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/rdf-terms/-/rdf-terms-1.9.0.tgz", - "integrity": "sha512-FGMPOIpr6vEN8gWd/dVuPpcE/7k+u4Ufqi8FvM5lczjhduT1MN9Shmrw50fWCpHFVE4n0T3lV0qti1PCaHxAfg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/rdf-terms/-/rdf-terms-1.9.1.tgz", + "integrity": "sha512-GrE8CbQSvuVEFRCywMu6VOgV1AFE6X+nFYcAhEc5pwYKI13bUvz4voiVufQiy3V8rzQKu21Sgl+dS2qcJavy7w==", "dev": true, "requires": { "@rdfjs/types": "*", @@ -13113,14 +12986,13 @@ } }, "rdf-test-suite": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/rdf-test-suite/-/rdf-test-suite-1.19.2.tgz", - "integrity": "sha512-Qvbf05SfcNcvwFzroBVSVf51zS6R74GaQmX43UwXKNxVWMoDyZlgXWLfznDtTJW2HfahnFkTsyosxrliN1zZ1Q==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/rdf-test-suite/-/rdf-test-suite-1.20.0.tgz", + "integrity": "sha512-5p26dxVT3Hxr0nV3fEmGHm/NYHr7i8R4zp1hijCNHVrwV22LhAGtYWqYUtRUe00HO0J0kUr+Hpxh3X/OejCLxA==", "dev": true, "requires": { "@rdfjs/types": "*", "@types/json-stable-stringify": "^1.0.32", - "@types/log-symbols": "^3.0.0", "@types/minimist": "^1.2.0", "@types/n3": "^1.10.3", "@types/sax": "^1.0.1", @@ -13148,50 +13020,10 @@ "streamify-string": "^1.0.1" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, "arrayify-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arrayify-stream/-/arrayify-stream-2.0.0.tgz", - "integrity": "sha512-Z2NRtxpWQIz3NRA2bEZOziIungBH+fpsFFEolc5u8uVRheYitvsDNvejlfyh/hjZ9VyS9Ba62oY0zc5oa6Wu7g==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/arrayify-stream/-/arrayify-stream-2.0.1.tgz", + "integrity": "sha512-z8fB6PtmnewQpFB53piS2d1KlUi3BPMICH2h7leCOUXpQcwvZ4GbHHSpdKoUrgLMR6b4Qan/uDe1St3Ao3yIHg==", "dev": true }, "is-stream": { @@ -13199,32 +13031,13 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "rdfxml-streaming-parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/rdfxml-streaming-parser/-/rdfxml-streaming-parser-2.1.0.tgz", - "integrity": "sha512-G2kYXekAy7TUE5G6PAI5/Y/5ugqwFkA+305dcqbnRqqnK+a5gq2ubLGGmxJIIIEFbcFoUZ5UfQRHvqZdsWC8xQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/rdfxml-streaming-parser/-/rdfxml-streaming-parser-2.2.1.tgz", + "integrity": "sha512-1r7aXfSRCLkBYXGcko/GpSZdHxXKvYaeUi2ulEbB7cLvACD7DNoAA/uW6dsETEhgmsEipJZI7NLqBl2whOse8Q==", "dev": true, "requires": { "@rdfjs/types": "*", @@ -13233,7 +13046,8 @@ "rdf-data-factory": "^1.1.0", "readable-stream": "^4.0.0", "relative-to-absolute-iri": "^1.0.0", - "saxes": "^6.0.0" + "saxes": "^6.0.0", + "validate-iri": "^1.0.0" }, "dependencies": { "buffer": { @@ -13423,9 +13237,9 @@ } }, "relative-to-absolute-iri": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/relative-to-absolute-iri/-/relative-to-absolute-iri-1.0.6.tgz", - "integrity": "sha512-Xw5/Zx6iWSCMJUXwXVOjySjH8Xli4hVFL9QQFvkl1qEmFBG94J+QUI9emnoctOCD3285f1jNV+QWV9eDYwIdfQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/relative-to-absolute-iri/-/relative-to-absolute-iri-1.0.7.tgz", + "integrity": "sha512-Xjyl4HmIzg2jzK/Un2gELqbcE8Fxy85A/aLSHE6PE/3+OGsFwmKVA1vRyGaz6vLWSqLDMHA+5rjD/xbibSQN1Q==", "dev": true }, "release-zalgo": { @@ -13665,9 +13479,9 @@ } }, "sparqljson-parse": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sparqljson-parse/-/sparqljson-parse-2.1.0.tgz", - "integrity": "sha512-JKyoDNDR9xrJwR6x6N41UWfER6kfeirE9BRBHdSFSfQF3eF3pxpuUTcJ6QGwHQ4wC/JsQdG/4OGmzkuk1B+8gg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/sparqljson-parse/-/sparqljson-parse-2.1.2.tgz", + "integrity": "sha512-RqPeyy+RYQMeqgEsKPTY+ME5ZNXcgXJzg1v0o+tROiTntS9CwUW8mAY3wsx6seSvW3LVyNDEtsqOxnAokoGXOA==", "dev": true, "requires": { "@rdfjs/types": "*", @@ -13691,9 +13505,9 @@ } }, "sparqlxml-parse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/sparqlxml-parse/-/sparqlxml-parse-2.0.1.tgz", - "integrity": "sha512-7HZMm0l9a+NQW6mEHzur+KEXA2/gpLYsyiq9yMLKa7Us8yfUJG/+fbHmuBoN7yE0t41SfCXtq4EU/nDjFSGudw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sparqlxml-parse/-/sparqlxml-parse-2.0.2.tgz", + "integrity": "sha512-Iqso0WSTCSuMUYoX2pOEJxteCq9U+7AkOqwlFcvFG1S1aM87xWrp28njQOIiyIrL7Y8CkVXBZG1ec+DhZYUNXA==", "dev": true, "requires": { "@rdfjs/types": "*", @@ -14326,6 +14140,12 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, + "validate-iri": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/validate-iri/-/validate-iri-1.0.1.tgz", + "integrity": "sha512-gLXi7351CoyVVQw8XE5sgpYawRKatxE7kj/xmCxXOZS1kMdtcqC0ILIqLuVEVnAUQSL/evOGG3eQ+8VgbdnstA==", + "dev": true + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", diff --git a/package.json b/package.json index 50cb7554..efde53b7 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "mocha": "^8.0.0", "nyc": "^14.1.1", "pre-commit": "^1.2.2", - "rdf-test-suite": "^1.19.2", + "rdf-test-suite": "^1.20.0", "streamify-string": "^1.0.1", "uglify-js": "^3.14.3" }, @@ -54,7 +54,7 @@ "mocha": "mocha", "lint": "eslint src perf test spec", "prepare": "npm run build", - "spec": "npm run spec-turtle && npm run spec-ntriples && npm run spec-nquads && npm run spec-trig", + "spec": "npm run spec-turtle && npm run spec-ntriples && npm run spec-nquads && npm run spec-trig && npm run spec-rdf-star", "spec-earl": "npm run spec-earl-turtle && npm run spec-earl-ntriples && npm run spec-earl-nquads && npm run spec-earl-trig", "spec-ntriples": "rdf-test-suite spec/parser.js http://w3c.github.io/rdf-tests/ntriples/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/", "spec-nquads": "rdf-test-suite spec/parser.js http://w3c.github.io/rdf-tests/nquads/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/", @@ -64,6 +64,12 @@ "spec-earl-nquads": "rdf-test-suite spec/parser.js http://w3c.github.io/rdf-tests/nquads/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-nquads.ttl", "spec-earl-turtle": "rdf-test-suite spec/parser.js http://w3c.github.io/rdf-tests/turtle/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-turtle.ttl", "spec-earl-trig": "rdf-test-suite spec/parser.js http://w3c.github.io/rdf-tests/trig/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-trig.ttl", + "spec-rdf-star": "npm run spec-trig-rdf-star && npm run spec-trig-eval-rdf-star && npm run spec-turtle-rdf-star && npm run spec-turtle-eval-rdf-star", + "spec-trig-rdf-star": "node ../rdf-test-suite.js/bin/Runner.js spec/parser.js https://w3c.github.io/rdf-star/tests/trig/syntax/manifest.jsonld -i '{ \"format\": \"trig-star\" }' -c .rdf-test-suite-cache/", + "spec-trig-eval-rdf-star": "node ../rdf-test-suite.js/bin/Runner.js spec/parser.js https://w3c.github.io/rdf-star/tests/trig/eval/manifest.jsonld -i '{ \"format\": \"trig-star\" }' -c .rdf-test-suite-cache/", + "spec-turtle-rdf-star": "node ../rdf-test-suite.js/bin/Runner.js spec/parser.js https://w3c.github.io/rdf-star/tests/turtle/syntax/manifest.jsonld -i '{ \"format\": \"turtle-star\" }' -c .rdf-test-suite-cache/", + "spec-turtle-eval-rdf-star": "node ../rdf-test-suite.js/bin/Runner.js spec/parser.js https://w3c.github.io/rdf-star/tests/turtle/eval/manifest.jsonld -i '{ \"format\": \"turtle-star\" }' -c .rdf-test-suite-cache/", + "spec-ntriples-rdf-star": "node ../rdf-test-suite.js/bin/Runner.js spec/parser.js https://w3c.github.io/rdf-star/tests/nt/syntax/manifest.jsonld -i '{ \"format\": \"n-quads-star\" }' -c .rdf-test-suite-cache/", "spec-clean": "rm -r .rdf-test-suite-cache/", "docs": "cd src && docco *.js -o ../docs && cd .." }, diff --git a/src/N3Parser.js b/src/N3Parser.js index 4af2750b..d8efe834 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -197,6 +197,10 @@ export default class N3Parser { this._subject = this._blankNode(), null, null); return this._readBlankNodeHead; case '(': + // Lists are not allowed inside quoted triples + if (this._contextStack.length > 0 && this._contextStack[this._contextStack.length - 1].type === '<<') { + return this._error(`Unexpected list inside quoted triple`, token); + } // Start a new list this._saveContext('list', this._graph, this.RDF_NIL, null, null); this._subject = null; @@ -315,6 +319,10 @@ export default class N3Parser { this._subject = this._blankNode()); return this._readBlankNodeHead; case '(': + // Lists are not allowed inside quoted triples + if (this._contextStack.length > 0 && this._contextStack[this._contextStack.length - 1].type === '<<') { + return this._error(`Unexpected list inside quoted triple`, token); + } // Start a new list this._saveContext('list', this._graph, this._subject, this._predicate, this.RDF_NIL); this._subject = null; @@ -362,8 +370,9 @@ export default class N3Parser { if (token.type === ']') { this._subject = null; return this._readBlankNodeTail(token); - } - else { + } else if (this._contextStack.length > 1 && this._contextStack[this._contextStack.length - 2].type === '<<') { + return this._error('Compound blank node expressions not permitted within quoted triples', token) + } else { this._predicate = null; return this._readPredicate(token); } diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 5b236b36..05ac15cb 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -1126,6 +1126,36 @@ describe('Parser', () => { shouldParse(' <<<< >> >>.', ['d', 'e', [['a', 'b', 'c'], 'f', 'g']])); + it('should not parse empty list inside quoted triple subject', + shouldNotParse('<< () >> .', + 'Unexpected list inside quoted triple on line 1.' + )); + + it('should not parse non-empty list inside quoted triple subject', + shouldNotParse('<< ( ) >> .', + 'Unexpected list inside quoted triple on line 1.' + )); + + it('should not parse empty list inside quoted triple predicate', + shouldNotParse('<< () >> .', + 'Expected entity but got ( on line 1.' + )); + + it('should not parse non-empty list inside quoted triple predicate', + shouldNotParse('<< ( ) >> .', + 'Expected entity but got ( on line 1.' + )); + + it('should not parse empty list inside quoted triple object', + shouldNotParse('<< () >> .', + 'Unexpected list inside quoted triple on line 1.' + )); + + it('should not parse non-empty list inside quoted triple object', + shouldNotParse('<< ( ) >> .', + 'Unexpected list inside quoted triple on line 1.' + )); + it('should not parse nested RDF* statements that are partially closed', shouldNotParse(' <<<< >> .', 'Expected >> to follow "http://example.org/g" but got . on line 1.' From d27b9203519a34a37aca59aa01c8f14cc8a1bb09 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 23 Nov 2022 17:52:35 +1100 Subject: [PATCH 06/29] feat: turtle-star spec tests are passing --- src/N3Parser.js | 25 +++++++++++++++++++------ test/N3Parser-test.js | 12 +++++++++--- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/N3Parser.js b/src/N3Parser.js index d8efe834..c734818b 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -199,7 +199,7 @@ export default class N3Parser { case '(': // Lists are not allowed inside quoted triples if (this._contextStack.length > 0 && this._contextStack[this._contextStack.length - 1].type === '<<') { - return this._error(`Unexpected list inside quoted triple`, token); + return this._error('Unexpected list inside quoted triple', token); } // Start a new list this._saveContext('list', this._graph, this.RDF_NIL, null, null); @@ -321,7 +321,7 @@ export default class N3Parser { case '(': // Lists are not allowed inside quoted triples if (this._contextStack.length > 0 && this._contextStack[this._contextStack.length - 1].type === '<<') { - return this._error(`Unexpected list inside quoted triple`, token); + return this._error('Unexpected list inside quoted triple', token); } // Start a new list this._saveContext('list', this._graph, this._subject, this._predicate, this.RDF_NIL); @@ -370,9 +370,11 @@ export default class N3Parser { if (token.type === ']') { this._subject = null; return this._readBlankNodeTail(token); - } else if (this._contextStack.length > 1 && this._contextStack[this._contextStack.length - 2].type === '<<') { - return this._error('Compound blank node expressions not permitted within quoted triples', token) - } else { + } + else if (this._contextStack.length > 1 && this._contextStack[this._contextStack.length - 2].type === '<<') { + return this._error('Compound blank node expressions not permitted within quoted triples', token); + } + else { this._predicate = null; return this._readPredicate(token); } @@ -896,10 +898,21 @@ export default class N3Parser { // ### `_readRDFStarTail` reads the end of a nested RDF* triple _readAnnotatedTail(token) { - this._emit(this._subject, this._predicate, this._object, this._graph); // if (this._subject && this._predicate && this._object) { // this._emit(this._subject, this._predicate, this._object, this._graph); // } + if (token.type === '{|') { + this._saveContext('{|', this._graph, this._subject, this._predicate, this._object); + + // Note - we always use the default graph for the quoted triple component + this._subject = this._quad(this._subject, this._predicate, this._object, this.DEFAULTGRAPH); + this._predicate = null; + this._object = null; + return this._readPredicate; + } else { + this._emit(this._subject, this._predicate, this._object, this._graph); + } + if (token.type !== '|}') return this._readPredicate; this._restoreContext('{|', token); diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 05ac15cb..4a841a92 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -1130,7 +1130,7 @@ describe('Parser', () => { shouldNotParse('<< () >> .', 'Unexpected list inside quoted triple on line 1.' )); - + it('should not parse non-empty list inside quoted triple subject', shouldNotParse('<< ( ) >> .', 'Unexpected list inside quoted triple on line 1.' @@ -1140,7 +1140,7 @@ describe('Parser', () => { shouldNotParse('<< () >> .', 'Expected entity but got ( on line 1.' )); - + it('should not parse non-empty list inside quoted triple predicate', shouldNotParse('<< ( ) >> .', 'Expected entity but got ( on line 1.' @@ -1150,7 +1150,7 @@ describe('Parser', () => { shouldNotParse('<< () >> .', 'Unexpected list inside quoted triple on line 1.' )); - + it('should not parse non-empty list inside quoted triple object', shouldNotParse('<< ( ) >> .', 'Unexpected list inside quoted triple on line 1.' @@ -1225,6 +1225,12 @@ describe('Parser', () => { ['a', 'b', 'c'], [['a', 'b', 'c'], 'd', 'e'])); + it('should parse an explicit triple with nested reified annotation', + shouldParse(' {| {| |} |} .', + ['a', 'b', 'c'], + [['a', 'b', 'c'], 'd', 'e'], + [[['a', 'b', 'c'], 'd', 'e'], 'f', 'g'])); + const q = ['http://example.com/ns#s', 'http://example.com/ns#p', ['http://example.com/ns#a', 'http://example.com/ns#b', 'http://example.com/ns#c']]; From 2af5e0531024e46f1d1cc2241d8a9503ee2ff9ca Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 23 Nov 2022 18:38:44 +1100 Subject: [PATCH 07/29] chore: fix lint and coverage errors --- src/N3Parser.js | 7 +++---- test/N3Parser-test.js | 21 +++++++++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/N3Parser.js b/src/N3Parser.js index c734818b..c3ec120a 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -372,7 +372,7 @@ export default class N3Parser { return this._readBlankNodeTail(token); } else if (this._contextStack.length > 1 && this._contextStack[this._contextStack.length - 2].type === '<<') { - return this._error('Compound blank node expressions not permitted within quoted triples', token); + return this._error('Compound blank node expressions not permitted within quoted triple', token); } else { this._predicate = null; @@ -639,8 +639,6 @@ export default class N3Parser { if (!this._supportsRDFStar) return this._error('Unexpected RDF* syntax', token); - // TODO: Have error handling behavior here - // TODO: See if we can just emit and then save null context this._saveContext('{|', this._graph, this._subject, this._predicate, this._object); // Note - we always use the default graph for the quoted triple component @@ -909,7 +907,8 @@ export default class N3Parser { this._predicate = null; this._object = null; return this._readPredicate; - } else { + } + else { this._emit(this._subject, this._predicate, this._object, this._graph); } diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 4a841a92..0ceecf19 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -1126,6 +1126,21 @@ describe('Parser', () => { shouldParse(' <<<< >> >>.', ['d', 'e', [['a', 'b', 'c'], 'f', 'g']])); + it('should not parse compound blank node inside quoted triple subject', + shouldNotParse('<< [ ] >> .', + 'Compound blank node expressions not permitted within quoted triple on line 1.' + )); + + it('should not parse compound blank node inside quoted triple predicate', + shouldNotParse('<< [ ] >> .', + 'Disallowed blank node as predicate on line 1.' + )); + + it('should not parse compound blank node inside quoted triple object', + shouldNotParse('<< [ ] >> .', + 'Compound blank node expressions not permitted within quoted triple on line 1.' + )); + it('should not parse empty list inside quoted triple subject', shouldNotParse('<< () >> .', 'Unexpected list inside quoted triple on line 1.' @@ -1244,12 +1259,6 @@ describe('Parser', () => { [['a', 'b', 'c'], 'd', 'e'], [['a', 'b', 'c'], 'f', 'g'])); - // TODO: See if this is required by the spec tests - // it('should parse an explicit triple with reified annotation containing punctuation', - // shouldParse(' {| . |} .', - // ['a', 'b', 'c'], - // [['a', 'b', 'c'], 'd', 'e'])); - it('should parse an explicit triple with reified annotation in a named graph', shouldParse(' { {| |} . }', ['a', 'b', 'c', 'G'], From 0ac4c46acbaf93cc74741c419126786b8fc7f065 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 23 Nov 2022 18:41:20 +1100 Subject: [PATCH 08/29] chore: remove commented code --- src/N3Parser.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/N3Parser.js b/src/N3Parser.js index c3ec120a..a5155f19 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -896,9 +896,6 @@ export default class N3Parser { // ### `_readRDFStarTail` reads the end of a nested RDF* triple _readAnnotatedTail(token) { - // if (this._subject && this._predicate && this._object) { - // this._emit(this._subject, this._predicate, this._object, this._graph); - // } if (token.type === '{|') { this._saveContext('{|', this._graph, this._subject, this._predicate, this._object); From 0337ed8b9b5e5f95384774b9b79ceb49cdca707a Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Thu, 24 Nov 2022 08:51:13 +1100 Subject: [PATCH 09/29] chore: rename RDF* -> RDF-star --- README.md | 10 ++--- src/N3DataFactory.js | 2 +- src/N3Parser.js | 12 +++--- src/N3Writer.js | 2 +- test/N3Lexer-test.js | 32 +++++++-------- test/N3Parser-test.js | 96 +++++++++++++++++++++---------------------- test/N3Store-test.js | 4 +- 7 files changed, 79 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index cf5e554d..6e2704ee 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,14 @@ It offers: [TriG](https://www.w3.org/TR/trig/), [N-Triples](https://www.w3.org/TR/n-triples/), [N-Quads](https://www.w3.org/TR/n-quads/), - [RDF*](https://blog.liu.se/olafhartig/2019/01/10/position-statement-rdf-star-and-sparql-star/) + [RDF-star](https://blog.liu.se/olafhartig/2019/01/10/position-statement-rdf-star-and-sparql-star/) and [Notation3 (N3)](https://www.w3.org/TeamSubmission/n3/) - [**Writing**](#writing) triples/quads to [Turtle](https://www.w3.org/TR/turtle/), [TriG](https://www.w3.org/TR/trig/), [N-Triples](https://www.w3.org/TR/n-triples/), [N-Quads](https://www.w3.org/TR/n-quads/) - and [RDF*](https://blog.liu.se/olafhartig/2019/01/10/position-statement-rdf-star-and-sparql-star/) + and [RDF-star](https://blog.liu.se/olafhartig/2019/01/10/position-statement-rdf-star-and-sparql-star/) - [**Storage**](#storing) of triples/quads in memory Parsing and writing is: @@ -358,16 +358,16 @@ The N3.js parser and writer is fully compatible with the following W3C specifica In addition, the N3.js parser also supports [Notation3 (N3)](https://www.w3.org/TeamSubmission/n3/) (no official specification yet). -The N3.js parser and writer are also fully compatible with the RDF* variants +The N3.js parser and writer are also fully compatible with the RDF-star variants of the W3C specifications. The default mode is permissive -and allows a mixture of different syntaxes, including RDF*. +and allows a mixture of different syntaxes, including RDF-star. Pass a `format` option to the constructor with the name or MIME type of a format for strict, fault-intolerant behavior. If a format string contains `star` or `*` (e.g., `turtlestar` or `TriG*`), -RDF* support for that format will be enabled. +RDF-star support for that format will be enabled. ### Interface specifications The N3.js submodules are compatible with the following [RDF.js](http://rdf.js.org) interfaces: diff --git a/src/N3DataFactory.js b/src/N3DataFactory.js index dec7aaef..f37bc0c5 100644 --- a/src/N3DataFactory.js +++ b/src/N3DataFactory.js @@ -247,7 +247,7 @@ export function termToId(term) { term.language ? `@${term.language}` : (term.datatype && term.datatype.value !== xsd.string ? `^^${term.datatype.value}` : '')}`; case 'Quad': - // To identify RDF* quad components, we escape quotes by doubling them. + // To identify RDF-star quad components, we escape quotes by doubling them. // This avoids the overhead of backslash parsing of Turtle-like syntaxes. return `<<${ escapeQuotes(termToId(term.subject)) diff --git a/src/N3Parser.js b/src/N3Parser.js index a5155f19..22cfb331 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -243,7 +243,7 @@ export default class N3Parser { break; case '<<': if (!this._supportsRDFStar) - return this._error('Unexpected RDF* syntax', token); + return this._error('Unexpected RDF-star syntax', token); this._saveContext('<<', this._graph, null, null, null); this._graph = null; return this._readSubject; @@ -336,7 +336,7 @@ export default class N3Parser { return this._readSubject; case '<<': if (!this._supportsRDFStar) - return this._error('Unexpected RDF* syntax', token); + return this._error('Unexpected RDF-star syntax', token); this._saveContext('<<', this._graph, this._subject, this._predicate, null); this._graph = null; return this._readSubject; @@ -486,7 +486,7 @@ export default class N3Parser { return this._readSubject; case '<<': if (!this._supportsRDFStar) - return this._error('Unexpected RDF* syntax', token); + return this._error('Unexpected RDF-star syntax', token); this._saveContext('<<', this._graph, this._subject, null, null); return this._readSubject; @@ -637,7 +637,7 @@ export default class N3Parser { break; case '{|': if (!this._supportsRDFStar) - return this._error('Unexpected RDF* syntax', token); + return this._error('Unexpected RDF-star syntax', token); this._saveContext('{|', this._graph, this._subject, this._predicate, this._object); @@ -867,7 +867,7 @@ export default class N3Parser { return this._readPath; } - // ### `_readRDFStarTail` reads the end of a nested RDF* triple + // ### `_readRDFStarTail` reads the end of a nested RDF-star triple _readRDFStarTail(token) { if (token.type !== '>>') return this._error(`Expected >> to follow "${this._object.id}" but got ${token.type}`, token); @@ -894,7 +894,7 @@ export default class N3Parser { } } - // ### `_readRDFStarTail` reads the end of a nested RDF* triple + // ### `_readRDFStarTail` reads the end of a nested RDF-star triple _readAnnotatedTail(token) { if (token.type === '{|') { this._saveContext('{|', this._graph, this._subject, this._predicate, this._object); diff --git a/src/N3Writer.js b/src/N3Writer.js index abc3852f..8fc4f282 100644 --- a/src/N3Writer.js +++ b/src/N3Writer.js @@ -225,7 +225,7 @@ export default class N3Writer { } } - // ### `_encodeQuad` encodes an RDF* quad + // ### `_encodeQuad` encodes an RDF-star quad _encodeQuad({ subject, predicate, object, graph }) { return `<<${ this._encodeSubject(subject)} ${ diff --git a/test/N3Lexer-test.js b/test/N3Lexer-test.js index 8034f596..13b6730b 100644 --- a/test/N3Lexer-test.js +++ b/test/N3Lexer-test.js @@ -855,7 +855,7 @@ describe('Lexer', () => { { type: '>>', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize an RDF* statement with IRIs', + it('should tokenize an RDF-star statement with IRIs', shouldTokenize('<< \n\t \n\t>> .', { type: '<<', line: 1 }, { type: 'IRI', value: 'http://ex.org/?bla#foo', line: 1 }, @@ -865,7 +865,7 @@ describe('Lexer', () => { { type: '.', line: 3 }, { type: 'eof', line: 3 })); - it('should not tokenize a wrongly closed RDF* statement with IRIs', + it('should not tokenize a wrongly closed RDF-star statement with IRIs', shouldNotTokenize('<< \n\t \n\t> .', 'Unexpected ">" on line 3.')); @@ -915,7 +915,7 @@ describe('Lexer', () => { shouldNotTokenize(' {| [ "f" ]; |', 'Unexpected "|" on line 1.')); - it('should tokenize a split RDF* statement with IRIs', + it('should tokenize a split RDF-star statement with IRIs', shouldTokenize(streamOf('<', '< \n\t \n\t>> .'), { type: '<<', line: 1 }, { type: 'IRI', value: 'http://ex.org/?bla#foo', line: 1 }, @@ -925,7 +925,7 @@ describe('Lexer', () => { { type: '.', line: 3 }, { type: 'eof', line: 3 })); - it('should tokenize an RDF* statement with string literals', + it('should tokenize an RDF-star statement with string literals', shouldTokenize('<<"string"@en "string"@nl-be "string"@EN>> .', { type: '<<', line: 1 }, { type: 'literal', value: 'string', line: 1 }, @@ -938,7 +938,7 @@ describe('Lexer', () => { { type: '.', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize an RDF* statement with integers', + it('should tokenize an RDF-star statement with integers', shouldTokenize('<<1 2 3>>.', { type: '<<', line: 1 }, { type: 'literal', value: '1', prefix: 'http://www.w3.org/2001/XMLSchema#integer', line: 1 }, @@ -948,7 +948,7 @@ describe('Lexer', () => { { type: '.', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize an RDF* statement with decimals', + it('should tokenize an RDF-star statement with decimals', shouldTokenize('<<1.2 3.4 5.6>>.', { type: '<<', line: 1 }, { type: 'literal', value: '1.2', prefix: 'http://www.w3.org/2001/XMLSchema#decimal', line: 1 }, @@ -958,7 +958,7 @@ describe('Lexer', () => { { type: '.', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize an RDF* statement with booleans', + it('should tokenize an RDF-star statement with booleans', shouldTokenize('<>.', { type: '<<', line: 1 }, { type: 'literal', value: 'true', prefix: 'http://www.w3.org/2001/XMLSchema#boolean', line: 1 }, @@ -975,7 +975,7 @@ describe('Lexer', () => { { type: '.', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize an RDF* statement with prefixed names', + it('should tokenize an RDF-star statement with prefixed names', shouldTokenize('<> .', { type: '<<', line: 1 }, { type: 'prefixed', prefix: 'a', value: 'a', line: 1 }, @@ -985,7 +985,7 @@ describe('Lexer', () => { { type: '.', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize an RDF* statement with blank nodes', + it('should tokenize an RDF-star statement with blank nodes', shouldTokenize('<<_:a _:b _:c>> .', { type: '<<', line: 1 }, { type: 'blank', prefix: '_', value: 'a', line: 1 }, @@ -995,7 +995,7 @@ describe('Lexer', () => { { type: '.', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize an RDF* statement with variables', + it('should tokenize an RDF-star statement with variables', shouldTokenize('<> .', { type: '<<', line: 1 }, { type: 'var', value: '?a', line: 1 }, @@ -1005,7 +1005,7 @@ describe('Lexer', () => { { type: '.', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize an RDF* statement with mixed types', + it('should tokenize an RDF-star statement with mixed types', shouldTokenize('<< "string"@nl-be c:c>> .', { type: '<<', line: 1 }, { type: 'IRI', value: 'http://ex.org/?bla#foo', line: 1 }, @@ -1016,7 +1016,7 @@ describe('Lexer', () => { { type: '.', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize an RDF* statement with mixed types', + it('should tokenize an RDF-star statement with mixed types', shouldTokenize('<<_:a a:a "string"@EN>> .', { type: '<<', line: 1 }, { type: 'blank', prefix: '_', value: 'a', line: 1 }, @@ -1027,7 +1027,7 @@ describe('Lexer', () => { { type: '.', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize an RDF* statement with mixed types', + it('should tokenize an RDF-star statement with mixed types', shouldTokenize('<<"literal"@AU _:a>> .', { type: '<<', line: 1 }, { type: 'literal', value: 'literal', line: 1 }, @@ -1038,7 +1038,7 @@ describe('Lexer', () => { { type: '.', line: 1 }, { type: 'eof', line: 1 })); - it('should tokenize RDF* statements with shared subjects', + it('should tokenize RDF-star statements with shared subjects', shouldTokenize('<< ;\n >>.', { type: '<<', line: 1 }, { type: 'IRI', value: 'a', line: 1 }, @@ -1051,7 +1051,7 @@ describe('Lexer', () => { { type: '.', line: 2 }, { type: 'eof', line: 2 })); - it('should tokenize RDF* statements with shared subjects and predicates', + it('should tokenize RDF-star statements with shared subjects and predicates', shouldTokenize('<< ,\n>>.', { type: '<<', line: 1 }, { type: 'IRI', value: 'a', line: 1 }, @@ -1063,7 +1063,7 @@ describe('Lexer', () => { { type: '.', line: 2 }, { type: 'eof', line: 2 })); - it('should tokenize an RDF* statement with shared subjects and predicates and prefixed names', + it('should tokenize an RDF-star statement with shared subjects and predicates and prefixed names', shouldTokenize('<> .', { type: '<<', line: 1 }, { type: 'prefixed', prefix: 'a', value: 'a', line: 1 }, diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 0ceecf19..40012632 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -1084,32 +1084,32 @@ describe('Parser', () => { .should.throw('Expected entity but got eof on line 1'); }); - it('should parse an RDF* triple with a triple with iris as subject correctly', () => { + it('should parse an RDF-star triple with a triple with iris as subject correctly', () => { shouldParse('<< >> .', [['a', 'b', 'c'], 'b', 'c']); }); - it('should not parse an RDF* triple with a triple as predicate', + it('should not parse an RDF-star triple with a triple as predicate', shouldNotParse(' << >> ', 'Expected entity but got << on line 1.')); - it('should parse an RDF* triple with a triple with blanknodes as subject correctly', + it('should parse an RDF-star triple with a triple with blanknodes as subject correctly', shouldParse('<<_:a _:c>> .', [['_:b0_a', 'b', '_:b0_c'], 'b', 'c'])); - it('should parse an RDF* triple with a triple with blanknodes and literals as subject correctly', + it('should parse an RDF-star triple with a triple with blanknodes and literals as subject correctly', shouldParse('<<_:a "c"^^>> .', [['_:b0_a', 'b', '"c"^^http://example.org/d'], 'b', 'c'])); - it('should parse an RDF* triple with a triple as object correctly', + it('should parse an RDF-star triple with a triple as object correctly', shouldParse(' << >>.', ['a', 'b', ['a', 'b', 'c']])); - it('should parse an RDF* triple with a triple as object correctly', + it('should parse an RDF-star triple with a triple as object correctly', shouldParse(' <<_:a _:c>>.', ['a', 'b', ['_:b0_a', 'b', '_:b0_c']])); - it('should parse an RDF* triple with a triple as object correctly', + it('should parse an RDF-star triple with a triple as object correctly', shouldParse(' <<_:a "c"^^>>.', ['a', 'b', ['_:b0_a', 'b', '"c"^^http://example.org/d']])); @@ -1171,42 +1171,42 @@ describe('Parser', () => { 'Unexpected list inside quoted triple on line 1.' )); - it('should not parse nested RDF* statements that are partially closed', + it('should not parse nested RDF-star statements that are partially closed', shouldNotParse(' <<<< >> .', 'Expected >> to follow "http://example.org/g" but got . on line 1.' )); - it('should not parse partially closed nested RDF* statements', + it('should not parse partially closed nested RDF-star statements', shouldNotParse(' <<<< >>.', 'Expected >> to follow "http://example.org/c" but got IRI on line 1.' )); - it('should not parse nested RDF* statements with too many closing tags', + it('should not parse nested RDF-star statements with too many closing tags', shouldNotParse(' <<<< >>>> >>.', 'Expected entity but got >> on line 1.' )); - it('should not parse nested RDF* statements with too many closing tags', + it('should not parse nested RDF-star statements with too many closing tags', shouldNotParse(' <<<< >> >>>>.', 'Expected entity but got >> on line 1.' )); - it('should not parse RDF* statements with too many closing tags', + it('should not parse RDF-star statements with too many closing tags', shouldNotParse(' >>.', 'Expected entity but got >> on line 1.' )); - it('should not parse incomplete RDF* statements', + it('should not parse incomplete RDF-star statements', shouldNotParse(' << >>.', 'Expected entity but got >> on line 1.' )); - it('should not parse incomplete RDF* statements', + it('should not parse incomplete RDF-star statements', shouldNotParse('<< >> .', 'Expected entity but got >> on line 1.' )); - it('should not parse incorrectly nested RDF* statements', + it('should not parse incorrectly nested RDF-star statements', shouldNotParse('>> <<', 'Expected entity but got >> on line 1.' )); @@ -1216,16 +1216,16 @@ describe('Parser', () => { 'Unexpected . on line 1.' )); - it('should not parse a malformed RDF* quad', + it('should not parse a malformed RDF-star quad', shouldNotParse('<< >> .', 'Expected >> to follow "http://example.org/c" but got IRI on line 1.')); - it('should parse statements with a shared RDF* subject', + it('should parse statements with a shared RDF-star subject', shouldParse('<< >> ;\n .', [['a', 'b', 'c'], 'b', 'c'], [['a', 'b', 'c'], 'd', 'c'])); - it('should parse statements with a shared RDF* subject', + it('should parse statements with a shared RDF-star subject', shouldParse('<< >> ;\n << >>.', [['a', 'b', 'c'], 'b', 'c'], [['a', 'b', 'c'], 'd', ['a', 'b', 'c']])); @@ -1469,31 +1469,31 @@ describe('Parser', () => { shouldNotParse(parser, '1 .', 'Unexpected literal on line 1.')); - it('should not parse RDF* in the subject position', + it('should not parse RDF-star in the subject position', shouldNotParse(parser, '<< >> .', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); it('should not parse annotated statement', shouldNotParse(parser, ' {| |} .', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); - it('should not parse RDF* in the object position', + it('should not parse RDF-star in the object position', shouldNotParse(parser, ' < >>.', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); - it('should not parse RDF* in the object list', + it('should not parse RDF-star in the object list', shouldNotParse(parser, ' ( < >> ).', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); - it('should not parse RDF* in the subject list', + it('should not parse RDF-star in the subject list', shouldNotParse(parser, '( < >> ) .', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); }); describe('A Parser instance for the TurtleStar format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'TurtleStar' }); } - it('should parse RDF*', + it('should parse RDF-star', shouldParse(parser, '<< >> .', [['a', 'b', 'c'], 'b', 'c'])); @@ -1542,19 +1542,19 @@ describe('Parser', () => { it('should not parse @forAll', shouldNotParse(parser, '@forAll .', 'Unexpected "@forAll" on line 1.')); - it('should not parse RDF* in the subject position', + it('should not parse RDF-star in the subject position', shouldNotParse(parser, '<< >> .', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); - it('should not parse RDF* in the object position', + it('should not parse RDF-star in the object position', shouldNotParse(parser, ' << >>.', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); }); describe('A Parser instance for the TriGStar format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'TriGStar' }); } - it('should parse RDF*', + it('should parse RDF-star', shouldParse(parser, '<< >> .', [['a', 'b', 'c'], 'a', 'b'])); @@ -1617,19 +1617,19 @@ describe('Parser', () => { it('should not parse @forAll', shouldNotParse(parser, '@forAll .', 'Unexpected "@forAll" on line 1.')); - it('should not parse RDF* in the subject position', + it('should not parse RDF-star in the subject position', shouldNotParse(parser, '<< >> .', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); - it('should not parse RDF* in the object position', + it('should not parse RDF-star in the object position', shouldNotParse(parser, ' << >>.', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); }); describe('A Parser instance for the N-TriplesStar format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-TriplesStar' }); } - it('should parse RDF*', + it('should parse RDF-star', shouldParse(parser, '<<_:a _:c>> _:b .', [['_:b0_a', 'b', '_:b0_c'], 'a', '_:b0_b'])); @@ -1676,19 +1676,19 @@ describe('Parser', () => { it('should not parse @forAll', shouldNotParse(parser, '@forAll .', 'Unexpected "@forAll" on line 1.')); - it('should not parse RDF* in the subject position', + it('should not parse RDF-star in the subject position', shouldNotParse(parser, '<< >> .', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); - it('should not parse RDF* in the object position', + it('should not parse RDF-star in the object position', shouldNotParse(parser, '_:a << >>.', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); }); describe('A Parser instance for the N-QuadsStar format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-QuadsStar' }); } - it('should parse RDF*', + it('should parse RDF-star', shouldParse(parser, '<<_:a _:c>> _:c .', [['_:b0_a', 'b', '_:b0_c'], 'a', '_:b0_c'])); }); @@ -2031,19 +2031,19 @@ describe('Parser', () => { ['"bonjour"@fr', 'sameAs', '"hello"@en', '_:b0'] )); - it('should not parse RDF* in the subject position', + it('should not parse RDF-star in the subject position', shouldNotParse(parser, '<< >> .', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); - it('should not parse RDF* in the object position', + it('should not parse RDF-star in the object position', shouldNotParse(parser, ' << >>.', - 'Unexpected RDF* syntax on line 1.')); + 'Unexpected RDF-star syntax on line 1.')); }); describe('A Parser instance for the N3Star format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N3Star' }); } - it('should parse RDF*', + it('should parse RDF-star', shouldParse(parser, '<< >> .', [['a', 'b', 'c'], 'a', 'b'])); diff --git a/test/N3Store-test.js b/test/N3Store-test.js index b7ca2dd9..10b1f29f 100644 --- a/test/N3Store-test.js +++ b/test/N3Store-test.js @@ -275,7 +275,7 @@ describe('Store', () => { }); }); - describe('removing matching quads for RDF*', () => { + describe('removing matching quads for RDF-star', () => { let store; beforeEach(() => { store = new Store([ @@ -295,7 +295,7 @@ describe('Store', () => { store.size.should.eql(5); }); - it('should match RDF* and normal quads at the same time', done => { + it('should match RDF-star and normal quads at the same time', done => { const stream = store.removeMatches(null, 'p1', 'o2'); stream.on('end', () => { store.size.should.eql(3); From 99d6b72b9f9466e7f4c87bb292db9820baa60493 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Thu, 24 Nov 2022 11:03:58 +1100 Subject: [PATCH 10/29] chore: update RDF-star reference in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6e2704ee..60886684 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,14 @@ It offers: [TriG](https://www.w3.org/TR/trig/), [N-Triples](https://www.w3.org/TR/n-triples/), [N-Quads](https://www.w3.org/TR/n-quads/), - [RDF-star](https://blog.liu.se/olafhartig/2019/01/10/position-statement-rdf-star-and-sparql-star/) + [RDF-star](https://www.w3.org/2021/12/rdf-star.html) and [Notation3 (N3)](https://www.w3.org/TeamSubmission/n3/) - [**Writing**](#writing) triples/quads to [Turtle](https://www.w3.org/TR/turtle/), [TriG](https://www.w3.org/TR/trig/), [N-Triples](https://www.w3.org/TR/n-triples/), [N-Quads](https://www.w3.org/TR/n-quads/) - and [RDF-star](https://blog.liu.se/olafhartig/2019/01/10/position-statement-rdf-star-and-sparql-star/) + and [RDF-star](https://www.w3.org/2021/12/rdf-star.html) - [**Storage**](#storing) of triples/quads in memory Parsing and writing is: From d258f2212fc2543e602455512d4a6b66e15931f6 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Thu, 24 Nov 2022 17:19:14 +1100 Subject: [PATCH 11/29] chore: fix round trip on deeply nested rdfstar triples --- src/N3DataFactory.js | 45 ++++++++++++----------- test/Term-test.js | 87 ++++++++++++++++++++++++++++++++------------ 2 files changed, 86 insertions(+), 46 deletions(-) diff --git a/src/N3DataFactory.js b/src/N3DataFactory.js index f37bc0c5..786a774b 100644 --- a/src/N3DataFactory.js +++ b/src/N3DataFactory.js @@ -10,7 +10,6 @@ let DEFAULTGRAPH; let _blankNodeCounter = 0; const escapedLiteral = /^"(.*".*)(?="[^"]*$)/; -const quadId = /^<<("(?:""|[^"])*"[^ ]*|[^ ]+) ("(?:""|[^"])*"[^ ]*|[^ ]+) ("(?:""|[^"])*"[^ ]*|[^ ]+) ?("(?:""|[^"])*"[^ ]*|[^ ]+)?>>$/; // ## DataFactory singleton const DataFactory = { @@ -188,9 +187,8 @@ export class DefaultGraph extends Term { // ## DefaultGraph singleton DEFAULTGRAPH = new DefaultGraph(); - // ### Constructs a term from the given internal string ID -export function termFromId(id, factory) { +export function termFromId(id, factory, nested) { factory = factory || DataFactory; // Falsy value or empty string indicate the default graph @@ -215,21 +213,24 @@ export function termFromId(id, factory) { return factory.literal(id.substr(1, endPos - 1), id[endPos + 1] === '@' ? id.substr(endPos + 2) : factory.namedNode(id.substr(endPos + 3))); - case '<': - const components = quadId.exec(id); - return factory.quad( - termFromId(unescapeQuotes(components[1]), factory), - termFromId(unescapeQuotes(components[2]), factory), - termFromId(unescapeQuotes(components[3]), factory), - components[4] && termFromId(unescapeQuotes(components[4]), factory) - ); + case '[': + id = JSON.parse(id); + break; default: - return factory.namedNode(id); + if (!nested || !Array.isArray(id)) { + return factory.namedNode(id); + } } + return factory.quad( + termFromId(id[0], factory, true), + termFromId(id[1], factory, true), + termFromId(id[2], factory, true), + id[3] && termFromId(id[3], factory, true) + ); } // ### Constructs an internal string ID from the given term or ID string -export function termToId(term) { +export function termToId(term, nested) { if (typeof term === 'string') return term; if (term instanceof Term && term.termType !== 'Quad') @@ -249,15 +250,15 @@ export function termToId(term) { case 'Quad': // To identify RDF-star quad components, we escape quotes by doubling them. // This avoids the overhead of backslash parsing of Turtle-like syntaxes. - return `<<${ - escapeQuotes(termToId(term.subject)) - } ${ - escapeQuotes(termToId(term.predicate)) - } ${ - escapeQuotes(termToId(term.object)) - }${ - (isDefaultGraph(term.graph)) ? '' : ` ${termToId(term.graph)}` - }>>`; + const res = [ + termToId(term.subject, true), + termToId(term.predicate, true), + termToId(term.object, true), + ]; + if (!isDefaultGraph(term.graph)) { + res.push(termToId(term.graph, true)); + } + return nested ? res : JSON.stringify(res); default: throw new Error(`Unexpected termType: ${term.termType}`); } } diff --git a/test/Term-test.js b/test/Term-test.js index d052216d..6798a81b 100644 --- a/test/Term-test.js +++ b/test/Term-test.js @@ -81,16 +81,17 @@ describe('Term', () => { }); it('should create a Quad with the default graph if the id doesnt specify the graph', () => { - termFromId('<>').should.deep.equal(new Quad( + const q = new Quad( new NamedNode('http://ex.org/a'), new NamedNode('http://ex.org/b'), new Literal('"abc"@en-us'), new DefaultGraph() - )); + ); + expect(q.equals(termFromId(termToId(q)))).equals(true); }); it('should create a Quad with the correct graph if the id specifies a graph', () => { - const id = '<>'; + const id = '["http://ex.org/a", "http://ex.org/b", "\\"abc\\"@en-us", "http://ex.org/d"]'; termFromId(id).should.deep.equal(new Quad( new NamedNode('http://ex.org/a'), new NamedNode('http://ex.org/b'), @@ -100,7 +101,7 @@ describe('Term', () => { }); it('should create a Quad correctly', () => { - const id = '<>'; + const id = '["http://ex.org/a", "http://ex.org/b", "http://ex.org/c"]'; termFromId(id).should.deep.equal(new Quad( new NamedNode('http://ex.org/a'), new NamedNode('http://ex.org/b'), @@ -110,7 +111,7 @@ describe('Term', () => { }); it('should create a Quad correctly', () => { - const id = '<<_:n3-123 ?var-a ?var-b _:n3-000>>'; + const id = '["_:n3-123", "?var-a", "?var-b", "_:n3-000"]'; termFromId(id).should.deep.equal(new Quad( new BlankNode('n3-123'), new Variable('var-a'), @@ -120,7 +121,7 @@ describe('Term', () => { }); it('should create a Quad correctly', () => { - const id = '<>'; + const id = '["?var-a", "?var-b", "\\"abc\\"@en-us", "?var-d"]'; termFromId(id).should.deep.equal(new Quad( new Variable('var-a'), new Variable('var-b'), @@ -130,7 +131,7 @@ describe('Term', () => { }); it('should create a Quad correctly', () => { - const id = '<<_:n3-000 ?var-b _:n3-123 http://ex.org/d>>'; + const id = '["_:n3-000", "?var-b", "_:n3-123", "http://ex.org/d"]'; termFromId(id).should.deep.equal(new Quad( new BlankNode('n3-000'), new Variable('var-b'), @@ -140,7 +141,7 @@ describe('Term', () => { }); it('should create a Quad correctly from literal containing escaped quotes', () => { - const id = '<<_:n3-000 ?var-b "Hello ""W""orl""d!"@en-us http://ex.org/d>>'; + const id = '["_:n3-000", "?var-b", "\\"Hello \\"W\\"orl\\"d!\\"@en-us", "http://ex.org/d"]'; termFromId(id).should.deep.equal(new Quad( new BlankNode('n3-000'), new Variable('var-b'), @@ -150,13 +151,14 @@ describe('Term', () => { }); it('should create a Quad correctly from literal containing escaped quotes', () => { - const id = '<<"Hello ""W""orl""d!"@en-us http://ex.org/b http://ex.org/c>>'; - termFromId(id).should.deep.equal(new Quad( + const q = new Quad( new Literal('"Hello "W"orl"d!"@en-us'), new NamedNode('http://ex.org/b'), new NamedNode('http://ex.org/c'), new DefaultGraph() - )); + ); + + termFromId(termToId(q)).should.deep.equal(q); }); describe('with a custom factory', () => { @@ -283,7 +285,7 @@ describe('Term', () => { new NamedNode('http://ex.org/b'), new Literal('"abc"@en-us'), new DefaultGraph() - )).should.equal('<>'); + )).should.equal('["http://ex.org/a","http://ex.org/b","\\"abc\\"@en-us"]'); }); it('should create an id from a Quad', () => { @@ -292,7 +294,7 @@ describe('Term', () => { new NamedNode('http://ex.org/b'), new Literal('"abc"@en-us'), new NamedNode('http://ex.org/d') - )).should.equal('<>'); + )).should.equal('["http://ex.org/a","http://ex.org/b","\\"abc\\"@en-us","http://ex.org/d"]'); }); it('should create an id from a manually created Quad', () => { @@ -303,7 +305,7 @@ describe('Term', () => { graph: new NamedNode('http://ex.org/d'), termType: 'Quad', value: '', - }).should.equal('<>'); + }).should.equal('["http://ex.org/a","http://ex.org/b","\\"abc\\"@en-us","http://ex.org/d"]'); }); it('should create an id with escaped literals from a Quad', () => { @@ -312,7 +314,7 @@ describe('Term', () => { new Variable('var-b'), new Literal('"Hello "W"orl"d!"@en-us'), new NamedNode('http://ex.org/d') - )).should.equal('<<_:n3-000 ?var-b "Hello ""W""orl""d!"@en-us http://ex.org/d>>'); + )).should.equal('["_:n3-000","?var-b","\\"Hello \\"W\\"orl\\"d!\\"@en-us","http://ex.org/d"]'); }); it('should create an id without graph from a Quad with default graph and Quad as subject', () => { @@ -326,7 +328,7 @@ describe('Term', () => { new NamedNode('http://ex.org/b'), new Literal('"abc"@en-us'), new DefaultGraph() - )).should.equal('<<<<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> http://ex.org/b "abc"@en-us>>'); + )).should.equal('[["_:n3-000","?var-b","\\"abc\\"@en-us","http://ex.org/d"],"http://ex.org/b","\\"abc\\"@en-us"]'); }); it('should create an id without graph from a Quad with default graph and Quad as object', () => { @@ -340,7 +342,7 @@ describe('Term', () => { new NamedNode('http://ex.org/d') ), new DefaultGraph() - )).should.equal('<<"abc"@en-us http://ex.org/b <<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>>>>'); + )).should.equal('["\\"abc\\"@en-us","http://ex.org/b",["_:n3-000","?var-b","\\"abc\\"@en-us","http://ex.org/d"]]'); }); it('should create an id without graph from a Quad with default graph and Quad as subject and object', () => { @@ -359,7 +361,7 @@ describe('Term', () => { new NamedNode('http://ex.org/d') ), new DefaultGraph() - )).should.equal('<<<<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> http://ex.org/b <<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>>>>'); + )).should.equal('[["_:n3-000","?var-b","\\"abc\\"@en-us","http://ex.org/d"],"http://ex.org/b",["_:n3-000","?var-b","\\"abc\\"@en-us","http://ex.org/d"]]'); }); it('should create an id without graph from a Quad with Quad as subject', () => { @@ -373,7 +375,7 @@ describe('Term', () => { new NamedNode('http://ex.org/b'), new Literal('"abc"@en-us'), new NamedNode('http://ex.org/d') - )).should.equal('<<<<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> http://ex.org/b "abc"@en-us http://ex.org/d>>'); + )).should.equal('[["_:n3-000","?var-b","\\"abc\\"@en-us","http://ex.org/d"],"http://ex.org/b","\\"abc\\"@en-us","http://ex.org/d"]'); }); it('should create an id without graph from a Quad with Quad as object', () => { @@ -387,7 +389,7 @@ describe('Term', () => { new NamedNode('http://ex.org/d') ), new NamedNode('http://ex.org/d') - )).should.equal('<<"abc"@en-us http://ex.org/b <<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> http://ex.org/d>>'); + )).should.equal('["\\"abc\\"@en-us","http://ex.org/b",["_:n3-000","?var-b","\\"abc\\"@en-us","http://ex.org/d"],"http://ex.org/d"]'); }); it('should create an id from a Quad with Quad as subject and object', () => { @@ -406,7 +408,7 @@ describe('Term', () => { new NamedNode('http://ex.org/d') ), new NamedNode('http://ex.org/d') - )).should.equal('<<<<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> http://ex.org/b <<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> http://ex.org/d>>'); + )).should.equal('[["_:n3-000","?var-b","\\"abc\\"@en-us","http://ex.org/d"],"http://ex.org/b",["_:n3-000","?var-b","\\"abc\\"@en-us","http://ex.org/d"],"http://ex.org/d"]'); }); it('should escape literals in nested Quads', () => { @@ -425,11 +427,46 @@ describe('Term', () => { new NamedNode('http://ex.org/d') ), new DefaultGraph() - )).should.equal('<<<<_:n3-000 ?var-b "Hello ""W""orl""d!"@en-us http://ex.org/d>> http://ex.org/b <<_:n3-000 ?var-b "Hello ""W""orl""d!"@en-us http://ex.org/d>>>>'); + )).should.equal('[["_:n3-000","?var-b","\\"Hello \\"W\\"orl\\"d!\\"@en-us","http://ex.org/d"],"http://ex.org/b",["_:n3-000","?var-b","\\"Hello \\"W\\"orl\\"d!\\"@en-us","http://ex.org/d"]]'); + }); + + + it('should termToId <-> termFromId should roundtrip on deeply nested quad', () => { + const q = new Quad( + new Quad( + new NamedNode('http://example.org/s1'), + new NamedNode('http://example.org/p1'), + new NamedNode('http://example.org/o1') + ), + new NamedNode('http://example.org/p1'), + new Quad( + new Quad( + new Literal('"s1"'), + new NamedNode('http://example.org/p1'), + new BlankNode('o1') + ), + new NamedNode('p2'), + new Quad( + new Quad( + new Literal('"s1"'), + new NamedNode('http://example.org/p1'), + new BlankNode('o1') + ), + new NamedNode('http://example.org/p1'), + new NamedNode('http://example.org/o1') + ) + ) + ); + + expect(q).deep.equals(termFromId(termToId(q))); + expect(termFromId(termToId(q))).deep.equals(q); + expect(q.equals(termFromId(termToId(q)))).equal(true); + expect(termFromId(termToId(q)).equals(q)).equal(true); + expect(termFromId(termToId(q)).equals(termFromId(termToId(q)))).equal(true); }); it('should correctly handle deeply nested quads', () => { - termToId(new Quad( + const q = new Quad( new Quad( new Quad( new Quad( @@ -474,7 +511,9 @@ describe('Term', () => { new NamedNode('http://ex.org/d') ), new NamedNode('http://ex.org/d') - )).should.equal('<<<<<<<<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> ?var-b <<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> http://ex.org/d>> ?var-b <<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> http://ex.org/d>> http://ex.org/b <<<<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> ?var-b <<_:n3-000 ?var-b "abc"@en-us http://ex.org/d>> http://ex.org/d>> http://ex.org/d>>'); + ); + + expect(q.equals(termFromId(termToId(q)))).equal(true); }); it('should throw on an unknown type', () => { From 56cc184f78761db27751aa4a8489239e0c287666 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Sun, 27 Nov 2022 00:38:09 +1100 Subject: [PATCH 12/29] chore: add tests from https://github.com/rdfjs/N3.js/pull/303 --- test/N3Store-test.js | 26 ++++++++++++++++++++++ test/Term-test.js | 53 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/test/N3Store-test.js b/test/N3Store-test.js index 10b1f29f..424880e2 100644 --- a/test/N3Store-test.js +++ b/test/N3Store-test.js @@ -1653,6 +1653,32 @@ describe('Store', () => { [...store.match(null, null, null, null)].should.have.length(3); }); + it('should include added elements in match if iteration has not yet started (deeply nested)', () => { + const m = store.match(null, null, null, null); + store.add(new Quad( + new NamedNode('s1'), + new NamedNode('p1'), + new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o3')) + ) + ); + store.add(new Quad( + new NamedNode('s1'), + new NamedNode('p1'), + new Quad( + new NamedNode('s1'), + new NamedNode('p1'), + new Quad( + new NamedNode('s1'), + new NamedNode('p1'), + new NamedNode('o3') + ) + ) + ) + ); + [...m].should.have.length(4); + [...store.match(null, null, null, null)].should.have.length(4); + }); + it('should still include results of original match after iterating while adding new data', () => { const m = store.match(null, null, null, null)[Symbol.iterator](); m.next().value.should.deep.equal(new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o1'))); diff --git a/test/Term-test.js b/test/Term-test.js index 6798a81b..750bc3e5 100644 --- a/test/Term-test.js +++ b/test/Term-test.js @@ -15,6 +15,54 @@ import { unescapeQuotes, } from '../src/N3DataFactory'; + +const DEEP_TRIPLE = new Quad( + new Quad( + new Quad( + new Quad( + new BlankNode('n3-000'), + new Variable('var-b'), + new Literal('"abc"@en-us'), + new NamedNode('http://ex.org/d') + ), + new Variable('var-b'), + new Quad( + new BlankNode('n3-000'), + new Variable('var-b'), + new Literal('"abc"@en-us'), + new NamedNode('http://ex.org/d') + ), + new NamedNode('http://ex.org/d') + ), + new Variable('var-b'), + new Quad( + new BlankNode('n3-000'), + new Variable('var-b'), + new Literal('"abc"@en-us'), + new NamedNode('http://ex.org/d') + ), + new NamedNode('http://ex.org/d') + ), + new NamedNode('http://ex.org/b'), + new Quad( + new Quad( + new BlankNode('n3-000'), + new Variable('var-b'), + new Literal('"abc"@en-us'), + new NamedNode('http://ex.org/d') + ), + new Variable('var-b'), + new Quad( + new BlankNode('n3-000'), + new Variable('var-b'), + new Literal('"abc"@en-us'), + new NamedNode('http://ex.org/d') + ), + new NamedNode('http://ex.org/d') + ), + new NamedNode('http://ex.org/d') +); + describe('Term', () => { describe('The Term module', () => { it('should be a function', () => { @@ -161,6 +209,11 @@ describe('Term', () => { termFromId(termToId(q)).should.deep.equal(q); }); + it('should correctly handle deeply nested quads', () => { + DEEP_TRIPLE.equals(termFromId(termToId(DEEP_TRIPLE))).should.equal(true); + termFromId(termToId(DEEP_TRIPLE)).equals(DEEP_TRIPLE).should.equal(true); + }); + describe('with a custom factory', () => { const factory = { defaultGraph: function () { return ['d']; }, From 2f0f57d6708eefa9426df5c62515ca28270a81e7 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:03:03 +1100 Subject: [PATCH 13/29] chore: describe quoted triple predicate parsing Co-authored-by: Ruben Verborgh --- src/N3Parser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/N3Parser.js b/src/N3Parser.js index 22cfb331..97b36b37 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -909,6 +909,7 @@ export default class N3Parser { this._emit(this._subject, this._predicate, this._object, this._graph); } + // If the quoted triple is not finished, the next token must be a predicate if (token.type !== '|}') return this._readPredicate; this._restoreContext('{|', token); From 0539be97902ca83afa7f879b67b231d26728237e Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:09:36 +1100 Subject: [PATCH 14/29] chore: clarify use of graph term in quoted quads --- src/N3Parser.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/N3Parser.js b/src/N3Parser.js index 97b36b37..66eb7a07 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -641,7 +641,8 @@ export default class N3Parser { this._saveContext('{|', this._graph, this._subject, this._predicate, this._object); - // Note - we always use the default graph for the quoted triple component + // As a convention we use set the graph term as the Default Graph in quads representing quoted triples + // see https://github.com/rdfjs/N3.js/pull/311#discussion_r1061039556 for details this._subject = this._quad(this._subject, this._predicate, this._object, this.DEFAULTGRAPH); this._predicate = null; this._object = null; @@ -899,7 +900,8 @@ export default class N3Parser { if (token.type === '{|') { this._saveContext('{|', this._graph, this._subject, this._predicate, this._object); - // Note - we always use the default graph for the quoted triple component + // As a convention we use set the graph term as the Default Graph in quads representing quoted triples + // see https://github.com/rdfjs/N3.js/pull/311#discussion_r1061039556 for details this._subject = this._quad(this._subject, this._predicate, this._object, this.DEFAULTGRAPH); this._predicate = null; this._object = null; From e7646d9809056ae98f22fb176a93d9ff92f593ad Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 4 Jan 2023 14:29:14 +1100 Subject: [PATCH 15/29] fix: allow a split between '|' and '}' (see https://github.com/rdfjs/N3.js/pull/311#discussion_r1060687020) --- src/N3Lexer.js | 6 +- test/N3Parser-test.js | 534 ++++++++++++++++++++++++++---------------- 2 files changed, 342 insertions(+), 198 deletions(-) diff --git a/src/N3Lexer.js b/src/N3Lexer.js index 19ceca32..6bfa2c81 100644 --- a/src/N3Lexer.js +++ b/src/N3Lexer.js @@ -306,7 +306,11 @@ export default class N3Lexer { type = '{|', matchLength = 2; break; } - if (!this._lineMode) { + if ( + !this._lineMode && + // The token might actually be {| and we just have not encountered the pipe yet + (input !== '{' || input.length > 1) + ) { matchLength = 1; type = firstChar; } diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 40012632..08a3bad9 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -22,45 +22,45 @@ describe('Parser', () => { }); describe('A Parser instance', () => { - it('should parse the empty string', + describe('should parse the empty string', shouldParse('' /* no triples */)); - it('should parse a whitespace string', + describe('should parse a whitespace string', shouldParse(' \t \n ' /* no triples */)); - it('should parse a single triple', + describe('should parse a single triple', shouldParse(' .', ['a', 'b', 'c'])); - it('should parse three triples', + describe('should parse three triples', shouldParse(' .\n .\n .', ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'])); - it('should parse a triple with a literal', + describe('should parse a triple with a literal', shouldParse(' "string".', ['a', 'b', '"string"'])); - it('should parse a triple with a numeric literal', + describe('should parse a triple with a numeric literal', shouldParse(' 3.0.', ['a', 'b', '"3.0"^^http://www.w3.org/2001/XMLSchema#decimal'])); - it('should parse a triple with an integer literal', + describe('should parse a triple with an integer literal', shouldParse(' 3.', ['a', 'b', '"3"^^http://www.w3.org/2001/XMLSchema#integer'])); - it('should parse a triple with a floating point literal', + describe('should parse a triple with a floating point literal', shouldParse(' 1.3e2.', ['a', 'b', '"1.3e2"^^http://www.w3.org/2001/XMLSchema#double'])); - it('should parse a triple with a boolean literal', + describe('should parse a triple with a boolean literal', shouldParse(' true.', ['a', 'b', '"true"^^http://www.w3.org/2001/XMLSchema#boolean'])); - it('should parse a triple with a literal and a language code', + describe('should parse a triple with a literal and a language code', shouldParse(' "string"@en.', ['a', 'b', '"string"@en'])); @@ -68,11 +68,11 @@ describe('Parser', () => { shouldParse(' "string"@EN.', ['a', 'b', '"string"@en'])); - it('should parse a triple with a literal and an IRI type', + describe('should parse a triple with a literal and an IRI type', shouldParse(' "string"^^.', ['a', 'b', '"string"^^http://example.org/type'])); - it('should parse a triple with a literal and a prefixed name type', + describe('should parse a triple with a literal and a prefixed name type', shouldParse('@prefix x: . "string"^^x:z.', ['a', 'b', '"string"^^urn:x:y#z'])); @@ -103,27 +103,27 @@ describe('Parser', () => { }, })); - it('should parse a triple with the "a" shorthand predicate', + describe('should parse a triple with the "a" shorthand predicate', shouldParse(' a .', ['a', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 't'])); - it('should parse triples with prefixes', + describe('should parse triples with prefixes', shouldParse('@prefix : <#>.\n' + '@prefix a: .\n' + ':x a:a a:b.', ['#x', 'a#a', 'a#b'])); - it('should parse triples with the prefix "prefix"', + describe('should parse triples with the prefix "prefix"', shouldParse('@prefix prefix: .' + 'prefix:a prefix:b prefix:c.', ['http://prefix.cc/a', 'http://prefix.cc/b', 'http://prefix.cc/c'])); - it('should parse triples with the prefix "base"', + describe('should parse triples with the prefix "base"', shouldParse('PREFIX base: ' + 'base:a base:b base:c.', ['http://prefix.cc/a', 'http://prefix.cc/b', 'http://prefix.cc/c'])); - it('should parse triples with the prefix "graph"', + describe('should parse triples with the prefix "graph"', shouldParse('PREFIX graph: ' + 'graph:a graph:b graph:c.', ['http://prefix.cc/a', 'http://prefix.cc/b', 'http://prefix.cc/c'])); @@ -143,7 +143,7 @@ describe('Parser', () => { line: 1, })); - it('should parse triples with prefixes and different punctuation', + describe('should parse triples with prefixes and different punctuation', shouldParse('@prefix : <#>.\n' + '@prefix a: .\n' + ':x a:a a:b;a:c a:d,a:e.', @@ -171,7 +171,7 @@ describe('Parser', () => { shouldNotParse(' "c"^^d:e ', 'Undefined prefix "d:" on line 1.')); - it('should parse triples with SPARQL prefixes', + describe('should parse triples with SPARQL prefixes', shouldParse('PREFIX : <#>\n' + 'PrEfIX a: ' + ':x a:a a:b.', @@ -189,22 +189,22 @@ describe('Parser', () => { shouldNotParse('@prefix : ;', 'Expected declaration to end with a dot on line 1.')); - it('should parse statements with shared subjects', + describe('should parse statements with shared subjects', shouldParse(' ;\n .', ['a', 'b', 'c'], ['a', 'd', 'e'])); - it('should parse statements with shared subjects and trailing semicolon', + describe('should parse statements with shared subjects and trailing semicolon', shouldParse(' ;\n ;\n.', ['a', 'b', 'c'], ['a', 'd', 'e'])); - it('should parse statements with shared subjects and multiple semicolons', + describe('should parse statements with shared subjects and multiple semicolons', shouldParse(' ;;\n .', ['a', 'b', 'c'], ['a', 'd', 'e'])); - it('should parse statements with shared subjects and predicates', + describe('should parse statements with shared subjects and predicates', shouldParse(' , .', ['a', 'b', 'c'], ['a', 'b', 'd'])); @@ -217,7 +217,7 @@ describe('Parser', () => { shouldNotParse(' . , .', 'Expected entity but got , on line 1.')); - it('should parse diamonds', + describe('should parse diamonds', shouldParse('<> <> <> <>.\n(<>) <> (<>) <>.', [BASE_IRI, BASE_IRI, BASE_IRI, BASE_IRI], ['_:b0', BASE_IRI, '_:b1', BASE_IRI], @@ -226,7 +226,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', BASE_IRI], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with named blank nodes', + describe('should parse statements with named blank nodes', shouldParse('_:a _:c.', ['_:b0_a', 'b', '_:b0_c'])); @@ -274,21 +274,21 @@ describe('Parser', () => { }, })); - it('should parse statements with empty blank nodes', + describe('should parse statements with empty blank nodes', shouldParse('[] [].', ['_:b0', 'b', '_:b1'])); - it('should parse statements with unnamed blank nodes in the subject', + describe('should parse statements with unnamed blank nodes in the subject', shouldParse('[ ] .', ['_:b0', 'c', 'd'], ['_:b0', 'a', 'b'])); - it('should parse statements with unnamed blank nodes in the object', + describe('should parse statements with unnamed blank nodes in the object', shouldParse(' [ ].', ['a', 'b', '_:b0'], ['_:b0', 'c', 'd'])); - it('should parse statements with unnamed blank nodes with a string object', + describe('should parse statements with unnamed blank nodes with a string object', shouldParse(' [ "x"].', ['a', 'b', '_:b0'], ['_:b0', 'c', '"x"'])); @@ -305,65 +305,65 @@ describe('Parser', () => { shouldNotParse(' ; ]', 'Unexpected ] on line 1.')); - it('should parse a blank node with a trailing semicolon', + describe('should parse a blank node with a trailing semicolon', shouldParse(' [ ; ].', ['a', 'b', '_:b0'], ['_:b0', 'u', 'v'])); - it('should parse a blank node with multiple trailing semicolons', + describe('should parse a blank node with multiple trailing semicolons', shouldParse(' [ ;;; ].', ['a', 'b', '_:b0'], ['_:b0', 'u', 'v'])); - it('should parse a multi-predicate blank node', + describe('should parse a multi-predicate blank node', shouldParse(' [ ; ].', ['a', 'b', '_:b0'], ['_:b0', 'u', 'v'], ['_:b0', 'w', 'z'])); - it('should parse a multi-predicate blank node with multiple semicolons', + describe('should parse a multi-predicate blank node with multiple semicolons', shouldParse(' [ ;;; ].', ['a', 'b', '_:b0'], ['_:b0', 'u', 'v'], ['_:b0', 'w', 'z'])); - it('should parse a multi-object blank node', + describe('should parse a multi-object blank node', shouldParse(' [ , ].', ['a', 'b', '_:b0'], ['_:b0', 'u', 'v'], ['_:b0', 'u', 'z'])); - it('should parse a multi-statement blank node ending with a literal', + describe('should parse a multi-statement blank node ending with a literal', shouldParse(' [ ; "z" ].', ['a', 'b', '_:b0'], ['_:b0', 'u', 'v'], ['_:b0', 'w', '"z"'])); - it('should parse a multi-statement blank node ending with a typed literal', + describe('should parse a multi-statement blank node ending with a typed literal', shouldParse(' [ ; "z"^^ ].', ['a', 'b', '_:b0'], ['_:b0', 'u', 'v'], ['_:b0', 'w', '"z"^^http://example.org/t'])); - it('should parse a multi-statement blank node ending with a string with language', + describe('should parse a multi-statement blank node ending with a string with language', shouldParse(' [ ; "z"^^ ].', ['a', 'b', '_:b0'], ['_:b0', 'u', 'v'], ['_:b0', 'w', '"z"^^http://example.org/t'])); - it('should parse a multi-statement blank node with trailing semicolon', + describe('should parse a multi-statement blank node with trailing semicolon', shouldParse(' [ ; ; ].', ['a', 'b', '_:b0'], ['_:b0', 'u', 'v'], ['_:b0', 'w', 'z'])); - it('should parse statements with nested blank nodes in the subject', + describe('should parse statements with nested blank nodes in the subject', shouldParse('[ [ ]] .', ['_:b0', 'c', 'd'], ['_:b0', 'a', '_:b1'], ['_:b1', 'x', 'y'])); - it('should parse statements with nested blank nodes in the object', + describe('should parse statements with nested blank nodes in the object', shouldParse(' [ [ ]].', ['a', 'b', '_:b0'], ['_:b0', 'c', '_:b1'], @@ -378,7 +378,7 @@ describe('Parser', () => { shouldNotParse('[ .', 'Expected punctuation to follow "http://example.org/b" on line 1.')); - it('should parse a statements with only an anonymous node', + describe('should parse a statements with only an anonymous node', shouldParse('[

].', ['_:b0', 'p', 'o'])); @@ -390,45 +390,45 @@ describe('Parser', () => { shouldNotParse('[[

]].', 'Disallowed blank node as predicate on line 1.')); - it('should parse statements with an empty list in the subject', + describe('should parse statements with an empty list in the subject', shouldParse('() .', ['http://www.w3.org/1999/02/22-rdf-syntax-ns#nil', 'a', 'b'])); - it('should parse statements with an empty list in the object', + describe('should parse statements with an empty list in the object', shouldParse(' ().', ['a', 'b', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a single-element list in the subject', + describe('should parse statements with a single-element list in the subject', shouldParse('() .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'x'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a single-element list in the object', + describe('should parse statements with a single-element list in the object', shouldParse(' ().', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'x'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse a list with a literal', + describe('should parse a list with a literal', shouldParse(' ("x").', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"x"'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse a list with a typed literal', + describe('should parse a list with a typed literal', shouldParse(' ("x"^^).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"x"^^http://example.org/y'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse a list with a language-tagged literal', + describe('should parse a list with a language-tagged literal', shouldParse(' ("x"@en-GB).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"x"@en-gb'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a multi-element list in the subject', + describe('should parse statements with a multi-element list in the subject', shouldParse('( ) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'x'], @@ -436,7 +436,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'y'], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a multi-element list in the object', + describe('should parse statements with a multi-element list in the object', shouldParse(' ( ).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'x'], @@ -444,7 +444,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'y'], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a multi-element literal list in the object', + describe('should parse statements with a multi-element literal list in the object', shouldParse(' ("x" "y"@en-GB 1 "z"^^).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"x"'], @@ -456,7 +456,7 @@ describe('Parser', () => { ['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"z"^^http://example.org/t'], ['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with prefixed names in lists', + describe('should parse statements with prefixed names in lists', shouldParse('@prefix a: . (a:x a:y).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'a#x'], @@ -468,7 +468,7 @@ describe('Parser', () => { shouldNotParse(' (a:x a:y).', 'Undefined prefix "a:" on line 1.')); - it('should parse statements with blank nodes in lists', + describe('should parse statements with blank nodes in lists', shouldParse(' (_:x _:y).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b0_x'], @@ -476,7 +476,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b0_y'], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse a nested list', + describe('should parse a nested list', shouldParse(' ( ( ) ).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], @@ -484,7 +484,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'c'], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a nested empty list', + describe('should parse statements with a nested empty list', shouldParse(' ( ()).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'x'], @@ -492,7 +492,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with non-empty nested lists', + describe('should parse statements with non-empty nested lists', shouldParse(' ( ()).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'x'], @@ -502,13 +502,13 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'y'], ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a quoted triple', + describe('should parse statements with a list containing a quoted triple', shouldParse(' ( << >> ) .', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a quoted triple and iri after', + describe('should parse statements with a list containing a quoted triple and iri after', shouldParse(' ( << >> ) .', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], @@ -516,7 +516,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'f'], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a quoted triple and iri before', + describe('should parse statements with a list containing a quoted triple and iri before', shouldParse(' ( << >> ) .', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'f'], @@ -524,7 +524,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a 2 quoted triples', + describe('should parse statements with a list containing a 2 quoted triples', shouldParse(' ( << >> << >> ) .', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], @@ -533,7 +533,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a 3 quoted triples', + describe('should parse statements with a list containing a 3 quoted triples', shouldParse(' ( << >> << >> << >> ) .', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], @@ -543,7 +543,7 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c2', 'd2', 'e2']], ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a 1 quoted triple and 2 iris', + describe('should parse statements with a list containing a 1 quoted triple and 2 iris', shouldParse(' ( << >> ) .', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], @@ -553,7 +553,7 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'i'], ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a 1 quoted triple between 2 iris', + describe('should parse statements with a list containing a 1 quoted triple between 2 iris', shouldParse(' ( << >> ) .', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'h'], @@ -564,7 +564,7 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse a nested list containing 1 quoted triple', + describe('should parse a nested list containing 1 quoted triple', shouldParse(' ( ( << >> ) ).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], @@ -572,13 +572,13 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a quoted triple with list as subject', + describe('should parse statements with a list containing a quoted triple with list as subject', shouldParse('( << >> ) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a quoted triple and iri after with list as subject', + describe('should parse statements with a list containing a quoted triple and iri after with list as subject', shouldParse('( << >> ) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], @@ -586,7 +586,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'f'], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a quoted triple and iri before with list as subject', + describe('should parse statements with a list containing a quoted triple and iri before with list as subject', shouldParse('( << >> ) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'f'], @@ -594,7 +594,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a 2 quoted triples with list as subject', + describe('should parse statements with a list containing a 2 quoted triples with list as subject', shouldParse('( << >> << >> ) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], @@ -603,7 +603,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a 3 quoted triples with list as subject', + describe('should parse statements with a list containing a 3 quoted triples with list as subject', shouldParse('( << >> << >> << >> ) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], @@ -613,7 +613,7 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c2', 'd2', 'e2']], ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a 1 quoted triple and 2 iris with list as subject', + describe('should parse statements with a list containing a 1 quoted triple and 2 iris with list as subject', shouldParse('( << >> ) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], @@ -623,7 +623,7 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'i'], ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a 1 quoted triple between 2 iris with list as subject', + describe('should parse statements with a list containing a 1 quoted triple between 2 iris with list as subject', shouldParse('( << >> ) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'h'], @@ -633,7 +633,7 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'i'], ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse a nested list containing 1 quoted triple with list as subject', + describe('should parse a nested list containing 1 quoted triple with list as subject', shouldParse('( ( << >> ) ) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], @@ -641,13 +641,13 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', ['c', 'd', 'e']], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing a blank node with list as subject', + describe('should parse statements with a list containing a blank node with list as subject', shouldParse('([]) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should parse statements with a list containing multiple blank nodes with list as subject', + describe('should parse statements with a list containing multiple blank nodes with list as subject', shouldParse('([] [ ]) .', ['_:b0', 'a', 'b'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], @@ -656,7 +656,7 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], ['_:b3', 'x', 'y'])); - it('should parse statements with a blank node containing a list with list as subject', + describe('should parse statements with a blank node containing a list with list as subject', shouldParse('[ ()] .', ['_:b0', 'c', 'd'], ['_:b0', 'a', '_:b1'], @@ -746,142 +746,142 @@ describe('Parser', () => { 'ex:a ex:b ex:c .', ['http://ex.org/a/bb/ccc/../a', 'http://ex.org/a/bb/ccc/../b', 'http://ex.org/a/bb/ccc/../c'])); - it('should parse an empty default graph', + describe('should parse an empty default graph', shouldParse('{}')); - it('should parse a one-triple default graph ending without a dot', + describe('should parse a one-triple default graph ending without a dot', shouldParse('{ }', ['a', 'b', 'c'])); - it('should parse a one-triple default graph ending with a dot', + describe('should parse a one-triple default graph ending with a dot', shouldParse('{ .}', ['a', 'b', 'c'])); - it('should parse a three-triple default graph ending without a dot', + describe('should parse a three-triple default graph ending without a dot', shouldParse('{ ; ,}', ['a', 'b', 'c'], ['a', 'd', 'e'], ['a', 'd', 'f'])); - it('should parse a three-triple default graph ending with a dot', + describe('should parse a three-triple default graph ending with a dot', shouldParse('{ ; ,.}', ['a', 'b', 'c'], ['a', 'd', 'e'], ['a', 'd', 'f'])); - it('should parse a three-triple default graph ending with a semicolon', + describe('should parse a three-triple default graph ending with a semicolon', shouldParse('{ ; ,;}', ['a', 'b', 'c'], ['a', 'd', 'e'], ['a', 'd', 'f'])); - it('should parse a default graph with a blank node ending with a dot', + describe('should parse a default graph with a blank node ending with a dot', shouldParse('{ [

]. }', ['_:b0', 'p', 'o'])); - it('should parse a default graph with a blank node ending without a dot', + describe('should parse a default graph with a blank node ending without a dot', shouldParse('{ [

] }', ['_:b0', 'p', 'o'])); - it('should parse an empty named graph with an IRI', + describe('should parse an empty named graph with an IRI', shouldParse('{}')); - it('should parse a one-triple named graph with an IRI ending without a dot', + describe('should parse a one-triple named graph with an IRI ending without a dot', shouldParse(' { }', ['a', 'b', 'c', 'g'])); - it('should parse a one-triple named graph with an IRI ending with a dot', + describe('should parse a one-triple named graph with an IRI ending with a dot', shouldParse('{ .}', ['a', 'b', 'c', 'g'])); - it('should parse a three-triple named graph with an IRI ending without a dot', + describe('should parse a three-triple named graph with an IRI ending without a dot', shouldParse(' { ; ,}', ['a', 'b', 'c', 'g'], ['a', 'd', 'e', 'g'], ['a', 'd', 'f', 'g'])); - it('should parse a three-triple named graph with an IRI ending with a dot', + describe('should parse a three-triple named graph with an IRI ending with a dot', shouldParse('{ ; ,.}', ['a', 'b', 'c', 'g'], ['a', 'd', 'e', 'g'], ['a', 'd', 'f', 'g'])); - it('should parse an empty named graph with a prefixed name', + describe('should parse an empty named graph with a prefixed name', shouldParse('@prefix g: .\ng:h {}')); - it('should parse a one-triple named graph with a prefixed name ending without a dot', + describe('should parse a one-triple named graph with a prefixed name ending without a dot', shouldParse('@prefix g: .\ng:h { }', ['a', 'b', 'c', 'g#h'])); - it('should parse a one-triple named graph with a prefixed name ending with a dot', + describe('should parse a one-triple named graph with a prefixed name ending with a dot', shouldParse('@prefix g: .\ng:h{ .}', ['a', 'b', 'c', 'g#h'])); - it('should parse a three-triple named graph with a prefixed name ending without a dot', + describe('should parse a three-triple named graph with a prefixed name ending without a dot', shouldParse('@prefix g: .\ng:h { ; ,}', ['a', 'b', 'c', 'g#h'], ['a', 'd', 'e', 'g#h'], ['a', 'd', 'f', 'g#h'])); - it('should parse a three-triple named graph with a prefixed name ending with a dot', + describe('should parse a three-triple named graph with a prefixed name ending with a dot', shouldParse('@prefix g: .\ng:h{ ; ,.}', ['a', 'b', 'c', 'g#h'], ['a', 'd', 'e', 'g#h'], ['a', 'd', 'f', 'g#h'])); - it('should parse a named graph with a blank node ending with a dot', + describe('should parse a named graph with a blank node ending with a dot', shouldParse(' { [

]. }', ['_:b0', 'p', 'o', 'g'])); - it('should parse a named graph with a blank node ending without a dot', + describe('should parse a named graph with a blank node ending without a dot', shouldParse(' { [

] }', ['_:b0', 'p', 'o', 'g'])); - it('should parse an empty anonymous graph', + describe('should parse an empty anonymous graph', shouldParse('[] {}')); - it('should parse a one-triple anonymous graph ending without a dot', + describe('should parse a one-triple anonymous graph ending without a dot', shouldParse('[] { }', ['a', 'b', 'c', '_:b0'])); - it('should parse a one-triple anonymous graph ending with a dot', + describe('should parse a one-triple anonymous graph ending with a dot', shouldParse('[]{ .}', ['a', 'b', 'c', '_:b0'])); - it('should parse a three-triple anonymous graph ending without a dot', + describe('should parse a three-triple anonymous graph ending without a dot', shouldParse('[] { ; ,}', ['a', 'b', 'c', '_:b0'], ['a', 'd', 'e', '_:b0'], ['a', 'd', 'f', '_:b0'])); - it('should parse a three-triple anonymous graph ending with a dot', + describe('should parse a three-triple anonymous graph ending with a dot', shouldParse('[]{ ; ,.}', ['a', 'b', 'c', '_:b0'], ['a', 'd', 'e', '_:b0'], ['a', 'd', 'f', '_:b0'])); - it('should parse an empty named graph with an IRI and the GRAPH keyword', + describe('should parse an empty named graph with an IRI and the GRAPH keyword', shouldParse('GRAPH {}')); - it('should parse an empty named graph with a prefixed name and the GRAPH keyword', + describe('should parse an empty named graph with a prefixed name and the GRAPH keyword', shouldParse('@prefix g: .\nGRAPH g:h {}')); - it('should parse an empty anonymous graph and the GRAPH keyword', + describe('should parse an empty anonymous graph and the GRAPH keyword', shouldParse('GRAPH [] {}')); - it('should parse a one-triple named graph with an IRI and the GRAPH keyword', + describe('should parse a one-triple named graph with an IRI and the GRAPH keyword', shouldParse('GRAPH { }', ['a', 'b', 'c', 'g'])); - it('should parse a one-triple named graph with a prefixed name and the GRAPH keyword', + describe('should parse a one-triple named graph with a prefixed name and the GRAPH keyword', shouldParse('@prefix g: .\nGRAPH g:h { }', ['a', 'b', 'c', 'g#h'])); - it('should parse a one-triple anonymous graph and the GRAPH keyword', + describe('should parse a one-triple anonymous graph and the GRAPH keyword', shouldParse('GRAPH [] { }', ['a', 'b', 'c', '_:b0'])); - it('should parse a graph with 8-bit unicode escape sequences', + describe('should parse a graph with 8-bit unicode escape sequences', shouldParse('<\\U0001d400> {\n<\\U0001d400> <\\U0001d400> "\\U0001d400"^^<\\U0001d400>\n}\n', ['\ud835\udC00', '\ud835\udc00', '"\ud835\udc00"^^http://example.org/\ud835\udc00', '\ud835\udc00'])); @@ -891,7 +891,7 @@ describe('Parser', () => { it('should not parse a single opening brace', shouldNotParse('{', - 'Expected entity but got eof on line 1.')); + 'Unexpected "{" on line 1.')); it('should not parse a superfluous closing brace ', shouldNotParse('{}}', @@ -929,7 +929,7 @@ describe('Parser', () => { shouldNotParse('GRAPH GRAPH {}', 'Invalid graph label on line 1.')); - it('should parse a quad with 4 IRIs', + describe('should parse a quad with 4 IRIs', shouldParse(' .', ['a', 'b', 'c', 'g'])); @@ -937,7 +937,7 @@ describe('Parser', () => { shouldNotParse('<< >> .', 'Expected >> to follow "http://example.org/c" but got IRI on line 1.')); - it('should parse a quad with 4 prefixed names', + describe('should parse a quad with 4 prefixed names', shouldParse('@prefix p: .\np:a p:b p:c p:g.', ['p#a', 'p#b', 'p#c', 'p#g'])); @@ -945,11 +945,11 @@ describe('Parser', () => { shouldNotParse(' p:g.', 'Undefined prefix "p:" on line 1.')); - it('should parse a quad with 3 IRIs and a literal', + describe('should parse a quad with 3 IRIs and a literal', shouldParse(' "c"^^ .', ['a', 'b', '"c"^^http://example.org/d', 'g'])); - it('should parse a quad with 2 blank nodes and a literal', + describe('should parse a quad with 2 blank nodes and a literal', shouldParse('_:a "c"^^ _:g.', ['_:b0_a', 'b', '"c"^^http://example.org/d', '_:b0_g'])); @@ -1066,7 +1066,7 @@ describe('Parser', () => { } }); - it('should parse a string synchronously if no callback is given', () => { + describe('should parse a string synchronously if no callback is given', () => { const triples = new Parser().parse('@prefix a: . a:a a:b a:c.'); triples.should.deep.equal([ new Quad(termFromId('urn:a:a'), termFromId('urn:a:b'), @@ -1084,7 +1084,7 @@ describe('Parser', () => { .should.throw('Expected entity but got eof on line 1'); }); - it('should parse an RDF-star triple with a triple with iris as subject correctly', () => { + describe('should parse an RDF-star triple with a triple with iris as subject correctly', () => { shouldParse('<< >> .', [['a', 'b', 'c'], 'b', 'c']); }); @@ -1093,36 +1093,36 @@ describe('Parser', () => { shouldNotParse(' << >> ', 'Expected entity but got << on line 1.')); - it('should parse an RDF-star triple with a triple with blanknodes as subject correctly', + describe('should parse an RDF-star triple with a triple with blanknodes as subject correctly', shouldParse('<<_:a _:c>> .', [['_:b0_a', 'b', '_:b0_c'], 'b', 'c'])); - it('should parse an RDF-star triple with a triple with blanknodes and literals as subject correctly', + describe('should parse an RDF-star triple with a triple with blanknodes and literals as subject correctly', shouldParse('<<_:a "c"^^>> .', [['_:b0_a', 'b', '"c"^^http://example.org/d'], 'b', 'c'])); - it('should parse an RDF-star triple with a triple as object correctly', + describe('should parse an RDF-star triple with a triple as object correctly', shouldParse(' << >>.', ['a', 'b', ['a', 'b', 'c']])); - it('should parse an RDF-star triple with a triple as object correctly', + describe('should parse an RDF-star triple with a triple as object correctly', shouldParse(' <<_:a _:c>>.', ['a', 'b', ['_:b0_a', 'b', '_:b0_c']])); - it('should parse an RDF-star triple with a triple as object correctly', + describe('should parse an RDF-star triple with a triple as object correctly', shouldParse(' <<_:a "c"^^>>.', ['a', 'b', ['_:b0_a', 'b', '"c"^^http://example.org/d']])); - it('should parse nested triples correctly', + describe('should parse nested triples correctly', shouldParse('<<<< >> >> .', [[['a', 'b', 'c'], 'f', 'g'], 'd', 'e'])); - it('should parse nested triples correctly', + describe('should parse nested triples correctly', shouldParse(' << << >>>>.', ['d', 'e', ['f', 'g', ['a', 'b', 'c']]])); - it('should parse nested triples correctly', + describe('should parse nested triples correctly', shouldParse('<< << >>>> .', [['f', 'g', ['a', 'b', 'c']], 'd', 'e'])); - it('should parse nested triples correctly', + describe('should parse nested triples correctly', shouldParse(' <<<< >> >>.', ['d', 'e', [['a', 'b', 'c'], 'f', 'g']])); @@ -1220,12 +1220,25 @@ describe('Parser', () => { shouldNotParse('<< >> .', 'Expected >> to follow "http://example.org/c" but got IRI on line 1.')); - it('should parse statements with a shared RDF-star subject', + describe('should parse statements with a shared RDF-star subject', shouldParse('<< >> ;\n .', [['a', 'b', 'c'], 'b', 'c'], [['a', 'b', 'c'], 'd', 'c'])); - it('should parse statements with a shared RDF-star subject', + // it('should parse no chunks (i.e. onEnd called immediately)', + // shouldParseChunks([])); + + it('should parse statements with a shared RDF-star subject that is chunked at double quotes', + shouldParseChunks(['<', '< >> ;\n .'], + [['a', 'b', 'c'], 'b', 'c'], + [['a', 'b', 'c'], 'd', 'c'])); + + it('should parse statements with a shared RDF-star subject that is chunked at every character', + shouldParseChunks('<< >> ;\n .'.split(''), + [['a', 'b', 'c'], 'b', 'c'], + [['a', 'b', 'c'], 'd', 'c'])); + + describe('should parse statements with a shared RDF-star subject', shouldParse('<< >> ;\n << >>.', [['a', 'b', 'c'], 'b', 'c'], [['a', 'b', 'c'], 'd', ['a', 'b', 'c']])); @@ -1235,12 +1248,40 @@ describe('Parser', () => { ['a', 'b', 'c', 'g'], [['a', 'b', 'c'], 'd', 'e'])); - it('should parse an explicit triple with reified annotation', + describe('should parse an explicit triple with reified annotation', shouldParse(' {| |} .', ['a', 'b', 'c'], [['a', 'b', 'c'], 'd', 'e'])); - it('should parse an explicit triple with nested reified annotation', + it('should parse an explicit triple with reified annotation that is chunked at the pipe', + shouldParseChunks([' {| |', '} .'], + ['a', 'b', 'c'], + [['a', 'b', 'c'], 'd', 'e'])); + + it('should parse an explicit triple with reified annotation that is chunked at the pipe and each character after', + shouldParseChunks([' {| |', '}', ' ', '.', ''], + ['a', 'b', 'c'], + [['a', 'b', 'c'], 'd', 'e'])); + + it('should parse an explicit triple with reified annotation that is chunked at each annotation', + shouldParseChunks([' {', '| |', '} .'], + ['a', 'b', 'c'], + [['a', 'b', 'c'], 'd', 'e'])); + + it('should parse an quoted triple that is chunked on first quote', + shouldParseChunks(['<', '< >> .'], + [['a', 'b', 'c'], 'd', 'e'])); + + it('should parse an quoted triple that is chunked on ending quote', + shouldParseChunks(['<< >', '> .'], + [['a', 'b', 'c'], 'd', 'e'])); + + it('should parse an explicit triple with reified annotation that is chunked before annotation', + shouldParseChunks([' ', '{| |} .'], + ['a', 'b', 'c'], + [['a', 'b', 'c'], 'd', 'e'])); + + describe('should parse an explicit triple with nested reified annotation', shouldParse(' {| {| |} |} .', ['a', 'b', 'c'], [['a', 'b', 'c'], 'd', 'e'], @@ -1249,22 +1290,22 @@ describe('Parser', () => { const q = ['http://example.com/ns#s', 'http://example.com/ns#p', ['http://example.com/ns#a', 'http://example.com/ns#b', 'http://example.com/ns#c']]; - it('should parse an explicit triple with reified annotation containing prefixed iris', + describe('should parse an explicit triple with reified annotation containing prefixed iris', shouldParse('PREFIX : \n :s :p <<:a :b :c>> {| :q :z |} .', q, [q, 'http://example.com/ns#q', 'http://example.com/ns#z'])); - it('should parse an explicit triple with 2 reified annotations', + describe('should parse an explicit triple with 2 reified annotations', shouldParse(' {| ; |} .', ['a', 'b', 'c'], [['a', 'b', 'c'], 'd', 'e'], [['a', 'b', 'c'], 'f', 'g'])); - it('should parse an explicit triple with reified annotation in a named graph', + describe('should parse an explicit triple with reified annotation in a named graph', shouldParse(' { {| |} . }', ['a', 'b', 'c', 'G'], [['a', 'b', 'c'], 'd', 'e', 'G'])); - it('should parse an explicit triple with 2 reified annotations in a named graph', + describe('should parse an explicit triple with 2 reified annotations in a named graph', shouldParse(' { {| ; |} . }', ['a', 'b', 'c', 'G'], [['a', 'b', 'c'], 'd', 'e', 'G'], @@ -1397,7 +1438,7 @@ describe('Parser', () => { describe('A Parser instance with a blank node prefix', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, blankNodePrefix: '_:blank' }); } - it('should use the given prefix for blank nodes', + describe('should use the given prefix for blank nodes', shouldParse(parser, '_:a _:c.\n', ['_:blanka', 'b', '_:blankc'])); @@ -1406,7 +1447,7 @@ describe('Parser', () => { describe('A Parser instance with an empty blank node prefix', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, blankNodePrefix: '' }); } - it('should not use a prefix for blank nodes', + describe('should not use a prefix for blank nodes', shouldParse(parser, '_:a _:c.\n', ['_:a', 'b', '_:c'])); @@ -1415,17 +1456,17 @@ describe('Parser', () => { describe('A Parser instance with a non-string format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 1 }); } - it('should parse a single triple', + describe('should parse a single triple', shouldParse(parser, ' .', ['a', 'b', 'c'])); - it('should parse a graph', + describe('should parse a graph', shouldParse(parser, '{ }', ['a', 'b', 'c'])); }); describe('A Parser instance for the Turtle format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'Turtle' }); } - it('should parse a single triple', + describe('should parse a single triple', shouldParse(parser, ' .', ['a', 'b', 'c'])); it('should not parse a default graph', @@ -1493,7 +1534,7 @@ describe('Parser', () => { describe('A Parser instance for the TurtleStar format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'TurtleStar' }); } - it('should parse RDF-star', + describe('should parse RDF-star', shouldParse(parser, '<< >> .', [['a', 'b', 'c'], 'b', 'c'])); @@ -1506,16 +1547,22 @@ describe('Parser', () => { describe('A Parser instance for the TriG format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'TriG' }); } + it('should parse a single triple chunked before a closing bracket', + shouldParseChunks(parser, [' .'], ['a', 'b', 'c'])); + + it('should parse a single triple chunked after an opening bracket', + shouldParseChunks(parser, [' <', 'b> .'], ['a', 'b', 'c'])); + it('should parse a single triple', shouldParse(parser, ' .', ['a', 'b', 'c'])); - it('should parse a default graph', + describe('should parse a default graph', shouldParse(parser, '{}')); - it('should parse a named graph', + describe('should parse a named graph', shouldParse(parser, ' {}')); - it('should parse a named graph with the GRAPH keyword', + describe('should parse a named graph with the GRAPH keyword', shouldParse(parser, 'GRAPH {}')); it('should not parse a quad', @@ -1554,7 +1601,7 @@ describe('Parser', () => { describe('A Parser instance for the TriGStar format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'TriGStar' }); } - it('should parse RDF-star', + describe('should parse RDF-star', shouldParse(parser, '<< >> .', [['a', 'b', 'c'], 'a', 'b'])); @@ -1566,11 +1613,11 @@ describe('Parser', () => { describe('A Parser instance for the N-Triples format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-Triples' }); } - it('should parse a single triple', + describe('should parse a single triple', shouldParse(parser, '_:a "c".', ['_:b0_a', 'http://ex.org/b', '"c"'])); - it('should parse a single triple starting with Bom', + describe('should parse a single triple starting with Bom', shouldParse(parser, '\ufeff_:a "c".', ['_:b0_a', 'http://ex.org/b', '"c"'])); @@ -1629,7 +1676,7 @@ describe('Parser', () => { describe('A Parser instance for the N-TriplesStar format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-TriplesStar' }); } - it('should parse RDF-star', + describe('should parse RDF-star', shouldParse(parser, '<<_:a _:c>> _:b .', [['_:b0_a', 'b', '_:b0_c'], 'a', '_:b0_b'])); @@ -1641,11 +1688,11 @@ describe('Parser', () => { describe('A Parser instance for the N-Quads format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-Quads' }); } - it('should parse a single triple', + describe('should parse a single triple', shouldParse(parser, '_:a "c".', ['_:b0_a', 'http://ex.org/b', '"c"'])); - it('should parse a single quad', + describe('should parse a single quad', shouldParse(parser, '_:a "c" .', ['_:b0_a', 'http://ex.org/b', '"c"', 'http://ex.org/g'])); @@ -1688,7 +1735,7 @@ describe('Parser', () => { describe('A Parser instance for the N-QuadsStar format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-QuadsStar' }); } - it('should parse RDF-star', + describe('should parse RDF-star', shouldParse(parser, '<<_:a _:c>> _:c .', [['_:b0_a', 'b', '_:b0_c'], 'a', '_:b0_c'])); }); @@ -1696,7 +1743,7 @@ describe('Parser', () => { describe('A Parser instance for the N3 format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N3' }); } - it('should parse a single triple', + describe('should parse a single triple', shouldParse(parser, ' .', ['a', 'b', 'c'])); it('should not parse a default graph', @@ -1722,28 +1769,35 @@ describe('Parser', () => { ['a', '_:b0', 'c'], ['_:b0', 'p', 'o'])); - it('should parse a variable', + describe('should parse a variable', shouldParse(parser, '?a ?b ?c.', ['?a', '?b', '?c'])); - it('should parse a simple equality', + describe('should parse a simple equality', shouldParse(parser, ' = .', ['a', 'http://www.w3.org/2002/07/owl#sameAs', 'b'])); - it('should parse a simple right implication', + describe('should parse a simple right implication', shouldParse(parser, ' => .', ['a', 'http://www.w3.org/2000/10/swap/log#implies', 'b'])); - it('should parse a simple left implication', + describe('should parse a simple left implication', shouldParse(parser, ' <= .', ['b', 'http://www.w3.org/2000/10/swap/log#implies', 'a'])); - it('should parse a right implication between one-triple graphs', + describe('should parse a right implication between one-triple graphs', shouldParse(parser, '{ ?a ?b . } => { ?a }.', ['_:b0', 'http://www.w3.org/2000/10/swap/log#implies', '_:b1'], ['?a', '?b', 'c', '_:b0'], ['d', 'e', '?a', '_:b1'])); - it('should parse a right implication between two-triple graphs', + it('should parse a right implication between one-triple graphs with chunk at first bracket', + shouldParseChunks(parser, ['{', ' ?a ?b . } => { ?a }.'], + ['_:b0', 'http://www.w3.org/2000/10/swap/log#implies', '_:b1'], + ['?a', '?b', 'c', '_:b0'], + ['d', 'e', '?a', '_:b1'])); + + + describe('should parse a right implication between two-triple graphs', shouldParse(parser, '{ ?a ?b . . } => { ?a, }.', ['_:b0', 'http://www.w3.org/2000/10/swap/log#implies', '_:b1'], ['?a', '?b', 'c', '_:b0'], @@ -1751,13 +1805,13 @@ describe('Parser', () => { ['d', 'e', '?a', '_:b1'], ['d', 'e', 'f', '_:b1'])); - it('should parse a left implication between one-triple graphs', + describe('should parse a left implication between one-triple graphs', shouldParse(parser, '{ ?a ?b . } <= { ?a }.', ['_:b1', 'http://www.w3.org/2000/10/swap/log#implies', '_:b0'], ['?a', '?b', 'c', '_:b0'], ['d', 'e', '?a', '_:b1'])); - it('should parse a left implication between two-triple graphs', + describe('should parse a left implication between two-triple graphs', shouldParse(parser, '{ ?a ?b . . } <= { ?a, }.', ['_:b1', 'http://www.w3.org/2000/10/swap/log#implies', '_:b0'], ['?a', '?b', 'c', '_:b0'], @@ -1765,13 +1819,13 @@ describe('Parser', () => { ['d', 'e', '?a', '_:b1'], ['d', 'e', 'f', '_:b1'])); - it('should parse an equality of one-triple graphs', + describe('should parse an equality of one-triple graphs', shouldParse(parser, '{ ?a ?b . } = { ?a }.', ['_:b0', 'http://www.w3.org/2002/07/owl#sameAs', '_:b1'], ['?a', '?b', 'c', '_:b0'], ['d', 'e', '?a', '_:b1'])); - it('should parse an equality of two-triple graphs', + describe('should parse an equality of two-triple graphs', shouldParse(parser, '{ ?a ?b . . } = { ?a, }.', ['_:b0', 'http://www.w3.org/2002/07/owl#sameAs', '_:b1'], ['?a', '?b', 'c', '_:b0'], @@ -1779,7 +1833,7 @@ describe('Parser', () => { ['d', 'e', '?a', '_:b1'], ['d', 'e', 'f', '_:b1'])); - it('should parse nested implication graphs', + describe('should parse nested implication graphs', shouldParse(parser, '{ { ?a ?b ?c }<={ ?d ?e ?f }. } <= { { ?g ?h ?i } => { ?j ?k ?l } }.', ['_:b3', 'http://www.w3.org/2000/10/swap/log#implies', '_:b0'], ['_:b2', 'http://www.w3.org/2000/10/swap/log#implies', '_:b1', '_:b0'], @@ -1798,11 +1852,11 @@ describe('Parser', () => { ['_:b2.a', '_:b2.b', '_:b2.c', '_:b2'], ['_:b3.a', '_:b3.b', '_:b3.c', '_:b3'])); - it('should parse a @forSome statement', + describe('should parse a @forSome statement', shouldParse(parser, '@forSome . .', ['_:b0', '_:b0', '_:b0'])); - it('should parse a @forSome statement with multiple entities', + describe('should parse a @forSome statement with multiple entities', shouldParse(parser, '@prefix a: . @base . @forSome a:x, , a:z. a:x a:z.', ['_:b0', '_:b1', '_:b2'])); @@ -1824,11 +1878,11 @@ describe('Parser', () => { ['_:b2', '_:b2', '_:b2', '_:b1'], ['_:b0', '_:b0', '_:b0'])); - it('should parse a @forAll statement', + describe('should parse a @forAll statement', shouldParse(parser, '@forAll . .', ['?b0', '?b0', '?b0'])); - it('should parse a @forAll statement with multiple entities', + describe('should parse a @forAll statement with multiple entities', shouldParse(parser, '@prefix a: . @base . @forAll a:x, , a:z. a:x a:z.', ['?b0', '?b1', '?b2'])); @@ -1850,13 +1904,13 @@ describe('Parser', () => { ['?b2', '?b2', '?b2', '_:b1'], ['?b0', '?b0', '?b0'])); - it('should parse a ! path of length 2 as subject', + describe('should parse a ! path of length 2 as subject', shouldParse(parser, '@prefix : . @prefix fam: .' + ':joe!fam:mother a fam:Person.', ['ex:joe', 'f:mother', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person'])); - it('should parse a ! path of length 4 as subject', + describe('should parse a ! path of length 4 as subject', shouldParse(parser, '@prefix : . @prefix fam: . @prefix loc: .' + ':joe!fam:mother!loc:office!loc:zip loc:code 1234.', ['ex:joe', 'f:mother', '_:b0'], @@ -1864,13 +1918,13 @@ describe('Parser', () => { ['_:b1', 'l:zip', '_:b2'], ['_:b2', 'l:code', '"1234"^^http://www.w3.org/2001/XMLSchema#integer'])); - it('should parse a ! path of length 2 as object', + describe('should parse a ! path of length 2 as object', shouldParse(parser, '@prefix : . @prefix fam: .' + ' :joe!fam:mother.', ['x', 'is', '_:b0'], ['ex:joe', 'f:mother', '_:b0'])); - it('should parse a ! path of length 4 as object', + describe('should parse a ! path of length 4 as object', shouldParse(parser, '@prefix : . @prefix fam: . @prefix loc: .' + ' :joe!fam:mother!loc:office!loc:zip.', ['x', 'is', '_:b2'], @@ -1878,13 +1932,13 @@ describe('Parser', () => { ['_:b0', 'l:office', '_:b1'], ['_:b1', 'l:zip', '_:b2'])); - it('should parse a ^ path of length 2 as subject', + describe('should parse a ^ path of length 2 as subject', shouldParse(parser, '@prefix : . @prefix fam: .' + ':joe^fam:son a fam:Person.', ['_:b0', 'f:son', 'ex:joe'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person'])); - it('should parse a ^ path of length 4 as subject', + describe('should parse a ^ path of length 4 as subject', shouldParse(parser, '@prefix : . @prefix fam: .' + ':joe^fam:son^fam:sister^fam:mother a fam:Person.', ['_:b0', 'f:son', 'ex:joe'], @@ -1892,13 +1946,13 @@ describe('Parser', () => { ['_:b2', 'f:mother', '_:b1'], ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person'])); - it('should parse a ^ path of length 2 as object', + describe('should parse a ^ path of length 2 as object', shouldParse(parser, '@prefix : . @prefix fam: .' + ' :joe^fam:son.', ['x', 'is', '_:b0'], ['_:b0', 'f:son', 'ex:joe'])); - it('should parse a ^ path of length 4 as object', + describe('should parse a ^ path of length 4 as object', shouldParse(parser, '@prefix : . @prefix fam: .' + ' :joe^fam:son^fam:sister^fam:mother.', ['x', 'is', '_:b2'], @@ -1906,49 +1960,49 @@ describe('Parser', () => { ['_:b1', 'f:sister', '_:b0'], ['_:b2', 'f:mother', '_:b1'])); - it('should parse mixed !/^ paths as subject', + describe('should parse mixed !/^ paths as subject', shouldParse(parser, '@prefix : . @prefix fam: .' + ':joe!fam:mother^fam:mother a fam:Person.', ['ex:joe', 'f:mother', '_:b0'], ['_:b1', 'f:mother', '_:b0'], ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person'])); - it('should parse mixed !/^ paths as object', + describe('should parse mixed !/^ paths as object', shouldParse(parser, '@prefix : . @prefix fam: .' + ' :joe!fam:mother^fam:mother.', ['x', 'is', '_:b1'], ['ex:joe', 'f:mother', '_:b0'], ['_:b1', 'f:mother', '_:b0'])); - it('should parse a ! path in a blank node as subject', + describe('should parse a ! path in a blank node as subject', shouldParse(parser, '@prefix : . @prefix fam: .' + '[fam:knows :joe!fam:mother] a fam:Person.', ['_:b0', 'f:knows', '_:b1'], ['ex:joe', 'f:mother', '_:b1'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person'])); - it('should parse a ! path in a blank node as object', + describe('should parse a ! path in a blank node as object', shouldParse(parser, '@prefix : . @prefix fam: .' + ' [fam:knows :joe!fam:mother].', ['x', 'is', '_:b0'], ['_:b0', 'f:knows', '_:b1'], ['ex:joe', 'f:mother', '_:b1'])); - it('should parse a ^ path in a blank node as subject', + describe('should parse a ^ path in a blank node as subject', shouldParse(parser, '@prefix : . @prefix fam: .' + '[fam:knows :joe^fam:son] a fam:Person.', ['_:b0', 'f:knows', '_:b1'], ['_:b1', 'f:son', 'ex:joe'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person'])); - it('should parse a ^ path in a blank node as object', + describe('should parse a ^ path in a blank node as object', shouldParse(parser, '@prefix : . @prefix fam: .' + ' [fam:knows :joe^fam:son].', ['x', 'is', '_:b0'], ['_:b0', 'f:knows', '_:b1'], ['_:b1', 'f:son', 'ex:joe'])); - it('should parse a ! path in a list as subject', + describe('should parse a ! path in a list as subject', shouldParse(parser, '@prefix : . @prefix fam: .' + '( :joe!fam:mother ) a :List.', ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'ex:List'], @@ -1960,7 +2014,7 @@ describe('Parser', () => { ['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], ['ex:joe', 'f:mother', '_:b2'])); - it('should parse a ! path in a list as object', + describe('should parse a ! path in a list as object', shouldParse(parser, '@prefix : . @prefix fam: .' + ' ( :joe!fam:mother ).', ['l', 'is', '_:b0'], @@ -1972,7 +2026,7 @@ describe('Parser', () => { ['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], ['ex:joe', 'f:mother', '_:b2'])); - it('should parse a ^ path in a list as subject', + describe('should parse a ^ path in a list as subject', shouldParse(parser, '@prefix : . @prefix fam: .' + '( :joe^fam:son ) a :List.', ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'ex:List'], @@ -1984,7 +2038,7 @@ describe('Parser', () => { ['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], ['_:b2', 'f:son', 'ex:joe'])); - it('should parse a ^ path in a list as object', + describe('should parse a ^ path in a list as object', shouldParse(parser, '@prefix : . @prefix fam: .' + ' ( :joe^fam:son ).', ['l', 'is', '_:b0'], @@ -1996,7 +2050,7 @@ describe('Parser', () => { ['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], ['_:b2', 'f:son', 'ex:joe'])); - it('should parse a formula as list item', + describe('should parse a formula as list item', shouldParse(parser, ' ( { a . } ).', ['a', 'findAll', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'b'], @@ -2013,19 +2067,19 @@ describe('Parser', () => { it('should not parse an invalid ^ path', shouldNotParse(parser, '^"invalid" ', 'Expected entity but got literal on line 1.')); - it('should parse literal as subject', + describe('should parse literal as subject', shouldParse(parser, ' {1 0}.', ['a', 'b', '_:b0'], ['"1"^^http://www.w3.org/2001/XMLSchema#integer', 'greaterThan', '"0"^^http://www.w3.org/2001/XMLSchema#integer', '_:b0'] )); - it('should parse literals with datatype as subject', + describe('should parse literals with datatype as subject', shouldParse(parser, ' {"a"^^ "b"^^}.', ['a', 'b', '_:b0'], ['"a"^^http://example.org/c', 'greaterThan', '"b"^^http://example.org/c', '_:b0'] )); - it('should parse literals with language as subject', + describe('should parse literals with language as subject', shouldParse(parser, ' {"bonjour"@fr "hello"@en}.', ['a', 'b', '_:b0'], ['"bonjour"@fr', 'sameAs', '"hello"@en', '_:b0'] @@ -2043,7 +2097,7 @@ describe('Parser', () => { describe('A Parser instance for the N3Star format', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N3Star' }); } - it('should parse RDF-star', + describe('should parse RDF-star', shouldParse(parser, '<< >> .', [['a', 'b', 'c'], 'a', 'b'])); @@ -2055,14 +2109,14 @@ describe('Parser', () => { describe('A Parser instance for the N3 format with the explicitQuantifiers option', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N3', explicitQuantifiers: true }); } - it('should parse a @forSome statement', + describe('should parse a @forSome statement', shouldParse(parser, '@forSome . .', ['', 'http://www.w3.org/2000/10/swap/reify#forSome', '_:b0', 'urn:n3:quantifiers'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'x', 'urn:n3:quantifiers'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil', 'urn:n3:quantifiers'], ['x', 'x', 'x'])); - it('should parse a @forSome statement with multiple entities', + describe('should parse a @forSome statement with multiple entities', shouldParse(parser, '@prefix a: . @base . @forSome a:x, , a:z. a:x a:z.', ['', 'http://www.w3.org/2000/10/swap/reify#forSome', '_:b0', 'urn:n3:quantifiers'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'a:x', 'urn:n3:quantifiers'], @@ -2085,14 +2139,14 @@ describe('Parser', () => { ['x', 'x', 'x', '_:b1'], ['x', 'x', 'x'])); - it('should parse a @forAll statement', + describe('should parse a @forAll statement', shouldParse(parser, '@forAll . .', ['', 'http://www.w3.org/2000/10/swap/reify#forAll', '_:b0', 'urn:n3:quantifiers'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'x', 'urn:n3:quantifiers'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil', 'urn:n3:quantifiers'], ['x', 'x', 'x'])); - it('should parse a @forAll statement with multiple entities', + describe('should parse a @forAll statement with multiple entities', shouldParse(parser, '@prefix a: . @base . @forAll a:x, , a:z. a:x a:z.', ['', 'http://www.w3.org/2000/10/swap/reify#forAll', '_:b0', 'urn:n3:quantifiers'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'a:x', 'urn:n3:quantifiers'], @@ -2564,7 +2618,31 @@ describe('Parser', () => { }); }); -function shouldParse(parser, input) { +// Split string into all combinations possible +function splitAllWays(result, left, right, chunkSize) { + // Push current left + right to the result list + result.push(left.concat(right)); + // document.write(left.concat(right) + '
'); + + // If we still have chars to work with in the right side then keep splitting + if (right.length > 1) { + // For each combination left/right split call splitAllWays() + for (let i = chunkSize; i < right.length; i += chunkSize) { + splitAllWays(result, left.concat(right.substring(0, i)), right.substring(i), chunkSize); + } + } + + // Return result + return result; +} + +// Return a large number of combinations for splitting the string to test chunking - any thing with 5 characters +// or less will test every permutation of splits possible on the string +function getSplits(str) { + return splitAllWays([], [], str, Math.max(Math.floor(str.length / 6), 1)); +} + +function shouldParseChunks(parser, input) { const expected = Array.prototype.slice.call(arguments, 1); // Shift parameters as necessary if (parser.call) @@ -2572,15 +2650,77 @@ function shouldParse(parser, input) { else input = parser, parser = Parser; + const items = expected.map(mapToQuad); + + return _shouldParseChunks(parser, input, items); +} + +function _shouldParseChunks(parser, input, items) { return function (done) { - const results = []; + const results2 = []; + + let onData, onEnd; + new parser({ baseIRI: BASE_IRI }).parse({ + baseIRI: BASE_IRI, + on: (event, callback) => { + switch (event) { + case 'data': onData = callback; break; + case 'end': onEnd = callback; break; + } + }, + }, + (error, triple) => { + expect(error).not.to.exist; + if (triple) + results2.push(triple); + else + toSortedJSON(results2).should.equal(toSortedJSON(items)), done(); + } + ); + + for (const chunk of input) { + onData(chunk); + } + + onEnd(); + }; +} + +function shouldParse(parser, input) { + return () => { + const expected = Array.prototype.slice.call(arguments, 1); + // Shift parameters as necessary + if (parser.call) + expected.shift(); + else + input = parser, parser = Parser; + const items = expected.map(mapToQuad); - new parser({ baseIRI: BASE_IRI }).parse(input, (error, triple) => { - expect(error).not.to.exist; - if (triple) - results.push(triple); - else + + for (const chunk of [ + // Split at every character + input.split(''), + // Random splits + ...getSplits(input), + // Exactly one split in each position + ...input.split('').map((_, i) => [input.slice(0, i), input.slice(i)]), + ] + // Ignore degenerate cases (for now) + .filter(arr => arr.length > 0 && (arr.length !== 1 || arr[0] !== '')) + ) { + it(`should run on chunking ${JSON.stringify(chunk)}`, _shouldParseChunks(parser, chunk, items)); + } + + it('should run on full string', done => { + // Test parsing of whole string + const results = []; + new parser({ baseIRI: BASE_IRI }).parse(input, (error, triple) => { + expect(error).not.to.exist; + if (triple) + results.push(triple); + else toSortedJSON(results).should.equal(toSortedJSON(items)), done(); + }); }); }; } From 6140e86140d0698a303555d82e5df738d7160962 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 4 Jan 2023 14:31:17 +1100 Subject: [PATCH 16/29] chore: remove doubling comment --- src/N3DataFactory.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/N3DataFactory.js b/src/N3DataFactory.js index 786a774b..ce472554 100644 --- a/src/N3DataFactory.js +++ b/src/N3DataFactory.js @@ -248,8 +248,6 @@ export function termToId(term, nested) { term.language ? `@${term.language}` : (term.datatype && term.datatype.value !== xsd.string ? `^^${term.datatype.value}` : '')}`; case 'Quad': - // To identify RDF-star quad components, we escape quotes by doubling them. - // This avoids the overhead of backslash parsing of Turtle-like syntaxes. const res = [ termToId(term.subject, true), termToId(term.predicate, true), From bec8395f00aa52317dcc4088cb00560634c8bd5d Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 4 Jan 2023 14:34:34 +1100 Subject: [PATCH 17/29] chore: add comment about nested parameter --- src/N3DataFactory.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/N3DataFactory.js b/src/N3DataFactory.js index ce472554..4c253a7c 100644 --- a/src/N3DataFactory.js +++ b/src/N3DataFactory.js @@ -188,6 +188,10 @@ export class DefaultGraph extends Term { DEFAULTGRAPH = new DefaultGraph(); // ### Constructs a term from the given internal string ID +// The third 'nested' parameter of this function is to aid +// with recursion over nested terms. It should not be used +// by consumers of this library. +// See https://github.com/rdfjs/N3.js/pull/311#discussion_r1061042725 export function termFromId(id, factory, nested) { factory = factory || DataFactory; @@ -230,6 +234,10 @@ export function termFromId(id, factory, nested) { } // ### Constructs an internal string ID from the given term or ID string +// The third 'nested' parameter of this function is to aid +// with recursion over nested terms. It should not be used +// by consumers of this library. +// See https://github.com/rdfjs/N3.js/pull/311#discussion_r1061042725 export function termToId(term, nested) { if (typeof term === 'string') return term; From 8ce8428800b2e76038801896b66e35801af6c1da Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 4 Jan 2023 15:00:45 +1100 Subject: [PATCH 18/29] fix: use describe for all shouldParse test suites --- test/N3Parser-test.js | 69 +++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 08a3bad9..1303665c 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -64,7 +64,7 @@ describe('Parser', () => { shouldParse('
"string"@en.', ['a', 'b', '"string"@en'])); - it('should normalize language codes to lowercase', + describe('should normalize language codes to lowercase', shouldParse(' "string"@EN.', ['a', 'b', '"string"@en'])); @@ -76,7 +76,7 @@ describe('Parser', () => { shouldParse('@prefix x: . "string"^^x:z.', ['a', 'b', '"string"^^urn:x:y#z'])); - it('should differentiate between IRI and prefixed name types', + describe('should differentiate between IRI and prefixed name types', shouldParse('@prefix : . :a :b "x"^^. :a :b "x"^^:urn:foo.', ['noturn:a', 'noturn:b', '"x"^^urn:foo'], ['noturn:a', 'noturn:b', '"x"^^noturn:urn:foo'])); @@ -369,7 +369,7 @@ describe('Parser', () => { ['_:b0', 'c', '_:b1'], ['_:b1', 'd', 'e'])); - it('should reuse identifiers of blank nodes within and outside of graphs', + describe('should reuse identifiers of blank nodes within and outside of graphs', shouldParse('_:a _:c. { _:a _:c }', ['_:b0_a', 'b', '_:b0_c'], ['_:b0_a', 'b', '_:b0_c', 'g'])); @@ -477,7 +477,7 @@ describe('Parser', () => { ['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); describe('should parse a nested list', - shouldParse(' ( ( ) ).', + shouldParse(' ( ( ) ).', ['a', 'b', '_:b0'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'], @@ -667,7 +667,7 @@ describe('Parser', () => { shouldNotParse(' (]).', 'Expected entity but got ] on line 1.')); - it('should resolve IRIs against @base', + describe('should resolve IRIs against @base', shouldParse('@base .\n' + ' .\n' + '@base .\n' + @@ -679,7 +679,7 @@ describe('Parser', () => { shouldNotParse('@BASE .', 'Expected entity but got @BASE on line 1.')); - it('should resolve IRIs against SPARQL base', + describe('should resolve IRIs against SPARQL base', shouldParse('BASE \n' + ' . ' + 'BASE ' + @@ -687,7 +687,7 @@ describe('Parser', () => { ['http://ex.org/a', 'http://ex.org/b', 'http://ex.org/c'], ['http://ex.org/d/e', 'http://ex.org/d/f', 'http://ex.org/d/g'])); - it('should resolve IRIs against a @base with query string', + describe('should resolve IRIs against a @base with query string', shouldParse('@base .\n' + '<> .\n' + '@base .\n' + @@ -695,7 +695,7 @@ describe('Parser', () => { ['http://ex.org/?foo', 'http://ex.org/b', 'http://ex.org/c'], ['http://ex.org/d/?bar', 'http://ex.org/d/f', 'http://ex.org/d/g'])); - it('should resolve IRIs with query string against @base', + describe('should resolve IRIs with query string against @base', shouldParse('@base .\n' + ' .\n' + '@base .\n' + @@ -706,7 +706,7 @@ describe('Parser', () => { ['http://ex.org/d?', 'http://ex.org/d?a', 'http://ex.org/d?a=b'], ['http://ex.org/d?e', 'http://ex.org/d?a', 'http://ex.org/d?a=b'])); - it('should not resolve IRIs with colons', + describe('should not resolve IRIs with colons', shouldParse('@base .\n' + ' .\n' + ' .\n' + @@ -723,7 +723,7 @@ describe('Parser', () => { shouldNotParse('@base .', 'Expected valid IRI to follow base declaration on line 1.')); - it('should resolve datatype IRIs against @base', + describe('should resolve datatype IRIs against @base', shouldParse('@base .\n' + ' "c"^^.\n' + '@base .\n' + @@ -731,17 +731,17 @@ describe('Parser', () => { ['http://ex.org/a', 'http://ex.org/b', '"c"^^http://ex.org/d'], ['http://ex.org/d/e', 'http://ex.org/d/f', '"g"^^http://ex.org/d/h'])); - it('should resolve IRIs against a base with a fragment', + describe('should resolve IRIs against a base with a fragment', shouldParse('@base .\n' + ' <#c>.\n', ['http://ex.org/a', 'http://ex.org/b', 'http://ex.org/foo#c'])); - it('should resolve IRIs with an empty fragment', + describe('should resolve IRIs with an empty fragment', shouldParse('@base .\n' + '<#> <#c>.\n', ['http://ex.org/foo#', 'http://ex.org/b#', 'http://ex.org/foo#c'])); - it('should not resolve prefixed names', + describe('should not resolve prefixed names', shouldParse('PREFIX ex: \n' + 'ex:a ex:b ex:c .', ['http://ex.org/a/bb/ccc/../a', 'http://ex.org/a/bb/ccc/../b', 'http://ex.org/a/bb/ccc/../c'])); @@ -1243,7 +1243,7 @@ describe('Parser', () => { [['a', 'b', 'c'], 'b', 'c'], [['a', 'b', 'c'], 'd', ['a', 'b', 'c']])); - it('should put nested triples in the default graph', + describe('should put nested triples in the default graph', shouldParse(' .\n<< >> .', ['a', 'b', 'c', 'g'], [['a', 'b', 'c'], 'd', 'e'])); @@ -1339,7 +1339,7 @@ describe('Parser', () => { describe('An Parser instance without document IRI', () => { function parser() { return new Parser(); } - it('should keep relative IRIs', + describe('should keep relative IRIs', shouldParse(parser, '@prefix : <#>.\n' + ' .\n' + @@ -1347,7 +1347,7 @@ describe('Parser', () => { [termFromId('a'), termFromId('b'), termFromId('c'), termFromId('g')], [termFromId('#d'), termFromId('#e'), termFromId('#f'), termFromId('#g')])); - it('should keep empty IRIs', + describe('should keep empty IRIs', shouldParse(parser, '@prefix : <>.\n' + '<> <> <> <>.\n' + @@ -1359,7 +1359,7 @@ describe('Parser', () => { describe('An Parser instance with a document IRI', () => { function parser() { return new Parser({ baseIRI: 'http://ex.org/x/yy/zzz/f.ttl' }); } - it('should resolve IRIs against the document IRI', + describe('should resolve IRIs against the document IRI', shouldParse(parser, '@prefix : <#>.\n' + ' .\n' + @@ -1367,47 +1367,47 @@ describe('Parser', () => { ['http://ex.org/x/yy/zzz/a', 'http://ex.org/x/yy/zzz/b', 'http://ex.org/x/yy/zzz/c', 'http://ex.org/x/yy/zzz/g'], ['http://ex.org/x/yy/zzz/f.ttl#d', 'http://ex.org/x/yy/zzz/f.ttl#e', 'http://ex.org/x/yy/zzz/f.ttl#f', 'http://ex.org/x/yy/zzz/f.ttl#g'])); - it('should resolve IRIs with a trailing slash against the document IRI', + describe('should resolve IRIs with a trailing slash against the document IRI', shouldParse(parser, ' .\n', ['http://ex.org/a', 'http://ex.org/a/b', 'http://ex.org/a/b/c'])); - it('should resolve IRIs starting with ./ against the document IRI', + describe('should resolve IRIs starting with ./ against the document IRI', shouldParse(parser, '<./a> <./a/b> <./a/b/c>.\n', ['http://ex.org/x/yy/zzz/a', 'http://ex.org/x/yy/zzz/a/b', 'http://ex.org/x/yy/zzz/a/b/c'])); - it('should resolve IRIs starting with multiple ./ sequences against the document IRI', + describe('should resolve IRIs starting with multiple ./ sequences against the document IRI', shouldParse(parser, '<./././a> <./././././a/b> <././././././a/b/c>.\n', ['http://ex.org/x/yy/zzz/a', 'http://ex.org/x/yy/zzz/a/b', 'http://ex.org/x/yy/zzz/a/b/c'])); - it('should resolve IRIs starting with ../ against the document IRI', + describe('should resolve IRIs starting with ../ against the document IRI', shouldParse(parser, '<../a> <../a/b> <../a/b/c>.\n', ['http://ex.org/x/yy/a', 'http://ex.org/x/yy/a/b', 'http://ex.org/x/yy/a/b/c'])); - it('should resolve IRIs starting multiple ../ sequences against the document IRI', + describe('should resolve IRIs starting multiple ../ sequences against the document IRI', shouldParse(parser, '<../../a> <../../../a/b> <../../../../../../../../a/b/c>.\n', ['http://ex.org/x/a', 'http://ex.org/a/b', 'http://ex.org/a/b/c'])); - it('should resolve IRIs starting with mixes of ./ and ../ sequences against the document IRI', + describe('should resolve IRIs starting with mixes of ./ and ../ sequences against the document IRI', shouldParse(parser, '<.././a> <./.././a/b> <./.././.././a/b/c>.\n', ['http://ex.org/x/yy/a', 'http://ex.org/x/yy/a/b', 'http://ex.org/x/a/b/c'])); - it('should resolve IRIs starting with .x, ..x, or .../ against the document IRI', + describe('should resolve IRIs starting with .x, ..x, or .../ against the document IRI', shouldParse(parser, '<.x/a> <..x/a/b> <.../a/b/c>.\n', ['http://ex.org/x/yy/zzz/.x/a', 'http://ex.org/x/yy/zzz/..x/a/b', 'http://ex.org/x/yy/zzz/.../a/b/c'])); - it('should resolve datatype IRIs against the document IRI', + describe('should resolve datatype IRIs against the document IRI', shouldParse(parser, ' "c"^^.', ['http://ex.org/x/yy/zzz/a', 'http://ex.org/x/yy/zzz/b', '"c"^^http://ex.org/x/yy/zzz/d'])); - it('should resolve IRIs in lists against the document IRI', + describe('should resolve IRIs in lists against the document IRI', shouldParse(parser, '( )

( ).', ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'http://ex.org/x/yy/zzz/a'], @@ -1420,7 +1420,7 @@ describe('Parser', () => { ['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'http://ex.org/x/yy/zzz/d'], ['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'])); - it('should respect @base statements', + describe('should respect @base statements', shouldParse(parser, ' .\n' + '@base .\n' + @@ -1553,7 +1553,7 @@ describe('Parser', () => { it('should parse a single triple chunked after an opening bracket', shouldParseChunks(parser, [' <', 'b> .'], ['a', 'b', 'c'])); - it('should parse a single triple', + describe('should parse a single triple', shouldParse(parser, ' .', ['a', 'b', 'c'])); describe('should parse a default graph', @@ -1758,13 +1758,13 @@ describe('Parser', () => { it('should not parse a quad', shouldNotParse(parser, ' .', 'Expected punctuation to follow "http://example.org/c" on line 1.')); - it('allows a blank node in predicate position', + describe('allows a blank node in predicate position', shouldParse(parser, ' [] .', ['a', '_:b0', 'c'])); - it('allows a blank node label in predicate position', + describe('allows a blank node label in predicate position', shouldParse(parser, ' _:b .', ['a', '_:b0_b', 'c'])); - it('allows a blank node with properties in predicate position', + describe('allows a blank node with properties in predicate position', shouldParse(parser, ' [

] .', ['a', '_:b0', 'c'], ['_:b0', 'p', 'o'])); @@ -1796,7 +1796,6 @@ describe('Parser', () => { ['?a', '?b', 'c', '_:b0'], ['d', 'e', '?a', '_:b1'])); - describe('should parse a right implication between two-triple graphs', shouldParse(parser, '{ ?a ?b . . } => { ?a, }.', ['_:b0', 'http://www.w3.org/2000/10/swap/log#implies', '_:b1'], @@ -1872,7 +1871,7 @@ describe('Parser', () => { shouldNotParse(parser, '@forSome ?a.', 'Unexpected var on line 1.')); - it('should correctly scope @forSome statements', + describe('should correctly scope @forSome statements', shouldParse(parser, '@forSome . { @forSome . . }. .', ['_:b0', '_:b0', '_:b1'], ['_:b2', '_:b2', '_:b2', '_:b1'], @@ -1898,7 +1897,7 @@ describe('Parser', () => { shouldNotParse(parser, '@forAll ?a.', 'Unexpected var on line 1.')); - it('should correctly scope @forAll statements', + describe('should correctly scope @forAll statements', shouldParse(parser, '@forAll . { @forAll . . }. .', ['?b0', '?b0', '_:b1'], ['?b2', '?b2', '?b2', '_:b1'], @@ -2127,7 +2126,7 @@ describe('Parser', () => { ['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil', 'urn:n3:quantifiers'], ['a:x', 'b:y', 'a:z'])); - it('should correctly scope @forSome statements', + describe('should correctly scope @forSome statements', shouldParse(parser, '@forSome . { @forSome . . }. .', ['', 'http://www.w3.org/2000/10/swap/reify#forSome', '_:b0', 'urn:n3:quantifiers'], ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'x', 'urn:n3:quantifiers'], From 211bf07ea86f247001698561e910450a88572fa7 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Wed, 4 Jan 2023 15:09:59 +1100 Subject: [PATCH 19/29] fix: dont interpret }| as {| --- src/N3Lexer.js | 11 ++++++----- test/N3Parser-test.js | 6 ++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/N3Lexer.js b/src/N3Lexer.js index 6bfa2c81..97a692d0 100644 --- a/src/N3Lexer.js +++ b/src/N3Lexer.js @@ -294,18 +294,19 @@ export default class N3Lexer { case '!': if (!this._n3Mode) break; + case '{': + // Note the input[0] === '{' is required as this could be a fall-through from the above case + if (input.length > 1 && input[0] === '{' && input[1] === '|') { + type = '{|', matchLength = 2; + break; + } case ',': case ';': case '[': case ']': case '(': case ')': - case '{': case '}': - if (input.length > 1 && input[1] === '|') { - type = '{|', matchLength = 2; - break; - } if ( !this._lineMode && // The token might actually be {| and we just have not encountered the pipe yet diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 1303665c..92bc8970 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -1253,6 +1253,12 @@ describe('Parser', () => { ['a', 'b', 'c'], [['a', 'b', 'c'], 'd', 'e'])); + it('should not parse }|', + shouldNotParse(' }| |} .', 'Unexpected graph closing on line 1.')); + + it('should not parse |{', + shouldNotParse(' {| |{ .', 'Unexpected "|{" on line 1.')); + it('should parse an explicit triple with reified annotation that is chunked at the pipe', shouldParseChunks([' {| |', '} .'], ['a', 'b', 'c'], From 4489772688cc21390e1b804d9d3f42da1fc665a3 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Thu, 5 Jan 2023 12:08:39 +1100 Subject: [PATCH 20/29] Update test/N3Parser-test.js Co-authored-by: Ted Thibodeau Jr --- test/N3Parser-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 92bc8970..52bfea4c 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -2641,8 +2641,8 @@ function splitAllWays(result, left, right, chunkSize) { return result; } -// Return a large number of combinations for splitting the string to test chunking - any thing with 5 characters -// or less will test every permutation of splits possible on the string +// Return a large number of combinations for splitting the string to test chunking - anything with 5 or fewer +// characters will test every permutation of splits possible on the string function getSplits(str) { return splitAllWays([], [], str, Math.max(Math.floor(str.length / 6), 1)); } From c76f3fdf1e489c530d2a658c0c463191d11af69d Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Thu, 5 Jan 2023 12:08:53 +1100 Subject: [PATCH 21/29] Update src/N3Parser.js Co-authored-by: Ted Thibodeau Jr --- src/N3Parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/N3Parser.js b/src/N3Parser.js index 66eb7a07..1dc681fb 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -900,7 +900,7 @@ export default class N3Parser { if (token.type === '{|') { this._saveContext('{|', this._graph, this._subject, this._predicate, this._object); - // As a convention we use set the graph term as the Default Graph in quads representing quoted triples + // As a convention, we set the graph term as the Default Graph in quads representing quoted triples // see https://github.com/rdfjs/N3.js/pull/311#discussion_r1061039556 for details this._subject = this._quad(this._subject, this._predicate, this._object, this.DEFAULTGRAPH); this._predicate = null; From 8a6dcbb853b9008189028869c7f6b483c1154270 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Thu, 5 Jan 2023 12:09:06 +1100 Subject: [PATCH 22/29] Update src/N3Parser.js Co-authored-by: Ted Thibodeau Jr --- src/N3Parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/N3Parser.js b/src/N3Parser.js index 1dc681fb..131b31d8 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -641,7 +641,7 @@ export default class N3Parser { this._saveContext('{|', this._graph, this._subject, this._predicate, this._object); - // As a convention we use set the graph term as the Default Graph in quads representing quoted triples + // As a convention, we set the graph term as the Default Graph in quads representing quoted triples // see https://github.com/rdfjs/N3.js/pull/311#discussion_r1061039556 for details this._subject = this._quad(this._subject, this._predicate, this._object, this.DEFAULTGRAPH); this._predicate = null; From 7df44bc10d3dcd417e35719e39bb627d8fa434c9 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Sat, 25 Feb 2023 15:26:27 +1100 Subject: [PATCH 23/29] chore: document writing rdf-star --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 60886684..e435a599 100644 --- a/README.md +++ b/README.md @@ -206,16 +206,28 @@ const writer2 = new N3.Writer({ format: 'application/trig' }); ```JavaScript const writer = new N3.Writer(process.stdout, { end: false, prefixes: { c: 'http://example.org/cartoons#' } }); -writer.addQuad( +writer.add(quad( namedNode('http://example.org/cartoons#Tom'), namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), namedNode('http://example.org/cartoons#Cat') -); -writer.addQuad(quad( +)); +writer.add(quad( namedNode('http://example.org/cartoons#Tom'), namedNode('http://example.org/cartoons#name'), literal('Tom') )); + +// Writing a quoted rdf-star triple +writer.add(quad( + quad( + namedNode('http://example.org/animals#Elephants'), + namedNode('http://example.org/skinAttribute#colour'), + namedNode('http://example.org/colours#blue'), + ), + namedNode('http://example.org/saidBy'), + namedNode('http://example.org/Jesse') +)); + writer.end(); ``` From 545e646e94dd87b313ab3a6a7a5a22bc65e1d4df Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Sat, 25 Feb 2023 15:54:49 +1100 Subject: [PATCH 24/29] BREAKING CHANGE: enable rdfStar support by default --- src/N3Parser.js | 2 +- test/N3Parser-test.js | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/N3Parser.js b/src/N3Parser.js index 131b31d8..126f7d16 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -28,7 +28,7 @@ export default class N3Parser { // Support triples in other graphs this._supportsQuads = !(isTurtle || isTriG || isNTriples || isN3); // Support nesting of triples - this._supportsRDFStar = format === '' || /star|\*$/.test(format); + this._supportsRDFStar = options.rdfStar !== false; // Disable relative IRIs in N-Triples or N-Quads mode if (isLineMode) this._resolveRelativeIRI = iri => { return null; }; diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 52bfea4c..a4ce8377 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -1470,7 +1470,7 @@ describe('Parser', () => { }); describe('A Parser instance for the Turtle format', () => { - function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'Turtle' }); } + function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'Turtle', rdfStar: false }); } describe('should parse a single triple', shouldParse(parser, ' .', ['a', 'b', 'c'])); @@ -1550,8 +1550,8 @@ describe('Parser', () => { 'Expected >> to follow "_:b0_b" but got IRI on line 1.')); }); - describe('A Parser instance for the TriG format', () => { - function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'TriG' }); } + describe('A Parser instance for the TriG format with rdfStar support disabled', () => { + function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'TriG', rdfStar: false }); } it('should parse a single triple chunked before a closing bracket', shouldParseChunks(parser, [' .'], ['a', 'b', 'c'])); @@ -1604,8 +1604,8 @@ describe('Parser', () => { 'Unexpected RDF-star syntax on line 1.')); }); - describe('A Parser instance for the TriGStar format', () => { - function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'TriGStar' }); } + describe('A Parser instance for the TriGS format testing rdfStar support', () => { + function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'TriG' }); } describe('should parse RDF-star', shouldParse(parser, '<< >> .', @@ -1616,8 +1616,8 @@ describe('Parser', () => { 'Expected >> to follow "_:b0_b" but got IRI on line 1.')); }); - describe('A Parser instance for the N-Triples format', () => { - function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-Triples' }); } + describe('A Parser instance for the N-Triples format with rdfStar support disabled', () => { + function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-Triples', rdfStar: false }); } describe('should parse a single triple', shouldParse(parser, '_:a "c".', @@ -1679,8 +1679,8 @@ describe('Parser', () => { 'Unexpected RDF-star syntax on line 1.')); }); - describe('A Parser instance for the N-TriplesStar format', () => { - function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-TriplesStar' }); } + describe('A Parser instance for the N-Triples format to test rdfStar support', () => { + function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-Triples' }); } describe('should parse RDF-star', shouldParse(parser, '<<_:a _:c>> _:b .', @@ -1691,8 +1691,8 @@ describe('Parser', () => { 'Expected >> to follow "_:b0_b" but got IRI on line 1.')); }); - describe('A Parser instance for the N-Quads format', () => { - function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-Quads' }); } + describe('A Parser instance for the N-Quads format with rdfStar support disabled', () => { + function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N-Quads', rdfStar: false }); } describe('should parse a single triple', shouldParse(parser, '_:a "c".', @@ -1746,8 +1746,8 @@ describe('Parser', () => { [['_:b0_a', 'b', '_:b0_c'], 'a', '_:b0_c'])); }); - describe('A Parser instance for the N3 format', () => { - function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N3' }); } + describe('A Parser instance for the N3 format with rdfStar support disabled', () => { + function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N3', rdfStar: false }); } describe('should parse a single triple', shouldParse(parser, ' .', ['a', 'b', 'c'])); @@ -2099,8 +2099,8 @@ describe('Parser', () => { 'Unexpected RDF-star syntax on line 1.')); }); - describe('A Parser instance for the N3Star format', () => { - function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N3Star' }); } + describe('A Parser instance for the N3 format testing rdfStar support', () => { + function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N3' }); } describe('should parse RDF-star', shouldParse(parser, '<< >> .', From 90e15b9476698b16a0a1e290ac5117201be7c7b0 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Sat, 25 Feb 2023 15:56:18 +1100 Subject: [PATCH 25/29] chore: update docs to not rdfStar default --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index e435a599..3eb3b824 100644 --- a/README.md +++ b/README.md @@ -377,9 +377,7 @@ The default mode is permissive and allows a mixture of different syntaxes, including RDF-star. Pass a `format` option to the constructor with the name or MIME type of a format for strict, fault-intolerant behavior. -If a format string contains `star` or `*` -(e.g., `turtlestar` or `TriG*`), -RDF-star support for that format will be enabled. +To disable RDF-star support pass `rdfStar` to `false` in the constructor. ### Interface specifications The N3.js submodules are compatible with the following [RDF.js](http://rdf.js.org) interfaces: From 6907296c805f0e84c9109e1f5bfadec2270e1c6e Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Sat, 25 Feb 2023 16:13:57 +1100 Subject: [PATCH 26/29] chore: refactor lexer --- src/N3Lexer.js | 32 +++++++++++++++++--------------- src/N3Parser.js | 2 +- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/N3Lexer.js b/src/N3Lexer.js index 97a692d0..cefe773a 100644 --- a/src/N3Lexer.js +++ b/src/N3Lexer.js @@ -53,6 +53,8 @@ export default class N3Lexer { this._endOfFile = /^(?:#[^\n\r]*)?$/; options = options || {}; + this._supportsRDFStar = options.rdfStar; + // In line mode (N-Triples or N-Quads), only simple features may be parsed if (this._lineMode = !!options.lineMode) { this._n3Mode = false; @@ -290,16 +292,22 @@ export default class N3Lexer { matchLength = 2, value = '>'; } break; - + case '{': + // We can properly evaluate this case if we are + // not in rdfStar mode or we can look ahead to + // see if there is a pipe following the { + if (input.length > 1 || !this._supportsRDFStar) { + if (input[1] === '|') { + type = '{|', matchLength = 2; + } + else if (!this._lineMode) { + matchLength = 1, type = firstChar; + } + } + break; case '!': if (!this._n3Mode) break; - case '{': - // Note the input[0] === '{' is required as this could be a fall-through from the above case - if (input.length > 1 && input[0] === '{' && input[1] === '|') { - type = '{|', matchLength = 2; - break; - } case ',': case ';': case '[': @@ -307,14 +315,8 @@ export default class N3Lexer { case '(': case ')': case '}': - if ( - !this._lineMode && - // The token might actually be {| and we just have not encountered the pipe yet - (input !== '{' || input.length > 1) - ) { - matchLength = 1; - type = firstChar; - } + matchLength = 1; + type = firstChar; break; case '|': if (input.length > 1 && input[1] === '}') { diff --git a/src/N3Parser.js b/src/N3Parser.js index 126f7d16..1aa32947 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -34,7 +34,7 @@ export default class N3Parser { this._resolveRelativeIRI = iri => { return null; }; this._blankNodePrefix = typeof options.blankNodePrefix !== 'string' ? '' : options.blankNodePrefix.replace(/^(?!_:)/, '_:'); - this._lexer = options.lexer || new N3Lexer({ lineMode: isLineMode, n3: isN3 }); + this._lexer = options.lexer || new N3Lexer({ lineMode: isLineMode, n3: isN3, rdfStar: this._supportsRDFStar }); // Disable explicit quantifiers by default this._explicitQuantifiers = !!options.explicitQuantifiers; } From 73791d82add83dbb1fdf5ce04915d31090daa94f Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Sun, 26 Feb 2023 11:54:26 +1100 Subject: [PATCH 27/29] fix: re-enable line mode check --- src/N3Lexer.js | 6 ++++-- test/N3Parser-test.js | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/N3Lexer.js b/src/N3Lexer.js index cefe773a..6ae7b559 100644 --- a/src/N3Lexer.js +++ b/src/N3Lexer.js @@ -315,8 +315,10 @@ export default class N3Lexer { case '(': case ')': case '}': - matchLength = 1; - type = firstChar; + if (!this._lineMode) { + matchLength = 1; + type = firstChar; + } break; case '|': if (input.length > 1 && input[1] === '}') { diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index a4ce8377..2166077e 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -1670,6 +1670,9 @@ describe('Parser', () => { it('should not parse @forAll', shouldNotParse(parser, '@forAll .', 'Unexpected "@forAll" on line 1.')); + it('should not parse an object list', + shouldNotParse(parser, ' , .', 'Invalid IRI on line 1.')); + it('should not parse RDF-star in the subject position', shouldNotParse(parser, '<< >> .', 'Unexpected RDF-star syntax on line 1.')); From 8e3b997e144f38e0b49540243c748cfacd45ab0c Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Sun, 26 Feb 2023 12:07:04 +1100 Subject: [PATCH 28/29] chore: refactor lexer --- src/N3Lexer.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/N3Lexer.js b/src/N3Lexer.js index 6ae7b559..64080dee 100644 --- a/src/N3Lexer.js +++ b/src/N3Lexer.js @@ -296,14 +296,11 @@ export default class N3Lexer { // We can properly evaluate this case if we are // not in rdfStar mode or we can look ahead to // see if there is a pipe following the { - if (input.length > 1 || !this._supportsRDFStar) { - if (input[1] === '|') { - type = '{|', matchLength = 2; - } - else if (!this._lineMode) { - matchLength = 1, type = firstChar; - } - } + const long = input.length !== 1; + if (long && input[1] === '|') + matchLength = 2, type = '{|'; + else if (!this._lineMode && (long || !this._supportsRDFStar)) + matchLength = 1, type = firstChar; break; case '!': if (!this._n3Mode) From 2e655fba7fef05973102d45b8c352e662751cf72 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:04:55 +1100 Subject: [PATCH 29/29] fix: make rdf-star work with n3 paths --- src/N3Parser.js | 6 +++++- test/N3Parser-test.js | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/N3Parser.js b/src/N3Parser.js index 1aa32947..9dcd5c4c 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -886,7 +886,11 @@ export default class N3Parser { // If the triple was the subject, continue by reading the predicate. if (this._subject === null) { this._subject = quad; - return this._readPredicate; + // In N3 mode, the subject might be a path + if (this._n3Mode) + return this._getPathReader(this._readPredicateOrNamedGraph); + else + return this._readPredicate; } // If the triple was the object, read context end. else { diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 2166077e..85c5c474 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -2105,6 +2105,14 @@ describe('Parser', () => { describe('A Parser instance for the N3 format testing rdfStar support', () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N3' }); } + describe('should parse RDF-star path', + shouldParse(parser, '<< >>! .', + [['a', 'b', 'c'], 'p1', '_:b0'], ['_:b0', 'p2', 'o'])); + + describe('should parse RDF-star path', + shouldParse(parser, '<< >>!^ .', + [['a', 'b', 'c'], 'p1', '_:b0'], ['_:b1', 'p2', '_:b0'], ['_:b1', 'p3', 'o'])); + describe('should parse RDF-star', shouldParse(parser, '<< >> .', [['a', 'b', 'c'], 'a', 'b']));