Skip to content

WIP: fix n3 paths #326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/N3Parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,12 +434,25 @@ export default class N3Parser {
this._restoreContext('list', token);
// If this list is contained within a parent list, return the membership quad here.
// This will be `<parent list element> rdf:first <this list>.`.
if (stack.length !== 0 && stack[stack.length - 1].type === 'list')
this._emit(this._subject, this._predicate, this._object, this._graph);
if (stack.length !== 0 && stack[stack.length - 1].type === 'list') {
if (this._n3Mode) {
const { _subject, _predicate, _graph } = this;
if (this._object !== this.RDF_NIL) {
this._emit(previousList, this.RDF_REST, this.RDF_NIL, _graph);
}
return this._getPathReader(tk => {
this._emit(_subject, _predicate, this._object, _graph);
return this._readListItem(tk);
});
}
else {
this._emit(this._subject, this._predicate, this._object, this._graph);
}
}
// Was this list the parent's subject?
if (this._predicate === null) {
// The next token is the predicate
next = this._readPredicate;
next = this._n3Mode ? this._getPathReader(this._readPredicateOrNamedGraph) : this._readPredicate;
// No list tail if this was an empty list
if (this._subject === this.RDF_NIL)
return next;
Expand Down Expand Up @@ -497,7 +510,7 @@ export default class N3Parser {
// If an item was read, add it to the list
if (item !== null) {
// In N3 mode, the item might be a path
if (this._n3Mode && (token.type === 'IRI' || token.type === 'prefixed')) {
if (this._n3Mode) {
// Create a new context to add the item's path
this._saveContext('item', this._graph, list, this.RDF_FIRST, item);
this._subject = item, this._predicate = null;
Expand Down
159 changes: 155 additions & 4 deletions test/N3Parser-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1733,13 +1733,103 @@ describe('Parser', () => {
['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'],
['_:b2', 'f:son', 'ex:joe']));

it('should parse a ! path of length 2 as subject in a list',
shouldParse(parser, '@prefix : <ex:>. @prefix fam: <f:>.' +
'(:joe!fam:mother) a fam:Person.',
['ex:joe', 'f:mother', '_:b1'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b1'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person']));

it('should parse a !^ path of length 3 as subject in a list',
shouldParse(parser, '@prefix : <ex:>. @prefix fam: <f:>.' +
'(:joe!fam:mother^fam:father) a fam:Person.',
['ex:joe', 'f:mother', '_:b1'],
['_:b2', 'f:father', '_:b1'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b2'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person']));

it('should parse a ! path of length 2 starting with an empty list',
shouldParse(parser, '@prefix : <ex:>. @prefix fam: <f:>.' +
'()!fam:mother a fam:Person.',
['http://www.w3.org/1999/02/22-rdf-syntax-ns#nil', 'f:mother', '_:b0'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person']));

it('should parse a ! path of length 2 starting with a non-empty list',
shouldParse(parser, '@prefix : <ex:>. @prefix fam: <f:>.' +
'( :a )!fam:mother a fam:Person.',
...list(['_:b0', 'ex:a']),
['_:b0', 'f:mother', '_:b1'],
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person']));

it('should parse a ! path of length 2 starting with a non-empty list in another list',
shouldParse(parser, '@prefix : <ex:>. @prefix fam: <f:>.' +
'(( :a )!fam:mother 1) a fam:Person.',
...list(['_:b0', '_:b2'], ['_:b3', '"1"^^http://www.w3.org/2001/XMLSchema#integer']),
...list(['_:b1', 'ex:a']),
['_:b1', 'f:mother', '_:b2'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person']));

it('should parse a ! path of length 2 starting with an empty list in another list',
shouldParse(parser, '@prefix : <ex:>. @prefix fam: <f:>.' +
'(()!fam:mother 1) a fam:Person.',
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person'],
...list(['_:b0', '_:b1'], ['_:b2', '"1"^^http://www.w3.org/2001/XMLSchema#integer']),
['http://www.w3.org/1999/02/22-rdf-syntax-ns#nil', 'f:mother', '_:b1']
));

it('should parse a ! path of length 2 starting with an empty list in another list as second element',
shouldParse(parser, '@prefix : <ex:>. @prefix fam: <f:>.' +
'(1 ()!fam:mother) a fam:Person.',
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person'],
...list(['_:b0', '"1"^^http://www.w3.org/2001/XMLSchema#integer'], ['_:b1', '_:b2']),
['http://www.w3.org/1999/02/22-rdf-syntax-ns#nil', 'f:mother', '_:b2']
));

it('should parse a ! path of length 2 starting with an empty list in another list of one element',
shouldParse(parser, '@prefix : <ex:>. @prefix fam: <f:>.' +
'(()!fam:mother) a fam:Person.',
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person'],
...list(['_:b0', '_:b1']),
['http://www.w3.org/1999/02/22-rdf-syntax-ns#nil', 'f:mother', '_:b1']));

it('should parse a ! path of length 2 as nested subject in a list',
shouldParse(parser, '@prefix : <ex:>. @prefix fam: <f:>.' +
'((:joe!fam:mother) 1) a fam:Person.',
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'f:Person'],
...list(['_:b0', '_:b1'], ['_:b3', '"1"^^http://www.w3.org/2001/XMLSchema#integer']),
...list(['_:b1', '_:b2']),
['ex:joe', 'f:mother', '_:b2']));

it('should parse a birthday rule',
shouldParse(parser,
'@prefix foaf: <http://xmlns.com/foaf/0.1/> .' +
'@prefix math: <http://www.w3.org/2000/10/swap/math#> .' +
'@prefix : <http://example.org/> .' +
'' +
'{' +
' ?x :trueOnDate ?date.' +
'} <= {' +
' ((?date ?s!foaf:birthday)!math:difference 31622400) math:integerQuotient ?age .' +
'} .',
['?x', 'http://example.org/trueOnDate', '?date', '_:b0'],
// eslint-disable-next-line no-warning-comments
// FIXME: when merging with https://github.com/rdfjs/N3.js/pull/327
['_:b1', 'http://www.w3.org/2000/10/swap/log#implies', '_:b0'],
...[
...list(['_:b2', '_:b6'], ['_:b7', '"31622400"^^http://www.w3.org/2001/XMLSchema#integer']),
['_:b2', 'http://www.w3.org/2000/10/swap/math#integerQuotient', '?age'],
...list(['_:b3', '?date'], ['_:b4', '_:b5']),
['?s', 'http://xmlns.com/foaf/0.1/birthday', '_:b5'],
['_:b3', 'http://www.w3.org/2000/10/swap/math#difference', '_:b6'],
].map(elem => [...elem, '_:b1'])
));

it('should parse a formula as list item',
shouldParse(parser, '<a> <findAll> ( <b> { <b> a <type>. <b> <something> <foo> } <o> ).',
['a', 'findAll', '_:b0'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'b'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b2'],
['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', 'o'],
['_:b2', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'],
...list(['_:b0', 'b'], ['_:b2', 'o']),
['b', 'something', 'foo', '_:b1'],
['b', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'type', '_:b1']
));
Expand Down Expand Up @@ -1787,6 +1877,55 @@ 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 "_:.b" on line 1.'));

for (const [elem, value] of [
['()', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'],
['( )', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'],
['( )', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'],
['<http://www.w3.org/1999/02/22-rdf-syntax-ns#nil>', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'],
[':joe', 'ex:joe'],
// ['<<:joe a :Person>>', '<<:joe a :Person>>']
]) {
for (const pathType of ['!', '^']) {
// eslint-disable-next-line no-inner-declarations
function son(bnode) {
return pathType === '!' ? [value, 'f:son', `_:b${bnode}`] : [`_:b${bnode}`, 'f:son', value];
}

for (const [f, triple] of [
[x => `(${x}) a :List .`, ['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'ex:List']],
// [x => `<l> (${x}) <m> .`, ['l', '_:b0', 'm']],
[x => `<l> <is> (${x}) .`, ['l', 'is', '_:b0']],
]) {
// eslint-disable-next-line no-inner-declarations
function check(content, ...triples) {
it(`should parse [${f(content)}]`,
shouldParse(parser, `@prefix : <ex:>. @prefix fam: <f:>.${f(content)}`,
triple, ...triples));
}

check(`${elem}${pathType}fam:son`, ...list(['_:b0', '_:b1']), son('1'));
check(`(${elem}${pathType}fam:son)`, ...list(['_:b0', '_:b1']), ...list(['_:b1', '_:b2']), son('2'));

check(`${elem}${pathType}fam:son <x> <y>`, ...list(['_:b0', '_:b1'], ['_:b2', 'x'], ['_:b3', 'y']), son('1'));
check(`<x> ${elem}${pathType}fam:son <y>`, ...list(['_:b0', 'x'], ['_:b1', '_:b2'], ['_:b3', 'y']), son('2'));
check(`<x> <y> ${elem}${pathType}fam:son`, ...list(['_:b0', 'x'], ['_:b1', 'y'], ['_:b2', '_:b3']), son('3'));

check(`(${elem}${pathType}fam:son) <x> <y>`,
...list(['_:b0', '_:b1'], ['_:b3', 'x'], ['_:b4', 'y']),
...list(['_:b1', '_:b2']),
son('2'));
check(`<x> (${elem}${pathType}fam:son) <y>`,
...list(['_:b0', 'x'], ['_:b1', '_:b2'], ['_:b4', 'y']),
...list(['_:b2', '_:b3']),
son('3'));
check(`<x> <y> (${elem}${pathType}fam:son)`,
...list(['_:b0', 'x'], ['_:b1', 'y'], ['_:b2', '_:b3']),
...list(['_:b3', '_:b4']),
son('4'));
}
}
}
});

describe('A Parser instance for the N3 format with the explicitQuantifiers option', () => {
Expand Down Expand Up @@ -2385,3 +2524,15 @@ function itShouldResolve(baseIRI, relativeIri, expected) {
});
});
}

// creates an RDF list from the input
function list(...elems) {
const arr = [];
for (let i = 0; i < elems.length; i++) {
arr.push(
[elems[i][0], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', elems[i][1]],
[elems[i][0], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', i + 1 === elems.length ? 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil' : elems[i + 1][0]]
);
}
return arr;
}