New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

n3

Package Overview
Dependencies
Maintainers
1
Versions
126
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

n3 - npm Package Compare versions

Comparing version 0.3.6 to 0.4.0

spec/nquads-spec.js

71

lib/N3Lexer.js

@@ -17,5 +17,22 @@ // **N3Lexer** tokenizes N3 documents.

// ## Constructor
function N3Lexer() {
function N3Lexer(options) {
if (!(this instanceof N3Lexer))
return new N3Lexer();
return new N3Lexer(options);
// In line mode (N-Triples or N-Quads), only simple features may be parsed
if (options && options.lineMode) {
// Don't tokenize special literals
this._tripleQuotedString = this._number = this._boolean = /$0^/;
// Swap the tokenize method for a restricted version
var self = this;
this._tokenize = this.tokenize;
this.tokenize = function (input, callback) {
this._tokenize(input, function (error, token) {
if (!error && /IRI|prefixed|literal|langcode|type|\.|eof/.test(token.type))
callback && callback(error, token);
else
callback && callback(error || self._syntaxError(token.type, callback = null));
});
};
}
}

@@ -33,8 +50,9 @@

_langcode: /^@([a-z]+(?:-[a-z0-9]+)*)(?=[^a-z0-9\-])/i,
_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|<)/,
_prefixedName: /^((?:[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-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}|\\[!#-\/;=?\-@_~]))?)?)(?=[,;\s#()\[\]]|\.[\s#()\[\]<"'])/,
_number: /^[\-+]?(?:\d+\.?\d*([eE](?:[\-\+])?\d+)|\d*\.?\d+)(?=[.,;\s#()\[\]])/,
_boolean: /^(?:true|false)(?=[.,;\s#()\[\]])/,
_keyword: /^@[a-z]+(?=\s)/,
_sparqlKeyword: /^(?:PREFIX|BASE)(?=\s)/i,
_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<])/,
_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}|\\[!#-\/;=?\-@_~]))?)?)(?=\.?[,;\s#()\[\]\{\}"'<])/,
_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])*)(?=\.?[,;:\s#()\[\]\{\}"'<])/,
_number: /^[\-+]?(?:\d+\.?\d*([eE](?:[\-\+])?\d+)|\d*\.?\d+)(?=[.,;:\s#()\[\]\{\}"'<])/,
_boolean: /^(?:true|false)(?=[.,;:\s#()\[\]\{\}"'<])/,
_keyword: /^@[a-z]+(?=[\s#<:])/,
_sparqlKeyword: /^(?:PREFIX|BASE|GRAPH)(?=[\s#<:])/i,
_shortPredicates: /^a(?=\s+|<)/,

@@ -45,3 +63,2 @@ _newline: /^[ \t]*(?:#[^\n\r]*)?(?:\r\n|\n|\r)[ \t]*/,

// ## Private methods

@@ -103,2 +120,14 @@

case '_':
// Try to find a blank node. Since it can contain (but not end with) a dot,
// we always need a non-dot character before deciding it is a prefixed name.
// Therefore, try inserting a space if we're at the end of the input.
if ((match = this._blank.exec(input)) ||
inputFinished && (match = this._blank.exec(input + ' '))) {
type = 'prefixed';
prefix = '_';
value = match[1];
}
break;
case '"':

@@ -177,2 +206,4 @@ case "'":

case 'P':
case 'G':
case 'g':
// Try to find a SPARQL-style keyword.

@@ -212,2 +243,4 @@ if (match = this._sparqlKeyword.exec(input))

case ')':
case '{':
case '}':
// The next token is punctuation

@@ -233,4 +266,4 @@ matchLength = 1;

// Therefore, try inserting a space if we're at the end of the input.
else if ((match = this._prefixedName.exec(input)) ||
inputFinished && (match = this._prefixedName.exec(input + ' '))) {
else if ((match = this._prefixed.exec(input)) ||
inputFinished && (match = this._prefixed.exec(input + ' '))) {
type = 'prefixed';

@@ -266,10 +299,6 @@ prefix = match[1] || '';

// Signals the syntax error through the callback
function reportSyntaxError(self) {
self._input = null;
match = /^\S*/.exec(input);
callback(new Error('Syntax error: unexpected "' + match[0] + '" on line ' + self._line + '.'));
}
function reportSyntaxError(self) { callback(self._syntaxError(/^\S*/.exec(input)[0])); }
},
// ### `unescape` replaces N3 escape codes by their corresponding characters.
// ### `_unescape` replaces N3 escape codes by their corresponding characters.
_unescape: function (item) {

@@ -288,3 +317,3 @@ try {

if (charCode < 0xFFFF) return fromCharCode(charCode);
return fromCharCode(0xD800 + ((charCode -= 0x10000) >> 10), 0xDC00 + (charCode & 0x3FF));
return fromCharCode(0xD800 + ((charCode -= 0x10000) / 0x400), 0xDC00 + (charCode & 0x3FF));
}

@@ -302,3 +331,9 @@ else {

// ### `_syntaxError` creates a syntax error for the given issue
_syntaxError: function (issue) {
this._input = null;
return new Error('Syntax error: unexpected "' + issue + '" on line ' + this._line + '.');
},
// ## Public methods

@@ -305,0 +340,0 @@

@@ -9,32 +9,47 @@ // **N3Parser** parses N3 documents.

var absoluteURI = /:/,
var absoluteIRI = /:/,
documentPart = /[^\/]*$/,
rootURI = /^(?:[^:]+:\/*)?[^\/]*/;
rootIRI = /^(?:[^:]+:\/*)?[^\/]*/;
// The next ID for new blank nodes
var blankNodeCount = 0;
var blankNodePrefix = 0, blankNodeCount = 0;
// ## Constructor
function N3Parser(config) {
function N3Parser(options) {
if (!(this instanceof N3Parser))
return new N3Parser(config);
// Initialize the parser settings
config = config || {};
return new N3Parser(options);
this._tripleStack = [];
this._lexer = config.lexer || new N3Lexer();
this._blankNodes = Object.create(null);
this._graph = null;
// Set the document URI
if (!config.documentURI) {
this._baseURI = null;
this._baseURIPath = null;
// Set the document IRI.
options = options || {};
if (!options.documentIRI) {
this._baseIRI = null;
this._baseIRIPath = null;
}
else {
if (config.documentURI.indexOf('#') > 0)
throw new Error('Invalid document URI');
this._baseURI = config.documentURI;
this._baseURIPath = this._baseURI.replace(documentPart, '');
this._baseURIRoot = this._baseURI.match(rootURI)[0];
if (options.documentIRI.indexOf('#') > 0)
throw new Error('Invalid document IRI');
this._baseIRI = options.documentIRI;
this._baseIRIPath = this._baseIRI.replace(documentPart, '');
this._baseIRIRoot = this._baseIRI.match(rootIRI)[0];
}
// Set supported features depending on the format.
var format = (typeof options.format === 'string') && options.format.match(/\w*$/)[0].toLowerCase(),
isTurtle = format === 'turtle', isTriG = format === 'trig',
isNTriples = /triple/.test(format), isNQuads = /quad/.test(format),
isLineMode = isNTriples || isNQuads;
if (!(this._supportsNamedGraphs = !isTurtle))
this._readPredicateOrNamedGraph = this._readPredicate;
this._supportsQuads = !(isTurtle || isTriG || isNTriples);
// Disable relative IRIs in N-Triples or N-Quads mode
if (isLineMode) {
this._baseIRI = '';
this._resolveIRI = function (token) {
this._error('Disallowed relative IRI', token);
return this._callback = noop, this._subject = null;
};
}
this._lexer = options.lexer || new N3Lexer({ lineMode: isLineMode });
}

@@ -46,3 +61,3 @@

N3Parser._resetBlankNodeIds = function () {
blankNodeCount = 0;
blankNodePrefix = blankNodeCount = 0;
};

@@ -58,2 +73,5 @@

case 'eof':
if (this._graph !== null)
return this._error('Unclosed graph', token);
delete this._prefixes._;
return this._callback(null, null, this._prefixes);

@@ -70,6 +88,17 @@ // It could be a prefix declaration.

this._sparqlStyle = false;
return this._readBaseURI;
return this._readBaseIRI;
case 'BASE':
this._sparqlStyle = true;
return this._readBaseURI;
return this._readBaseIRI;
// It could be a graph.
case '{':
if (this._supportsNamedGraphs) {
this._graph = '';
this._subject = null;
return this._readSubject;
}
case 'GRAPH':
if (this._supportsNamedGraphs) {
return this._readNamedGraphLabel;
}
// Otherwise, the next token must be a subject.

@@ -83,20 +112,15 @@ default:

_readSubject: function (token) {
this._predicate = null;
switch (token.type) {
case 'IRI':
if (this._baseURI === null || absoluteURI.test(token.value))
if (this._baseIRI === null || absoluteIRI.test(token.value))
this._subject = token.value;
else
this._subject = this._resolveURI(token.value);
this._subject = this._resolveIRI(token);
break;
case 'prefixed':
if (token.prefix === '_') {
this._subject = this._blankNodes[token.value] ||
(this._blankNodes[token.value] = '_:b' + blankNodeCount++);
}
else {
var prefix = this._prefixes[token.prefix];
if (prefix === undefined)
return this._error('Undefined prefix "' + token.prefix + ':"', token);
this._subject = prefix + token.value;
}
var prefix = this._prefixes[token.prefix];
if (prefix === undefined)
return this._error('Undefined prefix "' + token.prefix + ':"', token);
this._subject = prefix + token.value;
break;

@@ -113,8 +137,10 @@ case '[':

return this._readListItem;
case '}':
return this._readPunctuation(token);
default:
return this._error('Expected subject but got ' + token.type, token);
}
this._subjectHasPredicate = false;
// The next token must be a predicate.
return this._readPredicate;
// The next token must be a predicate,
// or, if the subject was actually a graph IRI, a named graph.
return this._readPredicateOrNamedGraph;
},

@@ -127,6 +153,6 @@

case 'abbreviation':
if (this._baseURI === null || absoluteURI.test(token.value))
if (this._baseIRI === null || absoluteIRI.test(token.value))
this._predicate = token.value;
else
this._predicate = this._resolveURI(token.value);
this._predicate = this._resolveIRI(token);
break;

@@ -144,11 +170,10 @@ case 'prefixed':

break;
case '.':
case ']':
case '}':
// Expected predicate didn't come, must have been trailing semicolon.
return this._readBlankNodeTail(token, true);
case '.':
// A dot is not allowed if the subject did not have a predicate yet
if (!this._subjectHasPredicate)
return this._error('Unexpected dot', token);
// Expected predicate didn't come, must have been trailing semicolon.
return this._readPunctuation(token, true);
if (this._predicate === null)
return this._error('Unexpected ' + token.type, token);
this._subject = null;
return this._readBlankNodeTail(token);
case ';':

@@ -160,3 +185,2 @@ // Extra semicolons can be safely ignored

}
this._subjectHasPredicate = true;
// The next token must be an object.

@@ -170,18 +194,12 @@ return this._readObject;

case 'IRI':
if (this._baseURI === null || absoluteURI.test(token.value))
if (this._baseIRI === null || absoluteIRI.test(token.value))
this._object = token.value;
else
this._object = this._resolveURI(token.value);
this._object = this._resolveIRI(token);
break;
case 'prefixed':
if (token.prefix === '_') {
this._object = this._blankNodes[token.value] ||
(this._blankNodes[token.value] = '_:b' + blankNodeCount++);
}
else {
var prefix = this._prefixes[token.prefix];
if (prefix === undefined)
return this._error('Undefined prefix "' + token.prefix + ':"', token);
this._object = prefix + token.value;
}
var prefix = this._prefixes[token.prefix];
if (prefix === undefined)
return this._error('Undefined prefix "' + token.prefix + ':"', token);
this._object = prefix + token.value;
break;

@@ -208,12 +226,28 @@ case 'literal':

// ### `_readPredicateOrNamedGraph` reads a triple's predicate, or a named graph.
_readPredicateOrNamedGraph: function (token) {
return token.type === '{' ? this._readGraph(token) : this._readPredicate(token);
},
// ### `_readGraph` reads a graph.
_readGraph: function (token) {
if (token.type !== '{')
return this._error('Expected graph but got ' + token.type, token);
// The "subject" we read is actually the GRAPH's label
this._graph = this._subject, this._subject = null;
return this._readSubject;
},
// ### `_readBlankNodeHead` reads the head of a blank node.
_readBlankNodeHead: function (token) {
if (token.type === ']')
return this._readBlankNodeTail(token, true);
else
return this._readPredicate(token);
if (token.type === ']') {
this._subject = null;
return this._readBlankNodeTail(token);
}
this._predicate = null;
return this._readPredicate(token);
},
// ### `_readBlankNodeTail` reads the end of a blank node.
_readBlankNodeTail: function (token, empty) {
_readBlankNodeTail: function (token) {
if (token.type !== ']')

@@ -223,7 +257,7 @@ return this._readPunctuation(token);

// Store blank node triple.
if (empty !== true)
this._callback(null, { subject: this._subject,
if (this._subject !== null)
this._callback(null, { subject: this._subject,
predicate: this._predicate,
object: this._object,
context: 'n3/contexts#default' });
object: this._object,
graph: this._graph || '' });

@@ -241,3 +275,4 @@ // Restore parent triple that contains the blank node.

// The blank node was the subject, so continue reading the predicate.
return this._readPredicate;
// If the blank node didn't contain any predicates, it could also be the label of a named graph.
return this._predicate !== null ? this._readPredicate : this._readPredicateOrNamedGraph;
},

@@ -251,3 +286,6 @@

if (token.prefix === '') {
value = token.value;
if (this._baseIRI === null || absoluteIRI.test(token.value))
value = token.value;
else
value = this._resolveIRI(token);
}

@@ -284,12 +322,6 @@ else {

case 'prefixed':
if (token.prefix === '_') {
item = this._blankNodes[token.value] ||
(this._blankNodes[token.value] = '_:b' + blankNodeCount++);
}
else {
var prefix = this._prefixes[token.prefix];
if (prefix === undefined)
return this._error('Undefined prefix "' + token.prefix + ':"', token);
item = prefix + token.value;
}
var prefix = this._prefixes[token.prefix];
if (prefix === undefined)
return this._error('Undefined prefix "' + token.prefix + ':"', token);
item = prefix + token.value;
break;

@@ -319,8 +351,8 @@ case 'literal':

// If this list is contained within a parent list, return the membership triple here.
// This will be `<parent list elemen>t rdf:first <this list>.`.
// This will be `<parent list element> rdf:first <this list>.`.
if (stack.length !== 0 && stack[stack.length - 1].type === 'list')
this._callback(null, { subject: parentTriple.subject,
this._callback(null, { subject: parentTriple.subject,
predicate: parentTriple.predicate,
object: parentTriple.object,
context: 'n3/contexts#default' });
object: parentTriple.object,
graph: this._graph || '' });
// Restore the parent triple's subject.

@@ -367,13 +399,13 @@ this._subject = parentTriple.subject;

// The rest of the list is in the current head.
this._callback(null, { subject: prevItemHead,
this._callback(null, { subject: prevItemHead,
predicate: RDF_REST,
object: itemHead,
context: 'n3/contexts#default' });
object: itemHead,
graph: this._graph || '' });
}
// Add the item's value.
if (item !== null)
this._callback(null, { subject: itemHead,
this._callback(null, { subject: itemHead,
predicate: RDF_FIRST,
object: item,
context: 'n3/contexts#default' });
object: item,
graph: this._graph || '' });
return next;

@@ -383,7 +415,13 @@ },

// ### `_readPunctuation` reads punctuation between triples or triple parts.
_readPunctuation: function (token, empty) {
var next;
_readPunctuation: function (token) {
var next, subject = this._subject, graph = this._graph;
switch (token.type) {
// A closing brace ends a graph
case '}':
if (this._graph === null)
return this._error('Unexpected graph closing', token);
this._graph = null;
// A dot just ends the statement, without sharing anything with the next.
case '.':
this._subject = null;
next = this._readInTopContext;

@@ -399,2 +437,23 @@ break;

break;
// An IRI means this is a quad (only allowed if not already inside a graph).
case 'IRI':
if (this._supportsQuads && this._graph === null) {
if (this._baseIRI === null || absoluteIRI.test(token.value))
graph = token.value;
else
graph = this._resolveIRI(token);
subject = this._subject;
next = this._readQuadPunctuation;
break;
}
// An prefixed name means this is a quad (only allowed if not already inside a graph).
case 'prefixed':
if (this._supportsQuads && this._graph === null) {
var prefix = this._prefixes[token.prefix];
if (prefix === undefined)
return this._error('Undefined prefix "' + token.prefix + ':"', token);
graph = prefix + token.value;
next = this._readQuadPunctuation;
break;
}
default:

@@ -404,10 +463,17 @@ return this._error('Expected punctuation to follow "' + this._object + '"', token);

// A triple has been completed now, so return it.
if (!empty)
this._callback(null, { subject: this._subject,
if (subject !== null)
this._callback(null, { subject: subject,
predicate: this._predicate,
object: this._object,
context: 'n3/contexts#default' });
object: this._object,
graph: graph || '' });
return next;
},
// ### `_readQuadPunctuation` reads punctuation after a quad.
_readQuadPunctuation: function (token) {
if (token.type !== '.')
return this._error('Expected dot to follow quad', token);
return this._readInTopContext;
},
// ### `_readPrefix` reads the prefix of a prefix declaration.

@@ -418,34 +484,55 @@ _readPrefix: function (token) {

this._prefix = token.value;
return this._readPrefixURI;
return this._readPrefixIRI;
},
// ### `_readPrefixURI` reads the URI of a prefix declaration.
_readPrefixURI: function (token) {
// ### `_readPrefixIRI` reads the IRI of a prefix declaration.
_readPrefixIRI: function (token) {
if (token.type !== 'IRI')
return this._error('Expected IRI to follow prefix "' + this._prefix + ':"', token);
var prefixURI;
if (this._baseURI === null || absoluteURI.test(token.value))
prefixURI = token.value;
var prefixIRI;
if (this._baseIRI === null || absoluteIRI.test(token.value))
prefixIRI = token.value;
else
prefixURI = this._resolveURI(token.value);
this._prefixes[this._prefix] = prefixURI;
this._prefixCallback(this._prefix, prefixURI);
prefixIRI = this._resolveIRI(token);
this._prefixes[this._prefix] = prefixIRI;
this._prefixCallback(this._prefix, prefixIRI);
return this._readDeclarationPunctuation;
},
// ### `_readBaseURI` reads the URI of a base declaration.
_readBaseURI: function (token) {
// ### `_readBaseIRI` reads the IRI of a base declaration.
_readBaseIRI: function (token) {
if (token.type !== 'IRI')
return this._error('Expected IRI to follow base declaration', token);
if (token.value.indexOf('#') > 0)
return this._error('Invalid base URI', token);
if (this._baseURI === null || absoluteURI.test(token.value))
this._baseURI = token.value;
return this._error('Invalid base IRI', token);
if (this._baseIRI === null || absoluteIRI.test(token.value))
this._baseIRI = token.value;
else
this._baseURI = this._resolveURI(token.value);
this._baseURIPath = this._baseURI.replace(documentPart, '');
this._baseURIRoot = this._baseURI.match(rootURI)[0];
this._baseIRI = this._resolveIRI(token);
this._baseIRIPath = this._baseIRI.replace(documentPart, '');
this._baseIRIRoot = this._baseIRI.match(rootIRI)[0];
return this._readDeclarationPunctuation;
},
// ### `_readNamedGraphLabel` reads the label of a named graph.
_readNamedGraphLabel: function (token) {
switch (token.type) {
case 'IRI':
case 'prefixed':
return this._readSubject(token), this._readGraph;
case '[':
return this._readNamedGraphBlankLabel;
default:
return this._error('Invalid graph label', token);
}
},
// ### `_readNamedGraphLabel` reads a blank node label of a named graph.
_readNamedGraphBlankLabel: function (token) {
if (token.type !== ']')
return this._error('Invalid graph label', token);
this._subject = '_:b' + blankNodeCount++;
return this._readGraph;
},
// ### `_readDeclarationPunctuation` reads the punctuation of a declaration.

@@ -481,20 +568,21 @@ _readDeclarationPunctuation: function (token) {

// ### `_resolveURI` resolves a URI against a base path
_resolveURI: function (uri) {
switch (uri[0]) {
// An empty relative URI indicates the base URI
// ### `_resolveIRI` resolves an IRI token against the base path
_resolveIRI: function (token) {
var iri = token.value;
switch (iri[0]) {
// An empty relative IRI indicates the base IRI
case undefined:
return this._baseURI;
// Resolve relative fragment URIs against the base URI
return this._baseIRI;
// Resolve relative fragment IRIs against the base IRI
case '#':
return this._baseURI + uri;
// Resolve relative query string URIs by replacing the query string
return this._baseIRI + iri;
// Resolve relative query string IRIs by replacing the query string
case '?':
return this._baseURI.replace(/(?:\?.*)?$/, uri);
// Resolve root relative URIs at the root of the base URI
return this._baseIRI.replace(/(?:\?.*)?$/, iri);
// Resolve root relative IRIs at the root of the base IRI
case '/':
return this._baseURIRoot + uri;
// Resolve all other URIs at the base URI's path
return this._baseIRIRoot + iri;
// Resolve all other IRIs at the base IRI's path
default:
return this._baseURIPath + uri;
return this._baseIRIPath + iri;
}

@@ -510,3 +598,4 @@ },

this._readCallback = this._readInTopContext;
this._prefixes = {};
this._prefixes = Object.create(null);
this._prefixes._ = '_:b' + blankNodePrefix++ + '_';

@@ -524,8 +613,6 @@ // If the input argument is not given, shift parameters

this._lexer.tokenize(input, function (error, token) {
if (self._readCallback !== undefined) {
if (error !== null)
self._callback(error);
else
self._readCallback = self._readCallback(token);
}
if (error !== null)
self._callback(error), self._callback = noop;
else if (self._readCallback !== undefined)
self._readCallback = self._readCallback(token);
});

@@ -532,0 +619,0 @@

@@ -1,2 +0,2 @@

// **N3Store** objects store N3 triples with an associated context in memory.
// **N3Store** objects store N3 triples by graph in memory.

@@ -6,14 +6,14 @@ var expandPrefixedName = require('./N3Util').expandPrefixedName;

// ## Constructor
function N3Store(triples, prefixes) {
function N3Store(triples, options) {
if (!(this instanceof N3Store))
return new N3Store(triples, prefixes);
return new N3Store(triples, options);
// The number of triples is initially zero.
this._size = 0;
// `_contexts` contains subject, predicate, and object indexes per context.
this._contexts = Object.create(null);
// `_graphs` contains subject, predicate, and object indexes per graph.
this._graphs = Object.create(null);
// `_entities` maps entities such as `http://xmlns.com/foaf/0.1/name` to numbers.
// This saves memory, since only the numbers have to be stored in `_contexts`.
// This saves memory, since only the numbers have to be stored in `_graphs`.
this._entities = Object.create(null);
this._entities['>>____unused_item_to_make_first_entity_key_non-falsy____<<'] = 0;
this._entities['><'] = 0; // Dummy entry, so the first actual key is non-zero
this._entityCount = 0;

@@ -24,9 +24,11 @@ // `_blankNodeIndex` is the index of the last created blank node that was automatically named

// Shift parameters if `triples` is not given
if (!prefixes && triples && !triples[0])
prefixes = triples, triples = null;
if (!options && triples && !triples[0])
options = triples, triples = null;
// Add triples and prefixes if passed
this._prefixes = Object.create(null);
triples && this.addTriples(triples);
prefixes && this.addPrefixes(prefixes);
if (options && options.prefixes)
this.addPrefixes(options.prefixes);
if (triples)
this.addTriples(triples);
}

@@ -37,7 +39,2 @@

// `defaultContext` is the default context wherein triples are stored.
get defaultContext() {
return 'n3/contexts#default';
},
// ### `size` returns the number of triples in the store.

@@ -51,5 +48,5 @@ get size() {

// Calculate the number of triples by counting to the deepest level.
var contexts = this._contexts, subjects, subject;
for (var contextKey in contexts)
for (var subjectKey in (subjects = contexts[contextKey].subjects))
var graphs = this._graphs, subjects, subject;
for (var graphKey in graphs)
for (var subjectKey in (subjects = graphs[graphKey].subjects))
for (var predicateKey in (subject = subjects[subjectKey]))

@@ -90,4 +87,4 @@ size += Object.keys(subject[predicateKey]).length;

// (for instance: _subject_, _predicate_, and _object_).
// Finally, `context` will be the context of the created triples.
_findInIndex: function (index0, key0, key1, key2, name0, name1, name2, context) {
// Finally, `graph` will be the graph of the created triples.
_findInIndex: function (index0, key0, key1, key2, name0, name1, name2, graph) {
var results = [], entityKeys = Object.keys(this._entities), tmp, index1, index2;

@@ -111,3 +108,3 @@

for (var l = values.length - 1; l >= 0; l--) {
var result = { context: context };
var result = { subject: '', predicate: '', object: '', graph: graph };
result[name0] = entity0;

@@ -154,14 +151,14 @@ result[name1] = entity1;

// ### `addTriple` adds a new N3 triple to the store.
addTriple: function (subject, predicate, object, context) {
addTriple: function (subject, predicate, object, graph) {
// Shift arguments if a triple object is given instead of components
if (!predicate)
context = subject.context, object = subject.object,
graph = subject.graph, object = subject.object,
predicate = subject.predicate, subject = subject.subject;
// Find the context that will contain the triple.
context = context || this.defaultContext;
var contextItem = this._contexts[context];
// Create the context if it doesn't exist yet.
if (!contextItem) {
contextItem = this._contexts[context] = {
// Find the graph that will contain the triple.
graph = graph || '';
var graphItem = this._graphs[graph];
// Create the graph if it doesn't exist yet.
if (!graphItem) {
graphItem = this._graphs[graph] = {
subjects: {},

@@ -171,8 +168,8 @@ predicates: {},

};
// Freezing a context helps subsequent `add` performance,
// Freezing a graph helps subsequent `add` performance,
// and properties will never be modified anyway.
Object.freeze(contextItem);
Object.freeze(graphItem);
}
// Since entities can often be long URIs, we avoid storing them in every index.
// Since entities can often be long IRIs, we avoid storing them in every index.
// Instead, we have a separate index that maps entities to numbers,

@@ -185,5 +182,5 @@ // which are then used as keys in the other indexes.

this._addToIndex(contextItem.subjects, subject, predicate, object);
this._addToIndex(contextItem.predicates, predicate, object, subject);
this._addToIndex(contextItem.objects, object, subject, predicate);
this._addToIndex(graphItem.subjects, subject, predicate, object);
this._addToIndex(graphItem.predicates, predicate, object, subject);
this._addToIndex(graphItem.objects, object, subject, predicate);

@@ -201,4 +198,4 @@ // The cached triple count is now invalid.

// ### `addPrefix` adds support for querying with the given prefix
addPrefix: function (prefix, uri) {
this._prefixes[prefix] = uri;
addPrefix: function (prefix, iri) {
this._prefixes[prefix] = iri;
},

@@ -213,19 +210,19 @@

// ### `removeTriple` removes an N3 triple from the store if it exists.
removeTriple: function (subject, predicate, object, context) {
removeTriple: function (subject, predicate, object, graph) {
// Shift arguments if a triple object is given instead of components.
if (!predicate)
context = subject.context, object = subject.object,
graph = subject.graph, object = subject.object,
predicate = subject.predicate, subject = subject.subject;
context = context || this.defaultContext;
graph = graph || '';
// Find internal identifiers for all components.
var contextItem, entities = this._entities, contexts = this._contexts;
var graphItem, entities = this._entities, graphs = this._graphs;
if (!(subject = entities[subject])) return;
if (!(predicate = entities[predicate])) return;
if (!(object = entities[object])) return;
if (!(contextItem = contexts[context])) return;
if (!(graphItem = graphs[graph])) return;
// Verify that the triple exists.
var subjects, predicates;
if (!(subjects = contextItem.subjects[subject])) return;
if (!(subjects = graphItem.subjects[subject])) return;
if (!(predicates = subjects[predicate])) return;

@@ -235,10 +232,10 @@ if (!(object in predicates)) return;

// Remove it from all indexes.
this._removeFromIndex(contextItem.subjects, subject, predicate, object);
this._removeFromIndex(contextItem.predicates, predicate, object, subject);
this._removeFromIndex(contextItem.objects, object, subject, predicate);
this._removeFromIndex(graphItem.subjects, subject, predicate, object);
this._removeFromIndex(graphItem.predicates, predicate, object, subject);
this._removeFromIndex(graphItem.objects, object, subject, predicate);
if (this._size !== null) this._size--;
// Remove the context if it is empty.
for (subject in contextItem.subjects) return;
delete contexts[context];
// Remove the graph if it is empty.
for (subject in graphItem.subjects) return;
delete graphs[graph];
},

@@ -254,24 +251,24 @@

// Setting `subject`, `predicate`, or `object` to `null` means an _anything_ wildcard.
// Setting `context` to `null` means the default context.
find: function (subject, predicate, object, context) {
// Setting `graph` to `null` means the default graph.
find: function (subject, predicate, object, graph) {
var prefixes = this._prefixes;
return this.findByUri(
return this.findByIRI(
expandPrefixedName(subject, prefixes),
expandPrefixedName(predicate, prefixes),
expandPrefixedName(object, prefixes),
expandPrefixedName(context, prefixes)
expandPrefixedName(graph, prefixes)
);
},
// ### `findByUri` finds a set of triples matching a pattern.
// Setting `subject`, `predicate`, or `object` to `null` means an _anything_ wildcard.
// Setting `context` to `null` means the default context.
findByUri: function (subject, predicate, object, context) {
context = context || this.defaultContext;
var contextItem = this._contexts[context], entities = this._entities;
// ### `findByIRI` finds a set of triples matching a pattern.
// Setting `subject`, `predicate`, or `object` to a falsy value means an _anything_ wildcard.
// Setting `graph` to a falsy value means the default graph.
findByIRI: function (subject, predicate, object, graph) {
graph = graph || '';
var graphItem = this._graphs[graph], entities = this._entities;
// If the specified context contain no triples, there are no results.
if (!contextItem) return [];
// If the specified graph contain no triples, there are no results.
if (!graphItem) return [];
// Translate URIs to internal index keys.
// Translate IRIs to internal index keys.
// Optimization: if the entity doesn't exist, no triples with it exist.

@@ -286,21 +283,21 @@ if (subject && !(subject = entities[subject])) return [];

// If subject and object are given, the object index will be the fastest.
return this._findInIndex(contextItem.objects, object, subject, predicate,
'object', 'subject', 'predicate', context);
return this._findInIndex(graphItem.objects, object, subject, predicate,
'object', 'subject', 'predicate', graph);
else
// If only subject and possibly predicate are given, the subject index will be the fastest.
return this._findInIndex(contextItem.subjects, subject, predicate, null,
'subject', 'predicate', 'object', context);
return this._findInIndex(graphItem.subjects, subject, predicate, null,
'subject', 'predicate', 'object', graph);
}
else if (predicate)
// If only predicate and possibly object are given, the predicate index will be the fastest.
return this._findInIndex(contextItem.predicates, predicate, object, null,
'predicate', 'object', 'subject', context);
return this._findInIndex(graphItem.predicates, predicate, object, null,
'predicate', 'object', 'subject', graph);
else if (object)
// If only object is given, the object index will be the fastest.
return this._findInIndex(contextItem.objects, object, null, null,
'object', 'subject', 'predicate', context);
return this._findInIndex(graphItem.objects, object, null, null,
'object', 'subject', 'predicate', graph);
else
// If nothing is given, iterate subjects and predicates first
return this._findInIndex(contextItem.subjects, null, null, null,
'subject', 'predicate', 'object', context);
return this._findInIndex(graphItem.subjects, null, null, null,
'subject', 'predicate', 'object', graph);
},

@@ -310,24 +307,24 @@

// Setting `subject`, `predicate`, or `object` to `null` means an _anything_ wildcard.
// Setting `context` to `null` means the default context.
count: function (subject, predicate, object, context) {
// Setting `graph` to `null` means the default graph.
count: function (subject, predicate, object, graph) {
var prefixes = this._prefixes;
return this.countByUri(
return this.countByIRI(
expandPrefixedName(subject, prefixes),
expandPrefixedName(predicate, prefixes),
expandPrefixedName(object, prefixes),
expandPrefixedName(context, prefixes)
expandPrefixedName(graph, prefixes)
);
},
// ### `countByUri` returns the number of triples matching a pattern.
// ### `countByIRI` returns the number of triples matching a pattern.
// Setting `subject`, `predicate`, or `object` to `null` means an _anything_ wildcard.
// Setting `context` to `null` means the default context.
countByUri: function (subject, predicate, object, context) {
context = context || this.defaultContext;
var contextItem = this._contexts[context], entities = this._entities;
// Setting `graph` to `null` means the default graph.
countByIRI: function (subject, predicate, object, graph) {
graph = graph || '';
var graphItem = this._graphs[graph], entities = this._entities;
// If the specified context contain no triples, there are no results.
if (!contextItem) return 0;
// If the specified graph contain no triples, there are no results.
if (!graphItem) return 0;
// Translate URIs to internal index keys.
// Translate IRIs to internal index keys.
// Optimization: if the entity doesn't exist, no triples with it exist.

@@ -342,14 +339,14 @@ if (subject && !(subject = entities[subject])) return 0;

// If subject and object are given, the object index will be the fastest.
return this._countInIndex(contextItem.objects, object, subject, predicate);
return this._countInIndex(graphItem.objects, object, subject, predicate);
else
// If only subject and possibly predicate are given, the subject index will be the fastest.
return this._countInIndex(contextItem.subjects, subject, predicate, object);
return this._countInIndex(graphItem.subjects, subject, predicate, object);
}
else if (predicate) {
// If only predicate and possibly object are given, the predicate index will be the fastest.
return this._countInIndex(contextItem.predicates, predicate, object, subject);
return this._countInIndex(graphItem.predicates, predicate, object, subject);
}
else {
// If only object is possibly given, the object index will be the fastest.
return this._countInIndex(contextItem.objects, object, subject, predicate);
return this._countInIndex(graphItem.objects, object, subject, predicate);
}

@@ -368,5 +365,3 @@ },

else {
do {
name = '_:b' + this._blankNodeIndex++;
}
do { name = '_:b' + this._blankNodeIndex++; }
while (this._entities[name]);

@@ -373,0 +368,0 @@ }

@@ -7,5 +7,5 @@ // **N3StreamParser** parses an N3 stream into a triple stream

// ## Constructor
function N3StreamParser(config) {
function N3StreamParser(options) {
if (!(this instanceof N3StreamParser))
return new N3StreamParser(config);
return new N3StreamParser(options);

@@ -17,3 +17,3 @@ // Initialize Transform base class

// Set up parser
var self = this, parser = new N3Parser(config);
var self = this, parser = new N3Parser(options);
parser.parse(

@@ -20,0 +20,0 @@ // Handle triples by pushing them down the pipeline

@@ -7,5 +7,5 @@ // **N3StreamWriter** serializes a triple stream into an N3 stream

// ## Constructor
function N3StreamWriter(prefixes) {
function N3StreamWriter(options) {
if (!(this instanceof N3StreamWriter))
return new N3StreamWriter(prefixes);
return new N3StreamWriter(options);

@@ -21,3 +21,3 @@ // Initialize Transform base class

end: function (callback) { self.push(null); callback && callback(); },
}, prefixes);
}, options);

@@ -24,0 +24,0 @@ // Implement Transform methods on top of writer

@@ -7,4 +7,4 @@ // **N3Util** provides N3 utility functions

var N3Util = {
// Tests whether the given entity (triple object) represents a URI in the N3 library
isUri: function (entity) {
// Tests whether the given entity (triple object) represents an IRI in the N3 library
isIRI: function (entity) {
if (!entity)

@@ -55,3 +55,3 @@ return entity;

// Expands the prefixed name to a full URI (also when it occurs as a literal's type)
// Expands the prefixed name to a full IRI (also when it occurs as a literal's type)
expandPrefixedName: function (prefixedName, prefixes) {

@@ -58,0 +58,0 @@ var match = /(?:^|"\^\^)([^:\/#"'\^_]*):[^\/]/.exec(prefixedName);

@@ -11,15 +11,15 @@ // **N3Writer** writes N3 documents.

// Characters in literals that require escaping
var literalEscape = /["\\\t\n\r\b\f]/,
literalEscapeAll = /["\\\t\n\r\b\f]/g,
literalReplacements = { '\\': '\\\\', '"': '\\"', '\t': '\\t',
'\n': '\\n', '\r': '\\r', '\b': '\\b', '\f': '\\f' };
var escape = /["\\\t\n\r\b\f\u0000-\u0019\ud800-\udbff]/,
escapeAll = /["\\\t\n\r\b\f\u0000-\u0019]|[\ud800-\udbff][\udc00-\udfff]/g,
escapeReplacements = { '\\': '\\\\', '"': '\\"', '\t': '\\t',
'\n': '\\n', '\r': '\\r', '\b': '\\b', '\f': '\\f' };
// ## Constructor
function N3Writer(outputStream, prefixes) {
function N3Writer(outputStream, options) {
if (!(this instanceof N3Writer))
return new N3Writer(outputStream, prefixes);
return new N3Writer(outputStream, options);
// Shift arguments if the first argument is not a stream
if (outputStream && typeof outputStream.write !== 'function')
prefixes = outputStream, outputStream = null;
options = outputStream, outputStream = null;

@@ -35,7 +35,15 @@ // If no output stream given, send the output as string through the end callback

}
this._outputStream = outputStream;
this._outputStream = outputStream;
this._prefixURIs = Object.create(null);
if (prefixes)
this.addPrefixes(prefixes);
// Initialize writer, depending on the format
this._subject = null;
options = options || {};
if (!(/triple|quad/i).test(options.format)) {
this._graph = '';
this._prefixIRIs = Object.create(null);
options.prefixes && this.addPrefixes(options.prefixes);
}
else {
this._writeTriple = this._writeTripleLine;
}
}

@@ -46,70 +54,104 @@

// ### `_write` writes the arguments to the output stream (last argument is callback)
_write: function () {
for (var i = 0, l = arguments.length - 2; i <= l; i++)
this._outputStream.write(arguments[i], 'utf8', i === l ? arguments[l + 1] : null);
// ### `_write` writes the argument to the output stream
_write: function (string, callback) {
this._outputStream.write(string, 'utf8', callback);
},
// ### `_writeUriOrBlankNode` writes a URI or blank node to the output stream
_writeUriOrBlankNode: function (uri, done) {
// Write blank node
if (uri[0] === '_' && uri[1] === ':')
return this._write(uri, done);
// Write prefixed name if possible
var prefixMatch = this._prefixRegex.exec(uri);
if (prefixMatch)
this._write(this._prefixURIs[prefixMatch[1]], prefixMatch[2], done);
// Otherwise, just write the URI
else
this._write('<', uri, '>', done);
// ### `_writeTriple` writes the triple to the output stream
_writeTriple: function (subject, predicate, object, graph, done) {
try {
// Write the graph's label if it has changed
if (this._graph !== graph) {
// Close the previous graph and start the new one
this._write((this._subject === null ? '' : (this._graph ? '\n}\n' : '.\n')) +
(graph ? this._encodeIriOrBlankNode(graph) + ' {\n' : ''));
this._graph = graph, this._subject = null;
}
// Don't repeat the subject if it's the same
if (this._subject === subject) {
// Don't repeat the predicate if it's the same
if (this._predicate === predicate)
this._write(', ' + this._encodeObject(object), done);
// Same subject, different predicate
else
this._write(';\n ' +
this._encodePredicate(this._predicate = predicate) + ' ' +
this._encodeObject(object), done);
}
// Different subject; write the whole triple
else
this._write((this._subject === null ? '' : '.\n') +
this._encodeSubject(this._subject = subject) + ' ' +
this._encodePredicate(this._predicate = predicate) + ' ' +
this._encodeObject(object), done);
}
catch (error) { done && done(error); }
},
// ### `_writeLiteral` writes a literal to the output stream
_writeLiteral: function (value, type, language, done) {
// Escape the literal if necessary
if (literalEscape.test(value)) {
value = value.replace(literalEscapeAll, function (match) {
return literalReplacements[match];
});
// ### `_writeTripleLine` writes the triple or quad to the output stream as a single line
_writeTripleLine: function (subject, predicate, object, graph, done) {
// Don't use prefixes
delete this._prefixMatch;
// Write the triple
try {
this._write(this._encodeIriOrBlankNode(subject) + ' ' +
this._encodeIriOrBlankNode(predicate) + ' ' +
this._encodeObject(object) +
(graph ? ' ' + this._encodeIriOrBlankNode(graph) + '.\n' : '.\n'), done);
}
catch (error) { done && done(error); }
},
// ### `_encodeIriOrBlankNode` represents an IRI or blank node
_encodeIriOrBlankNode: function (iri) {
// A blank node is represented as-is
if (iri[0] === '_' && iri[1] === ':') return iri;
// Escape special characters
if (escape.test(iri))
iri = iri.replace(escapeAll, characterReplacer);
// Try to represent the IRI as prefixed name
var prefixMatch = this._prefixRegex.exec(iri);
return prefixMatch ? this._prefixIRIs[prefixMatch[1]] + prefixMatch[2] : '<' + iri + '>';
},
// ### `_encodeLiteral` represents a literal
_encodeLiteral: function (value, type, language) {
// Escape special characters
if (escape.test(value))
value = value.replace(escapeAll, characterReplacer);
// Write the literal, possibly with type or language
this._write('"', value, '"', type || language ? null : done);
if (type) {
this._write('^^', null);
this._writeUriOrBlankNode(type, done);
}
else if (language) {
this._write('@', language, done);
}
if (language)
return '"' + value + '"@' + language;
else if (type)
return '"' + value + '"^^' + this._encodeIriOrBlankNode(type);
else
return '"' + value + '"';
},
// ### `_writeSubject` writes a subject to the output stream
_writeSubject: function (subject, done) {
// ### `_encodeSubject` represents a subject
_encodeSubject: function (subject) {
if (subject[0] === '"')
throw new Error('A literal as subject is not allowed: ' + subject);
this._writeUriOrBlankNode(subject, done);
return this._encodeIriOrBlankNode(subject);
},
// ### `_writePredicate` writes a predicate to the output stream
_writePredicate: function (predicate, done) {
// ### `_encodePredicate` represents a predicate
_encodePredicate: function (predicate) {
if (predicate[0] === '"')
throw new Error('A literal as predicate is not allowed: ' + predicate);
if (predicate === RDF_TYPE)
this._write('a', done);
else
this._writeUriOrBlankNode(predicate, done);
return predicate === RDF_TYPE ? 'a' : this._encodeIriOrBlankNode(predicate);
},
// ### `_writeObject` writes an object to the output stream
_writeObject: function (object, done) {
var literalMatch = N3LiteralMatcher.exec(object);
if (literalMatch !== null)
this._writeLiteral(literalMatch[1], literalMatch[2], literalMatch[3], done);
else
this._writeUriOrBlankNode(object, done);
// ### `_encodeObject` represents an object
_encodeObject: function (object) {
// Represent an IRI or blank node
if (object[0] !== '"')
return this._encodeIriOrBlankNode(object);
// Represent a literal
var match = N3LiteralMatcher.exec(object);
if (!match) throw new Error('Invalid literal: ' + object);
return this._encodeLiteral(match[1], match[2], match[3]);
},
// ### `_blockedWrite` replaces `_writer_ after the writer has been closed
// ### `_blockedWrite` replaces `_write` after the writer has been closed
_blockedWrite: function () {

@@ -120,46 +162,13 @@ throw new Error('Cannot write because the writer has been closed.');

// ### `addTriple` adds the triple to the output stream
addTriple: function (subject, predicate, object, done) {
// If the triple was given as a triple object, shift parameters
if (!object) {
done = predicate;
object = subject.object;
predicate = subject.predicate;
subject = subject.subject;
}
// Write the triple
try {
// Don't repeat the subject if it's the same
if (this._prevSubject === subject) {
// Don't repeat the predicate if it's the same
if (this._prevPredicate === predicate) {
this._write(', ', null);
}
// Same subject, different predicate
else {
this._write(';\n ', null);
this._writePredicate(predicate);
this._write(' ', null);
this._prevPredicate = predicate;
}
}
// Different subject; write the whole triple
else {
// End a possible previous triple
if ('_prevSubject' in this)
this._write('.\n', null);
this._writeSubject(subject);
this._write(' ', null);
this._writePredicate(predicate);
this._write(' ', null);
this._prevSubject = subject;
this._prevPredicate = predicate;
}
// In all cases, write the object
this._writeObject(object, done);
}
catch (error) {
done && done(error);
}
addTriple: function (subject, predicate, object, graph, done) {
// The triple was given as a triple object, so shift parameters
if (!object)
this._writeTriple(subject.subject, subject.predicate, subject.object,
subject.graph || '', predicate);
// The optional `graph` parameter was not provided
else if (typeof graph !== 'string')
this._writeTriple(subject, predicate, object, '', graph);
// The `graph` parameter was provided
else
this._writeTriple(subject, predicate, object, graph, done);
},

@@ -174,5 +183,5 @@

// ### `addPrefix` adds the prefix to the output stream
addPrefix: function (prefix, uri, done) {
addPrefix: function (prefix, iri, done) {
var prefixes = {};
prefixes[prefix] = uri;
prefixes[prefix] = iri;
this.addPrefixes(prefixes, done);

@@ -187,13 +196,13 @@ },

// Verify whether the prefix can be used and does not exist yet
var uri = prefixes[prefix];
if (/[#\/]$/.test(uri) && this._prefixURIs[uri] !== (prefix += ':')) {
var iri = prefixes[prefix];
if (/[#\/]$/.test(iri) && this._prefixIRIs[iri] !== (prefix += ':')) {
hasPrefixes = true;
this._prefixURIs[uri] = prefix;
this._prefixIRIs[iri] = prefix;
// Finish a possible pending triple
if ('_prevSubject' in this) {
this._write('.\n', null);
delete this._prevSubject;
if (this._subject !== null) {
this._write(this._graph ? '\n}\n' : '.\n');
this._subject = null, this._graph = '';
}
// Write prefix
this._write('@prefix ', prefix, ' <', uri, '>.\n', null);
this._write('@prefix ' + prefix + ' <' + iri + '>.\n');
}

@@ -203,7 +212,7 @@ }

if (hasPrefixes) {
var prefixURIs = '';
for (var prefixURI in this._prefixURIs)
prefixURIs += prefixURIs ? '|' + prefixURI : prefixURI;
prefixURIs = prefixURIs.replace(/[\]\/\(\)\*\+\?\.\\\$]/g, '\\$&');
this._prefixRegex = new RegExp('^(' + prefixURIs + ')([a-zA-Z][\\-_a-zA-Z0-9]*)$');
var prefixIRIs = '';
for (var prefixIRI in this._prefixIRIs)
prefixIRIs += prefixIRIs ? '|' + prefixIRI : prefixIRI;
prefixIRIs = prefixIRIs.replace(/[\]\/\(\)\*\+\?\.\\\$]/g, '\\$&');
this._prefixRegex = new RegExp('^(' + prefixIRIs + ')([a-zA-Z][\\-_a-zA-Z0-9]*)$');
}

@@ -214,4 +223,4 @@ // End a prefix block with a newline

// ### `_prefixRegex` matches an URL that begins with one of the added prefixes
_prefixRegex: { exec: function () { return null; } },
// ### `_prefixRegex` matches an IRI that begins with one of the added prefixes
_prefixRegex: /$0^/,

@@ -221,5 +230,5 @@ // ### `end` signals the end of the output stream

// Finish a possible pending triple
if ('_prevSubject' in this) {
this._write('.\n', null);
delete this._prevSubject;
if (this._subject !== null) {
this._write(this._graph ? '\n}\n' : '.\n');
this._subject = null;
}

@@ -242,2 +251,22 @@ // Disallow further writing

// Replaces a character by its escaped version
function characterReplacer(character) {
// Replace a single character by its escaped version
var result = escapeReplacements[character];
if (result === undefined) {
// Replace a single character with its 4-bit unicode escape sequence
if (character.length === 1) {
result = character.charCodeAt(0).toString(16);
result = '\\u0000'.substr(0, 6 - result.length) + result;
}
// Replace a surrogate pair with its 8-bit unicode escape sequence
else {
result = ((character.charCodeAt(0) - 0xD800) * 0x400 +
character.charCodeAt(1) + 0x2400).toString(16);
result = '\\U00000000'.substr(0, 10 - result.length) + result;
}
}
return result;
}
// ## Exports

@@ -244,0 +273,0 @@

{
"name": "n3",
"version": "0.3.6",
"version": "0.4.0",
"description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.",

@@ -19,3 +19,3 @@ "author": "Ruben Verborgh <ruben.verborgh@gmail.com>",

"devDependencies": {
"async": "~0.1.22",
"async": "~0.9.0",
"chai": "~1.4.2",

@@ -27,6 +27,6 @@ "chai-things": "~0.1.1",

"request": "~2.22.0",
"mocha": ">=1.15.0",
"mocha": "~1.15.0",
"istanbul": "~0.3.0",
"uglify-js": "~2.4.3",
"browserify": ">=3.x",
"browserify": "~3.x",
"pre-commit": "~0.0.9"

@@ -36,7 +36,7 @@ },

"test": "mocha",
"hint": "jshint lib perf test",
"hint": "jshint lib perf test spec",
"browser": "node browser/build-browser-versions",
"coverage": "istanbul cover node_modules/.bin/_mocha -- -R spec --timeout 100",
"spec": "node spec/turtle-spec.js",
"spec-clean": "rm -r spec/turtle",
"spec": "node spec/turtle-spec && node spec/trig-spec && node spec/ntriples-spec && node spec/nquads-spec",
"spec-clean": "rm -r spec/turtle spec/trig",
"docs": "docco lib/*.js"

@@ -43,0 +43,0 @@ },

#!/usr/bin/env node
var n3 = require('../N3');
var N3 = require('../N3');
var fs = require('fs'),

@@ -15,3 +15,3 @@ assert = require('assert');

var count = 0;
new n3.Lexer().tokenize(fs.createReadStream(filename), function (error, token) {
new N3.Lexer().tokenize(fs.createReadStream(filename), function (error, token) {
assert(!error, error);

@@ -18,0 +18,0 @@ count++;

#!/usr/bin/env node
var n3 = require('../N3');
var N3 = require('../N3');
var fs = require('fs'),

@@ -15,3 +15,3 @@ assert = require('assert');

var count = 0;
new n3.Parser().parse(fs.createReadStream(filename), function (error, triple) {
new N3.Parser().parse(fs.createReadStream(filename), function (error, triple) {
assert(!error, error);

@@ -18,0 +18,0 @@ if (triple) {

#!/usr/bin/env node
var n3 = require('../N3');
var N3 = require('../N3');
var assert = require('assert');

@@ -13,3 +13,3 @@

var store = new n3.Store();
var store = new N3.Store();

@@ -16,0 +16,0 @@ TEST = '- Adding ' + dimCubed + ' triples';

@@ -1,20 +0,25 @@

# Lightning fast, asynchronous, streaming Turtle for JavaScript
# Lightning fast, asynchronous, streaming RDF for JavaScript
The N3.js library lets you handle [Turtle](http://www.w3.org/TR/turtle/) and [RDF](http://www.w3.org/TR/rdf-primer/) in JavaScript _([Node](http://nodejs.org/) and browser)_ easily.
The N3.js library lets you handle [RDF](http://www.w3.org/TR/rdf-primer/) in JavaScript easily, in [Node.js](http://nodejs.org/) and the browser.
It offers:
- [**Turtle parsing**](#parsing) _([fully compliant](https://github.com/RubenVerborgh/N3.js/tree/master/spec) with the [Turtle standard](http://www.w3.org/TR/turtle/))_
- [**in-memory storage and manipulation**](#storing)
- [**Turtle writing**](#writing)
- [**Parsing**](#parsing) triples/quads from
[Turtle](http://www.w3.org/TR/turtle/),
[TriG](http://www.w3.org/TR/trig/),
[N-Triples](http://www.w3.org/TR/ntriples/)
and [N-Quads](http://www.w3.org/TR/nquads/).
- [**Writing**](#Writing) triples/quads to
[Turtle](http://www.w3.org/TR/turtle/),
[TriG](http://www.w3.org/TR/trig/),
[N-Triples](http://www.w3.org/TR/ntriples/)
and [N-Quads](http://www.w3.org/TR/nquads/).
- **Storage** of triples/quads in memory
It has the following characteristics:
- extreme performance – by far the [fastest parser in JavaScript](https://github.com/RubenVerborgh/N3.js/tree/master/perf)
- asynchronous – triples arrive as soon as possible
- streaming – streams are parsed as data comes in, so you can easily parse files that don't fit into memory
Parsing and writing is:
- **asynchronous** – triples arrive as soon as possible
- **streaming** – streams are parsed as data comes in, so you can parse files larger than memory
- **fast** – by far the [fastest parser in JavaScript](https://github.com/RubenVerborgh/N3.js/tree/master/perf)
At a later stage, this library will support [Notation3 (N3)](http://www.w3.org/TeamSubmission/n3/),
a Turtle superset.
## Installation
N3.js comes as an [npm package](https://npmjs.org/package/n3).
For Node.js, N3.js comes as an [npm package](https://npmjs.org/package/n3).

@@ -29,8 +34,6 @@ ``` bash

It is also fully compatible with [browserify](http://browserify.org/).
<br>
Alternatively, it offers a minimal browser version (without Node stream support).
N3.js seamlessly works in browsers. Generate a browser version as follows:
``` bash
$ cd n3
$ cd N3.js
$ npm install

@@ -44,9 +47,10 @@ $ npm run browser

In addition, N3.js is fully compatible with [browserify](http://browserify.org/),
so you can write code for Node.js and deploy it to browsers.
## Triple representation
For maximum performance and easy of use,
triples are represented as simple objects.
<br>
Since URIs are most common when dealing with RDF,
they are represented as simple strings.
triples are simple objects with string properties.
**URLs, URIs and IRIs are simple strings.** For example, parsing this RDF document:
``` Turtle

@@ -56,3 +60,3 @@ @prefix c: <http://example.org/cartoons#>.

```
is represented as
results in this JavaScript object:
``` js

@@ -66,8 +70,7 @@ {

Literals are represented as double quoted strings.
**Literals are represented as double quoted strings.** For example, parsing this RDF document:
``` Turtle
c:Tom c:name "Tom".
```
is represented as
results in this JavaScript object:
``` js

@@ -87,16 +90,19 @@ {

The [Utility](#utility) section details entity representation in more depth.
An optional fourth element signals the graph to which a triple belongs:
``` js
{
subject: 'http://example.org/cartoons#Tom',
predicate: 'http://example.org/cartoons#name',
object: '"Tom"',
graph: 'http://example.org/mycartoon'
}
```
The N3.js [Utility](#utility) (`N3.Util`) can help you with these representations.
## Parsing
### From a Turtle string to triples
### From an RDF document to triples
`N3.Parser` parses strings into triples using a callback.
<br>
The callback's first argument is an error value,
the second is a triple.
If there are no more triples,
the callback is invoked one last time with `null` as `triple` value
and a hash of prefixes as the third argument.
`N3.Parser` transforms Turtle, TriG, N-Triples or N-Quads document into triples through a callback:
``` js

@@ -115,9 +121,24 @@ var parser = N3.Parser();

```
The callback's first argument is an error value, the second is a triple.
If there are no more triples,
the callback is invoked one last time with `null` for `triple`
and a hash of prefixes as third argument.
<br>
Pass a second callback to `parse` to retrieve prefixes as they are read.
Addionally, a second callback `function (prefix, uri)` can be passed to `parse`.
By default, `N3.Parser` parses a permissive superset of Turtle, TriG, N-Triples and N-Quads.
<br>
For strict compatibility with any of those languages, pass a `format` argument upon creation:
### From Turtle fragments to triples
``` js
var parser1 = N3.Parser({ format: 'N-Triples' });
var parser2 = N3.Parser({ format: 'application/trig' });
```
`N3.Parser` can also parse triples from a Turtle document that arrives in fragments.
### From RDF chunks to triples
`N3.Parser` can also parse triples from RDF documents arriving in chunks,
for instance, when being downloaded or read from disk.
Use `addChunk` to add a piece of data, and `end` to signal the end.
``` js

@@ -137,23 +158,24 @@ var parser = N3.Parser(), triples = [];

### From a Turtle stream to triples
### From an RDF stream to triples
`N3.Parser` can parse streams as they grow, returning triples as soon as they're ready.
`N3.Parser` can parse [Node.js streams](http://nodejs.org/api/stream.html) as they grow,
returning triples as soon as they're ready.
<br>
This behavior sets N3.js apart from most other Turtle libraries.
This behavior sets N3.js apart from most other libraries.
``` js
var parser = N3.Parser(),
turtleStream = fs.createReadStream('cartoons.ttl');
parser.parse(turtleStream, console.log);
rdfStream = fs.createReadStream('cartoons.ttl');
parser.parse(rdfStream, console.log);
```
In addition, `N3.StreamParser` offers a [Node Stream](http://nodejs.org/api/stream.html) implementation,
so you can transform Turtle streams and pipe them to anywhere.
In addition, `N3.StreamParser` offers a [Node.js stream](http://nodejs.org/api/stream.html) implementation,
so you can transform RDF streams and pipe them to anywhere.
This solution is ideal if your consumer is slower,
as it avoids parser backpressure.
since source data is only read when the consumer is ready.
``` js
var streamParser = N3.StreamParser(),
turtleStream = fs.createReadStream('cartoons.ttl');
turtleStream.pipe(streamParser);
rdfStream = fs.createReadStream('cartoons.ttl');
rdfStream.pipe(streamParser);
streamParser.pipe(new SlowConsumer());

@@ -171,18 +193,4 @@

A dedicated `prefix` event signals every prefix with `prefix` and `uri` arguments.
A dedicated `prefix` event signals every prefix with `prefix` and `iri` arguments.
## Storing
In this example below, we create a new store and add the triples `:Pluto a :Dog.` and `:Mickey a :Mouse`.
Then, we find a triple with `:Mickey` as subject.
``` js
var store = N3.Store();
store.addTriple('http://example.org/Pluto', 'a', 'http://example.org/Dog');
store.addTriple('http://example.org/Mickey', 'a', 'http://example.org/Mouse');
var mickey = store.find('http://example.org/Mickey', null, null)[0];
console.log(mickey.subject, mickey.predicate, mickey.object, '.');
```
## Writing

@@ -192,6 +200,7 @@

`N3.Writer` can serialize triples as a Turtle string.
`N3.Writer` serializes triples as an RDF document.
Write triples through `addTriple`.
``` js
var writer = N3.Writer({ 'c': 'http://example.org/cartoons#' });
var writer = N3.Writer({ prefixes: { 'c': 'http://example.org/cartoons#' } });
writer.addTriple('http://example.org/cartoons#Tom',

@@ -208,8 +217,17 @@ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',

### From triples to a Turtle stream
By default, `N3.Writer` writes Turtle (or TriG for triples with a `graph` property).
<br>
To write N-Triples (or N-Quads) instead, pass a `format` argument upon creation:
`N3.Writer` can also write triples to an output stream.
``` js
var writer1 = N3.Writer({ format: 'N-Triples' });
var writer2 = N3.Writer({ format: 'application/trig' });
```
### From triples to an RDF stream
`N3.Writer` can also write triples to a Node.js stream.
``` js
var writer = N3.Writer(process.stdout, { 'c': 'http://example.org/cartoons#' });
var writer = N3.Writer(process.stdout, { prefixes: { 'c': 'http://example.org/cartoons#' } });
writer.addTriple('http://example.org/cartoons#Tom',

@@ -226,5 +244,5 @@ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',

### From a triple stream to a Turtle stream
### From a triple stream to an RDF stream
`N3.StreamWriter` is a Turtle writer implementation as a [Node Stream](http://nodejs.org/api/stream.html).
`N3.StreamWriter` is a writer implementation as a Node.js stream.

@@ -240,9 +258,26 @@ ``` js

## Storing
`N3.Store` allows you to store triples in memory and find them fast.
In this example, we create a new store and add the triples `:Pluto a :Dog.` and `:Mickey a :Mouse`.
<br>
Then, we find a triple with `:Mickey` as subject.
``` js
var store = N3.Store();
store.addTriple('http://ex.org/Pluto', 'http://ex.org/type', 'http://ex.org/Dog');
store.addTriple('http://ex.org/Mickey', 'http://ex.org/type', 'http://ex.org/Mouse');
var mickey = store.find('http://ex.org/Mickey', null, null)[0];
console.log(mickey.subject, mickey.predicate, mickey.object, '.');
```
## Utility
`N3.Util` offers helpers for URI and literal representations.
`N3.Util` offers helpers for IRI and literal representations.
<br>
As URIs are most common, they are represented as simple strings:
As IRIs are most common, they are represented as simple strings:
``` js
var N3Util = N3.Util;
N3Util.isUri('http://example.org/cartoons#Mickey'); // true
N3Util.isIRI('http://example.org/cartoons#Mickey'); // true
```

@@ -260,5 +295,5 @@ **Literals** are represented as double quoted strings:

```
Note the difference between `'http://example.org/'` (URI) and `'"http://example.org/"'` (literal).
Note the difference between `'http://example.org/'` (IRI) and `'"http://example.org/"'` (literal).
<br>
Also note that the double quoted literals are _not_ raw Turtle syntax:
Also note that the double quoted literals are _not_ raw Turtle/TriG syntax:
``` js

@@ -269,4 +304,4 @@ N3Util.isLiteral('"This word is "quoted"!"'); // true

The above string represents the string _This word is "quoted"!_,
even though the correct Turtle syntax for that is `"This word is \"quoted\"!"`
N3.js thus always parses literals, but adds quotes to differentiate from URIs:
even though the correct Turtle/TriG syntax for that is `"This word is \"quoted\"!"`
N3.js thus always parses literals, but adds quotes to differentiate from IRIs:
``` js

@@ -280,3 +315,3 @@ new N3.Parser().parse('<a> <b> "This word is \\"quoted\\"!".', console.log);

N3Util.isBlank('_:b1'); // true
N3Util.isUri('_:b1'); // false
N3Util.isIRI('_:b1'); // false
N3Util.isLiteral('_:b1'); // false

@@ -293,17 +328,41 @@ ```

### Loading the utility globally
For convenience, `N3Util` can also be loaded globally:
For convenience, `N3Util` can be loaded globally:
``` js
require('n3').Util(global);
isUri('http://example.org/cartoons#Mickey'); // true
isIRI('http://example.org/cartoons#Mickey'); // true
isLiteral('"Mickey Mouse"'); // true
```
If desired, the methods can even be added directly on all strings:
If desired, its methods can even be added directly on all strings:
``` js
require('n3').Util(String, true);
'http://example.org/cartoons#Mickey'.isUri(); // true
'http://example.org/cartoons#Mickey'.isIRI(); // true
'"Mickey Mouse"'.isLiteral(); // true
```
# License, status and contributions
## Compatibility
### Specifications
The N3.js parser and writer is fully compatible with the following W3C specifications:
- [RDF 1.1 Turtle](http://www.w3.org/TR/turtle/)
– [EARL report](https://raw.githubusercontent.com/RubenVerborgh/N3.js/earl/n3js-earl-report-turtle.ttl)
- [RDF 1.1 TriG](http://www.w3.org/TR/trig/)
– [EARL report](https://raw.githubusercontent.com/RubenVerborgh/N3.js/earl/n3js-earl-report-trig.ttl)
- [RDF 1.1 N-Triples](http://www.w3.org/TR/n-triples/)
– [EARL report](https://raw.githubusercontent.com/RubenVerborgh/N3.js/earl/n3js-earl-report-ntriples.ttl)
- [RDF 1.1 N-Quads](http://www.w3.org/TR/n-quads/)
– [EARL report](https://raw.githubusercontent.com/RubenVerborgh/N3.js/earl/n3js-earl-report-nquads.ttl)
Pass a `format` option to the constructor with the name or MIME type of a format
for strict, fault-intolerant behavior.
Note that the library does not support full [Notation3](http://www.w3.org/TeamSubmission/n3/) yet
(and a standardized specification for this syntax is currently lacking).
### Breaking changes
N3.js 0.4.x introduces the following breaking changes from 0.3.x versions:
- The fourth element of a quad is named `graph` instead of `context`.
- `N3.Writer` and `N3.Store` constructor options are passed as a hash `{ prefixes: { … } }`.
- `N3.Util` URI methods such as `isUri` are now IRI methods such as `isIRI`.
## License, status and contributions
The N3.js library is copyrighted by [Ruben Verborgh](http://ruben.verborgh.org/)

@@ -317,2 +376,2 @@ and released under the [MIT License](https://github.com/RubenVerborgh/N3.js/blob/master/LICENSE.md).

Contributions are welcome, and bug reports or pull requests are always helpful.
If you plan to implement larger features, it's best to contact me first.
If you plan to implement a larger feature, it's best to contact me first.
#!/usr/bin/env node
var N3Parser = require('../lib/N3Parser.js'),
N3Store = require('../lib/N3Store.js');
var fs = require('fs'),
path = require('path'),
request = require('request'),
exec = require('child_process').exec,
colors = require('colors'),
async = require('async');
// Should the tests run in parallel?
var parallel = false;
// Path to the tests and the tests' manifest
var testPath = "http://www.w3.org/2013/TurtleTests/",
manifest = "manifest.ttl";
// Prefixes
var prefixes = {
mf: "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
rdfs: "http://www.w3.org/2000/01/rdf-schema#",
rdft: "http://www.w3.org/ns/rdftest#",
dc: "http://purl.org/dc/terms/",
doap: "http://usefulinc.com/ns/doap#",
earl: "http://www.w3.org/ns/earl#",
foaf: "http://xmlns.com/foaf/0.1/",
xsd: "http://www.w3.org/2001/XMLSchema#",
manifest: testPath + manifest + '#',
};
// List predicates
var first = prefixes.rdf + "first",
rest = prefixes.rdf + "rest",
nil = prefixes.rdf + "nil";
// Create the folders that will contain the spec files and results
var specFolder = __dirname,
testFolder = path.join(specFolder, 'turtle/'),
outputFolder = path.join(testFolder, 'results/');
[specFolder, testFolder, outputFolder].forEach(function (folder) {
if (!fs.existsSync(folder))
fs.mkdirSync(folder);
});
runSpecTest();
/*************************************************
Test suite execution
**************************************************/
// Fetches the manifest, executes all tests, and reports results
function runSpecTest() {
console.log("Turtle Terse RDF Triple Language Test Cases".bold);
async.waterfall([
// Fetch and parse the manifest
fetch.bind(null, manifest),
parseManifest,
// Perform the tests in the manifest
function performTests(manifest, callback) {
async[parallel ? 'map' : 'mapSeries'](manifest.tests, function (test, callback) {
async.parallel({ actionTurtle: fetch.bind(null, test.action),
resultTurtle: fetch.bind(null, test.result) },
function (err, results) {
performTest(test, results.actionTurtle, callback);
});
},
// Show the summary of the performed tests
function showSummary(error, tests) {
var score = tests.reduce(function (sum, test) { return sum + test.success; }, 0);
console.log(("* passed " + score + " out of " + manifest.tests.length + " tests").bold);
callback(error, tests);
});
},
// Generate the EARL report
generateEarlReport,
],
function (error) {
if (error) {
console.error("ERROR".red);
console.error(error.red);
process.exit(1);
}
});
}
// Parses the tests manifest into tests
function parseManifest(manifestContents, callback) {
var manifest = {},
testStore = new N3Store();
// Parse the manifest into triples
new N3Parser().parse(manifestContents, function (err, triple) {
if (err)
return callback(err);
// Store triples until there are no more
if (triple)
return testStore.addTriple(triple.subject, triple.predicate, triple.object);
// Once all triples are there, get the first item of the test list
var tests = manifest.tests = [],
itemHead = testStore.find('', prefixes.mf + 'entries', null)[0].object;
// Loop through all test items
while (itemHead && itemHead !== nil) {
// Find and store the item's properties
var itemValue = testStore.find(itemHead, first, null)[0].object,
itemTriples = testStore.find(itemValue, null, null),
test = { id: itemValue.replace(/^#/, '') };
itemTriples.forEach(function (triple) {
var propertyMatch = triple.predicate.match(/#(.+)/);
if (propertyMatch)
test[propertyMatch[1]] = triple.object;
});
tests.push(test);
// Find the next test item
itemHead = testStore.find(itemHead, rest, null)[0].object;
}
return callback(null, manifest);
});
}
// Fetches and caches the specified file, or retrieves it from disk
function fetch(testFile, callback) {
if (!testFile)
return callback(null, null);
var localFile = path.join(testFolder, testFile);
fs.exists(localFile, function (exists) {
if (exists)
fs.readFile(localFile, 'utf8', callback);
else
request.get(testPath + testFile,
function (error, response, body) { callback(error, body); })
.pipe(fs.createWriteStream(localFile));
});
}
/*************************************************
Individual test execution
**************************************************/
// Performs the specified test by parsing the specified Turtle document
function performTest(test, actionTurtle, callback) {
// Create the results file
var resultFile = path.join(outputFolder, test.action.replace(/\.ttl$/, '.nt')),
resultStream = fs.createWriteStream(resultFile);
resultStream.once('open', function () {
// Try to parse the specified document
var config = { documentURI: testPath + test.action };
new N3Parser(config).parse(actionTurtle,
function (error, triple) {
if (error)
test.error = error;
// Write the triple to the results file, or end if none are left
if (triple)
resultStream.write(toNTriple(triple));
else
resultStream.end();
});
});
// Verify the result if the result has been written
resultStream.once('close', function () {
verifyResult(test, resultFile, test.result && (testFolder + test.result), callback);
});
}
// Verifies and reports the test result
function verifyResult(test, resultFile, correctFile, callback) {
// Negative tests are successful if an error occurred
var negativeTest = /TestTurtleNegative/.test(test.type);
if (negativeTest) {
displayResult(null, !!test.error);
}
// Positive tests are successful if the results are equal,
// or if the correct solution is not given but no error occurred
else {
if (!correctFile)
displayResult(null, !test.error);
else if (resultFile)
compareGraphs(resultFile, correctFile, displayResult);
else
displayResult(null, false);
}
// Display the test result
function displayResult(error, success, comparison) {
console.log(unString(test.name).bold + ':', unString(test.comment),
(success ? 'OK'.green : 'FAIL'.red).bold);
if (error || !success) {
console.log((correctFile ? fs.readFileSync(correctFile, 'utf8') : '(empty)').grey);
console.log(' was expected, but got'.bold.grey);
console.log((resultFile ? fs.readFileSync(resultFile, 'utf8') : '(empty)').grey);
console.log((' error: '.bold + (test.error || '(none)')).grey);
if (comparison)
console.log((' comparison: ' + comparison).grey);
}
test.success = success;
callback(null, test);
}
}
// Verifies whether the two graphs are equal
function compareGraphs(actual, expected, callback) {
// Try a full-text comparison (fastest)
async.parallel({
actualContents: fs.readFile.bind(fs, actual, 'utf8'),
expectedContents: fs.readFile.bind(fs, expected, 'utf8'),
},
function (error, results) {
// If the full-text comparison was successful, graphs are certainly equal
if (results.actualContents === results.expectedContents)
callback(error, !error);
// If not, we check for proper graph equality with SWObjects
else
exec('sparql -d ' + expected + ' --compare ' + actual, function (error, stdout, stderr) {
callback(error, /^matched\s*$/.test(stdout), stdout);
});
});
}
/*************************************************
Conversion routines
**************************************************/
// Converts the triple to NTriples format (primitive and incomplete)
function toNTriple(triple) {
var subject = triple.subject,
predicate = triple.predicate,
object = triple.object;
if (/^".*"$/.test(object))
object = escapeString(object);
else
object = escape(object).replace(/"\^\^(.*)$/, '"^^<$1>');
return (subject.match(/^_/) ? subject : '<' + subject + '>') + ' ' +
(predicate.match(/^_/) ? predicate : '<' + predicate + '>') + ' ' +
(object.match(/^_|^"/) ? object : '<' + object + '>') + ' .\n';
}
// Removes the quotes around a string
function unString(value) {
return value ? value.replace(/^("""|")(.*)\1$/, '$2') : '';
}
// Escapes unicode characters in a URI
function escape(value) {
var result = '';
// Add all characters, converting to an unicode escape code if necessary
for (var i = 0; i < value.length; i++) {
var code = value.charCodeAt(i);
if (code >= 32 && code < 128) {
result += value[i];
}
else {
var hexCode = code.toString(16);
while (hexCode.length < 4)
hexCode = '0' + hexCode;
result += '\\u' + hexCode;
}
}
// Convert surrogate pairs to actual unicode (http://mathiasbynens.be/notes/javascript-encoding)
result = result.replace(/\\u([a-z0-9]{4})\\u([a-z0-9]{4})/gi, function (all, high, low) {
high = parseInt(high, 16);
low = parseInt(low, 16);
if (high >= 0xD800 && high <= 0xDBFF && low >= 0xDC00 && low <= 0xDFFF) {
var result = (high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000;
result = result.toString(16);
while (result.length < 8)
result = '0' + result;
return '\\U' + result;
}
return all;
});
return result;
}
// Escapes characters in a string
function escapeString(value) {
value = value.replace(/\\/g, '\\\\');
value = escape(unString(value));
value = value.replace(/"/g, '\\"');
return '"' + value + '"';
}
/*************************************************
EARL report generation
**************************************************/
var homepage = 'https://github.com/RubenVerborgh/N3.js',
application = homepage + '#n3js',
developer = 'http://ruben.verborgh.org/#me';
function generateEarlReport(tests, callback) {
// Create the report file
var reportFile = path.join(outputFolder, 'earl-report.ttl'),
report = fs.createWriteStream(reportFile),
date = new Date().toISOString();
report.once('open', function () {
for (var prefix in prefixes)
writeln('@prefix ', prefix, ': <', prefixes[prefix], '>.');
writeln();
writeln('<> foaf:primaryTopic <', application, '>;');
writeln(' dc:issued "', date, '"^^xsd:dateTime;');
writeln(' foaf:maker <', developer, '>.');
writeln();
writeln('<', application, '> a earl:Software, earl:TestSubject, doap:Project;');
writeln(' doap:name "N3.js";');
writeln(' doap:homepage <', homepage, '>;');
writeln(' doap:license <http://opensource.org/licenses/MIT>;');
writeln(' doap:programming-language "JavaScript";');
writeln(' doap:implements <http://www.w3.org/TR/turtle/>;');
writeln(' doap:category <http://dbpedia.org/resource/Resource_Description_Framework>;');
writeln(' doap:download-page <https://npmjs.org/package/n3>;');
writeln(' doap:bug-database <', homepage, '/issues>;');
writeln(' doap:blog <http://ruben.verborgh.org/blog/>;');
writeln(' doap:developer <', developer, '>;');
writeln(' doap:maintainer <', developer, '>;');
writeln(' doap:documenter <', developer, '>;');
writeln(' doap:maker <', developer, '>;');
writeln(' dc:title "N3.js";');
writeln(' dc:description "N3.js is an asynchronous, streaming Turtle parser for JavaScript."@en;');
writeln(' doap:description "N3.js is an asynchronous, streaming Turtle parser for JavaScript."@en;');
writeln(' dc:creator <', developer, '>.');
writeln();
writeln('<', developer, '> a foaf:Person, earl:Assertor;');
writeln(' foaf:name "Ruben Verborgh";');
writeln(' foaf:homepage <http://ruben.verborgh.org/>;');
writeln(' foaf:primaryTopicOf <http://ruben.verborgh.org/profile/>;');
writeln(' rdfs:isDefinedBy <http://ruben.verborgh.org/profile/>.');
tests.forEach(function (test) {
writeln();
writeln('manifest:', test.id, ' a earl:TestCriterion, earl:TestCase;');
writeln(' dc:title ', escapeString(unString(test.name)), ';');
writeln(' dc:description ', escapeString(unString(test.comment)), ';');
writeln(' mf:action <', testPath, test.action, '>;');
if (test.result)
writeln(' mf:result <', testPath, test.result, '>;');
writeln(' earl:assertions (');
writeln(' [ a earl:Assertion;');
writeln(' earl:assertedBy <', developer, '>;');
writeln(' earl:test manifest:', test.id, ';');
writeln(' earl:subject <', application, '>;');
writeln(' earl:mode earl:automatic;');
writeln(' earl:result [ a earl:TestResult; ',
'earl:outcome earl:', (test.success ? 'passed' : 'failed'), '; ',
'dc:date "', date, '"^^xsd:dateTime',
' ]]');
writeln(' ).');
});
report.end();
});
report.once('close', callback);
function writeln() {
for(var i = 0; i < arguments.length; i++)
report.write(arguments[i]);
report.write('\n');
}
}
var SpecTester = require('./SpecTester');
new SpecTester({
name: 'turtle',
title: 'RDF 1.1 Turtle – Terse RDF Triple Language Test Cases',
manifest: 'http://www.w3.org/2013/TurtleTests/manifest.ttl',
}).run();

@@ -180,3 +180,3 @@ var N3Lexer = require('../N3').Lexer;

shouldNotTokenize('ex:bla"foo',
'Syntax error: unexpected "ex:bla"foo" on line 1.'));
'Syntax error: unexpected ""foo" on line 1.'));

@@ -464,20 +464,24 @@ it('should not tokenize a prefixed name with escaped characters',

it('should tokenize @prefix declarations',
shouldTokenize('@prefix : <http://uri.org/#>.\n@prefix abc:<http://uri.org/#>.',
shouldTokenize('@prefix : <http://iri.org/#>.\n@prefix abc:<http://iri.org/#>.',
{ type: '@prefix', line: 1 },
{ type: 'prefix', value: '', line: 1 },
{ type: 'IRI', value: 'http://uri.org/#', line: 1 },
{ type: 'IRI', value: 'http://iri.org/#', line: 1 },
{ type: '.', line: 1 },
{ type: '@prefix', line: 2 },
{ type: 'prefix', value: 'abc', line: 2 },
{ type: 'IRI', value: 'http://uri.org/#', line: 2 },
{ type: 'IRI', value: 'http://iri.org/#', line: 2 },
{ type: '.', line: 2 },
{ type: 'eof', line: 2 }));
it('should not tokenize prefixes that end with a dot',
shouldNotTokenize('@prefix abc.: <def>.',
'Syntax error: unexpected "abc.:" on line 1.'));
it('should tokenize @base declarations',
shouldTokenize('@base <http://uri.org/#>.\n@base <http://uri.org/#>.',
shouldTokenize('@base <http://iri.org/#>.\n@base <http://iri.org/#>.',
{ type: '@base', line: 1 },
{ type: 'IRI', value: 'http://uri.org/#', line: 1 },
{ type: 'IRI', value: 'http://iri.org/#', line: 1 },
{ type: '.', line: 1 },
{ type: '@base', line: 2 },
{ type: 'IRI', value: 'http://uri.org/#', line: 2 },
{ type: 'IRI', value: 'http://iri.org/#', line: 2 },
{ type: '.', line: 2 },

@@ -487,21 +491,21 @@ { type: 'eof', line: 2 }));

it('should tokenize PREFIX declarations',
shouldTokenize('PREFIX : <http://uri.org/#>\npreFiX abc: <http://uri.org/#>',
shouldTokenize('PREFIX : <http://iri.org/#>\npreFiX abc: <http://iri.org/#>',
{ type: 'PREFIX', line: 1 },
{ type: 'prefix', value: '', line: 1 },
{ type: 'IRI', value: 'http://uri.org/#', line: 1 },
{ type: 'IRI', value: 'http://iri.org/#', line: 1 },
{ type: 'PREFIX', line: 2 },
{ type: 'prefix', value: 'abc', line: 2 },
{ type: 'IRI', value: 'http://uri.org/#', line: 2 },
{ type: 'IRI', value: 'http://iri.org/#', line: 2 },
{ type: 'eof', line: 2 }));
it('should tokenize BASE declarations',
shouldTokenize('BASE <http://uri.org/#>\nbAsE <http://uri.org/#>',
shouldTokenize('BASE <http://iri.org/#>\nbAsE <http://iri.org/#>',
{ type: 'BASE', line: 1 },
{ type: 'IRI', value: 'http://uri.org/#', line: 1 },
{ type: 'IRI', value: 'http://iri.org/#', line: 1 },
{ type: 'BASE', line: 2 },
{ type: 'IRI', value: 'http://uri.org/#', line: 2 },
{ type: 'IRI', value: 'http://iri.org/#', line: 2 },
{ type: 'eof', line: 2 }));
it('should tokenize blank nodes',
shouldTokenize('[] [<a> <b>] [a:b "c"^^d:e][a:b[]]',
shouldTokenize('[] [<a> <b>] [a:b "c"^^d:e][a:b[]] _:a:b.',
{ type: '[', line: 1 },

@@ -523,4 +527,11 @@ { type: ']', line: 1 },

{ type: ']', line: 1 },
{ type: 'prefixed', prefix: '_', value: 'a', line: 1 },
{ type: 'prefixed', prefix: '', value: 'b', line: 1 },
{ type: '.', line: 1 },
{ type: 'eof', line: 1 }));
it('should not tokenize invalid blank nodes',
shouldNotTokenize('_::',
'Syntax error: unexpected "_::" on line 1.'));
it('should tokenize lists',

@@ -566,2 +577,70 @@ shouldTokenize('() (<a>) (<a> <b>)',

it('should tokenize an empty default graph',
shouldTokenize('{}',
{ type: '{', line: 1 },
{ type: '}', line: 1 },
{ type: 'eof', line: 1 }));
it('should tokenize a non-empty default graph',
shouldTokenize('{<a> <b> c:d}',
{ type: '{', line: 1 },
{ type: 'IRI', value: 'a', line: 1 },
{ type: 'IRI', value: 'b', line: 1 },
{ type: 'prefixed', prefix: 'c', value: 'd', line: 1 },
{ type: '}', line: 1 },
{ type: 'eof', line: 1 }));
it('should tokenize an empty graph identified by an IRI',
shouldTokenize('<g>{}',
{ type: 'IRI', value: 'g', line: 1 },
{ type: '{', line: 1 },
{ type: '}', line: 1 },
{ type: 'eof', line: 1 }));
it('should tokenize a non-empty graph identified by an IRI',
shouldTokenize('<g> {<a> <b> c:d}',
{ type: 'IRI', value: 'g', line: 1 },
{ type: '{', line: 1 },
{ type: 'IRI', value: 'a', line: 1 },
{ type: 'IRI', value: 'b', line: 1 },
{ type: 'prefixed', prefix: 'c', value: 'd', line: 1 },
{ type: '}', line: 1 },
{ type: 'eof', line: 1 }));
it('should tokenize an empty graph identified by a blank node',
shouldTokenize('_:g{}',
{ type: 'prefixed', prefix: '_', value: 'g', line: 1 },
{ type: '{', line: 1 },
{ type: '}', line: 1 },
{ type: 'eof', line: 1 }));
it('should tokenize a non-empty graph identified by a blank node',
shouldTokenize('_:g {<a> <b> c:d}',
{ type: 'prefixed', prefix: '_', value: 'g', line: 1 },
{ type: '{', line: 1 },
{ type: 'IRI', value: 'a', line: 1 },
{ type: 'IRI', value: 'b', line: 1 },
{ type: 'prefixed', prefix: 'c', value: 'd', line: 1 },
{ type: '}', line: 1 },
{ type: 'eof', line: 1 }));
it('should tokenize an empty graph with the GRAPH keyword',
shouldTokenize('GRAPH<g>{}',
{ type: 'GRAPH', line: 1 },
{ type: 'IRI', value: 'g', line: 1 },
{ type: '{', line: 1 },
{ type: '}', line: 1 },
{ type: 'eof', line: 1 }));
it('should tokenize a non-empty graph with the GRAPH keyword',
shouldTokenize('graph <g> {<a> <b> c:d}',
{ type: 'GRAPH', line: 1 },
{ type: 'IRI', value: 'g', line: 1 },
{ type: '{', line: 1 },
{ type: 'IRI', value: 'a', line: 1 },
{ type: 'IRI', value: 'b', line: 1 },
{ type: 'prefixed', prefix: 'c', value: 'd', line: 1 },
{ type: '}', line: 1 },
{ type: 'eof', line: 1 }));
it('should not tokenize an invalid document',

@@ -568,0 +647,0 @@ shouldNotTokenize(' \n @!', 'Syntax error: unexpected "@!" on line 2.'));

@@ -71,3 +71,3 @@ var N3Parser = require('../N3').Parser;

it('should parse a triple with a literal and a URI type',
it('should parse a triple with a literal and an IRI type',
shouldParse('<a> <b> "string"^^<type>.',

@@ -158,5 +158,5 @@ ['a', 'b', '"string"^^type']));

shouldParse('_:a <b> _:c.',
['_:b0', 'b', '_:b1']));
['_:b0_a', 'b', '_:b0_c']));
it('should not parse statements with named blank nodes',
it('should not parse statements with blank predicates',
shouldNotParse('<a> _:b <c>.',

@@ -212,2 +212,6 @@ 'Disallowed blank node as predicate at line 1.'));

it('should not parse a blank node with only a semicolon',
shouldNotParse('<a> <b> [;].',
'Unexpected ] at line 1.'));
it('should parse a multi-statement blank node with trailing semicolon',

@@ -286,7 +290,7 @@ shouldParse('<a> <b> [ <u> <v>; <w> <z>; ].',

shouldParse('<a> <b> (_:x _:y).',
['a', 'b', '_:b1'],
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b0'],
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b3'],
['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b2'],
['_:b3', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest',
['a', 'b', '_:b0'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b0_x'],
['_:b0', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest', '_:b1' ],
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first', '_:b0_y'],
['_:b1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest',
'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil']));

@@ -352,3 +356,3 @@

it('should resolve URIs against @base',
it('should resolve IRIs against @base',
shouldParse('@base <http://ex.org/>.\n' +

@@ -361,3 +365,3 @@ '<a> <b> <c>.\n' +

it('should resolve URIs against SPARQL base',
it('should resolve IRIs against SPARQL base',
shouldParse('BASE <http://ex.org/>\n' +

@@ -370,3 +374,3 @@ '<a> <b> <c>. ' +

it('should resolve URIs against a @base with query string',
it('should resolve IRIs against a @base with query string',
shouldParse('@base <http://ex.org/?foo>.\n' +

@@ -379,3 +383,3 @@ '<> <b> <c>.\n' +

it('should resolve URIs with query string against @base',
it('should resolve IRIs with query string against @base',
shouldParse('@base <http://ex.org/>.\n' +

@@ -391,3 +395,3 @@ '<?> <?a> <?a=b>.\n' +

it('should not resolve URIs with colons',
it('should not resolve IRIs with colons',
shouldParse('@base <http://ex.org/>.\n' +

@@ -401,2 +405,205 @@ '<a> <b> <c>.\n' +

it('should resolve datatype IRIs against @base',
shouldParse('@base <http://ex.org/>.\n' +
'<a> <b> "c"^^<d>.\n' +
'@base <d/>.\n' +
'<e> <f> "g"^^<h>.',
['http://ex.org/a', 'http://ex.org/b', '"c"^^http://ex.org/d'],
['http://ex.org/d/e', 'http://ex.org/d/f', '"g"^^http://ex.org/d/h']));
it('should parse an empty default graph',
shouldParse('{}'));
it('should parse a one-triple default graph ending without a dot',
shouldParse('{<a> <b> <c>}',
['a', 'b', 'c']));
it('should parse a one-triple default graph ending with a dot',
shouldParse('{<a> <b> <c>.}',
['a', 'b', 'c']));
it('should parse a three-triple default graph ending without a dot',
shouldParse('{<a> <b> <c>;<d> <e>,<f>}',
['a', 'b', 'c'],
['a', 'd', 'e'],
['a', 'd', 'f']));
it('should parse a three-triple default graph ending with a dot',
shouldParse('{<a> <b> <c>;<d> <e>,<f>.}',
['a', 'b', 'c'],
['a', 'd', 'e'],
['a', 'd', 'f']));
it('should parse a three-triple default graph ending with a semicolon',
shouldParse('{<a> <b> <c>;<d> <e>,<f>;}',
['a', 'b', 'c'],
['a', 'd', 'e'],
['a', 'd', 'f']));
it('should parse an empty named graph with an IRI',
shouldParse('<g>{}'));
it('should parse a one-triple named graph with an IRI ending without a dot',
shouldParse('<g> {<a> <b> <c>}',
['a', 'b', 'c', 'g']));
it('should parse a one-triple named graph with an IRI ending with a dot',
shouldParse('<g>{<a> <b> <c>.}',
['a', 'b', 'c', 'g']));
it('should parse a three-triple named graph with an IRI ending without a dot',
shouldParse('<g> {<a> <b> <c>;<d> <e>,<f>}',
['a', 'b', 'c', 'g'],
['a', 'd', 'e', 'g'],
['a', 'd', 'f', 'g']));
it('should parse a three-triple named graph with an IRI ending with a dot',
shouldParse('<g>{<a> <b> <c>;<d> <e>,<f>.}',
['a', 'b', 'c', 'g'],
['a', 'd', 'e', 'g'],
['a', 'd', 'f', 'g']));
it('should parse an empty named graph with a prefixed name',
shouldParse('@prefix g: <g#>.\ng:h {}'));
it('should parse a one-triple named graph with a prefixed name ending without a dot',
shouldParse('@prefix g: <g#>.\ng:h {<a> <b> <c>}',
['a', 'b', 'c', 'g#h']));
it('should parse a one-triple named graph with a prefixed name ending with a dot',
shouldParse('@prefix g: <g#>.\ng:h{<a> <b> <c>.}',
['a', 'b', 'c', 'g#h']));
it('should parse a three-triple named graph with a prefixed name ending without a dot',
shouldParse('@prefix g: <g#>.\ng:h {<a> <b> <c>;<d> <e>,<f>}',
['a', 'b', 'c', 'g#h'],
['a', 'd', 'e', 'g#h'],
['a', 'd', 'f', 'g#h']));
it('should parse a three-triple named graph with a prefixed name ending with a dot',
shouldParse('@prefix g: <g#>.\ng:h{<a> <b> <c>;<d> <e>,<f>.}',
['a', 'b', 'c', 'g#h'],
['a', 'd', 'e', 'g#h'],
['a', 'd', 'f', 'g#h']));
it('should parse an empty anonymous graph',
shouldParse('[] {}'));
it('should parse a one-triple anonymous graph ending without a dot',
shouldParse('[] {<a> <b> <c>}',
['a', 'b', 'c', '_:b0']));
it('should parse a one-triple anonymous graph ending with a dot',
shouldParse('[]{<a> <b> <c>.}',
['a', 'b', 'c', '_:b0']));
it('should parse a three-triple anonymous graph ending without a dot',
shouldParse('[] {<a> <b> <c>;<d> <e>,<f>}',
['a', 'b', 'c', '_:b0'],
['a', 'd', 'e', '_:b0'],
['a', 'd', 'f', '_:b0']));
it('should parse a three-triple anonymous graph ending with a dot',
shouldParse('[]{<a> <b> <c>;<d> <e>,<f>.}',
['a', 'b', 'c', '_:b0'],
['a', 'd', 'e', '_:b0'],
['a', 'd', 'f', '_:b0']));
it('should parse an empty named graph with an IRI and the GRAPH keyword',
shouldParse('GRAPH <g> {}'));
it('should parse an empty named graph with a prefixed name and the GRAPH keyword',
shouldParse('@prefix g: <g#>.\nGRAPH g:h {}'));
it('should parse an empty anonymous graph and the GRAPH keyword',
shouldParse('GRAPH [] {}'));
it('should parse a one-triple named graph with an IRI and the GRAPH keyword',
shouldParse('GRAPH <g> {<a> <b> <c>}',
['a', 'b', 'c', 'g']));
it('should parse a one-triple named graph with a prefixed name and the GRAPH keyword',
shouldParse('@prefix g: <g#>.\nGRAPH g:h {<a> <b> <c>}',
['a', 'b', 'c', 'g#h']));
it('should parse a one-triple anonymous graph and the GRAPH keyword',
shouldParse('GRAPH [] {<a> <b> <c>}',
['a', 'b', 'c', '_:b0']));
it('should parse a graph with 8-bit unicode escape sequences',
shouldParse('<\\U0001d400> {\n<\\U0001d400> <\\U0001d400> "\\U0001d400"^^<\\U0001d400>\n}\n',
['\ud835\udC00', '\ud835\udc00', '"\ud835\udc00"^^\ud835\udc00', '\ud835\udc00']));
it('should not parse a single closing brace',
shouldNotParse('}',
'Unexpected graph closing at line 1.'));
it('should not parse a single opening brace',
shouldNotParse('{',
'Expected subject but got eof at line 1.'));
it('should not parse a superfluous closing brace ',
shouldNotParse('{}}',
'Unexpected graph closing at line 1.'));
it('should not parse a graph with only a dot',
shouldNotParse('{.}',
'Expected subject but got . at line 1.'));
it('should not parse a graph with only a semicolon',
shouldNotParse('{;}',
'Expected subject but got ; at line 1.'));
it('should not parse an unclosed graph',
shouldNotParse('{<a> <b> <c>.',
'Unclosed graph at line 1.'));
it('should not parse a named graph with a list node as label',
shouldNotParse('() {}',
'Expected predicate to follow "http://www.w3.org/1999/02/22-rdf-syntax-ns#nil" at line 1.'));
it('should not parse a named graph with a non-empty blank node as label',
shouldNotParse('[<a> <b>] {}',
'Expected predicate to follow "_:b0" at line 1.'));
it('should not parse a named graph with the GRAPH keyword and a non-empty blank node as label',
shouldNotParse('GRAPH [<a> <b>] {}',
'Invalid graph label at line 1.'));
it('should not parse a triple after the GRAPH keyword',
shouldNotParse('GRAPH <a> <b> <c>.',
'Expected graph but got IRI at line 1.'));
it('should not parse repeated GRAPH keywords',
shouldNotParse('GRAPH GRAPH <g> {}',
'Invalid graph label at line 1.'));
it('should parse a quad with 4 IRIs',
shouldParse('<a> <b> <c> <g>.',
['a', 'b', 'c', 'g']));
it('should parse a quad with 4 prefixed names',
shouldParse('@prefix p: <p#>.\np:a p:b p:c p:g.',
['p#a', 'p#b', 'p#c', 'p#g']));
it('should not parse a quad with an undefined prefix',
shouldNotParse('<a> <b> <c> p:g.',
'Undefined prefix "p:" at line 1.'));
it('should parse a quad with 3 IRIs and a literal',
shouldParse('<a> <b> "c"^^<d> <g>.',
['a', 'b', '"c"^^d', 'g']));
it('should parse a quad with 2 blank nodes and a literal',
shouldParse('_:a <b> "c"^^<d> _:g.',
['_:b0_a', 'b', '"c"^^d', '_:b0_g']));
it('should not parse a quad in a graph',
shouldNotParse('{<a> <b> <c> <g>.}',
'Expected punctuation to follow "c" at line 1.'));
it('should not parse a quad with different punctuation',
shouldNotParse('<a> <b> <c> <g>;',
'Expected dot to follow quad at line 1.'));
it('should not parse base declarations without IRI',

@@ -409,4 +616,8 @@ shouldNotParse('@base a: ',

'<a> <b> <c>.\n',
'Invalid base URI at line 1.'));
'Invalid base IRI at line 1.'));
it('should not parse improperly nested parentheses and brackets',
shouldNotParse('<a> <b> [<c> (<d>]).',
'Expected list item instead of "]" at line 1.'));
it('should not parse improperly nested square brackets',

@@ -434,3 +645,3 @@ shouldNotParse('<a> <b> [<c> <d>]].',

shouldNotParse('<a> .',
'Unexpected dot at line 1.'));
'Unexpected . at line 1.'));

@@ -447,3 +658,3 @@ it('should error if an unexpected token follows a subject',

var prefixes = {};
new N3Parser().parse('@prefix a: <URIa>. a:a a:b a:c. @prefix b: <URIb>.',
new N3Parser().parse('@prefix a: <IRIa>. a:a a:b a:c. @prefix b: <IRIb>.',
tripleCallback, prefixCallback);

@@ -455,4 +666,4 @@

Object.keys(prefixes).should.have.length(2);
prefixes.should.have.property('a', 'URIa');
prefixes.should.have.property('b', 'URIb');
expect(prefixes).to.have.property('a', 'IRIa');
expect(prefixes).to.have.property('b', 'IRIb');
done();

@@ -462,6 +673,6 @@ }

function prefixCallback(prefix, uri) {
function prefixCallback(prefix, iri) {
expect(prefix).to.exist;
expect(uri).to.exist;
prefixes[prefix] = uri;
expect(iri).to.exist;
prefixes[prefix] = iri;
}

@@ -471,3 +682,3 @@ });

it('should return prefixes at the last triple callback', function (done) {
new N3Parser().parse('@prefix a: <URIa>. a:a a:b a:c. @prefix b: <URIb>.', tripleCallback);
new N3Parser().parse('@prefix a: <IRIa>. a:a a:b a:c. @prefix b: <IRIb>.', tripleCallback);

@@ -481,4 +692,4 @@ function tripleCallback(error, triple, prefixes) {

Object.keys(prefixes).should.have.length(2);
prefixes.should.have.property('a', 'URIa');
prefixes.should.have.property('b', 'URIb');
expect(prefixes).to.have.property('a', 'IRIa');
expect(prefixes).to.have.property('b', 'IRIb');
done();

@@ -502,14 +713,14 @@ }

describe('An N3Parser instance with a document URI', function () {
var parser = new N3Parser({ documentURI: 'http://ex.org/doc/f.ttl' });
describe('An N3Parser instance with a document IRI', function () {
var parser = new N3Parser({ documentIRI: 'http://ex.org/doc/f.ttl' });
it('should resolve URIs against the document URI',
it('should resolve IRIs against the document IRI',
shouldParse(parser,
'@prefix : <#>.\n' +
'<a> <b> <c>.\n' +
':e :f :g.',
['http://ex.org/doc/a', 'http://ex.org/doc/b', 'http://ex.org/doc/c'],
['http://ex.org/doc/f.ttl#e', 'http://ex.org/doc/f.ttl#f', 'http://ex.org/doc/f.ttl#g']));
'<a> <b> <c> <g>.\n' +
':d :e :f :g.',
['http://ex.org/doc/a', 'http://ex.org/doc/b', 'http://ex.org/doc/c', 'http://ex.org/doc/g'],
['http://ex.org/doc/f.ttl#d', 'http://ex.org/doc/f.ttl#e', 'http://ex.org/doc/f.ttl#f', 'http://ex.org/doc/f.ttl#g']));
it('should resolve URIs with a trailing slashes against the document URI',
it('should resolve IRIs with a trailing slashes against the document IRI',
shouldParse(parser,

@@ -519,2 +730,7 @@ '</a> </a/b> </a/b/c>.\n',

it('should resolve datatype IRIs against the document IRI',
shouldParse(parser,
'<a> <b> "c"^^<d>.',
['http://ex.org/doc/a', 'http://ex.org/doc/b', '"c"^^http://ex.org/doc/d']));
it('should respect @base statements',

@@ -535,9 +751,9 @@ shouldParse(parser,

describe('An N3Parser instance with an invalid document URI', function () {
describe('An N3Parser instance with an invalid document IRI', function () {
it('cannot be created', function (done) {
try {
new N3Parser({ documentURI: 'http://ex.org/doc/f#' });
new N3Parser({ documentIRI: 'http://ex.org/doc/f#' });
}
catch (error) {
error.message.should.equal('Invalid document URI');
error.message.should.equal('Invalid document IRI');
done();

@@ -547,2 +763,86 @@ }

});
describe('An N3Parser instance with a non-string format', function () {
var parser = new N3Parser({ format: 1 });
it('should parse a single triple',
shouldParse(parser, '<a> <b> <c>.', ['a', 'b', 'c']));
it('should parse a graph',
shouldParse(parser, '{<a> <b> <c>}', ['a', 'b', 'c']));
});
describe('An N3Parser instance for the Turtle format', function () {
var parser = new N3Parser({ format: 'Turtle' });
it('should parse a single triple',
shouldParse(parser, '<a> <b> <c>.', ['a', 'b', 'c']));
it('should not parse a default graph',
shouldNotParse(parser, '{}', 'Expected subject but got { at line 1.'));
it('should not parse a named graph',
shouldNotParse(parser, '<g> {}', 'Expected predicate to follow "g" at line 1.'));
it('should not parse a named graph with the GRAPH keyword',
shouldNotParse(parser, 'GRAPH <g> {}', 'Expected subject but got GRAPH at line 1.'));
it('should not parse a quad',
shouldNotParse(parser, '<a> <b> <c> <d>.', 'Expected punctuation to follow "c" at line 1.'));
});
describe('An N3Parser instance for the TriG format', function () {
var parser = new N3Parser({ format: 'TriG' });
it('should parse a single triple',
shouldParse(parser, '<a> <b> <c>.', ['a', 'b', 'c']));
it('should parse a default graph',
shouldParse(parser, '{}'));
it('should parse a named graph',
shouldParse(parser, '<g> {}'));
it('should parse a named graph with the GRAPH keyword',
shouldParse(parser, 'GRAPH <g> {}'));
it('should not parse a quad',
shouldNotParse(parser, '<a> <b> <c> <d>.', 'Expected punctuation to follow "c" at line 1.'));
});
describe('An N3Parser instance for the N-Triples format', function () {
var parser = new N3Parser({ format: 'N-Triples' });
it('should parse a single triple',
shouldParse(parser, '<http://ex.org/a> <http://ex.org/b> "c".',
['http://ex.org/a', 'http://ex.org/b', '"c"']));
it('should not parse a single quad',
shouldNotParse(parser, '<http://ex.org/a> <http://ex.org/b> "c" <http://ex.org/g>.',
'Expected punctuation to follow ""c"" at line 1.'));
it('should not parse relative IRIs',
shouldNotParse(parser, '<a> <b> <c>.', 'Disallowed relative IRI at line 1.'));
it('should not parse a prefix declaration',
shouldNotParse(parser, '@prefix : <p#>.', 'Syntax error: unexpected "@prefix" on line 1.'));
});
describe('An N3Parser instance for the N-Quads format', function () {
var parser = new N3Parser({ format: 'N-Quads' });
it('should parse a single triple',
shouldParse(parser, '<http://ex.org/a> <http://ex.org/b> <http://ex.org/c>.',
['http://ex.org/a', 'http://ex.org/b', 'http://ex.org/c']));
it('should parse a single quad',
shouldParse(parser, '<http://ex.org/a> <http://ex.org/b> "c" <http://ex.org/g>.',
['http://ex.org/a', 'http://ex.org/b', '"c"', 'http://ex.org/g']));
it('should not parse relative IRIs',
shouldNotParse(parser, '<a> <b> <c>.', 'Disallowed relative IRI at line 1.'));
it('should not parse a prefix declaration',
shouldNotParse(parser, '@prefix : <p#>.', 'Syntax error: unexpected "@prefix" on line 1.'));
});
});

@@ -554,5 +854,5 @@

items = expected.map(function (item) {
return { subject: item[0], predicate: item[1], object: item[2],
context: item[3] || 'n3/contexts#default' };
return { subject: item[0], predicate: item[1], object: item[2], graph: item[3] || '' };
});
N3Parser._resetBlankNodeIds();
// Shift parameters if necessary

@@ -578,5 +878,9 @@ if (!hasParser)

function shouldNotParse(input, expectedError) {
function shouldNotParse(parser, input, expectedError) {
// Shift parameters if necessary
if (!(parser instanceof N3Parser))
expectedError = input, input = parser, parser = new N3Parser();
return function (done) {
new N3Parser().parse(input, function (error, triple) {
parser.parse(input, function (error, triple) {
if (error) {

@@ -583,0 +887,0 @@ expect(triple).not.to.exist;

@@ -23,28 +23,24 @@ var N3Store = require('../N3').Store;

describe('An empty N3Store', function () {
var n3Store = new N3Store();
var store = new N3Store();
it('should have size 0', function () {
expect(n3Store.size).to.eql(0); // special test format for IE9
expect(store.size).to.eql(0);
});
it('should be empty', function () {
n3Store.find().should.be.empty;
store.find().should.be.empty;
});
it('should have a default context', function () {
n3Store.defaultContext.should.eql('n3/contexts#default');
});
it('should be able to create unnamed blank nodes', function () {
n3Store.createBlankNode().should.eql('_:b0');
n3Store.createBlankNode().should.eql('_:b1');
store.createBlankNode().should.eql('_:b0');
store.createBlankNode().should.eql('_:b1');
n3Store.addTriple('_:b0', '_:b1', '_:b2');
n3Store.createBlankNode().should.eql('_:b3');
store.addTriple('_:b0', '_:b1', '_:b2');
store.createBlankNode().should.eql('_:b3');
});
it('should be able to create named blank nodes', function () {
n3Store.createBlankNode('blank').should.eql('_:blank');
n3Store.createBlankNode('blank').should.eql('_:blank1');
n3Store.createBlankNode('blank').should.eql('_:blank2');
store.createBlankNode('blank').should.eql('_:blank');
store.createBlankNode('blank').should.eql('_:blank1');
store.createBlankNode('blank').should.eql('_:blank2');
});

@@ -54,3 +50,3 @@ });

describe('An N3Store with initialized with 3 elements', function () {
var n3Store = new N3Store([
var store = new N3Store([
{ subject: 's1', predicate: 'p1', object: 'o1'},

@@ -62,3 +58,3 @@ { subject: 's1', predicate: 'p1', object: 'o2'},

it('should have size 3', function () {
n3Store.size.should.eql(3);
store.size.should.eql(3);
});

@@ -68,18 +64,18 @@ });

describe('An N3Store with 5 elements', function () {
var n3Store = new N3Store();
n3Store.addTriple('s1', 'p1', 'o1');
n3Store.addTriple({ subject: 's1', predicate: 'p1', object: 'o2'});
n3Store.addTriples([
var store = new N3Store();
store.addTriple('s1', 'p1', 'o1');
store.addTriple({ subject: 's1', predicate: 'p1', object: 'o2'});
store.addTriples([
{ subject: 's1', predicate: 'p2', object: 'o2'},
{ subject: 's2', predicate: 'p1', object: 'o1'},
]);
n3Store.addTriple('s1', 'p2', 'o3', 'c4');
store.addTriple('s1', 'p2', 'o3', 'c4');
it('should have size 5', function () {
n3Store.size.should.eql(5);
store.size.should.eql(5);
});
describe('when searched without parameters', function () {
it('should return all items in the default context',
shouldIncludeAll(n3Store.find(),
it('should return all items in the default graph',
shouldIncludeAll(store.find(),
['s1', 'p1', 'o1'], ['s1', 'p1', 'o2'], ['s1', 'p2', 'o2'], ['s2', 'p1', 'o1']));

@@ -89,4 +85,4 @@ });

describe('when searched with an existing subject parameter', function () {
it('should return all items with this subject in the default context',
shouldIncludeAll(n3Store.find('s1', null, null),
it('should return all items with this subject in the default graph',
shouldIncludeAll(store.find('s1', null, null),
['s1', 'p1', 'o1'], ['s1', 'p1', 'o2'], ['s1', 'p2', 'o2']));

@@ -96,12 +92,12 @@ });

describe('when searched with a non-existing subject parameter', function () {
itShouldBeEmpty(n3Store.find('s3', null, null));
itShouldBeEmpty(store.find('s3', null, null));
});
describe('when searched with a non-existing subject parameter that exists elsewhere', function () {
itShouldBeEmpty(n3Store.find('p1', null, null));
itShouldBeEmpty(store.find('p1', null, null));
});
describe('when searched with an existing predicate parameter', function () {
it('should return all items with this predicate in the default context',
shouldIncludeAll(n3Store.find(null, 'p1', null),
it('should return all items with this predicate in the default graph',
shouldIncludeAll(store.find(null, 'p1', null),
['s1', 'p1', 'o1'], ['s1', 'p1', 'o2'], ['s2', 'p1', 'o1']));

@@ -111,68 +107,68 @@ });

describe('when searched with a non-existing predicate parameter', function () {
itShouldBeEmpty(n3Store.find(null, 'p3', null));
itShouldBeEmpty(store.find(null, 'p3', null));
});
describe('when searched with an existing object parameter', function () {
it('should return all items with this object in the default context',
shouldIncludeAll(n3Store.find(null, null, 'o1'), ['s1', 'p1', 'o1'], ['s2', 'p1', 'o1']));
it('should return all items with this object in the default graph',
shouldIncludeAll(store.find(null, null, 'o1'), ['s1', 'p1', 'o1'], ['s2', 'p1', 'o1']));
});
describe('when searched with a non-existing object parameter', function () {
itShouldBeEmpty(n3Store.find(null, null, 'o4'));
itShouldBeEmpty(store.find(null, null, 'o4'));
});
describe('when searched with existing subject and predicate parameters', function () {
it('should return all items with this subject and predicate in the default context',
shouldIncludeAll(n3Store.find('s1', 'p1', null), ['s1', 'p1', 'o1'], ['s1', 'p1', 'o2']));
it('should return all items with this subject and predicate in the default graph',
shouldIncludeAll(store.find('s1', 'p1', null), ['s1', 'p1', 'o1'], ['s1', 'p1', 'o2']));
});
describe('when searched with non-existing subject and predicate parameters', function () {
itShouldBeEmpty(n3Store.find('s2', 'p2', null));
itShouldBeEmpty(store.find('s2', 'p2', null));
});
describe('when searched with existing subject and object parameters', function () {
it('should return all items with this subject and object in the default context',
shouldIncludeAll(n3Store.find('s1', null, 'o2'), ['s1', 'p1', 'o2'], ['s1', 'p2', 'o2']));
it('should return all items with this subject and object in the default graph',
shouldIncludeAll(store.find('s1', null, 'o2'), ['s1', 'p1', 'o2'], ['s1', 'p2', 'o2']));
});
describe('when searched with non-existing subject and object parameters', function () {
itShouldBeEmpty(n3Store.find('s2', 'p2', null));
itShouldBeEmpty(store.find('s2', 'p2', null));
});
describe('when searched with existing predicate and object parameters', function () {
it('should return all items with this predicate and object in the default context',
shouldIncludeAll(n3Store.find(null, 'p1', 'o1'), ['s1', 'p1', 'o1'], ['s2', 'p1', 'o1']));
it('should return all items with this predicate and object in the default graph',
shouldIncludeAll(store.find(null, 'p1', 'o1'), ['s1', 'p1', 'o1'], ['s2', 'p1', 'o1']));
});
describe('when searched with non-existing predicate and object parameters', function () {
itShouldBeEmpty(n3Store.find(null, 'p2', 'o3'));
itShouldBeEmpty(store.find(null, 'p2', 'o3'));
});
describe('when searched with existing subject, predicate, and object parameters', function () {
it('should return all items with this subject, predicate, and object in the default context',
shouldIncludeAll(n3Store.find('s1', 'p1', 'o1'), ['s1', 'p1', 'o1']));
it('should return all items with this subject, predicate, and object in the default graph',
shouldIncludeAll(store.find('s1', 'p1', 'o1'), ['s1', 'p1', 'o1']));
});
describe('when searched with a non-existing triple', function () {
itShouldBeEmpty(n3Store.find('s2', 'p2', 'o1'));
itShouldBeEmpty(store.find('s2', 'p2', 'o1'));
});
describe('when searched with the default context parameter', function () {
it('should return all items in the default context',
shouldIncludeAll(n3Store.find(),
describe('when searched with the default graph parameter', function () {
it('should return all items in the default graph',
shouldIncludeAll(store.find(),
['s1', 'p1', 'o1'], ['s1', 'p1', 'o2'], ['s1', 'p2', 'o2'], ['s2', 'p1', 'o1']));
});
describe('when searched with an existing non-default context parameter', function () {
it('should return all items in that context',
shouldIncludeAll(n3Store.find(null, null, null, 'c4'), ['s1', 'p2', 'o3', 'c4']));
describe('when searched with an existing non-default graph parameter', function () {
it('should return all items in that graph',
shouldIncludeAll(store.find(null, null, null, 'c4'), ['s1', 'p2', 'o3', 'c4']));
});
describe('when searched with a non-existing non-default context parameter', function () {
itShouldBeEmpty(n3Store.find(null, null, null, 'c5'));
describe('when searched with a non-existing non-default graph parameter', function () {
itShouldBeEmpty(store.find(null, null, null, 'c5'));
});
describe('when counted without parameters', function () {
it('should count all items in the default context', function () {
n3Store.count().should.equal(4);
it('should count all items in the default graph', function () {
store.count().should.equal(4);
});

@@ -182,4 +178,4 @@ });

describe('when counted with an existing subject parameter', function () {
it('should count all items with this subject in the default context', function () {
n3Store.count('s1', null, null).should.equal(3);
it('should count all items with this subject in the default graph', function () {
store.count('s1', null, null).should.equal(3);
});

@@ -190,3 +186,3 @@ });

it('should be empty', function () {
n3Store.count('s3', null, null).should.equal(0);
store.count('s3', null, null).should.equal(0);
});

@@ -197,3 +193,3 @@ });

it('should be empty', function () {
n3Store.count('p1', null, null).should.equal(0);
store.count('p1', null, null).should.equal(0);
});

@@ -203,4 +199,4 @@ });

describe('when counted with an existing predicate parameter', function () {
it('should count all items with this predicate in the default context', function () {
n3Store.count(null, 'p1', null).should.equal(3);
it('should count all items with this predicate in the default graph', function () {
store.count(null, 'p1', null).should.equal(3);
});

@@ -211,3 +207,3 @@ });

it('should be empty', function () {
n3Store.count(null, 'p3', null).should.equal(0);
store.count(null, 'p3', null).should.equal(0);
});

@@ -217,4 +213,4 @@ });

describe('when counted with an existing object parameter', function () {
it('should count all items with this object in the default context', function () {
n3Store.count(null, null, 'o1').should.equal(2);
it('should count all items with this object in the default graph', function () {
store.count(null, null, 'o1').should.equal(2);
});

@@ -225,3 +221,3 @@ });

it('should be empty', function () {
n3Store.count(null, null, 'o4').should.equal(0);
store.count(null, null, 'o4').should.equal(0);
});

@@ -231,4 +227,4 @@ });

describe('when counted with existing subject and predicate parameters', function () {
it('should count all items with this subject and predicate in the default context', function () {
n3Store.count('s1', 'p1', null).should.equal(2);
it('should count all items with this subject and predicate in the default graph', function () {
store.count('s1', 'p1', null).should.equal(2);
});

@@ -239,3 +235,3 @@ });

it('should be empty', function () {
n3Store.count('s2', 'p2', null).should.equal(0);
store.count('s2', 'p2', null).should.equal(0);
});

@@ -245,4 +241,4 @@ });

describe('when counted with existing subject and object parameters', function () {
it('should count all items with this subject and object in the default context', function () {
n3Store.count('s1', null, 'o2').should.equal(2);
it('should count all items with this subject and object in the default graph', function () {
store.count('s1', null, 'o2').should.equal(2);
});

@@ -253,3 +249,3 @@ });

it('should be empty', function () {
n3Store.count('s2', 'p2', null).should.equal(0);
store.count('s2', 'p2', null).should.equal(0);
});

@@ -259,4 +255,4 @@ });

describe('when counted with existing predicate and object parameters', function () {
it('should count all items with this predicate and object in the default context', function () {
n3Store.count(null, 'p1', 'o1').should.equal(2);
it('should count all items with this predicate and object in the default graph', function () {
store.count(null, 'p1', 'o1').should.equal(2);
});

@@ -267,3 +263,3 @@ });

it('should be empty', function () {
n3Store.count(null, 'p2', 'o3').should.equal(0);
store.count(null, 'p2', 'o3').should.equal(0);
});

@@ -273,4 +269,4 @@ });

describe('when counted with existing subject, predicate, and object parameters', function () {
it('should count all items with this subject, predicate, and object in the default context', function () {
n3Store.count('s1', 'p1', 'o1').should.equal(1);
it('should count all items with this subject, predicate, and object in the default graph', function () {
store.count('s1', 'p1', 'o1').should.equal(1);
});

@@ -281,21 +277,21 @@ });

it('should be empty', function () {
n3Store.count('s2', 'p2', 'o1').should.equal(0);
store.count('s2', 'p2', 'o1').should.equal(0);
});
});
describe('when counted with the default context parameter', function () {
it('should count all items in the default context', function () {
n3Store.count().should.equal(4);
describe('when counted with the default graph parameter', function () {
it('should count all items in the default graph', function () {
store.count().should.equal(4);
});
});
describe('when counted with an existing non-default context parameter', function () {
it('should count all items in that context', function () {
n3Store.count(null, null, null, 'c4').should.equal(1);
describe('when counted with an existing non-default graph parameter', function () {
it('should count all items in that graph', function () {
store.count(null, null, null, 'c4').should.equal(1);
});
});
describe('when counted with a non-existing non-default context parameter', function () {
describe('when counted with a non-existing non-default graph parameter', function () {
it('should be empty', function () {
n3Store.count(null, null, null, 'c5').should.equal(0);
store.count(null, null, null, 'c5').should.equal(0);
});

@@ -305,62 +301,62 @@ });

describe('when trying to remove a triple with a non-existing subject', function () {
before(function () { n3Store.removeTriple('s0', 'p1', 'o1'); });
it('should still have size 5', function () { n3Store.size.should.eql(5); });
before(function () { store.removeTriple('s0', 'p1', 'o1'); });
it('should still have size 5', function () { store.size.should.eql(5); });
});
describe('when trying to remove a triple with a non-existing predicate', function () {
before(function () { n3Store.removeTriple('s1', 'p0', 'o1'); });
it('should still have size 5', function () { n3Store.size.should.eql(5); });
before(function () { store.removeTriple('s1', 'p0', 'o1'); });
it('should still have size 5', function () { store.size.should.eql(5); });
});
describe('when trying to remove a triple with a non-existing object', function () {
before(function () { n3Store.removeTriple('s1', 'p1', 'o0'); });
it('should still have size 5', function () { n3Store.size.should.eql(5); });
before(function () { store.removeTriple('s1', 'p1', 'o0'); });
it('should still have size 5', function () { store.size.should.eql(5); });
});
describe('when trying to remove a triple for which no subjects exist', function () {
before(function () { n3Store.removeTriple('o1', 'p1', 'o1'); });
it('should still have size 5', function () { n3Store.size.should.eql(5); });
before(function () { store.removeTriple('o1', 'p1', 'o1'); });
it('should still have size 5', function () { store.size.should.eql(5); });
});
describe('when trying to remove a triple for which no predicates exist', function () {
before(function () { n3Store.removeTriple('s1', 's1', 'o1'); });
it('should still have size 5', function () { n3Store.size.should.eql(5); });
before(function () { store.removeTriple('s1', 's1', 'o1'); });
it('should still have size 5', function () { store.size.should.eql(5); });
});
describe('when trying to remove a triple for which no objects exist', function () {
before(function () { n3Store.removeTriple('s1', 'p1', 's1'); });
it('should still have size 5', function () { n3Store.size.should.eql(5); });
before(function () { store.removeTriple('s1', 'p1', 's1'); });
it('should still have size 5', function () { store.size.should.eql(5); });
});
describe('when trying to remove a triple that does not exist', function () {
before(function () { n3Store.removeTriple('s1', 'p2', 'o1'); });
it('should still have size 5', function () { n3Store.size.should.eql(5); });
before(function () { store.removeTriple('s1', 'p2', 'o1'); });
it('should still have size 5', function () { store.size.should.eql(5); });
});
describe('when trying to remove an incomplete triple', function () {
before(function () { n3Store.removeTriple('s1', null, null); });
it('should still have size 5', function () { n3Store.size.should.eql(5); });
before(function () { store.removeTriple('s1', null, null); });
it('should still have size 5', function () { store.size.should.eql(5); });
});
describe('when trying to remove a triple with a non-existing context', function () {
before(function () { n3Store.removeTriple('s1', 'p1', 'o1', 'c0'); });
it('should still have size 5', function () { n3Store.size.should.eql(5); });
describe('when trying to remove a triple with a non-existing graph', function () {
before(function () { store.removeTriple('s1', 'p1', 'o1', 'c0'); });
it('should still have size 5', function () { store.size.should.eql(5); });
});
describe('when removing an existing triple', function () {
before(function () { n3Store.removeTriple('s1', 'p1', 'o1'); });
before(function () { store.removeTriple('s1', 'p1', 'o1'); });
it('should have size 4', function () { n3Store.size.should.eql(4); });
it('should have size 4', function () { store.size.should.eql(4); });
it('should not contain that triple anymore',
shouldIncludeAll(function () { return n3Store.find(); },
shouldIncludeAll(function () { return store.find(); },
['s1', 'p1', 'o2'], ['s1', 'p2', 'o2'], ['s2', 'p1', 'o1']));
});
describe('when removing an existing triple from a non-default context', function () {
before(function () { n3Store.removeTriple('s1', 'p2', 'o3', 'c4'); });
describe('when removing an existing triple from a non-default graph', function () {
before(function () { store.removeTriple('s1', 'p2', 'o3', 'c4'); });
it('should have size 3', function () { n3Store.size.should.eql(3); });
it('should have size 3', function () { store.size.should.eql(3); });
itShouldBeEmpty(function () { return n3Store.find(null, null, null, 'c4'); });
itShouldBeEmpty(function () { return store.find(null, null, null, 'c4'); });
});

@@ -370,3 +366,3 @@

before(function () {
n3Store.removeTriples([
store.removeTriples([
{ subject: 's1', predicate: 'p2', object: 'o2'},

@@ -377,6 +373,6 @@ { subject: 's2', predicate: 'p1', object: 'o1'},

it('should have size 1', function () { n3Store.size.should.eql(1); });
it('should have size 1', function () { store.size.should.eql(1); });
it('should not contain those triples anymore',
shouldIncludeAll(function () { return n3Store.find(); },
shouldIncludeAll(function () { return store.find(); },
['s1', 'p1', 'o2']));

@@ -387,7 +383,7 @@ });

before(function () {
n3Store.addTriple('a', 'b', 'c');
n3Store.removeTriple('a', 'b', 'c');
store.addTriple('a', 'b', 'c');
store.removeTriple('a', 'b', 'c');
});
it('should have an unchanged size', function () { n3Store.size.should.eql(1); });
it('should have an unchanged size', function () { store.size.should.eql(1); });
});

@@ -397,3 +393,3 @@ });

describe('An N3Store initialized with prefixes', function () {
var n3Store = new N3Store([
var store = new N3Store([
{ subject: 'http://foo.org/#s1', predicate: 'http://bar.org/p1', object: 'http://foo.org/#o1' },

@@ -403,12 +399,9 @@ { subject: 'http://foo.org/#s1', predicate: 'http://bar.org/p2', object: 'http://foo.org/#o1' },

{ subject: 'http://foo.org/#s3', predicate: 'http://bar.org/p3', object: '"a"^^http://foo.org/#t1' },
{ subject: 'http://foo.org/#s1', predicate: 'http://bar.org/p1', object: 'http://foo.org/#o1', graph: 'http://graphs.org/#g1' },
],
{
'a': 'http://foo.org/#',
'b': 'http://bar.org/',
'ctx': 'n3/contexts#',
});
{ prefixes: { 'a': 'http://foo.org/#', 'b': 'http://bar.org/', 'g': 'http://graphs.org/#' } });
describe('should allow to query subjects with prefixes', function () {
it('should return all triples with that subject',
shouldIncludeAll(n3Store.find('a:s1', null, null),
shouldIncludeAll(store.find('a:s1', null, null),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1'],

@@ -420,3 +413,3 @@ ['http://foo.org/#s1', 'http://bar.org/p2', 'http://foo.org/#o1']));

it('should return all triples with that predicate',
shouldIncludeAll(n3Store.find(null, 'b:p1', null),
shouldIncludeAll(store.find(null, 'b:p1', null),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1'],

@@ -428,3 +421,3 @@ ['http://foo.org/#s2', 'http://bar.org/p1', 'http://foo.org/#o2']));

it('should return all triples with that object',
shouldIncludeAll(n3Store.find(null, null, 'a:o1'),
shouldIncludeAll(store.find(null, null, 'a:o1'),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1'],

@@ -434,9 +427,6 @@ ['http://foo.org/#s1', 'http://bar.org/p2', 'http://foo.org/#o1']));

describe('should allow to query contexts with prefixes', function () {
it('should return all triples with that context',
shouldIncludeAll(n3Store.find(null, null, null, 'ctx:default'),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1'],
['http://foo.org/#s1', 'http://bar.org/p2', 'http://foo.org/#o1'],
['http://foo.org/#s2', 'http://bar.org/p1', 'http://foo.org/#o2'],
['http://foo.org/#s3', 'http://bar.org/p3', '"a"^^http://foo.org/#t1']));
describe('should allow to query graphs with prefixes', function () {
it('should return all triples with that graph',
shouldIncludeAll(store.find(null, null, null, 'http://graphs.org/#g1'),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1', 'http://graphs.org/#g1']));
});

@@ -446,14 +436,15 @@ });

describe('An N3Store with prefixes added later on', function () {
var n3Store = new N3Store([
var store = new N3Store([
{ subject: 'http://foo.org/#s1', predicate: 'http://bar.org/p1', object: 'http://foo.org/#o1' },
{ subject: 'http://foo.org/#s1', predicate: 'http://bar.org/p2', object: 'http://foo.org/#o1' },
{ subject: 'http://foo.org/#s2', predicate: 'http://bar.org/p1', object: 'http://foo.org/#o2' },
{ subject: 'http://foo.org/#s1', predicate: 'http://bar.org/p1', object: 'http://foo.org/#o1', graph: 'http://graphs.org/#g1' },
]);
n3Store.addPrefix('a', 'http://foo.org/#');
n3Store.addPrefixes({ 'b': 'http://bar.org/', 'ctx': 'n3/contexts#'});
store.addPrefix('a', 'http://foo.org/#');
store.addPrefixes({ 'b': 'http://bar.org/', 'g': 'http://graphs.org/#' });
describe('should allow to query subjects with prefixes', function () {
it('should return all triples with that subject',
shouldIncludeAll(n3Store.find('a:s1', null, null),
shouldIncludeAll(store.find('a:s1', null, null),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1'],

@@ -465,3 +456,3 @@ ['http://foo.org/#s1', 'http://bar.org/p2', 'http://foo.org/#o1']));

it('should return all triples with that predicate',
shouldIncludeAll(n3Store.find(null, 'b:p1', null),
shouldIncludeAll(store.find(null, 'b:p1', null),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1'],

@@ -473,3 +464,3 @@ ['http://foo.org/#s2', 'http://bar.org/p1', 'http://foo.org/#o2']));

it('should return all triples with that object',
shouldIncludeAll(n3Store.find(null, null, 'a:o1'),
shouldIncludeAll(store.find(null, null, 'a:o1'),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1'],

@@ -479,8 +470,6 @@ ['http://foo.org/#s1', 'http://bar.org/p2', 'http://foo.org/#o1']));

describe('should allow to query contexts with prefixes', function () {
it('should return all triples with that context',
shouldIncludeAll(n3Store.find(null, null, null, 'ctx:default'),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1'],
['http://foo.org/#s1', 'http://bar.org/p2', 'http://foo.org/#o1'],
['http://foo.org/#s2', 'http://bar.org/p1', 'http://foo.org/#o2']));
describe('should allow to query graphs with prefixes', function () {
it('should return all triples with that graph',
shouldIncludeAll(store.find(null, null, null, 'http://graphs.org/#g1'),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1', 'http://graphs.org/#g1']));
});

@@ -490,3 +479,3 @@ });

describe('An N3Store with the http prefix', function () {
var n3Store = new N3Store([
var store = new N3Store([
{ subject: 'http://foo.org/#s1', predicate: 'http://bar.org/p1', object: 'http://foo.org/#o1' },

@@ -496,9 +485,7 @@ { subject: 'http://foo.org/#s1', predicate: 'http://bar.org/p2', object: 'http://foo.org/#o1' },

],
{
'http': 'http://www.w3.org/2006/http#'
});
{ prefixes: { 'http': 'http://www.w3.org/2006/http#' } });
describe('should allow to query subjects without prefixes', function () {
it('should return all triples with that subject',
shouldIncludeAll(n3Store.find('http://foo.org/#s1', null, null),
shouldIncludeAll(store.find('http://foo.org/#s1', null, null),
['http://foo.org/#s1', 'http://bar.org/p1', 'http://foo.org/#o1'],

@@ -510,8 +497,8 @@ ['http://foo.org/#s1', 'http://bar.org/p2', 'http://foo.org/#o1']));

describe('An N3Store created without triples but with prefixes', function () {
var n3Store = new N3Store({ 'http': 'http://www.w3.org/2006/http#' });
n3Store.addTriple('a', 'http://www.w3.org/2006/http#b', 'c');
var store = new N3Store({ prefixes: { 'http': 'http://www.w3.org/2006/http#' } });
store.addTriple('a', 'http://www.w3.org/2006/http#b', 'c');
describe('should allow to query predicates with prefixes', function () {
it('should return all triples with that predicate',
shouldIncludeAll(n3Store.find(null, 'http:b', null),
shouldIncludeAll(store.find(null, 'http:b', null),
['a', 'http://www.w3.org/2006/http#b', 'c']));

@@ -522,3 +509,3 @@ });

describe('An N3Store', function () {
var n3Store = new N3Store();
var store = new N3Store();

@@ -528,4 +515,4 @@ // Test inspired by http://www.devthought.com/2012/01/18/an-object-is-not-a-hash/.

it('should be able to contain entities with JavaScript object property names', function () {
n3Store.addTriple('toString', 'valueOf', 'toLocaleString', 'hasOwnProperty');
shouldIncludeAll(n3Store.find(null, null, null, 'hasOwnProperty'),
store.addTriple('toString', 'valueOf', 'toLocaleString', 'hasOwnProperty');
shouldIncludeAll(store.find(null, null, null, 'hasOwnProperty'),
['toString', 'valueOf', 'toLocaleString', 'hasOwnProperty'])();

@@ -535,4 +522,4 @@ });

it('should be able to contain entities named "null"', function () {
n3Store.addTriple('null', 'null', 'null', 'null');
shouldIncludeAll(n3Store.find(null, null, null, 'null'), ['null', 'null', 'null', 'null'])();
store.addTriple('null', 'null', 'null', 'null');
shouldIncludeAll(store.find(null, null, null, 'null'), ['null', 'null', 'null', 'null'])();
});

@@ -551,4 +538,3 @@ });

var items = Array.prototype.slice.call(arguments, 1).map(function (arg) {
return { subject: arg[0], predicate: arg[1], object: arg[2],
context: arg[3] || 'n3/contexts#default' };
return { subject: arg[0], predicate: arg[1], object: arg[2], graph: arg[3] || '' };
});

@@ -555,0 +541,0 @@ return function () {

@@ -35,4 +35,4 @@ var N3StreamParser = require('../N3').StreamParser;

it('emits "prefix" events',
shouldEmitPrefixes(['@prefix a: <URIa>. a:a a:b a:c. @prefix b: <URIb>.'],
{ a: 'URIa', b: 'URIb' }));
shouldEmitPrefixes(['@prefix a: <IRIa>. a:a a:b a:c. @prefix b: <IRIb>.'],
{ a: 'IRIa', b: 'IRIb' }));
});

@@ -80,3 +80,3 @@ });

transform.on('data', function () {});
transform.on('prefix', function (prefix, uri) { prefixes[prefix] = uri; });
transform.on('prefix', function (prefix, iri) { prefixes[prefix] = iri; });
transform.on('error', done);

@@ -83,0 +83,0 @@ transform.on('end', function (error) {

@@ -54,5 +54,3 @@ var N3StreamWriter = require('../N3').StreamWriter;

it('should use prefixes when possible',
shouldSerialize({ a: 'http://a.org/',
b: 'http://a.org/b#',
c: 'http://a.org/b' },
shouldSerialize({ prefixes: { a: 'http://a.org/', b: 'http://a.org/b#', c: 'http://a.org/b' } },
[['http://a.org/bc', 'http://a.org/b#ef', 'http://a.org/bhi'],

@@ -70,8 +68,8 @@ ['http://a.org/bc/de', 'http://a.org/b#e#f', 'http://a.org/b#x/t'],

function shouldSerialize(prefixes, tripleArrays, expectedResult) {
function shouldSerialize(options, tripleArrays, expectedResult) {
if (!expectedResult)
expectedResult = tripleArrays, tripleArrays = prefixes, prefixes = null;
expectedResult = tripleArrays, tripleArrays = options, options = null;
return function (done) {
var inputStream = new ArrayReader(tripleArrays),
transform = new N3StreamWriter(prefixes),
transform = new N3StreamWriter(options),
outputStream = new StringWriter();

@@ -78,0 +76,0 @@ inputStream.pipe(transform);

@@ -15,3 +15,3 @@ var N3Util = require('../N3').Util;

N3Util(host).should.equal(host);
host.isUri.should.be.a('function');
host.isIRI.should.be.a('function');
host.isLiteral.should.be.a('function');

@@ -25,3 +25,3 @@ host.isPrefixedName('a:b').should.be.true;

N3Util(Constructor, true).should.equal(Constructor);
Constructor.prototype.isUri.should.be.a('function');
Constructor.prototype.isIRI.should.be.a('function');
Constructor.prototype.isLiteral.should.be.a('function');

@@ -34,21 +34,21 @@

describe('isUri', function () {
it('matches a URI', function () {
N3Util.isUri('http://example.org/').should.be.true;
describe('isIRI', function () {
it('matches an IRI', function () {
N3Util.isIRI('http://example.org/').should.be.true;
});
it('does not match a literal', function () {
N3Util.isUri('"http://example.org/"').should.be.false;
N3Util.isIRI('"http://example.org/"').should.be.false;
});
it('does not match a blank node', function () {
N3Util.isUri('_:x').should.be.false;
N3Util.isIRI('_:x').should.be.false;
});
it('does not match null', function () {
expect(N3Util.isUri(null)).to.be.null;
expect(N3Util.isIRI(null)).to.be.null;
});
it('does not match undefined', function () {
expect(N3Util.isUri(undefined)).to.be.undefined;
expect(N3Util.isIRI(undefined)).to.be.undefined;
});

@@ -82,3 +82,3 @@ });

it('does not match a URI', function () {
it('does not match an IRI', function () {
N3Util.isLiteral('http://example.org/').should.be.false;

@@ -105,3 +105,3 @@ });

it('does not match a URI', function () {
it('does not match an IRI', function () {
N3Util.isBlank('http://example.org/').should.be.false;

@@ -246,3 +246,3 @@ });

it('does not match a URI', function () {
it('does not match an IRI', function () {
N3Util.isPrefixedName('http://example.org/').should.be.false;

@@ -249,0 +249,0 @@ });

@@ -94,2 +94,6 @@ var N3Writer = require('../N3').Writer;

it('should serialize a literal containing special unicode characters',
shouldSerialize([['a', 'b', '"c\u0000\u0001"']],
'<a> <b> "c\\u0000\\u0001".\n'));
it('should serialize blank nodes',

@@ -100,9 +104,13 @@ shouldSerialize([['_:a', 'b', '_:c']],

it('should not serialize a literal in the subject',
shouldNotSerialize([['"a"', 'b', '"c']],
shouldNotSerialize([['"a"', 'b', '"c"']],
'A literal as subject is not allowed: "a"'));
it('should not serialize a literal in the predicate',
shouldNotSerialize([['a', '"b"', '"c']],
shouldNotSerialize([['a', '"b"', '"c"']],
'A literal as predicate is not allowed: "b"'));
it('should not serialize an invalid object literal',
shouldNotSerialize([['a', 'b', '"c']],
'Invalid literal: "c'));
it('should not leave leading whitespace if the prefix set is empty',

@@ -114,5 +122,3 @@ shouldSerialize({},

it('should serialize valid prefixes',
shouldSerialize({ a: 'http://a.org/',
b: 'http://a.org/b#',
c: 'http://a.org/b' },
shouldSerialize({ prefixes: { a: 'http://a.org/', b: 'http://a.org/b#', c: 'http://a.org/b' } },
[],

@@ -123,5 +129,3 @@ '@prefix a: <http://a.org/>.\n' +

it('should use prefixes when possible',
shouldSerialize({ a: 'http://a.org/',
b: 'http://a.org/b#',
c: 'http://a.org/b' },
shouldSerialize({ prefixes: { a: 'http://a.org/', b: 'http://a.org/b#', c: 'http://a.org/b' } },
[['http://a.org/bc', 'http://a.org/b#ef', 'http://a.org/bhi'],

@@ -158,2 +162,34 @@ ['http://a.org/bc/de', 'http://a.org/b#e#f', 'http://a.org/b#x/t'],

it('should serialize a graph with 1 triple',
shouldSerialize([['abc', 'def', 'ghi', 'xyz']],
'<xyz> {\n' +
'<abc> <def> <ghi>\n' +
'}\n'));
it('should serialize a graph with 3 triples',
shouldSerialize([['abc', 'def', 'ghi', 'xyz'],
['jkl', 'mno', 'pqr', 'xyz'],
['stu', 'vwx', 'yz', 'xyz']],
'<xyz> {\n' +
'<abc> <def> <ghi>.\n' +
'<jkl> <mno> <pqr>.\n' +
'<stu> <vwx> <yz>\n' +
'}\n'));
it('should serialize three graphs',
shouldSerialize([['abc', 'def', 'ghi', 'xyz'],
['jkl', 'mno', 'pqr', ''],
['stu', 'vwx', 'yz', 'abc']],
'<xyz> {\n<abc> <def> <ghi>\n}\n' +
'<jkl> <mno> <pqr>.\n' +
'<abc> {\n<stu> <vwx> <yz>\n}\n'));
it('should output 8-bit unicode characters as escape sequences',
shouldSerialize([['\ud835\udc00', '\ud835\udc00', '"\ud835\udc00"^^\ud835\udc00', '\ud835\udc00']],
'<\\U0001d400> {\n<\\U0001d400> <\\U0001d400> "\\U0001d400"^^<\\U0001d400>\n}\n'));
it('should not use escape sequences in blank nodes',
shouldSerialize([['_:\ud835\udc00', '_:\ud835\udc00', '_:\ud835\udc00', '_:\ud835\udc00']],
'_:\ud835\udc00 {\n_:\ud835\udc00 _:\ud835\udc00 _:\ud835\udc00\n}\n'));
it('calls the done callback when ending the outputstream errors', function (done) {

@@ -177,3 +213,3 @@ var writer = new N3Writer({

it('respects the prefixes argument when no stream argument is given', function (done) {
var writer = new N3Writer({ a: 'b#' });
var writer = new N3Writer({ prefixes: { a: 'b#' } });
writer.addTriple({ subject: 'b#a', predicate: 'b#b', object: 'b#c' });

@@ -202,2 +238,15 @@ writer.end(function (error, output) {

it('serializes triples of a graph with a prefix declaration in between', function (done) {
var writer = new N3Writer();
writer.addPrefix('a', 'b#');
writer.addTriple({ subject: 'b#a', predicate: 'b#b', object: 'b#c', graph: 'b#g' });
writer.addPrefix('d', 'e#');
writer.addTriple({ subject: 'b#a', predicate: 'b#b', object: 'b#d', graph: 'b#g' });
writer.end(function (error, output) {
output.should.equal('@prefix a: <b#>.\n\na:g {\na:a a:b a:c\n}\n' +
'@prefix d: <e#>.\n\na:g {\na:a a:b a:d\n}\n');
done(error);
});
});
it('should accept triples with separated components', function (done) {

@@ -213,2 +262,12 @@ var writer = N3Writer();

it('should accept quads with separated components', function (done) {
var writer = N3Writer();
writer.addTriple('a', 'b', 'c', 'g');
writer.addTriple('a', 'b', 'd', 'g');
writer.end(function (error, output) {
output.should.equal('<g> {\n<a> <b> <c>, <d>\n}\n');
done(error);
});
});
it('should accept triples in bulk', function (done) {

@@ -234,2 +293,40 @@ var writer = N3Writer();

});
it('should write simple triples in N-Triples mode', function (done) {
var writer = N3Writer({ format: 'N-Triples' });
writer.addTriple('a', 'b', 'c');
writer.addTriple('a', 'b', 'd');
writer.end(function (error, output) {
output.should.equal('<a> <b> <c>.\n<a> <b> <d>.\n');
done(error);
});
});
it('should not write an invalid literal in N-Triples mode', function (done) {
var writer = N3Writer({ format: 'N-Triples' });
writer.addTriple('a', 'b', '"c', function (error) {
error.should.be.an.instanceof(Error);
error.should.have.property('message', 'Invalid literal: "c');
done();
});
});
it('should write simple quads in N-Quads mode', function (done) {
var writer = N3Writer({ format: 'N-Quads' });
writer.addTriple('a', 'b', 'c');
writer.addTriple('a', 'b', 'd', 'g');
writer.end(function (error, output) {
output.should.equal('<a> <b> <c>.\n<a> <b> <d> <g>.\n');
done(error);
});
});
it('should not write an invalid literal in N-Quads mode', function (done) {
var writer = N3Writer({ format: 'N-Triples' });
writer.addTriple('a', 'b', '"c', function (error) {
error.should.be.an.instanceof(Error);
error.should.have.property('message', 'Invalid literal: "c');
done();
});
});
});

@@ -247,3 +344,3 @@ });

if (item)
writer.addTriple({ subject: item[0], predicate: item[1], object: item[2] }, next);
writer.addTriple({ subject: item[0], predicate: item[1], object: item[2], graph: item[3] }, next);
else

@@ -268,3 +365,3 @@ writer.end(function (error) {

item = tripleArrays.shift();
writer.addTriple({ subject: item[0], predicate: item[1], object: item[2] },
writer.addTriple({ subject: item[0], predicate: item[1], object: item[2], graph: item[3] },
function (error) {

@@ -271,0 +368,0 @@ if (error) {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc