diff --git a/src/N3Parser.js b/src/N3Parser.js index 7537661b..a2dd92c1 100644 --- a/src/N3Parser.js +++ b/src/N3Parser.js @@ -662,13 +662,35 @@ export default class N3Parser { const graph = this._graph; + let closingEmptyGraph = false; + // Store the last quad of the formula - if (this._subject !== null) - this._emit(this._subject, this._predicate, this._object, graph); + + if (this._subject !== null) { + // Catch the empty graph being closed when parsing N3. + // In this case, we emit the empty graph as the value "true"^^xsd:boolean + // The predicate value is set to 'null' in the _readSubject function, + // after which the graph is closed directly when processing an empty graph. + if (!this._predicate) { + closingEmptyGraph = true; + } + else { + this._emit(this._subject, this._predicate, this._object, graph); + } + } // Restore the parent context containing this formula this._restoreContext('formula', token); + if (closingEmptyGraph) { + const xsdBoolean = 'http://www.w3.org/2001/XMLSchema#boolean'; + if (this._subject && this._subject.equals(graph)) + this._subject = this._literal('true', this._namedNode(xsdBoolean)); + + if (this._object && this._object.equals(graph)) + this._object = this._literal('true', this._namedNode(xsdBoolean)); + } + // If the formula was in a list context, continue reading the list if (this._contextStack.length > 0 && this._contextStack[this._contextStack.length - 1].type === 'list') { return this._readListItem(token, graph); diff --git a/test/N3Parser-test.js b/test/N3Parser-test.js index 5a5eca47..dbfbbcb2 100644 --- a/test/N3Parser-test.js +++ b/test/N3Parser-test.js @@ -1763,7 +1763,7 @@ describe('Parser', () => { return isImpliedBy ? [to, 'http://www.w3.org/2000/10/swap/log#isImpliedBy', from] : [from, 'http://www.w3.org/2000/10/swap/log#implies', to]; } - describe(`A Parser instance for the N3 format with rdfStar support disabled and with ${isImpliedBy ? 'enabled' : 'disabled'}`, () => { + describe(`A Parser instance for the N3 format with rdfStar support disabled and with isImpliedBy ${isImpliedBy ? 'enabled' : 'disabled'}`, () => { function parser() { return new Parser({ baseIRI: BASE_IRI, format: 'N3', rdfStar: false, isImpliedBy }); } describe('should parse a single triple', @@ -2337,6 +2337,28 @@ describe('Parser', () => { it('should not parse RDF-star in the object position', shouldNotParse(parser, ' << >>.', 'Unexpected RDF-star syntax on line 1.')); + + describe('should parse the empty graph as an rdf:value of "true"^^xsd:boolean in the object position', + shouldParse(parser, ' {}.', + ['a', 'b', '"true"^^http://www.w3.org/2001/XMLSchema#boolean'] + )); + + describe('should parse the empty graph as an rdf:value of "true"^^xsd:boolean in the subject position', + shouldParse(parser, '{} .', + ['"true"^^http://www.w3.org/2001/XMLSchema#boolean', 'b', 'c'] + )); + + describe('should parse the empty graph in the object position as an rdf:value of "true"^^xsd:boolean while retaining the encompassing graph', + shouldParse(parser, ' { {} }.', + ['a', 'b', '_:b0'], + ['x', 'y', '"true"^^http://www.w3.org/2001/XMLSchema#boolean', '_:b0'] + )); + + describe('should parse the empty graph in the subject position as an rdf:value of "true"^^xsd:boolean while retaining the encompassing graph', + shouldParse(parser, ' { {} }.', + ['a', 'b', '_:b0'], + ['"true"^^http://www.w3.org/2001/XMLSchema#boolean', 'y', 'z', '_:b0'] + )); }); }