Skip to content

Commit

Permalink
Add support for the annotated triples syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
rubensworks committed Jun 22, 2023
1 parent 312d5d5 commit ddc9c60
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 2 deletions.
26 changes: 25 additions & 1 deletion src/N3Lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,37 @@ export default class N3Lexer {
case ']':
case '(':
case ')':
case '{':
case '}':
if (!this._lineMode) {
matchLength = 1;
type = firstChar;
break;
}
case '{':
if (!this._lineMode) {
// We need at least 2 tokens lookahead to distinguish "{|" and "{ "
if (input.length < 2)
break;

// Try to find a quoted triple annotation start
if (input.length > 1 && input[1] === '|') {
type = '{|', matchLength = 2;
break;
}

matchLength = 1;
type = firstChar;
}
break;
case '|':
// We need at least 2 tokens lookahead to distinguish "|}" and "|"
if (input.length < 2)
break;
// Try to find a quoted triple annotation end
if (input[0] === '|' && input.length > 1 && input[1] === '}') {
type = '|}', matchLength = 2;
break;
}

default:
inconclusive = true;
Expand Down
16 changes: 16 additions & 0 deletions src/N3Parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,22 @@ export default class N3Parser {
case ',':
next = this._readObject;
break;
// {| means that the current triple is annotated with predicate-object pairs.
case '{|':
if (!this._supportsRDFStar)
return this._error('Unexpected RDF* syntax', token);
// Continue using the last triple as quoted triple subject for the predicate-object pairs.
const predicate = this._predicate, object = this._object;
this._subject = this._quad(subject, predicate, object, this.DEFAULTGRAPH);
next = this._readPredicate;
break;
// |} means that the current quoted triple in annotation syntax is finalized.
case '|}':
if (this._subject.termType !== 'Quad')
return this._error('Unexpected asserted triple closing', token);
this._subject = null;
next = this._readPunctuation;
break;
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) {
Expand Down
43 changes: 43 additions & 0 deletions test/N3Lexer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,49 @@ describe('Lexer', () => {
{ type: '.', line: 1 },
{ type: 'eof', line: 1 }));

it('should tokenize a quoted triple annotation start',
shouldTokenize('{|',
{ type: '{|', line: 1 },
{ type: 'eof', line: 1 }));

it('should tokenize a split quoted triple annotation start',
shouldTokenize(streamOf('{', '|'),
{ type: '{|', line: 1 },
{ type: 'eof', line: 1 }));

it('should tokenize a quoted triple annotation end',
shouldTokenize('|}',
{ type: '|}', line: 1 },
{ type: 'eof', line: 1 }));

it('should tokenize a split quoted triple annotation end',
shouldTokenize(streamOf('|', '}'),
{ type: '|}', line: 1 },
{ type: 'eof', line: 1 }));

it('should tokenize an empty quoted triple annotation',
shouldTokenize('{| |}',
{ type: '{|', line: 1 },
{ type: '|}', line: 1 },
{ type: 'eof', line: 1 }));

it('should tokenize a non-empty quoted triple annotation',
shouldTokenize('{| <http://ex.org/?bla#bar> \n\t<http://ex.org/?bla#boo> |}.',
{ type: '{|', line: 1 },
{ type: 'IRI', value: 'http://ex.org/?bla#bar', line: 1 },
{ type: 'IRI', value: 'http://ex.org/?bla#boo', line: 2 },
{ type: '|}', line: 2 },
{ type: '.', line: 2 },
{ type: 'eof', line: 2 }));

it('should not tokenize an incomplete closing triple annotation',
shouldNotTokenize('{| |',
'Unexpected "|" on line 1.'));

it('should not tokenize an invalid closing triple annotation',
shouldNotTokenize('{| ||',
'Unexpected "||" on line 1.'));

it('returns start and end index for every token', () => {
const tokens = new Lexer().tokenize('<a:a> <b:c> "lit"@EN.');
tokens.should.deep.equal([
Expand Down
66 changes: 65 additions & 1 deletion test/N3Parser-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,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('{}}',
Expand Down Expand Up @@ -1044,6 +1044,46 @@ describe('Parser', () => {
shouldParse('<a> <b> <c> <g>.\n<<<a> <b> <c>>> <d> <e>.',
['a', 'b', 'c', 'g'],
[['a', 'b', 'c'], 'd', 'e']));

it('should parse an RDF* triple using annotation syntax with one predicate-object',
shouldParse('<a> <b> <c> {| <b> <c> |}.',
['a', 'b', 'c'], [['a', 'b', 'c'], 'b', 'c']));

it('should parse an RDF* triple using annotation syntax with two predicate-objects',
shouldParse('<a> <b> <c> {| <b1> <c1>; <b2> <c2> |}.',
['a', 'b', 'c'], [['a', 'b', 'c'], 'b1', 'c1'], [['a', 'b', 'c'], 'b2', 'c2']));

it('should parse an RDF* triple using annotation syntax with one predicate-object followed by regular triples',
shouldParse('<a> <b> <c> {| <b> <c> |}.\n<a2> <b2> <c2>.',
['a', 'b', 'c'], [['a', 'b', 'c'], 'b', 'c'], ['a2', 'b2', 'c2']));

it('should not parse an RDF* triple using annotation syntax with zero predicate-objects',
shouldNotParse('<a> <b> <c> {| |}',
'Expected entity but got |} on line 1.'));

it('should not parse an RDF* triple using an incomplete annotation syntax',
shouldNotParse('<a> <b> <c> {| <b> |}',
'Expected entity but got |} on line 1.'));

it('should not parse an RDF* triple using an incomplete annotation syntax after a semicolon',
shouldNotParse('<a> <b> <c> {| <b1> <c1>; |}',
'Expected entity but got |} on line 1.'));

it('should not parse an RDF* triple using an incomplete annotation syntax after a semicolon and entity',
shouldNotParse('<a> <b> <c> {| <b1> <c1>; <b2> |}',
'Expected entity but got |} on line 1.'));

it('should not parse an RDF* triple using an incomplete annotation syntax that misses |}',
shouldNotParse('<a> <b> <c> {| <b1> <c1>',
'Expected entity but got eof on line 1.'));

it('should not parse an RDF* triple using an incomplete annotation syntax that misses |} and starts a new subject',
shouldNotParse('<a> <b> <c> {| <b1> <c1>. <a2> <b2> <c2>',
'Expected entity but got eof on line 1.'));

it('should not parse an out of place |}',
shouldNotParse('<a> <b> <c> |}',
'Unexpected asserted triple closing on line 1.'));
});

describe('An Parser instance without document IRI', () => {
Expand Down Expand Up @@ -1227,6 +1267,10 @@ describe('Parser', () => {
it('should not parse RDF* in the object position',
shouldNotParse(parser, '<a> <b> <<a> <b> <c>>>.',
'Unexpected RDF* syntax on line 1.'));

it('should not parse RDF* with annotated syntax',
shouldNotParse(parser, '<a> <b> <c> {| <b> <c> |}.',
'Unexpected RDF* syntax on line 1.'));
});

describe('A Parser instance for the TurtleStar format', () => {
Expand Down Expand Up @@ -1288,6 +1332,10 @@ describe('Parser', () => {
it('should not parse RDF* in the object position',
shouldNotParse(parser, '<a> <b> <<<a> <b> <c>>>.',
'Unexpected RDF* syntax on line 1.'));

it('should not parse RDF* with annotated syntax',
shouldNotParse(parser, '<a> <b> <c> {| <b> <c> |}.',
'Unexpected RDF* syntax on line 1.'));
});

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

it('should not parse object lists',
shouldNotParse(parser, '<http://example/s> <http://example/p> <http://example/o>, <http://example/o2> .',
'Unexpected "," on line 1.'));

it('should not parse relative IRIs',
shouldNotParse(parser, '<a> <b> <c>.', 'Invalid IRI on line 1.'));

Expand Down Expand Up @@ -1375,6 +1427,10 @@ describe('Parser', () => {
it('should not parse nested quads',
shouldNotParse(parser, '<<_:a <http://ex.org/b> _:b <http://ex.org/b>>> <http://ex.org/b> "c" .',
'Expected >> to follow "_:b0_b" on line 1.'));

it('should not parse annotated triples',
shouldNotParse(parser, '_:a <http://ex.org/b> _:c {| <http://ex.org/b1> "c1" |} .',
'Unexpected "{|" on line 1.'));
});

describe('A Parser instance for the N-Quads format', () => {
Expand Down Expand Up @@ -1430,6 +1486,10 @@ describe('Parser', () => {
it('should parse RDF*',
shouldParse(parser, '<<_:a <http://example.org/b> _:c>> <http://example.org/a> _:c .',
[['_:b0_a', 'b', '_:b0_c'], 'a', '_:b0_c']));

it('should not parse annotated triples',
shouldNotParse(parser, '_:a <http://ex.org/b> _:c {| <http://ex.org/b1> "c1" |} .',
'Unexpected "{|" on line 1.'));
});

describe('A Parser instance for the N3 format', () => {
Expand Down Expand Up @@ -1777,6 +1837,10 @@ describe('Parser', () => {
it('should not parse RDF* in the object position',
shouldNotParse(parser, '<a> <b> <<<a> <b> <c>>>.',
'Unexpected RDF* syntax on line 1.'));

it('should not parse RDF* with annotated syntax',
shouldNotParse(parser, '<a> <b> <c> {| <b> <c> |}.',
'Unexpected RDF* syntax on line 1.'));
});

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

0 comments on commit ddc9c60

Please sign in to comment.