Skip to content

Commit cf1511a

Browse files
committed
Fix typed/lang-tagged literals in lists.
Fixes #93.
1 parent 67ccc60 commit cf1511a

File tree

2 files changed

+77
-35
lines changed

2 files changed

+77
-35
lines changed

lib/N3Parser.js

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -356,39 +356,15 @@ N3Parser.prototype = {
356356
return this._readPredicate(token);
357357
},
358358

359-
// ### `_readDataTypeOrLang` reads an _optional_ data type or language
360-
_readDataTypeOrLang: function (token) {
361-
// Determine the suffix of the literal
362-
var suffix;
363-
switch (token.type) {
364-
// Add a "^^type" suffix for types (IRIs and blank nodes)
365-
case 'type':
366-
case 'typeIRI':
367-
suffix = '^^' + this._readEntity(token);
368-
break;
369-
// Add a "@lang" suffix for languages
370-
case 'langcode':
371-
suffix = '@' + token.value.toLowerCase();
372-
break;
373-
// If no datatype or language present, read the end of the triple
374-
default:
375-
this._readCallback = this._getTripleEndReader();
376-
return this._readCallback(token);
377-
}
378-
379-
// Add the suffix and read the end of the triple
380-
this._object += suffix;
381-
return this._getTripleEndReader();
382-
},
383-
384359
// ### `_readListItem` reads items from a list
385360
_readListItem: function (token) {
386361
var item = null, // The item of the list
387362
list = null, // The list itself
388363
prevList = this._subject, // The previous list that contains this list
389364
stack = this._contextStack, // The stack of parent contexts
390365
parent = stack[stack.length - 1], // The parent containing the current list
391-
next = this._readListItem; // The next function to execute
366+
next = this._readListItem, // The next function to execute
367+
itemComplete = true; // Whether the item has been read fully
392368

393369
switch (token.type) {
394370
case '[':
@@ -430,7 +406,8 @@ N3Parser.prototype = {
430406
break;
431407
case 'literal':
432408
item = token.value;
433-
next = this._readDataTypeOrLang;
409+
itemComplete = false; // Can still have a datatype or language
410+
next = this._readListItemDataTypeOrLang;
434411
break;
435412
default:
436413
if ((item = this._readEntity(token)) === undefined)
@@ -463,12 +440,55 @@ N3Parser.prototype = {
463440
// _readPath will restore the context and output the item
464441
return this._getPathReader(this._readListItem);
465442
}
466-
// Output the item
467-
this._triple(list, RDF_FIRST, item, this._graph);
443+
// Output the item if it is complete
444+
if (itemComplete)
445+
this._triple(list, RDF_FIRST, item, this._graph);
446+
// Otherwise, save it for completion
447+
else
448+
this._object = item;
468449
}
469450
return next;
470451
},
471452

453+
// ### `_readDataTypeOrLang` reads an _optional_ data type or language
454+
_readDataTypeOrLang: function (token) {
455+
return this._completeLiteral(token, false);
456+
},
457+
458+
// ### `_readListItemDataTypeOrLang` reads an _optional_ data type or language in a list
459+
_readListItemDataTypeOrLang: function (token) {
460+
return this._completeLiteral(token, true);
461+
},
462+
463+
// ### `_completeLiteral` completes the object with a data type or language
464+
_completeLiteral: function (token, listItem) {
465+
var suffix = false;
466+
switch (token.type) {
467+
// Add a "^^type" suffix for types (IRIs and blank nodes)
468+
case 'type':
469+
case 'typeIRI':
470+
suffix = true;
471+
this._object += '^^' + this._readEntity(token);
472+
break;
473+
// Add an "@lang" suffix for language tags
474+
case 'langcode':
475+
suffix = true;
476+
this._object += '@' + token.value.toLowerCase();
477+
break;
478+
}
479+
// If this literal was part of a list, write the item
480+
// (we could also check the context stack, but passing in a flag is faster)
481+
if (listItem)
482+
this._triple(this._subject, RDF_FIRST, this._object, this._graph);
483+
// Continue with the rest of the input
484+
if (suffix)
485+
return this._getTripleEndReader();
486+
else {
487+
this._readCallback = this._getTripleEndReader();
488+
return this._readCallback(token);
489+
}
490+
},
491+
472492
// ### `_readFormulaTail` reads the end of a formula
473493
_readFormulaTail: function (token) {
474494
if (token.type !== '}')

test/N3Parser-test.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,24 @@ describe('N3Parser', function () {
325325
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'x'],
326326
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil']));
327327

328+
it('should parse a list with a literal',
329+
shouldParse('<a> <b> ("x").',
330+
['a', 'b', '_:b0'],
331+
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"x"'],
332+
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil']));
333+
334+
it('should parse a list with a typed literal',
335+
shouldParse('<a> <b> ("x"^^<y>).',
336+
['a', 'b', '_:b0'],
337+
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"x"^^y'],
338+
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil']));
339+
340+
it('should parse a list with a language-tagged literal',
341+
shouldParse('<a> <b> ("x"@en-GB).',
342+
['a', 'b', '_:b0'],
343+
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"x"@en-gb'],
344+
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil']));
345+
328346
it('should parse statements with a multi-element list in the subject',
329347
shouldParse('(<x> <y>) <a> <b>.',
330348
['_:b0', 'a', 'b'],
@@ -341,6 +359,16 @@ describe('N3Parser', function () {
341359
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'y'],
342360
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil']));
343361

362+
it('should parse statements with a multi-element literal list in the object',
363+
shouldParse('<a> <b> ("x" "y"@en-GB "z"^^<t>).',
364+
['a', 'b', '_:b0'],
365+
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"x"'],
366+
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1'],
367+
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"y"@en-gb'],
368+
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b2'],
369+
['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"z"^^t'],
370+
['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil']));
371+
344372
it('should parse statements with prefixed names in lists',
345373
shouldParse('@prefix a: <a#>. <a> <b> (a:x a:y).',
346374
['a', 'b', '_:b0'],
@@ -361,12 +389,6 @@ describe('N3Parser', function () {
361389
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b0_y'],
362390
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil']));
363391

364-
it('should parse statements with a list containing strings',
365-
shouldParse('("y") <a> <b>.',
366-
['_:b0', 'a', 'b'],
367-
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '"y"'],
368-
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil']));
369-
370392
it('should parse statements with a nested empty list',
371393
shouldParse('<a> <b> (<x> ()).',
372394
['a', 'b', '_:b0'],

0 commit comments

Comments
 (0)