Skip to content

Commit 3016dbd

Browse files
Merge pull request #212 from simonvbrae/feature/rdfstar_parsing
Added parsing for RDF* syntax
2 parents 2ed11b7 + f785a36 commit 3016dbd

File tree

4 files changed

+507
-16
lines changed

4 files changed

+507
-16
lines changed

src/N3Lexer.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ export default class N3Lexer {
3939
this._simpleApostropheString = /^'([^'\\\r\n]*)'(?=[^'])/;
4040
this._langcode = /^@([a-z]+(?:-[a-z0-9]+)*)(?=[^a-z0-9\-])/i;
4141
this._prefix = /^((?:[A-Za-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:\.?[\-0-9A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)?:(?=[#\s<])/;
42-
this._prefixed = /^((?:[A-Za-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:\.?[\-0-9A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)?:((?:(?:[0-:A-Z_a-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff]|%[0-9a-fA-F]{2}|\\[!#-\/;=?\-@_~])(?:(?:[\.\-0-:A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff]|%[0-9a-fA-F]{2}|\\[!#-\/;=?\-@_~])*(?:[\-0-:A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff]|%[0-9a-fA-F]{2}|\\[!#-\/;=?\-@_~]))?)?)(?:[ \t]+|(?=\.?[,;!\^\s#()\[\]\{\}"'<]))/;
42+
this._prefixed = /^((?:[A-Za-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:\.?[\-0-9A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)?:((?:(?:[0-:A-Z_a-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff]|%[0-9a-fA-F]{2}|\\[!#-\/;=?\-@_~])(?:(?:[\.\-0-:A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff]|%[0-9a-fA-F]{2}|\\[!#-\/;=?\-@_~])*(?:[\-0-:A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff]|%[0-9a-fA-F]{2}|\\[!#-\/;=?\-@_~]))?)?)(?:[ \t]+|(?=\.?[,;!\^\s#()\[\]\{\}"'<>]))/;
4343
this._variable = /^\?(?:(?:[A-Z_a-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:[\-0-:A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)(?=[.,;!\^\s#()\[\]\{\}"'<])/;
44-
this._blank = /^_:((?:[0-9A-Z_a-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:\.?[\-0-9A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)(?:[ \t]+|(?=\.?[,;:\s#()\[\]\{\}"'<]))/;
44+
this._blank = /^_:((?:[0-9A-Z_a-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:\.?[\-0-9A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)(?:[ \t]+|(?=\.?[,;:\s#()\[\]\{\}"'<>]))/;
4545
this._number = /^[\-+]?(?:(\d+\.\d*|\.?\d+)[eE][\-+]?|\d*(\.)?)\d+(?=\.?[,;:\s#()\[\]\{\}"'<])/;
4646
this._boolean = /^(?:true|false)(?=[.,;\s#()\[\]\{\}"'<])/;
4747
this._keyword = /^@[a-z]+(?=[\s#<:])/i;
@@ -143,11 +143,19 @@ export default class N3Lexer {
143143
return reportSyntaxError(this);
144144
type = 'IRI';
145145
}
146+
// Try to find a nested triple
147+
else if (input.length > 1 && input[1] === '<')
148+
type = '<<', matchLength = 2;
146149
// Try to find a backwards implication arrow
147150
else if (this._n3Mode && input.length > 1 && input[1] === '=')
148151
type = 'inverse', matchLength = 2, value = '>';
149152
break;
150153

154+
case '>':
155+
if (input.length > 1 && input[1] === '>')
156+
type = '>>', matchLength = 2;
157+
break;
158+
151159
case '_':
152160
// Try to find a blank node. Since it can contain (but not end with) a dot,
153161
// we always need a non-dot character before deciding it is a blank node.

src/N3Parser.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@ export default class N3Parser {
1919
// Set supported features depending on the format
2020
var format = (typeof options.format === 'string') ?
2121
options.format.match(/\w*$/)[0].toLowerCase() : '',
22-
isTurtle = format === 'turtle', isTriG = format === 'trig',
22+
isTurtle = /turtle/.test(format), isTriG = /trig/.test(format),
2323
isNTriples = /triple/.test(format), isNQuads = /quad/.test(format),
2424
isN3 = this._n3Mode = /n3/.test(format),
2525
isLineMode = isNTriples || isNQuads;
2626
if (!(this._supportsNamedGraphs = !(isTurtle || isN3)))
2727
this._readPredicateOrNamedGraph = this._readPredicate;
28+
// Support triples in other graphs
2829
this._supportsQuads = !(isTurtle || isTriG || isNTriples || isN3);
30+
// Support nesting of triples
31+
this._supportsRDFStar = format === '' || /star|\*$/.test(format);
2932
// Disable relative IRIs in N-Triples or N-Quads mode
3033
if (isLineMode)
3134
this._resolveRelativeIRI = function (iri) { return null; };
@@ -228,6 +231,12 @@ export default class N3Parser {
228231
this._subject = this._literal(token.value, this._namedNode(token.prefix));
229232

230233
break;
234+
case '<<':
235+
if (!this._supportsRDFStar)
236+
return this._error('Unexpected RDF* syntax', token);
237+
this._saveContext('<<', this._graph, null, null, null);
238+
this._graph = null;
239+
return this._readSubject;
231240
default:
232241
// Read the subject entity
233242
if ((this._subject = this._readEntity(token)) === undefined)
@@ -304,6 +313,12 @@ export default class N3Parser {
304313
this._saveContext('formula', this._graph, this._subject, this._predicate,
305314
this._graph = this._blankNode());
306315
return this._readSubject;
316+
case '<<':
317+
if (!this._supportsRDFStar)
318+
return this._error('Unexpected RDF* syntax', token);
319+
this._saveContext('<<', this._graph, this._subject, this._predicate, null);
320+
this._graph = null;
321+
return this._readSubject;
307322
default:
308323
// Read the object entity
309324
if ((this._object = this._readEntity(token)) === undefined)
@@ -802,6 +817,37 @@ export default class N3Parser {
802817
return this._readPath;
803818
}
804819

820+
// ### `_readRDFStarTailOrGraph` reads the graph of a nested RDF* quad or the end of a nested RDF* triple
821+
_readRDFStarTailOrGraph(token) {
822+
if (token.type !== '>>') {
823+
// An entity means this is a quad (only allowed if not already inside a graph)
824+
if (this._supportsQuads && this._graph === null && (this._graph = this._readEntity(token)) !== undefined)
825+
return this._readRDFStarTail;
826+
return this._error('Expected >> to follow "' + this._object.id + '"', token);
827+
}
828+
return this._readRDFStarTail(token);
829+
}
830+
831+
// ### `_readRDFStarTail` reads the end of a nested RDF* triple
832+
_readRDFStarTail(token) {
833+
if (token.type !== '>>')
834+
return this._error(`Expected >> but got ${token.type}`, token);
835+
// Read the quad and restore the previous context
836+
const quad = this._quad(this._subject, this._predicate, this._object,
837+
this._graph || this.DEFAULTGRAPH);
838+
this._restoreContext();
839+
// If the triple was the subject, continue by reading the predicate.
840+
if (this._subject === null) {
841+
this._subject = quad;
842+
return this._readPredicate;
843+
}
844+
// If the triple was the object, read context end.
845+
else {
846+
this._object = quad;
847+
return this._getContextEndReader();
848+
}
849+
}
850+
805851
// ### `_getContextEndReader` gets the next reader function at the end of a context
806852
_getContextEndReader() {
807853
var contextStack = this._contextStack;
@@ -815,6 +861,8 @@ export default class N3Parser {
815861
return this._readListItem;
816862
case 'formula':
817863
return this._readFormulaTail;
864+
case '<<':
865+
return this._readRDFStarTailOrGraph;
818866
}
819867
}
820868

test/N3Lexer-test.js

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ describe('Lexer', function () {
2828
{ type: 'IRI', value: 'http://ex.org/?bla#foo', line: 1 },
2929
{ type: 'eof', line: 1 }));
3030

31+
it('should tokenize a split IRI',
32+
shouldTokenize(streamOf('<', 'http://ex.org/?bla#foo>'),
33+
{ type: 'IRI', value: 'http://ex.org/?bla#foo', line: 1 },
34+
{ type: 'eof', line: 1 }));
35+
3136
it('should not tokenize an IRI with disallowed characters',
3237
shouldNotTokenize('<http://ex.org/bla"foo>',
3338
'Unexpected "<http://ex.org/bla"foo>" on line 1.'));
@@ -788,6 +793,13 @@ describe('Lexer', function () {
788793
{ type: 'IRI', value: 'b', line: 1 },
789794
{ type: 'eof', line: 1 }));
790795

796+
it('should tokenize a split left implication',
797+
shouldTokenize(streamOf('<a> <', '= <b> '),
798+
{ type: 'IRI', value: 'a', line: 1 },
799+
{ type: 'inverse', value: '>', line: 1 },
800+
{ type: 'IRI', value: 'b', line: 1 },
801+
{ type: 'eof', line: 1 }));
802+
791803
it('should tokenize paths',
792804
shouldTokenize(':joe!fam:mother!loc:office!loc:zip :joe!fam:mother^fam:mother',
793805
{ type: 'prefixed', prefix: '', value: 'joe', line: 1 },
@@ -812,6 +824,220 @@ describe('Lexer', function () {
812824
new Lexer().tokenize({ on: function () {} });
813825
});
814826

827+
it('should tokenize an Quadterm start',
828+
shouldTokenize('<<',
829+
{ type: '<<', line: 1 }, { type: 'eof', line: 1 }));
830+
831+
it('should tokenize a split Quadterm start',
832+
shouldTokenize(streamOf('<', '<'),
833+
{ type: '<<', line: 1 }, { type: 'eof', line: 1 }));
834+
835+
it('should tokenize an Quadterm end',
836+
shouldTokenize('>>',
837+
{ type: '>>', line: 1 }, { type: 'eof', line: 1 }));
838+
839+
it('should tokenize an empty Quadterm',
840+
shouldTokenize('<< >>',
841+
{ type: '<<', line: 1 },
842+
{ type: '>>', line: 1 },
843+
{ type: 'eof', line: 1 }));
844+
845+
it('should tokenize an RDF* statement with IRIs',
846+
shouldTokenize('<<<http://ex.org/?bla#foo> \n\t<http://ex.org/?bla#bar> \n\t<http://ex.org/?bla#boo>>> .',
847+
{ type: '<<', line: 1 },
848+
{ type: 'IRI', value: 'http://ex.org/?bla#foo', line: 1 },
849+
{ type: 'IRI', value: 'http://ex.org/?bla#bar', line: 2 },
850+
{ type: 'IRI', value: 'http://ex.org/?bla#boo', line: 3 },
851+
{ type: '>>', line: 3 },
852+
{ type: '.', line: 3 },
853+
{ type: 'eof', line: 3 }));
854+
855+
it('should not tokenize a wrongly closed RDF* statement with IRIs',
856+
shouldNotTokenize('<<<http://ex.org/?bla#foo> \n\t<http://ex.org/?bla#bar> \n\t<http://ex.org/?bla#boo>> .',
857+
'Unexpected ">" on line 3.'));
858+
859+
it('should tokenize a split RDF* statement with IRIs',
860+
shouldTokenize(streamOf('<', '<<http://ex.org/?bla#foo> \n\t<http://ex.org/?bla#bar> \n\t<http://ex.org/?bla#boo>>> .'),
861+
{ type: '<<', line: 1 },
862+
{ type: 'IRI', value: 'http://ex.org/?bla#foo', line: 1 },
863+
{ type: 'IRI', value: 'http://ex.org/?bla#bar', line: 2 },
864+
{ type: 'IRI', value: 'http://ex.org/?bla#boo', line: 3 },
865+
{ type: '>>', line: 3 },
866+
{ type: '.', line: 3 },
867+
{ type: 'eof', line: 3 }));
868+
869+
it('should tokenize an RDF* statement with literals',
870+
shouldTokenize('<<"string"@en "string"@nl-be "string"@EN>> .',
871+
{ type: '<<', line: 1 },
872+
{ type: 'literal', value: 'string', line: 1 },
873+
{ type: 'langcode', value: 'en', line: 1 },
874+
{ type: 'literal', value: 'string', line: 1 },
875+
{ type: 'langcode', value: 'nl-be', line: 1 },
876+
{ type: 'literal', value: 'string', line: 1 },
877+
{ type: 'langcode', value: 'EN', line: 1 },
878+
{ type: '>>', line: 1 },
879+
{ type: '.', line: 1 },
880+
{ type: 'eof', line: 1 }));
881+
882+
it('should tokenize a prefixed iri followed by the end of a QuadTerm',
883+
shouldTokenize('c:c>> .',
884+
{ type: 'prefixed', prefix: 'c', value: 'c', line: 1 },
885+
{ type: '>>', line: 1 },
886+
{ type: '.', line: 1 },
887+
{ type: 'eof', line: 1 }));
888+
889+
it('should tokenize an RDF* statement with prefixed names',
890+
shouldTokenize('<<a:a b:b c:c>> .',
891+
{ type: '<<', line: 1 },
892+
{ type: 'prefixed', prefix: 'a', value: 'a', line: 1 },
893+
{ type: 'prefixed', prefix: 'b', value: 'b', line: 1 },
894+
{ type: 'prefixed', prefix: 'c', value: 'c', line: 1 },
895+
{ type: '>>', line: 1 },
896+
{ type: '.', line: 1 },
897+
{ type: 'eof', line: 1 }));
898+
899+
it('should tokenize an RDF* statement with blank nodes',
900+
shouldTokenize('<<_:a _:b _:c>> .',
901+
{ type: '<<', line: 1 },
902+
{ type: 'blank', prefix: '_', value: 'a', line: 1 },
903+
{ type: 'blank', prefix: '_', value: 'b', line: 1 },
904+
{ type: 'blank', prefix: '_', value: 'c', line: 1 },
905+
{ type: '>>', line: 1 },
906+
{ type: '.', line: 1 },
907+
{ type: 'eof', line: 1 }));
908+
909+
it('should tokenize an RDF* statement with mixed types',
910+
shouldTokenize('<<<http://ex.org/?bla#foo> "string"@nl-be c:c>> .',
911+
{ type: '<<', line: 1 },
912+
{ type: 'IRI', value: 'http://ex.org/?bla#foo', line: 1 },
913+
{ type: 'literal', value: 'string', line: 1 },
914+
{ type: 'langcode', value: 'nl-be', line: 1 },
915+
{ type: 'prefixed', prefix: 'c', value: 'c', line: 1 },
916+
{ type: '>>', line: 1 },
917+
{ type: '.', line: 1 },
918+
{ type: 'eof', line: 1 }));
919+
920+
it('should tokenize an RDF* statement with mixed types',
921+
shouldTokenize('<<_:a a:a "string"@EN>> .',
922+
{ type: '<<', line: 1 },
923+
{ type: 'blank', prefix: '_', value: 'a', line: 1 },
924+
{ type: 'prefixed', prefix: 'a', value: 'a', line: 1 },
925+
{ type: 'literal', value: 'string', line: 1 },
926+
{ type: 'langcode', value: 'EN', line: 1 },
927+
{ type: '>>', line: 1 },
928+
{ type: '.', line: 1 },
929+
{ type: 'eof', line: 1 }));
930+
931+
it('should tokenize an RDF* statement with mixed types',
932+
shouldTokenize('<<"literal"@AU <http://ex.org/?bla#foo> _:a>> .',
933+
{ type: '<<', line: 1 },
934+
{ type: 'literal', value: 'literal', line: 1 },
935+
{ type: 'langcode', value: 'AU', line: 1 },
936+
{ type: 'IRI', value: 'http://ex.org/?bla#foo', line: 1 },
937+
{ type: 'blank', prefix: '_', value: 'a', line: 1 },
938+
{ type: '>>', line: 1 },
939+
{ type: '.', line: 1 },
940+
{ type: 'eof', line: 1 }));
941+
942+
it('should tokenize RDF* statements with shared subjects',
943+
shouldTokenize('<<<a> <b> <c>;\n<d> <e>>>.',
944+
{ type: '<<', line: 1 },
945+
{ type: 'IRI', value: 'a', line: 1 },
946+
{ type: 'IRI', value: 'b', line: 1 },
947+
{ type: 'IRI', value: 'c', line: 1 },
948+
{ type: ';', line: 1 },
949+
{ type: 'IRI', value: 'd', line: 2 },
950+
{ type: 'IRI', value: 'e', line: 2 },
951+
{ type: '>>', line: 2 },
952+
{ type: '.', line: 2 },
953+
{ type: 'eof', line: 2 }));
954+
955+
it('should tokenize RDF* statements with shared subjects and predicates',
956+
shouldTokenize('<<<a> <b> <c>,\n<d>>>.',
957+
{ type: '<<', line: 1 },
958+
{ type: 'IRI', value: 'a', line: 1 },
959+
{ type: 'IRI', value: 'b', line: 1 },
960+
{ type: 'IRI', value: 'c', line: 1 },
961+
{ type: ',', line: 1 },
962+
{ type: 'IRI', value: 'd', line: 2 },
963+
{ type: '>>', line: 2 },
964+
{ type: '.', line: 2 },
965+
{ type: 'eof', line: 2 }));
966+
967+
it('should tokenize an RDF* statement with shared subjects and predicates and prefixed names',
968+
shouldTokenize('<<a:a b:b c:c;d:d e:e,f:f>> .',
969+
{ type: '<<', line: 1 },
970+
{ type: 'prefixed', prefix: 'a', value: 'a', line: 1 },
971+
{ type: 'prefixed', prefix: 'b', value: 'b', line: 1 },
972+
{ type: 'prefixed', prefix: 'c', value: 'c', line: 1 },
973+
{ type: ';', line: 1 },
974+
{ type: 'prefixed', prefix: 'd', value: 'd', line: 1 },
975+
{ type: 'prefixed', prefix: 'e', value: 'e', line: 1 },
976+
{ type: ',', line: 1 },
977+
{ type: 'prefixed', prefix: 'f', value: 'f', line: 1 },
978+
{ type: '>>', line: 1 },
979+
{ type: '.', line: 1 },
980+
{ type: 'eof', line: 1 }));
981+
982+
it('should tokenize a QuadTerm followed by other tokens',
983+
shouldTokenize('<<_:a <b> "lit"@EN>> _:a b:b.',
984+
{ type: '<<', line: 1 },
985+
{ type: 'blank', prefix: '_', value: 'a', line: 1 },
986+
{ type: 'IRI', value: 'b', line: 1 },
987+
{ type: 'literal', value: 'lit', line: 1 },
988+
{ type: 'langcode', value: 'EN', line: 1 },
989+
{ type: '>>', line: 1 },
990+
{ type: 'blank', prefix: '_', value: 'a', line: 1 },
991+
{ type: 'prefixed', prefix: 'b', value: 'b', line: 1 },
992+
{ type: '.', line: 1 },
993+
{ type: 'eof', line: 1 }));
994+
995+
it('should tokenize a QuadTerm preceded by other tokens',
996+
shouldTokenize('"lit"@DE _:b <<_:a b:b "lit"@EN>>.',
997+
{ type: 'literal', value: 'lit', line: 1 },
998+
{ type: 'langcode', value: 'DE', line: 1 },
999+
{ type: 'blank', prefix: '_', value: 'b', line: 1 },
1000+
{ type: '<<', line: 1 },
1001+
{ type: 'blank', prefix: '_', value: 'a', line: 1 },
1002+
{ type: 'prefixed', prefix: 'b', value: 'b', line: 1 },
1003+
{ type: 'literal', value: 'lit', line: 1 },
1004+
{ type: 'langcode', value: 'EN', line: 1 },
1005+
{ type: '>>', line: 1 },
1006+
{ type: '.', line: 1 },
1007+
{ type: 'eof', line: 1 }));
1008+
1009+
it('should tokenize a nested QuadTerm as subject in a statement',
1010+
shouldTokenize('<<<<_:b <b> "lit"@DE>> <a> "lit"@EN>>.',
1011+
{ type: '<<', line: 1 },
1012+
{ type: '<<', line: 1 },
1013+
{ type: 'blank', prefix: '_', value: 'b', line: 1 },
1014+
{ type: 'IRI', value: 'b', line: 1 },
1015+
{ type: 'literal', value: 'lit', line: 1 },
1016+
{ type: 'langcode', value: 'DE', line: 1 },
1017+
{ type: '>>', line: 1 },
1018+
{ type: 'IRI', value: 'a', line: 1 },
1019+
{ type: 'literal', value: 'lit', line: 1 },
1020+
{ type: 'langcode', value: 'EN', line: 1 },
1021+
{ type: '>>', line: 1 },
1022+
{ type: '.', line: 1 },
1023+
{ type: 'eof', line: 1 }));
1024+
1025+
it('should tokenize a nested QuadTerm as object in a statement',
1026+
shouldTokenize('<<a:a _:a <<_:b <b> "lit"@DE>>>>.',
1027+
{ type: '<<', line: 1 },
1028+
{ type: 'prefixed', prefix: 'a', value: 'a', line: 1 },
1029+
{ type: 'blank', prefix: '_', value: 'a', line: 1 },
1030+
{ type: '<<', line: 1 },
1031+
{ type: 'blank', prefix: '_', value: 'b', line: 1 },
1032+
{ type: 'IRI', value: 'b', line: 1 },
1033+
{ type: 'literal', value: 'lit', line: 1 },
1034+
{ type: 'langcode', value: 'DE', line: 1 },
1035+
{ type: '>>', line: 1 },
1036+
{ type: '>>', line: 1 },
1037+
{ type: '.', line: 1 },
1038+
{ type: 'eof', line: 1 }));
1039+
1040+
8151041
describe('passing data after the stream has been finished', function () {
8161042
var tokens = [], error;
8171043
before(function () {

0 commit comments

Comments
 (0)