Skip to content

Commit ddc9c60

Browse files
committed
Add support for the annotated triples syntax
1 parent 312d5d5 commit ddc9c60

File tree

4 files changed

+149
-2
lines changed

4 files changed

+149
-2
lines changed

src/N3Lexer.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,13 +300,37 @@ export default class N3Lexer {
300300
case ']':
301301
case '(':
302302
case ')':
303-
case '{':
304303
case '}':
305304
if (!this._lineMode) {
306305
matchLength = 1;
307306
type = firstChar;
307+
break;
308+
}
309+
case '{':
310+
if (!this._lineMode) {
311+
// We need at least 2 tokens lookahead to distinguish "{|" and "{ "
312+
if (input.length < 2)
313+
break;
314+
315+
// Try to find a quoted triple annotation start
316+
if (input.length > 1 && input[1] === '|') {
317+
type = '{|', matchLength = 2;
318+
break;
319+
}
320+
321+
matchLength = 1;
322+
type = firstChar;
308323
}
309324
break;
325+
case '|':
326+
// We need at least 2 tokens lookahead to distinguish "|}" and "|"
327+
if (input.length < 2)
328+
break;
329+
// Try to find a quoted triple annotation end
330+
if (input[0] === '|' && input.length > 1 && input[1] === '}') {
331+
type = '|}', matchLength = 2;
332+
break;
333+
}
310334

311335
default:
312336
inconclusive = true;

src/N3Parser.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,22 @@ export default class N3Parser {
614614
case ',':
615615
next = this._readObject;
616616
break;
617+
// {| means that the current triple is annotated with predicate-object pairs.
618+
case '{|':
619+
if (!this._supportsRDFStar)
620+
return this._error('Unexpected RDF* syntax', token);
621+
// Continue using the last triple as quoted triple subject for the predicate-object pairs.
622+
const predicate = this._predicate, object = this._object;
623+
this._subject = this._quad(subject, predicate, object, this.DEFAULTGRAPH);
624+
next = this._readPredicate;
625+
break;
626+
// |} means that the current quoted triple in annotation syntax is finalized.
627+
case '|}':
628+
if (this._subject.termType !== 'Quad')
629+
return this._error('Unexpected asserted triple closing', token);
630+
this._subject = null;
631+
next = this._readPunctuation;
632+
break;
617633
default:
618634
// An entity means this is a quad (only allowed if not already inside a graph)
619635
if (this._supportsQuads && this._graph === null && (graph = this._readEntity(token)) !== undefined) {

test/N3Lexer-test.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,49 @@ describe('Lexer', () => {
10901090
{ type: '.', line: 1 },
10911091
{ type: 'eof', line: 1 }));
10921092

1093+
it('should tokenize a quoted triple annotation start',
1094+
shouldTokenize('{|',
1095+
{ type: '{|', line: 1 },
1096+
{ type: 'eof', line: 1 }));
1097+
1098+
it('should tokenize a split quoted triple annotation start',
1099+
shouldTokenize(streamOf('{', '|'),
1100+
{ type: '{|', line: 1 },
1101+
{ type: 'eof', line: 1 }));
1102+
1103+
it('should tokenize a quoted triple annotation end',
1104+
shouldTokenize('|}',
1105+
{ type: '|}', line: 1 },
1106+
{ type: 'eof', line: 1 }));
1107+
1108+
it('should tokenize a split quoted triple annotation end',
1109+
shouldTokenize(streamOf('|', '}'),
1110+
{ type: '|}', line: 1 },
1111+
{ type: 'eof', line: 1 }));
1112+
1113+
it('should tokenize an empty quoted triple annotation',
1114+
shouldTokenize('{| |}',
1115+
{ type: '{|', line: 1 },
1116+
{ type: '|}', line: 1 },
1117+
{ type: 'eof', line: 1 }));
1118+
1119+
it('should tokenize a non-empty quoted triple annotation',
1120+
shouldTokenize('{| <http://ex.org/?bla#bar> \n\t<http://ex.org/?bla#boo> |}.',
1121+
{ type: '{|', line: 1 },
1122+
{ type: 'IRI', value: 'http://ex.org/?bla#bar', line: 1 },
1123+
{ type: 'IRI', value: 'http://ex.org/?bla#boo', line: 2 },
1124+
{ type: '|}', line: 2 },
1125+
{ type: '.', line: 2 },
1126+
{ type: 'eof', line: 2 }));
1127+
1128+
it('should not tokenize an incomplete closing triple annotation',
1129+
shouldNotTokenize('{| |',
1130+
'Unexpected "|" on line 1.'));
1131+
1132+
it('should not tokenize an invalid closing triple annotation',
1133+
shouldNotTokenize('{| ||',
1134+
'Unexpected "||" on line 1.'));
1135+
10931136
it('returns start and end index for every token', () => {
10941137
const tokens = new Lexer().tokenize('<a:a> <b:c> "lit"@EN.');
10951138
tokens.should.deep.equal([

test/N3Parser-test.js

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ describe('Parser', () => {
746746

747747
it('should not parse a single opening brace',
748748
shouldNotParse('{',
749-
'Expected entity but got eof on line 1.'));
749+
'Unexpected "{" on line 1.'));
750750

751751
it('should not parse a superfluous closing brace ',
752752
shouldNotParse('{}}',
@@ -1044,6 +1044,46 @@ describe('Parser', () => {
10441044
shouldParse('<a> <b> <c> <g>.\n<<<a> <b> <c>>> <d> <e>.',
10451045
['a', 'b', 'c', 'g'],
10461046
[['a', 'b', 'c'], 'd', 'e']));
1047+
1048+
it('should parse an RDF* triple using annotation syntax with one predicate-object',
1049+
shouldParse('<a> <b> <c> {| <b> <c> |}.',
1050+
['a', 'b', 'c'], [['a', 'b', 'c'], 'b', 'c']));
1051+
1052+
it('should parse an RDF* triple using annotation syntax with two predicate-objects',
1053+
shouldParse('<a> <b> <c> {| <b1> <c1>; <b2> <c2> |}.',
1054+
['a', 'b', 'c'], [['a', 'b', 'c'], 'b1', 'c1'], [['a', 'b', 'c'], 'b2', 'c2']));
1055+
1056+
it('should parse an RDF* triple using annotation syntax with one predicate-object followed by regular triples',
1057+
shouldParse('<a> <b> <c> {| <b> <c> |}.\n<a2> <b2> <c2>.',
1058+
['a', 'b', 'c'], [['a', 'b', 'c'], 'b', 'c'], ['a2', 'b2', 'c2']));
1059+
1060+
it('should not parse an RDF* triple using annotation syntax with zero predicate-objects',
1061+
shouldNotParse('<a> <b> <c> {| |}',
1062+
'Expected entity but got |} on line 1.'));
1063+
1064+
it('should not parse an RDF* triple using an incomplete annotation syntax',
1065+
shouldNotParse('<a> <b> <c> {| <b> |}',
1066+
'Expected entity but got |} on line 1.'));
1067+
1068+
it('should not parse an RDF* triple using an incomplete annotation syntax after a semicolon',
1069+
shouldNotParse('<a> <b> <c> {| <b1> <c1>; |}',
1070+
'Expected entity but got |} on line 1.'));
1071+
1072+
it('should not parse an RDF* triple using an incomplete annotation syntax after a semicolon and entity',
1073+
shouldNotParse('<a> <b> <c> {| <b1> <c1>; <b2> |}',
1074+
'Expected entity but got |} on line 1.'));
1075+
1076+
it('should not parse an RDF* triple using an incomplete annotation syntax that misses |}',
1077+
shouldNotParse('<a> <b> <c> {| <b1> <c1>',
1078+
'Expected entity but got eof on line 1.'));
1079+
1080+
it('should not parse an RDF* triple using an incomplete annotation syntax that misses |} and starts a new subject',
1081+
shouldNotParse('<a> <b> <c> {| <b1> <c1>. <a2> <b2> <c2>',
1082+
'Expected entity but got eof on line 1.'));
1083+
1084+
it('should not parse an out of place |}',
1085+
shouldNotParse('<a> <b> <c> |}',
1086+
'Unexpected asserted triple closing on line 1.'));
10471087
});
10481088

10491089
describe('An Parser instance without document IRI', () => {
@@ -1227,6 +1267,10 @@ describe('Parser', () => {
12271267
it('should not parse RDF* in the object position',
12281268
shouldNotParse(parser, '<a> <b> <<a> <b> <c>>>.',
12291269
'Unexpected RDF* syntax on line 1.'));
1270+
1271+
it('should not parse RDF* with annotated syntax',
1272+
shouldNotParse(parser, '<a> <b> <c> {| <b> <c> |}.',
1273+
'Unexpected RDF* syntax on line 1.'));
12301274
});
12311275

12321276
describe('A Parser instance for the TurtleStar format', () => {
@@ -1288,6 +1332,10 @@ describe('Parser', () => {
12881332
it('should not parse RDF* in the object position',
12891333
shouldNotParse(parser, '<a> <b> <<<a> <b> <c>>>.',
12901334
'Unexpected RDF* syntax on line 1.'));
1335+
1336+
it('should not parse RDF* with annotated syntax',
1337+
shouldNotParse(parser, '<a> <b> <c> {| <b> <c> |}.',
1338+
'Unexpected RDF* syntax on line 1.'));
12911339
});
12921340

12931341
describe('A Parser instance for the TriGStar format', () => {
@@ -1317,6 +1365,10 @@ describe('Parser', () => {
13171365
shouldNotParse(parser, '_:a <http://ex.org/b> "c" <http://ex.org/g>.',
13181366
'Expected punctuation to follow ""c"" on line 1.'));
13191367

1368+
it('should not parse object lists',
1369+
shouldNotParse(parser, '<http://example/s> <http://example/p> <http://example/o>, <http://example/o2> .',
1370+
'Unexpected "," on line 1.'));
1371+
13201372
it('should not parse relative IRIs',
13211373
shouldNotParse(parser, '<a> <b> <c>.', 'Invalid IRI on line 1.'));
13221374

@@ -1375,6 +1427,10 @@ describe('Parser', () => {
13751427
it('should not parse nested quads',
13761428
shouldNotParse(parser, '<<_:a <http://ex.org/b> _:b <http://ex.org/b>>> <http://ex.org/b> "c" .',
13771429
'Expected >> to follow "_:b0_b" on line 1.'));
1430+
1431+
it('should not parse annotated triples',
1432+
shouldNotParse(parser, '_:a <http://ex.org/b> _:c {| <http://ex.org/b1> "c1" |} .',
1433+
'Unexpected "{|" on line 1.'));
13781434
});
13791435

13801436
describe('A Parser instance for the N-Quads format', () => {
@@ -1430,6 +1486,10 @@ describe('Parser', () => {
14301486
it('should parse RDF*',
14311487
shouldParse(parser, '<<_:a <http://example.org/b> _:c>> <http://example.org/a> _:c .',
14321488
[['_:b0_a', 'b', '_:b0_c'], 'a', '_:b0_c']));
1489+
1490+
it('should not parse annotated triples',
1491+
shouldNotParse(parser, '_:a <http://ex.org/b> _:c {| <http://ex.org/b1> "c1" |} .',
1492+
'Unexpected "{|" on line 1.'));
14331493
});
14341494

14351495
describe('A Parser instance for the N3 format', () => {
@@ -1777,6 +1837,10 @@ describe('Parser', () => {
17771837
it('should not parse RDF* in the object position',
17781838
shouldNotParse(parser, '<a> <b> <<<a> <b> <c>>>.',
17791839
'Unexpected RDF* syntax on line 1.'));
1840+
1841+
it('should not parse RDF* with annotated syntax',
1842+
shouldNotParse(parser, '<a> <b> <c> {| <b> <c> |}.',
1843+
'Unexpected RDF* syntax on line 1.'));
17801844
});
17811845

17821846
describe('A Parser instance for the N3Star format', () => {

0 commit comments

Comments
 (0)