Comparing version 0.11.3 to 1.0.0-alpha
// **N3Lexer** tokenizes N3 documents. | ||
var xsd = require('./IRIs').xsd; | ||
var fromCharCode = String.fromCharCode; | ||
@@ -52,5 +54,5 @@ var immediately = typeof setImmediate === 'function' ? setImmediate : | ||
_unescapedIri: /^<([^\x00-\x20<>\\"\{\}\|\^\`]*)>[ \t]*/, // IRI without escape sequences; no unescaping | ||
_unescapedString: /^"[^"\\\r\n]+"/, // non-empty string without escape sequences | ||
_singleQuotedString: /^"(?:[^"\\\r\n]|\\.)*"(?=[^"])|^'(?:[^'\\\r\n]|\\.)*'(?=[^'])/, | ||
_tripleQuotedString: /^""("[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*")""|^''('[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*')''/, | ||
_unescapedString: /^"([^"\\\r\n]+)"/, // non-empty string without escape sequences | ||
_singleQuotedString: /^"((?:[^"\\\r\n]|\\.)*)"(?=[^"])|^'((?:[^'\\\r\n]|\\.)*)'(?=[^'])/, | ||
_tripleQuotedString: /^"""([^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*)"""|^'''([^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*)'''/, | ||
_langcode: /^@([a-z]+(?:-[a-z0-9]+)*)(?=[^a-z0-9\-])/i, | ||
@@ -106,3 +108,3 @@ _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<])/, | ||
var line = this._line, type = '', value = '', prefix = '', | ||
firstChar = input[0], match = null, matchLength = 0, unescaped, inconclusive = false; | ||
firstChar = input[0], match = null, matchLength = 0, inconclusive = false; | ||
switch (firstChar) { | ||
@@ -138,10 +140,10 @@ case '^': | ||
else if (match = this._iri.exec(input)) { | ||
unescaped = this._unescape(match[1]); | ||
if (unescaped === null || illegalIriChars.test(unescaped)) | ||
value = this._unescape(match[1]); | ||
if (value === null || illegalIriChars.test(value)) | ||
return reportSyntaxError(this); | ||
type = 'IRI', value = unescaped; | ||
type = 'IRI'; | ||
} | ||
// Try to find a backwards implication arrow | ||
else if (this._n3Mode && input.length > 1 && input[1] === '=') | ||
type = 'inverse', matchLength = 2, value = 'http://www.w3.org/2000/10/swap/log#implies'; | ||
type = 'inverse', matchLength = 2, value = '>'; | ||
break; | ||
@@ -162,20 +164,17 @@ | ||
if (match = this._unescapedString.exec(input)) | ||
type = 'literal', value = match[0]; | ||
value = match[1]; | ||
// Try to find any other literal wrapped in a pair of single or double quotes | ||
else if (match = this._singleQuotedString.exec(input)) { | ||
unescaped = this._unescape(match[0]); | ||
if (unescaped === null) | ||
return reportSyntaxError(this); | ||
type = 'literal', value = unescaped.replace(/^'|'$/g, '"'); | ||
} | ||
else if (match = this._singleQuotedString.exec(input)) | ||
value = this._unescape(typeof match[1] === 'string' ? match[1] : match[2]); | ||
// Try to find a literal wrapped in three pairs of single or double quotes | ||
else if (match = this._tripleQuotedString.exec(input)) { | ||
unescaped = match[1] || match[2]; | ||
// Count the newlines and advance line counter | ||
this._line += unescaped.split(/\r\n|\r|\n/).length - 1; | ||
unescaped = this._unescape(unescaped); | ||
if (unescaped === null) | ||
return reportSyntaxError(this); | ||
type = 'literal', value = unescaped.replace(/^'|'$/g, '"'); | ||
value = typeof match[1] === 'string' ? match[1] : match[2]; | ||
// Advance line counter | ||
this._line += value.split(/\r\n|\r|\n/).length - 1; | ||
value = this._unescape(value); | ||
} | ||
if (value === null) | ||
return reportSyntaxError(this); | ||
if (match !== null) | ||
type = 'literal'; | ||
break; | ||
@@ -224,5 +223,5 @@ | ||
inputFinished && (match = this._number.exec(input + ' '))) { | ||
type = 'literal'; | ||
value = '"' + match[0] + '"^^http://www.w3.org/2001/XMLSchema#' + | ||
(match[1] ? 'double' : (/^[+\-]?\d+$/.test(match[0]) ? 'integer' : 'decimal')); | ||
type = 'literal', value = match[0]; | ||
prefix = (match[1] ? xsd.double : | ||
(/^[+\-]?\d+$/.test(match[0]) ? xsd.integer : xsd.decimal)); | ||
} | ||
@@ -248,3 +247,3 @@ break; | ||
if (match = this._boolean.exec(input)) | ||
type = 'literal', value = '"' + match[0] + '"^^http://www.w3.org/2001/XMLSchema#boolean'; | ||
type = 'literal', value = match[0], prefix = xsd.boolean; | ||
else | ||
@@ -257,3 +256,3 @@ inconclusive = true; | ||
if (match = this._shortPredicates.exec(input)) | ||
type = 'abbreviation', value = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; | ||
type = 'abbreviation', value = 'a'; | ||
else | ||
@@ -268,5 +267,5 @@ inconclusive = true; | ||
if (input[1] !== '>') | ||
matchLength = 1, value = 'http://www.w3.org/2002/07/owl#sameAs'; | ||
matchLength = 1, value = '='; | ||
else | ||
matchLength = 2, value = 'http://www.w3.org/2000/10/swap/log#implies'; | ||
matchLength = 2, value = '>'; | ||
} | ||
@@ -273,0 +272,0 @@ break; |
// **N3Parser** parses N3 documents. | ||
var N3Lexer = require('./N3Lexer'); | ||
var N3Lexer = require('./N3Lexer'), | ||
DataFactory = require('./N3DataFactory'), | ||
namespaces = require('./IRIs'); | ||
var RDF_PREFIX = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', | ||
RDF_NIL = RDF_PREFIX + 'nil', | ||
RDF_FIRST = RDF_PREFIX + 'first', | ||
RDF_REST = RDF_PREFIX + 'rest'; | ||
var QUANTIFIERS_GRAPH = 'urn:n3:quantifiers'; | ||
// Regexes for IRIs | ||
var absoluteIRI = /^[a-z][a-z0-9+.-]*:/i, | ||
@@ -27,3 +23,4 @@ schemeAuthority = /^(?:([a-z][a-z0-9+.-]*:))?(?:\/\/[^\/]*)?/i, | ||
options = options || {}; | ||
this._setBase(options.documentIRI); | ||
this._setBase(options.baseIRI); | ||
options.factory && initDataFactory(this, options.factory); | ||
@@ -49,3 +46,3 @@ // Set supported features depending on the format | ||
this._blankNodePrefix = typeof options.blankNodePrefix !== 'string' ? '' : | ||
'_:' + options.blankNodePrefix.replace(/^_:/, ''); | ||
options.blankNodePrefix.replace(/^(?!_:)/, '_:'); | ||
this._lexer = options.lexer || new N3Lexer({ lineMode: isLineMode, n3: isN3 }); | ||
@@ -66,2 +63,7 @@ // Disable explicit quantifiers by default | ||
// ### `_blank` creates a new blank node | ||
_blank: function () { | ||
return this._blankNode('b' + blankNodeCount++); | ||
}, | ||
// ### `_setBase` sets the base IRI to resolve relative IRIs | ||
@@ -103,3 +105,3 @@ _setBase: function (baseIRI) { | ||
// (using a dot as separator, as a blank node label cannot start with it) | ||
this._prefixes._ = this._graph + '.'; | ||
this._prefixes._ = (this._graph ? this._graph.id.substr(2) + '.' : '.'); | ||
// Quantifiers are scoped to a formula | ||
@@ -168,8 +170,7 @@ this._quantified = Object.create(this._quantified); | ||
case 'typeIRI': | ||
value = (this._base === null || absoluteIRI.test(token.value)) ? | ||
token.value : this._resolveIRI(token); | ||
value = this._namedNode(this._base === null || absoluteIRI.test(token.value) ? | ||
token.value : this._resolveIRI(token)); | ||
break; | ||
// Read a blank node or prefixed name | ||
// Read a prefixed name | ||
case 'type': | ||
case 'blank': | ||
case 'prefixed': | ||
@@ -179,7 +180,12 @@ var prefix = this._prefixes[token.prefix]; | ||
return this._error('Undefined prefix "' + token.prefix + ':"', token); | ||
value = prefix + token.value; | ||
value = this._namedNode(prefix + token.value); | ||
break; | ||
// Read a blank node | ||
case 'blank': | ||
value = this._blankNode(this._prefixes[token.prefix] + token.value); | ||
break; | ||
// Read a variable | ||
case 'var': | ||
return token.value; | ||
value = this._variable(token.value.substr(1)); | ||
break; | ||
// Everything else is not an entity | ||
@@ -190,8 +196,8 @@ default: | ||
// In N3 mode, replace the entity if it is quantified | ||
if (!quantifier && this._n3Mode && (value in this._quantified)) | ||
value = this._quantified[value]; | ||
if (!quantifier && this._n3Mode && (value.id in this._quantified)) | ||
value = this._quantified[value.id]; | ||
return value; | ||
}, | ||
// ### `_readSubject` reads a triple's subject | ||
// ### `_readSubject` reads a quad's subject | ||
_readSubject: function (token) { | ||
@@ -201,9 +207,9 @@ this._predicate = null; | ||
case '[': | ||
// Start a new triple with a new blank node as subject | ||
// Start a new quad with a new blank node as subject | ||
this._saveContext('blank', this._graph, | ||
this._subject = '_:b' + blankNodeCount++, null, null); | ||
this._subject = this._blank(), null, null); | ||
return this._readBlankNodeHead; | ||
case '(': | ||
// Start a new list | ||
this._saveContext('list', this._graph, RDF_NIL, null, null); | ||
this._saveContext('list', this._graph, this.RDF_NIL, null, null); | ||
this._subject = null; | ||
@@ -216,3 +222,3 @@ return this._readListItem; | ||
this._saveContext('formula', this._graph, | ||
this._graph = '_:b' + blankNodeCount++, null, null); | ||
this._graph = this._blank(), null, null); | ||
return this._readSubject; | ||
@@ -226,4 +232,4 @@ case '}': | ||
this._subject = null; | ||
this._predicate = 'http://www.w3.org/2000/10/swap/reify#forSome'; | ||
this._quantifiedPrefix = '_:b'; | ||
this._predicate = this.N3_FORSOME; | ||
this._quantifier = this._blankNode; | ||
return this._readQuantifierList; | ||
@@ -234,4 +240,4 @@ case '@forAll': | ||
this._subject = null; | ||
this._predicate = 'http://www.w3.org/2000/10/swap/reify#forAll'; | ||
this._quantifiedPrefix = '?b-'; | ||
this._predicate = this.N3_FORALL; | ||
this._quantifier = this._variable; | ||
return this._readQuantifierList; | ||
@@ -252,3 +258,3 @@ default: | ||
// ### `_readPredicate` reads a triple's predicate | ||
// ### `_readPredicate` reads a quad's predicate | ||
_readPredicate: function (token) { | ||
@@ -260,3 +266,3 @@ var type = token.type; | ||
case 'abbreviation': | ||
this._predicate = token.value; | ||
this._predicate = this.ABBREVIATIONS[token.value]; | ||
break; | ||
@@ -286,16 +292,21 @@ case '.': | ||
// ### `_readObject` reads a triple's object | ||
// ### `_readObject` reads a quad's object | ||
_readObject: function (token) { | ||
switch (token.type) { | ||
case 'literal': | ||
this._object = token.value; | ||
return this._readDataTypeOrLang; | ||
// Regular literal, can still get a datatype or language | ||
if (token.prefix.length === 0) | ||
return this._readDataTypeOrLang; | ||
// Pre-datatyped string literal (prefix stores the datatype) | ||
else | ||
this._object = this._literal(token.value, this._namedNode(token.prefix)); | ||
break; | ||
case '[': | ||
// Start a new triple with a new blank node as subject | ||
// Start a new quad with a new blank node as subject | ||
this._saveContext('blank', this._graph, this._subject, this._predicate, | ||
this._subject = '_:b' + blankNodeCount++); | ||
this._subject = this._blank()); | ||
return this._readBlankNodeHead; | ||
case '(': | ||
// Start a new list | ||
this._saveContext('list', this._graph, this._subject, this._predicate, RDF_NIL); | ||
this._saveContext('list', this._graph, this._subject, this._predicate, this.RDF_NIL); | ||
this._subject = null; | ||
@@ -308,3 +319,3 @@ return this._readListItem; | ||
this._saveContext('formula', this._graph, this._subject, this._predicate, | ||
this._graph = '_:b' + blankNodeCount++); | ||
this._graph = this._blank()); | ||
return this._readSubject; | ||
@@ -322,3 +333,3 @@ default: | ||
// ### `_readPredicateOrNamedGraph` reads a triple's predicate, or a named graph | ||
// ### `_readPredicateOrNamedGraph` reads a quad's predicate, or a named graph | ||
_readPredicateOrNamedGraph: function (token) { | ||
@@ -354,5 +365,5 @@ return token.type === '{' ? this._readGraph(token) : this._readPredicate(token); | ||
// Store blank node triple | ||
// Store blank node quad | ||
if (this._subject !== null) | ||
this._triple(this._subject, this._predicate, this._object, this._graph); | ||
this._emit(this._subject, this._predicate, this._object, this._graph); | ||
@@ -375,3 +386,3 @@ // Restore the parent context containing this blank node | ||
if (token.type === '.' && !this._contextStack.length) { | ||
this._subject = null; // cancel the current triple | ||
this._subject = null; // cancel the current quad | ||
return this._readPunctuation(token); | ||
@@ -389,16 +400,16 @@ } | ||
parent = stack[stack.length - 1], // The parent containing the current list | ||
next = this._readListItem, // The next function to execute | ||
itemComplete = true; // Whether the item has been read fully | ||
next = this._readListItem; // The next function to execute | ||
switch (token.type) { | ||
case '[': | ||
// Stack the current list triple and start a new triple with a blank node as subject | ||
this._saveContext('blank', this._graph, list = '_:b' + blankNodeCount++, | ||
RDF_FIRST, this._subject = item = '_:b' + blankNodeCount++); | ||
// Stack the current list quad and start a new quad with a blank node as subject | ||
this._saveContext('blank', this._graph, | ||
list = this._blank(), this.RDF_FIRST, | ||
this._subject = item = this._blank()); | ||
next = this._readBlankNodeHead; | ||
break; | ||
case '(': | ||
// Stack the current list triple and start a new list | ||
this._saveContext('list', this._graph, list = '_:b' + blankNodeCount++, | ||
RDF_FIRST, RDF_NIL); | ||
// Stack the current list quad and start a new list | ||
this._saveContext('list', this._graph, | ||
list = this._blank(), this.RDF_FIRST, this.RDF_NIL); | ||
this._subject = null; | ||
@@ -409,6 +420,6 @@ break; | ||
this._restoreContext(); | ||
// If this list is contained within a parent list, return the membership triple here. | ||
// If this list is contained within a parent list, return the membership quad here. | ||
// This will be `<parent list element> rdf:first <this list>.`. | ||
if (stack.length !== 0 && stack[stack.length - 1].type === 'list') | ||
this._triple(this._subject, this._predicate, this._object, this._graph); | ||
this._emit(this._subject, this._predicate, this._object, this._graph); | ||
// Was this list the parent's subject? | ||
@@ -419,3 +430,3 @@ if (this._predicate === null) { | ||
// No list tail if this was an empty list | ||
if (this._subject === RDF_NIL) | ||
if (this._subject === this.RDF_NIL) | ||
return next; | ||
@@ -427,12 +438,17 @@ } | ||
// No list tail if this was an empty list | ||
if (this._object === RDF_NIL) | ||
if (this._object === this.RDF_NIL) | ||
return next; | ||
} | ||
// Close the list by making the head nil | ||
list = RDF_NIL; | ||
list = this.RDF_NIL; | ||
break; | ||
case 'literal': | ||
item = token.value; | ||
itemComplete = false; // Can still have a datatype or language | ||
next = this._readListItemDataTypeOrLang; | ||
// Regular literal, can still get a datatype or language | ||
if (token.prefix.length === 0) | ||
next = this._readListItemDataTypeOrLang; | ||
// Pre-datatyped string literal (prefix stores the datatype) | ||
else { | ||
item = this._literal(token.value, this._namedNode(token.prefix)); | ||
next = this._getContextEndReader(); | ||
} | ||
break; | ||
@@ -446,3 +462,3 @@ default: | ||
if (list === null) | ||
this._subject = list = '_:b' + blankNodeCount++; | ||
this._subject = list = this._blank(); | ||
@@ -459,5 +475,5 @@ // Is this the first element of the list? | ||
// Continue the previous list with the current list | ||
this._triple(prevList, RDF_REST, list, this._graph); | ||
this._emit(prevList, this.RDF_REST, list, this._graph); | ||
} | ||
// Add the item's value | ||
// If an item was read, add it to the list | ||
if (item !== null) { | ||
@@ -467,3 +483,3 @@ // In N3 mode, the item might be a path | ||
// Create a new context to add the item's path | ||
this._saveContext('item', this._graph, list, RDF_FIRST, item); | ||
this._saveContext('item', this._graph, list, this.RDF_FIRST, item); | ||
this._subject = item, this._predicate = null; | ||
@@ -473,8 +489,4 @@ // _readPath will restore the context and output the item | ||
} | ||
// Output the item if it is complete | ||
if (itemComplete) | ||
this._triple(list, RDF_FIRST, item, this._graph); | ||
// Otherwise, save it for completion | ||
else | ||
this._object = item; | ||
// Output the item | ||
this._emit(list, this.RDF_FIRST, item, this._graph); | ||
} | ||
@@ -484,3 +496,3 @@ return next; | ||
// ### `_readDataTypeOrLang` reads an _optional_ data type or language | ||
// ### `_readDataTypeOrLang` reads an _optional_ datatype or language | ||
_readDataTypeOrLang: function (token) { | ||
@@ -490,3 +502,3 @@ return this._completeLiteral(token, false); | ||
// ### `_readListItemDataTypeOrLang` reads an _optional_ data type or language in a list | ||
// ### `_readListItemDataTypeOrLang` reads an _optional_ datatype or language in a list | ||
_readListItemDataTypeOrLang: function (token) { | ||
@@ -496,20 +508,22 @@ return this._completeLiteral(token, true); | ||
// ### `_completeLiteral` completes the object with a data type or language | ||
// ### `_completeLiteral` completes a literal with an optional datatype or language | ||
_completeLiteral: function (token, listItem) { | ||
var suffix = false; | ||
var value = this._lexer.previousToken.value; | ||
switch (token.type) { | ||
// Add a "^^type" suffix for types (IRIs and blank nodes) | ||
// Create a datatyped literal | ||
case 'type': | ||
case 'typeIRI': | ||
var datatype = this._readEntity(token); | ||
// No datatype means an error was reported, so abort | ||
if (datatype === undefined) return; | ||
suffix = true; | ||
this._object += '^^' + datatype; | ||
if (datatype === undefined) return; // No datatype means an error occurred | ||
this._object = this._literal(value, datatype); | ||
token = null; | ||
break; | ||
// Add an "@lang" suffix for language tags | ||
// Create a language-tagged string | ||
case 'langcode': | ||
suffix = true; | ||
this._object += '@' + token.value.toLowerCase(); | ||
this._object = this._literal(value, token.value); | ||
token = null; | ||
break; | ||
// Create a simple string literal | ||
default: | ||
this._object = this._literal(value); | ||
} | ||
@@ -519,6 +533,7 @@ // If this literal was part of a list, write the item | ||
if (listItem) | ||
this._triple(this._subject, RDF_FIRST, this._object, this._graph); | ||
// Continue with the rest of the input | ||
if (suffix) | ||
this._emit(this._subject, this.RDF_FIRST, this._object, this._graph); | ||
// If the token was consumed, continue with the rest of the input | ||
if (token === null) | ||
return this._getContextEndReader(); | ||
// Otherwise, consume the token now | ||
else { | ||
@@ -535,5 +550,5 @@ this._readCallback = this._getContextEndReader(); | ||
// Store the last triple of the formula | ||
// Store the last quad of the formula | ||
if (this._subject !== null) | ||
this._triple(this._subject, this._predicate, this._object, this._graph); | ||
this._emit(this._subject, this._predicate, this._object, this._graph); | ||
@@ -547,3 +562,3 @@ // Restore the parent context containing this formula | ||
// ### `_readPunctuation` reads punctuation between triples or triple parts | ||
// ### `_readPunctuation` reads punctuation between quads or quad parts | ||
_readPunctuation: function (token) { | ||
@@ -580,11 +595,11 @@ var next, subject = this._subject, graph = this._graph, | ||
} | ||
return this._error('Expected punctuation to follow "' + this._object + '"', token); | ||
return this._error('Expected punctuation to follow "' + this._object.id + '"', token); | ||
} | ||
// A triple has been completed now, so return it | ||
// A quad has been completed now, so return it | ||
if (subject !== null) { | ||
var predicate = this._predicate, object = this._object; | ||
if (!inversePredicate) | ||
this._triple(subject, predicate, object, graph); | ||
this._emit(subject, predicate, object, graph); | ||
else | ||
this._triple(object, predicate, subject, graph); | ||
this._emit(object, predicate, subject, graph); | ||
} | ||
@@ -607,6 +622,6 @@ return next; | ||
default: | ||
return this._error('Expected punctuation to follow "' + this._object + '"', token); | ||
return this._error('Expected punctuation to follow "' + this._object.id + '"', token); | ||
} | ||
// A triple has been completed now, so return it | ||
this._triple(this._subject, this._predicate, this._object, this._graph); | ||
// A quad has been completed now, so return it | ||
this._emit(this._subject, this._predicate, this._object, this._graph); | ||
return next; | ||
@@ -634,5 +649,5 @@ }, | ||
return this._error('Expected IRI to follow prefix "' + this._prefix + ':"', token); | ||
var prefixIRI = this._readEntity(token); | ||
this._prefixes[this._prefix] = prefixIRI; | ||
this._prefixCallback(this._prefix, prefixIRI); | ||
var prefixNode = this._readEntity(token); | ||
this._prefixes[this._prefix] = prefixNode.value; | ||
this._prefixCallback(this._prefix, prefixNode); | ||
return this._readDeclarationPunctuation; | ||
@@ -668,3 +683,3 @@ }, | ||
return this._error('Invalid graph label', token); | ||
this._subject = '_:b' + blankNodeCount++; | ||
this._subject = this._blank(); | ||
return this._readGraph; | ||
@@ -699,3 +714,3 @@ }, | ||
if (!this._explicitQuantifiers) | ||
this._quantified[entity] = this._quantifiedPrefix + blankNodeCount++; | ||
this._quantified[entity.id] = this._quantifier('b' + blankNodeCount++); | ||
// With explicit quantifiers, output the reified quantifier | ||
@@ -705,10 +720,10 @@ else { | ||
if (this._subject === null) | ||
this._triple(this._graph || '', this._predicate, | ||
this._subject = '_:b' + blankNodeCount++, QUANTIFIERS_GRAPH); | ||
this._emit(this._graph || this.DEFAULTGRAPH, this._predicate, | ||
this._subject = this._blank(), this.QUANTIFIERS_GRAPH); | ||
// Otherwise, continue the previous list | ||
else | ||
this._triple(this._subject, RDF_REST, | ||
this._subject = '_:b' + blankNodeCount++, QUANTIFIERS_GRAPH); | ||
this._emit(this._subject, this.RDF_REST, | ||
this._subject = this._blank(), this.QUANTIFIERS_GRAPH); | ||
// Output the list item | ||
this._triple(this._subject, RDF_FIRST, entity, QUANTIFIERS_GRAPH); | ||
this._emit(this._subject, this.RDF_FIRST, entity, this.QUANTIFIERS_GRAPH); | ||
} | ||
@@ -727,3 +742,3 @@ return this._readQuantifierPunctuation; | ||
if (this._explicitQuantifiers) { | ||
this._triple(this._subject, RDF_REST, RDF_NIL, QUANTIFIERS_GRAPH); | ||
this._emit(this._subject, this.RDF_REST, this.RDF_NIL, this.QUANTIFIERS_GRAPH); | ||
this._subject = null; | ||
@@ -760,3 +775,3 @@ } | ||
// Output the list item | ||
this._triple(this._subject, RDF_FIRST, item, this._graph); | ||
this._emit(this._subject, this.RDF_FIRST, item, this._graph); | ||
} | ||
@@ -769,3 +784,3 @@ return this._afterPath(token); | ||
_readForwardPath: function (token) { | ||
var subject, predicate, object = '_:b' + blankNodeCount++; | ||
var subject, predicate, object = this._blank(); | ||
// The next token is the predicate | ||
@@ -780,4 +795,4 @@ if ((predicate = this._readEntity(token)) === undefined) | ||
subject = this._object, this._object = object; | ||
// Emit the path's current triple and read its next section | ||
this._triple(subject, predicate, object, this._graph); | ||
// Emit the path's current quad and read its next section | ||
this._emit(subject, predicate, object, this._graph); | ||
return this._readPath; | ||
@@ -788,3 +803,3 @@ }, | ||
_readBackwardPath: function (token) { | ||
var subject = '_:b' + blankNodeCount++, predicate, object; | ||
var subject = this._blank(), predicate, object; | ||
// The next token is the predicate | ||
@@ -799,4 +814,4 @@ if ((predicate = this._readEntity(token)) === undefined) | ||
object = this._object, this._object = subject; | ||
// Emit the path's current triple and read its next section | ||
this._triple(subject, predicate, object, this._graph); | ||
// Emit the path's current quad and read its next section | ||
this._emit(subject, predicate, object, this._graph); | ||
return this._readPath; | ||
@@ -821,6 +836,5 @@ }, | ||
// ### `_triple` emits a triple through the callback | ||
_triple: function (subject, predicate, object, graph) { | ||
this._callback(null, | ||
{ subject: subject, predicate: predicate, object: object, graph: graph || '' }); | ||
// ### `_emit` sends a quad through the callback | ||
_emit: function (subject, predicate, object, graph) { | ||
this._callback(null, this._quad(subject, predicate, object, graph || this.DEFAULTGRAPH)); | ||
}, | ||
@@ -925,4 +939,4 @@ | ||
// ### `parse` parses the N3 input and emits each parsed triple through the callback | ||
parse: function (input, tripleCallback, prefixCallback) { | ||
// ### `parse` parses the N3 input and emits each parsed quad through the callback | ||
parse: function (input, quadCallback, prefixCallback) { | ||
var self = this; | ||
@@ -934,3 +948,4 @@ // The read callback is the next function to be executed when a token arrives. | ||
this._prefixes = Object.create(null); | ||
this._prefixes._ = this._blankNodePrefix || '_:b' + blankNodePrefix++ + '_'; | ||
this._prefixes._ = this._blankNodePrefix ? this._blankNodePrefix.substr(2) | ||
: 'b' + blankNodePrefix++ + '_'; | ||
this._prefixCallback = prefixCallback || noop; | ||
@@ -940,6 +955,6 @@ this._inversePredicate = false; | ||
// Parse synchronously if no triple callback is given | ||
if (!tripleCallback) { | ||
var triples = [], error; | ||
this._callback = function (e, t) { e ? (error = e) : t && triples.push(t); }; | ||
// Parse synchronously if no quad callback is given | ||
if (!quadCallback) { | ||
var quads = [], error; | ||
this._callback = function (e, t) { e ? (error = e) : t && quads.push(t); }; | ||
this._lexer.tokenize(input).every(function (token) { | ||
@@ -949,7 +964,7 @@ return self._readCallback = self._readCallback(token); | ||
if (error) throw error; | ||
return triples; | ||
return quads; | ||
} | ||
// Parse asynchronously otherwise, executing the read callback when a token arrives | ||
this._callback = tripleCallback; | ||
this._callback = quadCallback; | ||
this._lexer.tokenize(input, function (error, token) { | ||
@@ -967,3 +982,29 @@ if (error !== null) | ||
// Initializes the parser with the given data factory | ||
function initDataFactory(parser, factory) { | ||
// Set factory methods | ||
var namedNode = factory.namedNode; | ||
parser._namedNode = namedNode; | ||
parser._blankNode = factory.blankNode; | ||
parser._literal = factory.literal; | ||
parser._variable = factory.variable; | ||
parser._quad = factory.quad; | ||
parser.DEFAULTGRAPH = factory.defaultGraph(); | ||
// Set common named nodes | ||
parser.RDF_FIRST = namedNode(namespaces.rdf.first); | ||
parser.RDF_REST = namedNode(namespaces.rdf.rest); | ||
parser.RDF_NIL = namedNode(namespaces.rdf.nil); | ||
parser.N3_FORALL = namedNode(namespaces.r.forAll); | ||
parser.N3_FORSOME = namedNode(namespaces.r.forSome); | ||
parser.ABBREVIATIONS = { | ||
'a': namedNode(namespaces.rdf.type), | ||
'=': namedNode(namespaces.owl.sameAs), | ||
'>': namedNode(namespaces.log.implies), | ||
}; | ||
parser.QUANTIFIERS_GRAPH = namedNode('urn:n3:quantifiers'); | ||
} | ||
initDataFactory(N3Parser.prototype, DataFactory); | ||
// ## Exports | ||
module.exports = N3Parser; |
@@ -1,11 +0,13 @@ | ||
// **N3Store** objects store N3 triples by graph in memory. | ||
// **N3Store** objects store N3 quads by graph in memory. | ||
var expandPrefixedName = require('./N3Util').expandPrefixedName; | ||
var DataFactory = require('./N3DataFactory'); | ||
var toId = DataFactory.internal.toId, | ||
fromId = DataFactory.internal.fromId; | ||
// ## Constructor | ||
function N3Store(triples, options) { | ||
function N3Store(quads, options) { | ||
if (!(this instanceof N3Store)) | ||
return new N3Store(triples, options); | ||
return new N3Store(quads, options); | ||
// The number of triples is initially zero | ||
// The number of quads is initially zero | ||
this._size = 0; | ||
@@ -23,13 +25,11 @@ // `_graphs` contains subject, predicate, and object indexes per graph | ||
// Shift parameters if `triples` is not given | ||
if (!options && triples && !triples[0]) | ||
options = triples, triples = null; | ||
// Shift parameters if `quads` is not given | ||
if (!options && quads && !quads[0]) | ||
options = quads, quads = null; | ||
options = options || {}; | ||
this._factory = options.factory || DataFactory; | ||
// Add triples and prefixes if passed | ||
this._prefixes = Object.create(null); | ||
if (options.prefixes) | ||
this.addPrefixes(options.prefixes); | ||
if (triples) | ||
this.addTriples(triples); | ||
// Add quads if passed | ||
if (quads) | ||
this.addQuads(quads); | ||
} | ||
@@ -40,5 +40,5 @@ | ||
// ### `size` returns the number of triples in the store | ||
// ### `size` returns the number of quads in the store | ||
get size() { | ||
// Return the triple count if if was cached | ||
// Return the quad count if if was cached | ||
var size = this._size; | ||
@@ -48,3 +48,3 @@ if (size !== null) | ||
// Calculate the number of triples by counting to the deepest level | ||
// Calculate the number of quads by counting to the deepest level | ||
size = 0; | ||
@@ -61,3 +61,3 @@ var graphs = this._graphs, subjects, subject; | ||
// ### `_addToIndex` adds a triple to a three-layered index. | ||
// ### `_addToIndex` adds a quad to a three-layered index. | ||
// Returns if the index has changed, if the entry did not already exist. | ||
@@ -68,3 +68,3 @@ _addToIndex: function (index0, key0, key1, key2) { | ||
var index2 = index1[key1] || (index1[key1] = {}); | ||
// Setting the key to _any_ value signals the presence of the triple | ||
// Setting the key to _any_ value signals the presence of the quad | ||
var existed = key2 in index2; | ||
@@ -76,5 +76,5 @@ if (!existed) | ||
// ### `_removeFromIndex` removes a triple from a three-layered index | ||
// ### `_removeFromIndex` removes a quad from a three-layered index | ||
_removeFromIndex: function (index0, key0, key1, key2) { | ||
// Remove the triple from the index | ||
// Remove the quad from the index | ||
var index1 = index0[key0], index2 = index1[key1], key; | ||
@@ -90,11 +90,11 @@ delete index2[key2]; | ||
// ### `_findInIndex` finds a set of triples in a three-layered index. | ||
// ### `_findInIndex` finds a set of quads in a three-layered index. | ||
// The index base is `index0` and the keys at each level are `key0`, `key1`, and `key2`. | ||
// Any of these keys can be undefined, which is interpreted as a wildcard. | ||
// `name0`, `name1`, and `name2` are the names of the keys at each level, | ||
// used when reconstructing the resulting triple | ||
// used when reconstructing the resulting quad | ||
// (for instance: _subject_, _predicate_, and _object_). | ||
// Finally, `graph` will be the graph of the created triples. | ||
// Finally, `graph` will be the graph of the created quads. | ||
// If `callback` is given, each result is passed through it | ||
// and iteration halts when it returns truthy for any triple. | ||
// and iteration halts when it returns truthy for any quad. | ||
// If instead `array` is given, each result is added to the array. | ||
@@ -120,11 +120,13 @@ _findInIndex: function (index0, key0, key1, key2, name0, name1, name2, graph, callback, array) { | ||
var values = key2 ? (key2 in index2 ? [key2] : []) : Object.keys(index2); | ||
// Create triples for all items found in index 2. | ||
for (var l = values.length - 1; l >= 0; l--) { | ||
var result = { subject: '', predicate: '', object: '', graph: graph }; | ||
result[name0] = entity0; | ||
result[name1] = entity1; | ||
result[name2] = entityKeys[values[l]]; | ||
// Create quads for all items found in index 2. | ||
for (var l = 0; l < values.length; l++) { | ||
var parts = { subject: null, predicate: null, object: null }; | ||
parts[name0] = fromId(entity0, this._factory); | ||
parts[name1] = fromId(entity1, this._factory); | ||
parts[name2] = fromId(entityKeys[values[l]], this._factory); | ||
var quad = this._factory.quad( | ||
parts.subject, parts.predicate, parts.object, fromId(graph, this._factory)); | ||
if (array) | ||
array.push(result); | ||
else if (callback(result)) | ||
array.push(quad); | ||
else if (callback(quad)) | ||
return true; | ||
@@ -173,3 +175,3 @@ } | ||
// ### `_countInIndex` counts matching triples in a three-layered index. | ||
// ### `_countInIndex` counts matching quads in a three-layered index. | ||
// The index base is `index0` and the keys at each level are `key0`, `key1`, and `key2`. | ||
@@ -188,5 +190,5 @@ // Any of these keys can be undefined, which is interpreted as a wildcard. | ||
if (index2 = index1[value1]) { | ||
// If a key is specified, count the triple if it exists | ||
// If a key is specified, count the quad if it exists | ||
if (key2) (key2 in index2) && count++; | ||
// Otherwise, count all triples | ||
// Otherwise, count all quads | ||
else count += Object.keys(index2).length; | ||
@@ -217,3 +219,3 @@ } | ||
uniqueIds[id] = true; | ||
callback(entities[id]); | ||
callback(fromId(entities[id])); | ||
} | ||
@@ -225,6 +227,6 @@ }; | ||
// ### `addTriple` adds a new N3 triple to the store. | ||
// Returns if the triple index has changed, if the triple did not already exist. | ||
addTriple: function (subject, predicate, object, graph) { | ||
// Shift arguments if a triple object is given instead of components | ||
// ### `addQuad` adds a new quad to the store. | ||
// Returns if the quad index has changed, if the quad did not already exist. | ||
addQuad: function (subject, predicate, object, graph) { | ||
// Shift arguments if a quad object is given instead of components | ||
if (!predicate) | ||
@@ -234,4 +236,9 @@ graph = subject.graph, object = subject.object, | ||
// Convert terms to internal string representation | ||
subject = toId(subject); | ||
predicate = toId(predicate); | ||
object = toId(object); | ||
graph = toId(graph); | ||
// Find the graph that will contain the triple | ||
graph = graph || ''; | ||
var graphItem = this._graphs[graph]; | ||
@@ -259,3 +266,3 @@ // Create the graph if it doesn't exist yet | ||
// The cached triple count is now invalid | ||
// The cached quad count is now invalid | ||
this._size = null; | ||
@@ -265,29 +272,30 @@ return changed; | ||
// ### `addTriples` adds multiple N3 triples to the store | ||
addTriples: function (triples) { | ||
for (var i = triples.length - 1; i >= 0; i--) | ||
this.addTriple(triples[i]); | ||
// ### `addQuads` adds multiple quads to the store | ||
addQuads: function (quads) { | ||
for (var i = 0; i < quads.length; i++) | ||
this.addQuad(quads[i]); | ||
}, | ||
// ### `addPrefix` adds support for querying with the given prefix | ||
addPrefix: function (prefix, iri) { | ||
this._prefixes[prefix] = iri; | ||
// ### `import` adds a stream of quads to the store | ||
import: function (stream) { | ||
var self = this; | ||
stream.on('data', function (quad) { self.addQuad(quad); }); | ||
return stream; | ||
}, | ||
// ### `addPrefixes` adds support for querying with the given prefixes | ||
addPrefixes: function (prefixes) { | ||
for (var prefix in prefixes) | ||
this.addPrefix(prefix, prefixes[prefix]); | ||
}, | ||
// ### `removeTriple` removes an N3 triple from the store if it exists | ||
removeTriple: function (subject, predicate, object, graph) { | ||
// Shift arguments if a triple object is given instead of components | ||
// ### `removeQuad` removes a quad from the store if it exists | ||
removeQuad: function (subject, predicate, object, graph) { | ||
// Shift arguments if a quad object is given instead of components | ||
if (!predicate) | ||
graph = subject.graph, object = subject.object, | ||
predicate = subject.predicate, subject = subject.subject; | ||
graph = graph || ''; | ||
// Convert terms to internal string representation | ||
subject = toId(subject); | ||
predicate = toId(predicate); | ||
object = toId(object); | ||
graph = toId(graph); | ||
// Find internal identifiers for all components | ||
// and verify the triple exists. | ||
// and verify the quad exists. | ||
var graphItem, ids = this._ids, graphs = this._graphs, subjects, predicates; | ||
@@ -313,23 +321,24 @@ if (!(subject = ids[subject]) || !(predicate = ids[predicate]) || | ||
// ### `removeTriples` removes multiple N3 triples from the store | ||
removeTriples: function (triples) { | ||
for (var i = triples.length - 1; i >= 0; i--) | ||
this.removeTriple(triples[i]); | ||
// ### `removeQuads` removes multiple quads from the store | ||
removeQuads: function (quads) { | ||
for (var i = 0; i < quads.length; i++) | ||
this.removeQuad(quads[i]); | ||
}, | ||
// ### `getTriples` returns an array of triples matching a pattern, expanding prefixes as necessary. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
getTriples: function (subject, predicate, object, graph) { | ||
var prefixes = this._prefixes; | ||
return this.getTriplesByIRI( | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(object, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
// ### `remove` removes a stream of quads from the store | ||
remove: function (stream) { | ||
var self = this; | ||
stream.on('data', function (quad) { self.removeQuad(quad); }); | ||
return stream; | ||
}, | ||
// ### `getTriplesByIRI` returns an array of triples matching a pattern. | ||
// ### `getQuads` returns an array of quads matching a pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
getTriplesByIRI: function (subject, predicate, object, graph) { | ||
getQuads: function (subject, predicate, object, graph) { | ||
// Convert terms to internal string representation | ||
subject = subject && toId(subject); | ||
predicate = predicate && toId(predicate); | ||
object = object && toId(object); | ||
graph = graph && toId(graph); | ||
var quads = [], graphs = this._getGraphs(graph), content, | ||
@@ -375,17 +384,11 @@ ids = this._ids, subjectId, predicateId, objectId; | ||
// ### `countTriples` returns the number of triples matching a pattern, expanding prefixes as necessary. | ||
// ### `countQuads` returns the number of quads matching a pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
countTriples: function (subject, predicate, object, graph) { | ||
var prefixes = this._prefixes; | ||
return this.countTriplesByIRI( | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(object, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
}, | ||
countQuads: function (subject, predicate, object, graph) { | ||
// Convert terms to internal string representation | ||
subject = subject && toId(subject); | ||
predicate = predicate && toId(predicate); | ||
object = object && toId(object); | ||
graph = graph && toId(graph); | ||
// ### `countTriplesByIRI` returns the number of triples matching a pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
countTriplesByIRI: function (subject, predicate, object, graph) { | ||
var count = 0, graphs = this._getGraphs(graph), content, | ||
@@ -425,19 +428,6 @@ ids = this._ids, subjectId, predicateId, objectId; | ||
// ### `forEach` executes the callback on all triples. | ||
// ### `forEach` executes the callback on all quads. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
forEach: function (callback, subject, predicate, object, graph) { | ||
var prefixes = this._prefixes; | ||
this.forEachByIRI( | ||
callback, | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(object, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
}, | ||
// ### `forEachByIRI` executes the callback on all triples. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
forEachByIRI: function (callback, subject, predicate, object, graph) { | ||
this.someByIRI(function (quad) { | ||
this.some(function (quad) { | ||
callback(quad); | ||
@@ -448,22 +438,8 @@ return false; | ||
// ### `every` executes the callback on all triples, | ||
// ### `every` executes the callback on all quads, | ||
// and returns `true` if it returns truthy for all them. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
every: function (callback, subject, predicate, object, graph) { | ||
var prefixes = this._prefixes; | ||
return this.everyByIRI( | ||
callback, | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(object, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
}, | ||
// ### `everyByIRI` executes the callback on all triples, | ||
// and returns `true` if it returns truthy for all them. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
everyByIRI: function (callback, subject, predicate, object, graph) { | ||
var some = false; | ||
var every = !this.someByIRI(function (quad) { | ||
var every = !this.some(function (quad) { | ||
some = true; | ||
@@ -475,20 +451,12 @@ return !callback(quad); | ||
// ### `some` executes the callback on all triples, | ||
// ### `some` executes the callback on all quads, | ||
// and returns `true` if it returns truthy for any of them. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
some: function (callback, subject, predicate, object, graph) { | ||
var prefixes = this._prefixes; | ||
return this.someByIRI( | ||
callback, | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(object, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
}, | ||
// Convert terms to internal string representation | ||
subject = subject && toId(subject); | ||
predicate = predicate && toId(predicate); | ||
object = object && toId(object); | ||
graph = graph && toId(graph); | ||
// ### `someByIRI` executes the callback on all triples, | ||
// and returns `true` if it returns truthy for any of them. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
someByIRI: function (callback, subject, predicate, object, graph) { | ||
var graphs = this._getGraphs(graph), content, | ||
@@ -504,3 +472,3 @@ ids = this._ids, subjectId, predicateId, objectId; | ||
for (var graphId in graphs) { | ||
// Only if the specified graph contains triples, there can be result | ||
// Only if the specified graph contains triples, there can be results | ||
if (content = graphs[graphId]) { | ||
@@ -549,15 +517,4 @@ // Choose the optimal index, based on what fields are present | ||
getSubjects: function (predicate, object, graph) { | ||
var prefixes = this._prefixes; | ||
return this.getSubjectsByIRI( | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(object, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
}, | ||
// ### `getSubjectsByIRI` returns all subjects that match the pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
getSubjectsByIRI: function (predicate, object, graph) { | ||
var results = []; | ||
this.forSubjectsByIRI(function (s) { results.push(s); }, predicate, object, graph); | ||
this.forSubjects(function (s) { results.push(s); }, predicate, object, graph); | ||
return results; | ||
@@ -569,14 +526,7 @@ }, | ||
forSubjects: function (callback, predicate, object, graph) { | ||
var prefixes = this._prefixes; | ||
this.forSubjectsByIRI( | ||
callback, | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(object, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
}, | ||
// Convert terms to internal string representation | ||
predicate = predicate && toId(predicate); | ||
object = object && toId(object); | ||
graph = graph && toId(graph); | ||
// ### `forSubjectsByIRI` executes the callback on all subjects that match the pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
forSubjectsByIRI: function (callback, predicate, object, graph) { | ||
var ids = this._ids, graphs = this._getGraphs(graph), content, predicateId, objectId; | ||
@@ -615,15 +565,4 @@ callback = this._uniqueEntities(callback); | ||
getPredicates: function (subject, object, graph) { | ||
var prefixes = this._prefixes; | ||
return this.getPredicatesByIRI( | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(object, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
}, | ||
// ### `getPredicatesByIRI` returns all predicates that match the pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
getPredicatesByIRI: function (subject, object, graph) { | ||
var results = []; | ||
this.forPredicatesByIRI(function (p) { results.push(p); }, subject, object, graph); | ||
this.forPredicates(function (p) { results.push(p); }, subject, object, graph); | ||
return results; | ||
@@ -635,14 +574,7 @@ }, | ||
forPredicates: function (callback, subject, object, graph) { | ||
var prefixes = this._prefixes; | ||
this.forPredicatesByIRI( | ||
callback, | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(object, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
}, | ||
// Convert terms to internal string representation | ||
subject = subject && toId(subject); | ||
object = object && toId(object); | ||
graph = graph && toId(graph); | ||
// ### `forPredicatesByIRI` executes the callback on all predicates that match the pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
forPredicatesByIRI: function (callback, subject, object, graph) { | ||
var ids = this._ids, graphs = this._getGraphs(graph), content, subjectId, objectId; | ||
@@ -681,15 +613,4 @@ callback = this._uniqueEntities(callback); | ||
getObjects: function (subject, predicate, graph) { | ||
var prefixes = this._prefixes; | ||
return this.getObjectsByIRI( | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
}, | ||
// ### `getObjectsByIRI` returns all objects that match the pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
getObjectsByIRI: function (subject, predicate, graph) { | ||
var results = []; | ||
this.forObjectsByIRI(function (o) { results.push(o); }, subject, predicate, graph); | ||
this.forObjects(function (o) { results.push(o); }, subject, predicate, graph); | ||
return results; | ||
@@ -701,14 +622,7 @@ }, | ||
forObjects: function (callback, subject, predicate, graph) { | ||
var prefixes = this._prefixes; | ||
this.forObjectsByIRI( | ||
callback, | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(graph, prefixes) | ||
); | ||
}, | ||
// Convert terms to internal string representation | ||
subject = subject && toId(subject); | ||
predicate = predicate && toId(predicate); | ||
graph = graph && toId(graph); | ||
// ### `forObjectsByIRI` executes the callback on all objects that match the pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
forObjectsByIRI: function (callback, subject, predicate, graph) { | ||
var ids = this._ids, graphs = this._getGraphs(graph), content, subjectId, predicateId; | ||
@@ -747,15 +661,4 @@ callback = this._uniqueEntities(callback); | ||
getGraphs: function (subject, predicate, object) { | ||
var prefixes = this._prefixes; | ||
return this.getGraphsByIRI( | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(object, prefixes) | ||
); | ||
}, | ||
// ### `getGraphsByIRI` returns all graphs that match the pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
getGraphsByIRI: function (subject, predicate, object) { | ||
var results = []; | ||
this.forGraphsByIRI(function (g) { results.push(g); }, subject, predicate, object); | ||
this.forGraphs(function (g) { results.push(g); }, subject, predicate, object); | ||
return results; | ||
@@ -767,16 +670,4 @@ }, | ||
forGraphs: function (callback, subject, predicate, object) { | ||
var prefixes = this._prefixes; | ||
this.forGraphsByIRI( | ||
callback, | ||
expandPrefixedName(subject, prefixes), | ||
expandPrefixedName(predicate, prefixes), | ||
expandPrefixedName(object, prefixes) | ||
); | ||
}, | ||
// ### `forGraphsByIRI` executes the callback on all graphs that match the pattern. | ||
// Setting any field to `undefined` or `null` indicates a wildcard. | ||
forGraphsByIRI: function (callback, subject, predicate, object) { | ||
for (var graph in this._graphs) { | ||
this.someByIRI(function (quad) { | ||
this.some(function (quad) { | ||
callback(quad.graph); | ||
@@ -805,3 +696,3 @@ return true; // Halt iteration of some() | ||
this._entities[this._id] = name; | ||
return name; | ||
return this._factory.blankNode(name.substr(2)); | ||
}, | ||
@@ -808,0 +699,0 @@ }; |
@@ -1,2 +0,2 @@ | ||
// **N3StreamParser** parses an N3 stream into a triple stream. | ||
// **N3StreamParser** parses a text stream into a quad stream. | ||
var Transform = require('stream').Transform, | ||
@@ -19,11 +19,11 @@ util = require('util'), | ||
parser.parse({ | ||
on: function (event, cb) { | ||
on: function (event, callback) { | ||
switch (event) { | ||
case 'data': onData = cb; break; | ||
case 'end': onEnd = cb; break; | ||
case 'data': onData = callback; break; | ||
case 'end': onEnd = callback; break; | ||
} | ||
}, | ||
}, | ||
// Handle triples by pushing them down the pipeline | ||
function (error, t) { error && self.emit('error', error) || t && self.push(t); }, | ||
// Handle quads by pushing them down the pipeline | ||
function (error, quad) { error && self.emit('error', error) || quad && self.push(quad); }, | ||
// Emit prefixes through the `prefix` event | ||
@@ -38,3 +38,12 @@ function (prefix, uri) { self.emit('prefix', prefix, uri); }); | ||
// ### Parses a stream of strings | ||
N3StreamParser.prototype.import = function (stream) { | ||
var self = this; | ||
stream.on('data', function (chunk) { self.write(chunk); }); | ||
stream.on('end', function () { self.end(); }); | ||
stream.on('error', function (error) { self.emit('error', error); }); | ||
return this; | ||
}; | ||
// ## Exports | ||
module.exports = N3StreamParser; |
@@ -1,2 +0,2 @@ | ||
// **N3StreamWriter** serializes a triple stream into an N3 stream. | ||
// **N3StreamWriter** serializes a quad stream into a text stream. | ||
var Transform = require('stream').Transform, | ||
@@ -17,4 +17,4 @@ util = require('util'), | ||
var self = this; | ||
var writer = new N3Writer({ | ||
write: function (chunk, encoding, callback) { self.push(chunk); callback && callback(); }, | ||
var writer = this._writer = new N3Writer({ | ||
write: function (quad, encoding, callback) { self.push(quad); callback && callback(); }, | ||
end: function (callback) { self.push(null); callback && callback(); }, | ||
@@ -24,3 +24,3 @@ }, options); | ||
// Implement Transform methods on top of writer | ||
this._transform = function (triple, encoding, done) { writer.addTriple(triple, done); }; | ||
this._transform = function (quad, encoding, done) { writer.addQuad(quad, done); }; | ||
this._flush = function (done) { writer.end(done); }; | ||
@@ -30,3 +30,13 @@ } | ||
// ### Serializes a stream of quads | ||
N3StreamWriter.prototype.import = function (stream) { | ||
var self = this; | ||
stream.on('data', function (quad) { self.write(quad); }); | ||
stream.on('end', function () { self.end(); }); | ||
stream.on('error', function (error) { self.emit('error', error); }); | ||
stream.on('prefix', function (prefix, iri) { self._writer.addPrefix(prefix, iri); }); | ||
return this; | ||
}; | ||
// ## Exports | ||
module.exports = N3StreamWriter; |
// **N3Util** provides N3 utility functions. | ||
var Xsd = 'http://www.w3.org/2001/XMLSchema#'; | ||
var XsdString = Xsd + 'string'; | ||
var XsdInteger = Xsd + 'integer'; | ||
var XsdDouble = Xsd + 'double'; | ||
var XsdBoolean = Xsd + 'boolean'; | ||
var RdfLangString = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString'; | ||
var DataFactory = require('./N3DataFactory'); | ||
var N3Util = { | ||
// Tests whether the given entity (triple object) represents an IRI in the N3 library | ||
isIRI: function (entity) { | ||
if (typeof entity !== 'string') | ||
return false; | ||
else if (entity.length === 0) | ||
return true; | ||
else { | ||
var firstChar = entity[0]; | ||
return firstChar !== '"' && firstChar !== '_'; | ||
} | ||
// Tests whether the given term represents an IRI | ||
isNamedNode: function (term) { | ||
return !!term && term.termType === 'NamedNode'; | ||
}, | ||
// Tests whether the given entity (triple object) represents a literal in the N3 library | ||
isLiteral: function (entity) { | ||
return typeof entity === 'string' && entity[0] === '"'; | ||
// Tests whether the given term represents a blank node | ||
isBlankNode: function (term) { | ||
return !!term && term.termType === 'BlankNode'; | ||
}, | ||
// Tests whether the given entity (triple object) represents a blank node in the N3 library | ||
isBlank: function (entity) { | ||
return typeof entity === 'string' && entity.substr(0, 2) === '_:'; | ||
// Tests whether the given term represents a literal | ||
isLiteral: function (term) { | ||
return !!term && term.termType === 'Literal'; | ||
}, | ||
// Tests whether the given entity represents the default graph | ||
isDefaultGraph: function (entity) { | ||
return !entity; | ||
// Tests whether the given term represents a variable | ||
isVariable: function (term) { | ||
return !!term && term.termType === 'Variable'; | ||
}, | ||
// Tests whether the given triple is in the default graph | ||
inDefaultGraph: function (triple) { | ||
return !triple.graph; | ||
// Tests whether the given term represents the default graph | ||
isDefaultGraph: function (term) { | ||
return !!term && term.termType === 'DefaultGraph'; | ||
}, | ||
// Gets the string value of a literal in the N3 library | ||
getLiteralValue: function (literal) { | ||
var match = /^"([^]*)"/.exec(literal); | ||
if (!match) | ||
throw new Error(literal + ' is not a literal'); | ||
return match[1]; | ||
// Tests whether the given quad is in the default graph | ||
inDefaultGraph: function (quad) { | ||
return N3Util.isDefaultGraph(quad.graph); | ||
}, | ||
// Gets the type of a literal in the N3 library | ||
getLiteralType: function (literal) { | ||
var match = /^"[^]*"(?:\^\^([^"]+)|(@)[^@"]+)?$/.exec(literal); | ||
if (!match) | ||
throw new Error(literal + ' is not a literal'); | ||
return match[1] || (match[2] ? RdfLangString : XsdString); | ||
}, | ||
// Gets the language of a literal in the N3 library | ||
getLiteralLanguage: function (literal) { | ||
var match = /^"[^]*"(?:@([^@"]+)|\^\^[^"]+)?$/.exec(literal); | ||
if (!match) | ||
throw new Error(literal + ' is not a literal'); | ||
return match[1] ? match[1].toLowerCase() : ''; | ||
}, | ||
// Tests whether the given entity (triple object) represents a prefixed name | ||
isPrefixedName: function (entity) { | ||
return typeof entity === 'string' && /^[^:\/"']*:[^:\/"']+$/.test(entity); | ||
}, | ||
// Expands the prefixed name to a full IRI (also when it occurs as a literal's type) | ||
expandPrefixedName: function (prefixedName, prefixes) { | ||
var match = /(?:^|"\^\^)([^:\/#"'\^_]*):[^\/]*$/.exec(prefixedName), prefix, base, index; | ||
if (match) | ||
prefix = match[1], base = prefixes[prefix], index = match.index; | ||
if (base === undefined) | ||
return prefixedName; | ||
// The match index is non-zero when expanding a literal's type | ||
return index === 0 ? base + prefixedName.substr(prefix.length + 1) | ||
: prefixedName.substr(0, index + 3) + | ||
base + prefixedName.substr(index + prefix.length + 4); | ||
}, | ||
// Creates an IRI in N3.js representation | ||
createIRI: function (iri) { | ||
return iri && iri[0] === '"' ? N3Util.getLiteralValue(iri) : iri; | ||
}, | ||
// Creates a literal in N3.js representation | ||
createLiteral: function (value, modifier) { | ||
if (!modifier) { | ||
switch (typeof value) { | ||
case 'boolean': | ||
modifier = XsdBoolean; | ||
break; | ||
case 'number': | ||
if (isFinite(value)) | ||
modifier = value % 1 === 0 ? XsdInteger : XsdDouble; | ||
else { | ||
modifier = XsdDouble; | ||
if (!isNaN(value)) | ||
value = value > 0 ? 'INF' : '-INF'; | ||
} | ||
break; | ||
default: | ||
return '"' + value + '"'; | ||
} | ||
} | ||
return '"' + value + | ||
(/^[a-z]+(-[a-z0-9]+)*$/i.test(modifier) ? '"@' + modifier.toLowerCase() | ||
: '"^^' + modifier); | ||
}, | ||
// Creates a function that prepends the given IRI to a local name | ||
prefix: function (iri) { | ||
return N3Util.prefixes({ '': iri })(''); | ||
prefix: function (iri, factory) { | ||
return N3Util.prefixes({ '': iri }, factory)(''); | ||
}, | ||
// Creates a function that allows registering and expanding prefixes | ||
prefixes: function (defaultPrefixes) { | ||
prefixes: function (defaultPrefixes, factory) { | ||
// Add all of the default prefixes | ||
@@ -127,2 +47,4 @@ var prefixes = Object.create(null); | ||
processPrefix(prefix, defaultPrefixes[prefix]); | ||
// Set the default factory if none was specified | ||
factory = factory || DataFactory; | ||
@@ -133,10 +55,12 @@ // Registers a new prefix (if an IRI was specified) | ||
// Create a new prefix if an IRI is specified or the prefix doesn't exist | ||
if (iri || !(prefix in prefixes)) { | ||
if (typeof iri === 'string') { | ||
// Create a function that expands the prefix | ||
var cache = Object.create(null); | ||
iri = iri || ''; | ||
// Create a function that expands the prefix | ||
prefixes[prefix] = function (localName) { | ||
return cache[localName] || (cache[localName] = iri + localName); | ||
prefixes[prefix] = function (local) { | ||
return cache[local] || (cache[local] = factory.namedNode(iri + local)); | ||
}; | ||
} | ||
else if (!(prefix in prefixes)) { | ||
throw new Error('Unknown prefix: ' + prefix); | ||
} | ||
return prefixes[prefix]; | ||
@@ -143,0 +67,0 @@ } |
// **N3Writer** writes N3 documents. | ||
// Matches a literal as represented in memory by the N3 library | ||
var N3LiteralMatcher = /^"([^]*)"(?:\^\^(.+)|@([a-z]+(?:-[a-z0-9]+)*))?$/i; | ||
var namespaces = require('./IRIs'), | ||
DataFactory = require('./N3DataFactory'); | ||
// rdf:type predicate (for 'a' abbreviation) | ||
var RDF_PREFIX = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', | ||
RDF_TYPE = RDF_PREFIX + 'type'; | ||
var DEFAULTGRAPH = DataFactory.defaultGraph(); | ||
var rdf = namespaces.rdf, | ||
xsd = namespaces.xsd; | ||
// Characters in literals that require escaping | ||
@@ -45,3 +46,3 @@ var escape = /["\\\t\n\r\b\f\u0000-\u0019\ud800-\udbff]/, | ||
if (!(/triple|quad/i).test(options.format)) { | ||
this._graph = ''; | ||
this._graph = DEFAULTGRAPH; | ||
this._prefixIRIs = Object.create(null); | ||
@@ -51,3 +52,3 @@ options.prefixes && this.addPrefixes(options.prefixes); | ||
else { | ||
this._writeTriple = this._writeTripleLine; | ||
this._writeQuad = this._writeQuadLine; | ||
} | ||
@@ -59,2 +60,7 @@ } | ||
// ### Whether the current graph is the default graph | ||
get _inDefaultGraph() { | ||
return DEFAULTGRAPH.equals(this._graph); | ||
}, | ||
// ### `_write` writes the argument to the output stream | ||
@@ -65,18 +71,17 @@ _write: function (string, callback) { | ||
// ### `_writeTriple` writes the triple to the output stream | ||
_writeTriple: function (subject, predicate, object, graph, done) { | ||
// ### `_writeQuad` writes the quad to the output stream | ||
_writeQuad: function (subject, predicate, object, graph, done) { | ||
try { | ||
// Write the graph's label if it has changed | ||
if (this._graph !== graph) { | ||
if (!graph.equals(this._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._write((this._subject === null ? '' : (this._inDefaultGraph ? '.\n' : '\n}\n')) + | ||
(DEFAULTGRAPH.equals(graph) ? '' : this._encodeIriOrBlank(graph) + ' {\n')); | ||
this._graph = graph; | ||
this._subject = null; | ||
// Don't treat identical blank nodes as repeating graphs | ||
this._graph = graph[0] !== '[' ? graph : ']'; | ||
} | ||
// Don't repeat the subject if it's the same | ||
if (this._subject === subject) { | ||
if (subject.equals(this._subject)) { | ||
// Don't repeat the predicate if it's the same | ||
if (this._predicate === predicate) | ||
if (predicate.equals(this._predicate)) | ||
this._write(', ' + this._encodeObject(object), done); | ||
@@ -89,6 +94,6 @@ // Same subject, different predicate | ||
} | ||
// Different subject; write the whole triple | ||
// Different subject; write the whole quad | ||
else | ||
this._write((this._subject === null ? '' : '.\n') + | ||
this._encodeSubject(this._subject = subject) + ' ' + | ||
this._encodeIriOrBlank(this._subject = subject) + ' ' + | ||
this._encodePredicate(this._predicate = predicate) + ' ' + | ||
@@ -100,50 +105,50 @@ this._encodeObject(object), done); | ||
// ### `_writeTripleLine` writes the triple or quad to the output stream as a single line | ||
_writeTripleLine: function (subject, predicate, object, graph, done) { | ||
// Write the triple without prefixes | ||
// ### `_writeQuadLine` writes the quad to the output stream as a single line | ||
_writeQuadLine: function (subject, predicate, object, graph, done) { | ||
// Write the quad without prefixes | ||
delete this._prefixMatch; | ||
try { this._write(this.tripleToString(subject, predicate, object, graph), done); } | ||
catch (error) { done && done(error); } | ||
this._write(this.quadToString(subject, predicate, object, graph), done); | ||
}, | ||
// ### `tripleToString` serializes a triple or quad as a string | ||
tripleToString: function (subject, predicate, object, graph) { | ||
return this._encodeIriOrBlankNode(subject) + ' ' + | ||
this._encodeIriOrBlankNode(predicate) + ' ' + | ||
// ### `quadToString` serializes a quad as a string | ||
quadToString: function (subject, predicate, object, graph) { | ||
return this._encodeIriOrBlank(subject) + ' ' + | ||
this._encodeIriOrBlank(predicate) + ' ' + | ||
this._encodeObject(object) + | ||
(graph ? ' ' + this._encodeIriOrBlankNode(graph) + '.\n' : '.\n'); | ||
(graph && graph.value ? ' ' + this._encodeIriOrBlank(graph) + '.\n' : '.\n'); | ||
}, | ||
// ### `triplesToString` serializes an array of triples or quads as a string | ||
triplesToString: function (triples) { | ||
return triples.map(function (t) { | ||
return this.tripleToString(t.subject, t.predicate, t.object, t.graph); | ||
// ### `quadsToString` serializes an array of quads as a string | ||
quadsToString: function (quads) { | ||
return quads.map(function (t) { | ||
return this.quadToString(t.subject, t.predicate, t.object, t.graph); | ||
}, this).join(''); | ||
}, | ||
// ### `_encodeIriOrBlankNode` represents an IRI or blank node | ||
_encodeIriOrBlankNode: function (entity) { | ||
// ### `_encodeIriOrBlank` represents an IRI or blank node | ||
_encodeIriOrBlank: function (entity) { | ||
// A blank node or list is represented as-is | ||
var firstChar = entity[0]; | ||
if (firstChar === '[' || firstChar === '(' || firstChar === '_' && entity[1] === ':') | ||
return entity; | ||
if (entity.termType !== 'NamedNode') | ||
return 'id' in entity ? entity.id : '_:' + entity.value; | ||
// Escape special characters | ||
if (escape.test(entity)) | ||
entity = entity.replace(escapeAll, characterReplacer); | ||
var iri = entity.value; | ||
if (escape.test(iri)) | ||
iri = iri.replace(escapeAll, characterReplacer); | ||
// Try to represent the IRI as prefixed name | ||
var prefixMatch = this._prefixRegex.exec(entity); | ||
return !prefixMatch ? '<' + entity + '>' : | ||
(!prefixMatch[1] ? entity : this._prefixIRIs[prefixMatch[1]] + prefixMatch[2]); | ||
var prefixMatch = this._prefixRegex.exec(iri); | ||
return !prefixMatch ? '<' + iri + '>' : | ||
(!prefixMatch[1] ? iri : this._prefixIRIs[prefixMatch[1]] + prefixMatch[2]); | ||
}, | ||
// ### `_encodeLiteral` represents a literal | ||
_encodeLiteral: function (value, type, language) { | ||
_encodeLiteral: function (literal) { | ||
// Escape special characters | ||
var value = literal.value; | ||
if (escape.test(value)) | ||
value = value.replace(escapeAll, characterReplacer); | ||
// Write the literal, possibly with type or language | ||
if (language) | ||
return '"' + value + '"@' + language; | ||
else if (type) | ||
return '"' + value + '"^^' + this._encodeIriOrBlankNode(type); | ||
if (literal.language) | ||
return '"' + value + '"@' + literal.language; | ||
else if (literal.datatype.value !== xsd.string) | ||
return '"' + value + '"^^' + this._encodeIriOrBlank(literal.datatype); | ||
else | ||
@@ -153,17 +158,5 @@ return '"' + value + '"'; | ||
// ### `_encodeSubject` represents a subject | ||
_encodeSubject: function (subject) { | ||
if (subject[0] === '"') | ||
throw new Error('A literal as subject is not allowed: ' + subject); | ||
// Don't treat identical blank nodes as repeating subjects | ||
if (subject[0] === '[') | ||
this._subject = ']'; | ||
return this._encodeIriOrBlankNode(subject); | ||
}, | ||
// ### `_encodePredicate` represents a predicate | ||
_encodePredicate: function (predicate) { | ||
if (predicate[0] === '"') | ||
throw new Error('A literal as predicate is not allowed: ' + predicate); | ||
return predicate === RDF_TYPE ? 'a' : this._encodeIriOrBlankNode(predicate); | ||
return predicate.value === rdf.type ? 'a' : this._encodeIriOrBlank(predicate); | ||
}, | ||
@@ -173,9 +166,3 @@ | ||
_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]); | ||
return object.termType === 'Literal' ? this._encodeLiteral(object) : this._encodeIriOrBlank(object); | ||
}, | ||
@@ -188,20 +175,19 @@ | ||
// ### `addTriple` adds the triple to the output stream | ||
addTriple: function (subject, predicate, object, graph, done) { | ||
// The triple was given as a triple object, so shift parameters | ||
// ### `addQuad` adds the quad to the output stream | ||
addQuad: function (subject, predicate, object, graph, done) { | ||
// The quad was given as an object, so shift parameters | ||
if (object === undefined) | ||
this._writeTriple(subject.subject, subject.predicate, subject.object, | ||
subject.graph || '', predicate); | ||
this._writeQuad(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); | ||
else if (typeof graph === 'function') | ||
this._writeQuad(subject, predicate, object, DEFAULTGRAPH, graph); | ||
// The `graph` parameter was provided | ||
else | ||
this._writeTriple(subject, predicate, object, graph, done); | ||
this._writeQuad(subject, predicate, object, graph || DEFAULTGRAPH, done); | ||
}, | ||
// ### `addTriples` adds the triples to the output stream | ||
addTriples: function (triples) { | ||
for (var i = 0; i < triples.length; i++) | ||
this.addTriple(triples[i]); | ||
// ### `addQuads` adds the quads to the output stream | ||
addQuads: function (quads) { | ||
for (var i = 0; i < quads.length; i++) | ||
this.addQuad(quads[i]); | ||
}, | ||
@@ -223,8 +209,10 @@ | ||
var iri = prefixes[prefix]; | ||
if (typeof iri !== 'string') | ||
iri = iri.value; | ||
if (/[#\/]$/.test(iri) && prefixIRIs[iri] !== (prefix += ':')) { | ||
hasPrefixes = true; | ||
prefixIRIs[iri] = prefix; | ||
// Finish a possible pending triple | ||
// Finish a possible pending quad | ||
if (this._subject !== null) { | ||
this._write(this._graph ? '\n}\n' : '.\n'); | ||
this._write(this._inDefaultGraph ? '.\n' : '\n}\n'); | ||
this._subject = null, this._graph = ''; | ||
@@ -257,4 +245,4 @@ } | ||
children = []; | ||
// Blank node passed as blank("predicate", "object") | ||
else if (typeof predicate === 'string') | ||
// Blank node passed as blank(Term("predicate"), Term("object")) | ||
else if (predicate.termType) | ||
children = [{ predicate: predicate, object: object }]; | ||
@@ -268,9 +256,9 @@ // Blank node passed as blank({ predicate: predicate, object: object }) | ||
case 0: | ||
return '[]'; | ||
return new SerializedTerm('[]'); | ||
// Generate a non-nested one-triple blank node | ||
case 1: | ||
child = children[0]; | ||
if (child.object[0] !== '[') | ||
return '[ ' + this._encodePredicate(child.predicate) + ' ' + | ||
this._encodeObject(child.object) + ' ]'; | ||
if (!(child.object instanceof SerializedTerm)) | ||
return new SerializedTerm('[ ' + this._encodePredicate(child.predicate) + ' ' + | ||
this._encodeObject(child.object) + ' ]'); | ||
// Generate a multi-triple or nested blank node | ||
@@ -283,3 +271,3 @@ default: | ||
// Write only the object is the predicate is the same as the previous | ||
if (child.predicate === predicate) | ||
if (child.predicate.equals(predicate)) | ||
contents += ', ' + this._encodeObject(child.object); | ||
@@ -294,3 +282,3 @@ // Otherwise, write the predicate and the object | ||
} | ||
return contents + '\n]'; | ||
return new SerializedTerm(contents + '\n]'); | ||
} | ||
@@ -304,3 +292,3 @@ }, | ||
contents[i] = this._encodeObject(elements[i]); | ||
return '(' + contents.join(' ') + ')'; | ||
return new SerializedTerm('(' + contents.join(' ') + ')'); | ||
}, | ||
@@ -313,5 +301,5 @@ | ||
end: function (done) { | ||
// Finish a possible pending triple | ||
// Finish a possible pending quad | ||
if (this._subject !== null) { | ||
this._write(this._graph ? '\n}\n' : '.\n'); | ||
this._write(this._inDefaultGraph ? '.\n' : '\n}\n'); | ||
this._subject = null; | ||
@@ -352,3 +340,13 @@ } | ||
// ## Placeholder class to represent already pretty-printed terms | ||
function SerializedTerm(value) { | ||
this.id = value; | ||
} | ||
DataFactory.internal.Term.subclass(SerializedTerm); | ||
// Pretty-printed nodes are not equal to any other node | ||
// (e.g., [] does not equal []) | ||
SerializedTerm.prototype.equals = function () { return false; }; | ||
// ## Exports | ||
module.exports = N3Writer; |
# License | ||
The MIT License (MIT) | ||
Copyright ©2012–2016 Ruben Verborgh | ||
Copyright ©2012–2018 Ruben Verborgh | ||
@@ -5,0 +5,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
@@ -7,2 +7,3 @@ // Replace local require by a lazy loader | ||
var exports = module.exports = { | ||
DataFactory: require('./lib/N3DataFactory'), | ||
Lexer: require('./lib/N3Lexer'), | ||
@@ -9,0 +10,0 @@ Parser: require('./lib/N3Parser'), |
{ | ||
"name": "n3", | ||
"version": "0.11.3", | ||
"version": "1.0.0-alpha", | ||
"description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.", | ||
@@ -24,3 +24,2 @@ "author": "Ruben Verborgh <ruben.verborgh@gmail.com>", | ||
"async": "^2.0.1", | ||
"browserify": "^16.1.0", | ||
"chai": "^4.0.2", | ||
@@ -30,3 +29,2 @@ "chai-things": "^0.2.0", | ||
"coveralls": "^3.0.0", | ||
"cross-spawn": "^6.0.4", | ||
"docco": "^0.7.0", | ||
@@ -37,4 +35,3 @@ "eslint": "^4.1.1", | ||
"pre-commit": "^1.2.2", | ||
"request": "^2.74.0", | ||
"uglify-js": "^3.0.24" | ||
"request": "^2.74.0" | ||
}, | ||
@@ -44,3 +41,2 @@ "scripts": { | ||
"lint": "eslint lib perf test spec", | ||
"browser": "node browser/build-browser-versions", | ||
"coveralls": "nyc --reporter=text-lcov mocha | coveralls", | ||
@@ -53,19 +49,7 @@ "spec": "node spec/turtle-spec && node spec/trig-spec && node spec/ntriples-spec && node spec/nquads-spec", | ||
"type": "git", | ||
"url": "https://github.com/RubenVerborgh/N3.js.git" | ||
"url": "https://github.com/rdfjs/N3.js.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/RubenVerborgh/N3.js/issues" | ||
"url": "https://github.com/rdfjs/N3.js/issues" | ||
}, | ||
"testling": { | ||
"files": "test/*.js", | ||
"harness": "mocha", | ||
"browsers": [ | ||
"ie/9..latest", | ||
"firefox/24..latest", | ||
"chrome/29..latest", | ||
"safari/6..latest", | ||
"iphone/6..latest", | ||
"ipad/6..latest" | ||
] | ||
}, | ||
"pre-commit": [ | ||
@@ -72,0 +56,0 @@ "lint", |
503
README.md
# Lightning fast, asynchronous, streaming RDF for JavaScript | ||
[](https://travis-ci.org/RubenVerborgh/N3.js) | ||
[](https://ci.appveyor.com/project/RubenVerborgh/n3-js/branch/master) | ||
[](https://coveralls.io/github/RubenVerborgh/N3.js) | ||
[](https://travis-ci.org/rdfjs/N3.js) | ||
[](https://coveralls.io/github/rdfjs/N3.js) | ||
[](https://www.npmjs.com/package/n3) | ||
[](https://zenodo.org/badge/latestdoi/3058202) | ||
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. | ||
The N3.js library is an implementation of the [RDF.js low-level specification](http://rdf.js.org/) that lets you handle [RDF](https://www.w3.org/TR/rdf-primer/) in JavaScript easily. | ||
It offers: | ||
- [**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/n-triples/), | ||
[N-Quads](http://www.w3.org/TR/n-quads/), | ||
[Turtle](https://www.w3.org/TR/turtle/), | ||
[TriG](https://www.w3.org/TR/trig/), | ||
[N-Triples](https://www.w3.org/TR/n-triples/), | ||
[N-Quads](https://www.w3.org/TR/n-quads/), | ||
and [Notation3 (N3)](https://www.w3.org/TeamSubmission/n3/) | ||
- [**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/n-triples/), | ||
and [N-Quads](http://www.w3.org/TR/n-quads/) | ||
[Turtle](https://www.w3.org/TR/turtle/), | ||
[TriG](https://www.w3.org/TR/trig/), | ||
[N-Triples](https://www.w3.org/TR/n-triples/), | ||
and [N-Quads](https://www.w3.org/TR/n-quads/) | ||
- [**Storage**](#storing) of triples/quads in memory | ||
@@ -27,3 +26,3 @@ | ||
- **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) | ||
- **fast** – by far the [fastest spec-compatible parser in JavaScript](https://github.com/rdfjs/N3.js/tree/master/perf) | ||
@@ -33,101 +32,57 @@ ## Installation | ||
``` bash | ||
```Bash | ||
$ npm install n3 | ||
``` | ||
``` js | ||
var N3 = require('n3'); | ||
```JavaScript | ||
const N3 = require('n3'); | ||
``` | ||
N3.js seamlessly works in browsers. Generate a browser version as follows: | ||
N3.js seamlessly works in browsers via [webpack](https://webpack.js.org/) or [browserify](http://browserify.org/). | ||
``` bash | ||
$ cd N3.js | ||
$ npm install | ||
$ npm run browser | ||
``` | ||
## Creating triples/quads | ||
N3.js follows the [RDF.js low-level specification](http://rdf.js.org/). | ||
``` html | ||
<script src="n3-browser.min.js"></script> | ||
``` | ||
`N3.DataFactory` will give you the [factory](http://rdf.js.org/#datafactory-interface) functions to create triples and quads: | ||
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 ease of use, | ||
triples are simple objects with string properties. | ||
**URLs, URIs and IRIs are simple strings.** For example, parsing this RDF document: | ||
``` Turtle | ||
@prefix c: <http://example.org/cartoons#>. | ||
c:Tom a c:Cat. | ||
```JavaScript | ||
const { DataFactory } = N3; | ||
const { namedNode, literal, defaultGraph, quad } = DataFactory; | ||
const myQuad = quad( | ||
namedNode('https://ruben.verborgh.org/profile/#me'), | ||
namedNode('http://xmlns.com/foaf/0.1/givenName'), | ||
literal('Ruben', 'en'), | ||
defaultGraph(), | ||
); | ||
console.log(myQuad.subject.value); // https://ruben.verborgh.org/profile/#me | ||
console.log(myQuad.object.value); // Ruben | ||
console.log(myQuad.object.datatype.value); // http://www.w3.org/1999/02/22-rdf-syntax-ns#langString | ||
console.log(myQuad.object.language); // en | ||
``` | ||
results in this JavaScript object: | ||
``` js | ||
{ | ||
subject: 'http://example.org/cartoons#Tom', | ||
predicate: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', | ||
object: 'http://example.org/cartoons#Cat' | ||
} | ||
``` | ||
**Literals are represented as double quoted strings.** For example, parsing this RDF document: | ||
``` Turtle | ||
c:Tom c:name "Tom". | ||
``` | ||
results in this JavaScript object: | ||
``` js | ||
{ | ||
subject: 'http://example.org/cartoons#Tom', | ||
predicate: 'http://example.org/cartoons#name', | ||
object: '"Tom"' | ||
} | ||
``` | ||
In the rest of this document, we will treat “triples” and “quads” equally: | ||
we assume that a quad is simply a triple in a named or default graph. | ||
This allows you to create and compare literals fast and easily: | ||
``` js | ||
triple.object === 'http://example.org/cartoons#Cat' | ||
triple.object === '"Tom"' | ||
``` | ||
For literals with a language or type, add a marker (`@` or `^^`) and the corresponding value as-is: | ||
``` js | ||
'"Tom"@en-gb' // lowercase language | ||
'"1"^^http://www.w3.org/2001/XMLSchema#integer' // no angular brackets <> | ||
``` | ||
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 an RDF document to triples | ||
### From an RDF document to quads | ||
`N3.Parser` transforms Turtle, TriG, N-Triples or N-Quads document into triples through a callback: | ||
``` js | ||
var parser = N3.Parser(); | ||
parser.parse('@prefix c: <http://example.org/cartoons#>.\n' + | ||
'c:Tom a c:Cat.\n' + | ||
'c:Jerry a c:Mouse;\n' + | ||
' c:smarterThan c:Tom.', | ||
function (error, triple, prefixes) { | ||
if (triple) | ||
console.log(triple.subject, triple.predicate, triple.object, '.'); | ||
else | ||
console.log("# That's all, folks!", prefixes) | ||
}); | ||
`N3.Parser` transforms Turtle, TriG, N-Triples, or N-Quads document into quads through a callback: | ||
```JavaScript | ||
const parser = new N3.Parser(); | ||
parser.parse( | ||
`PREFIX c: <http://example.org/cartoons#> | ||
c:Tom a c:Cat. | ||
c:Jerry a c:Mouse; | ||
c:smarterThan c:Tom.`, | ||
(error, quad, prefixes) => { | ||
if (quad) | ||
console.log(quad); | ||
else | ||
console.log("# That's all, folks!", prefixes); | ||
}); | ||
``` | ||
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` | ||
The callback's first argument is an optional error value, the second is a quad. | ||
If there are no more quads, | ||
the callback is invoked one last time with `null` for `quad` | ||
and a hash of prefixes as third argument. | ||
@@ -139,9 +94,9 @@ <br> | ||
By default, `N3.Parser` parses a permissive superset of Turtle, TriG, N-Triples and N-Quads. | ||
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: | ||
``` js | ||
var parser1 = N3.Parser({ format: 'N-Triples' }); | ||
var parser2 = N3.Parser({ format: 'application/trig' }); | ||
```JavaScript | ||
const parser1 = N3.Parser({ format: 'N-Triples' }); | ||
const parser2 = N3.Parser({ format: 'application/trig' }); | ||
``` | ||
@@ -151,29 +106,26 @@ | ||
``` js | ||
var parser3 = N3.Parser({ format: 'N3' }); | ||
var parser4 = N3.Parser({ format: 'Notation3' }); | ||
var parser5 = N3.Parser({ format: 'text/n3' }); | ||
```JavaScript | ||
const parser3 = N3.Parser({ format: 'N3' }); | ||
const parser4 = N3.Parser({ format: 'Notation3' }); | ||
const parser5 = N3.Parser({ format: 'text/n3' }); | ||
``` | ||
### From an RDF stream to triples | ||
### From an RDF stream to quads | ||
`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 libraries. | ||
returning quads as soon as they're ready. | ||
``` js | ||
var parser = N3.Parser(), | ||
rdfStream = fs.createReadStream('cartoons.ttl'); | ||
```JavaScript | ||
const parser = N3.Parser(), | ||
rdfStream = fs.createReadStream('cartoons.ttl'); | ||
parser.parse(rdfStream, console.log); | ||
``` | ||
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. | ||
`N3.StreamParser` is a [Node.js stream](http://nodejs.org/api/stream.html) and [RDF.js Sink](http://rdf.js.org/#sink-interface) implementation. | ||
This solution is ideal if your consumer is slower, | ||
since source data is only read when the consumer is ready. | ||
``` js | ||
var streamParser = N3.StreamParser(), | ||
rdfStream = fs.createReadStream('cartoons.ttl'); | ||
```JavaScript | ||
const streamParser = N3.StreamParser(), | ||
rdfStream = fs.createReadStream('cartoons.ttl'); | ||
rdfStream.pipe(streamParser); | ||
@@ -183,5 +135,5 @@ streamParser.pipe(new SlowConsumer()); | ||
function SlowConsumer() { | ||
var writer = new require('stream').Writable({ objectMode: true }); | ||
writer._write = function (triple, encoding, done) { | ||
console.log(triple); | ||
const writer = new require('stream').Writable({ objectMode: true }); | ||
writer._write = (quad, encoding, done) => { | ||
console.log(quad); | ||
setTimeout(done, 1000); | ||
@@ -193,58 +145,62 @@ }; | ||
A dedicated `prefix` event signals every prefix with `prefix` and `iri` arguments. | ||
A dedicated `prefix` event signals every prefix with `prefix` and `term` arguments. | ||
## Writing | ||
### From triples to a string | ||
### From quads to a string | ||
`N3.Writer` serializes triples as an RDF document. | ||
Write triples through `addTriple`. | ||
`N3.Writer` serializes quads as an RDF document. | ||
Write quads through `addQuad`. | ||
``` js | ||
var writer = N3.Writer({ prefixes: { c: 'http://example.org/cartoons#' } }); | ||
writer.addTriple('http://example.org/cartoons#Tom', | ||
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', | ||
'http://example.org/cartoons#Cat'); | ||
writer.addTriple({ | ||
subject: 'http://example.org/cartoons#Tom', | ||
predicate: 'http://example.org/cartoons#name', | ||
object: '"Tom"' | ||
}); | ||
writer.end(function (error, result) { console.log(result); }); | ||
```JavaScript | ||
const writer = N3.Writer({ prefixes: { c: 'http://example.org/cartoons#' } }); | ||
writer.addQuad( | ||
namedNode('http://example.org/cartoons#Tom'), | ||
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), | ||
namedNode('http://example.org/cartoons#Cat') | ||
); | ||
writer.addQuad(quad( | ||
namedNode('http://example.org/cartoons#Tom'), | ||
namedNode('http://example.org/cartoons#name'), | ||
literal('Tom') | ||
)); | ||
writer.end((error, result) => console.log(result)); | ||
``` | ||
By default, `N3.Writer` writes Turtle (or TriG for triples with a `graph` property). | ||
By default, `N3.Writer` writes Turtle (or TriG if some quads are in a named graph). | ||
<br> | ||
To write N-Triples (or N-Quads) instead, pass a `format` argument upon creation: | ||
``` js | ||
var writer1 = N3.Writer({ format: 'N-Triples' }); | ||
var writer2 = N3.Writer({ format: 'application/trig' }); | ||
```JavaScript | ||
const writer1 = N3.Writer({ format: 'N-Triples' }); | ||
const writer2 = N3.Writer({ format: 'application/trig' }); | ||
``` | ||
### From triples to an RDF stream | ||
### From quads to an RDF stream | ||
`N3.Writer` can also write triples to a Node.js stream. | ||
`N3.Writer` can also write quads to a Node.js stream. | ||
``` js | ||
var writer = N3.Writer(process.stdout, { end: false, prefixes: { c: 'http://example.org/cartoons#' } }); | ||
writer.addTriple('http://example.org/cartoons#Tom', | ||
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', | ||
'http://example.org/cartoons#Cat'); | ||
writer.addTriple({ | ||
subject: 'http://example.org/cartoons#Tom', | ||
predicate: 'http://example.org/cartoons#name', | ||
object: '"Tom"' | ||
}); | ||
```JavaScript | ||
const writer = N3.Writer(process.stdout, { end: false, prefixes: { c: 'http://example.org/cartoons#' } }); | ||
writer.addQuad( | ||
namedNode('http://example.org/cartoons#Tom'), | ||
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), | ||
namedNode('http://example.org/cartoons#Cat') | ||
); | ||
writer.addQuad(quad( | ||
namedNode('http://example.org/cartoons#Tom'), | ||
namedNode('http://example.org/cartoons#name'), | ||
literal('Tom') | ||
)); | ||
writer.end(); | ||
``` | ||
### From a triple stream to an RDF stream | ||
### From a quad stream to an RDF stream | ||
`N3.StreamWriter` is a writer implementation as a Node.js stream. | ||
`N3.StreamWriter` is a [Node.js stream](http://nodejs.org/api/stream.html) and [RDF.js Sink](http://rdf.js.org/#sink-interface) implementation. | ||
``` js | ||
var streamParser = new N3.StreamParser(), | ||
inputStream = fs.createReadStream('cartoons.ttl'), | ||
streamWriter = new N3.StreamWriter({ prefixes: { c: 'http://example.org/cartoons#' } }); | ||
```JavaScript | ||
const streamParser = new N3.StreamParser(), | ||
inputStream = fs.createReadStream('cartoons.ttl'), | ||
streamWriter = new N3.StreamWriter({ prefixes: { c: 'http://example.org/cartoons#' } }); | ||
inputStream.pipe(streamParser); | ||
@@ -262,31 +218,34 @@ streamParser.pipe(streamWriter); | ||
The `blank` and `list` functions allow you to create them manually instead: | ||
```js | ||
var writer = N3.Writer({ prefixes: { c: 'http://example.org/cartoons#', | ||
foaf: 'http://xmlns.com/foaf/0.1/' } }); | ||
writer.addTriple(writer.blank('http://xmlns.com/foaf/0.1/givenName', '"Tom"@en'), | ||
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', | ||
'http://example.org/cartoons#Cat'); | ||
writer.addTriple('http://example.org/cartoons#Jerry', | ||
'http://xmlns.com/foaf/0.1/knows', | ||
writer.blank([{ | ||
predicate: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', | ||
object: 'http://example.org/cartoons#Cat' | ||
},{ | ||
predicate: 'http://xmlns.com/foaf/0.1/givenName', | ||
object: '"Tom"@en', | ||
}])); | ||
writer.addTriple('http://example.org/cartoons#Mammy', | ||
'http://example.org/cartoons#hasPets', | ||
writer.list([ | ||
'http://example.org/cartoons#Tom', | ||
'http://example.org/cartoons#Jerry' | ||
])); | ||
writer.end(function (error, result) { console.log(result); }); | ||
```JavaScript | ||
const writer = N3.Writer({ prefixes: { c: 'http://example.org/cartoons#', | ||
foaf: 'http://xmlns.com/foaf/0.1/' } }); | ||
writer.addQuad( | ||
writer.blank( | ||
namedNode('http://xmlns.com/foaf/0.1/givenName'), | ||
literal('Tom', 'en')), | ||
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), | ||
namedNode('http://example.org/cartoons#Cat') | ||
); | ||
writer.addQuad(quad( | ||
namedNode('http://example.org/cartoons#Jerry'), | ||
namedNode('http://xmlns.com/foaf/0.1/knows'), | ||
writer.blank([{ | ||
predicate: namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), | ||
object: namedNode('http://example.org/cartoons#Cat'), | ||
},{ | ||
predicate: namedNode('http://xmlns.com/foaf/0.1/givenName'), | ||
object: literal('Tom', 'en'), | ||
}]) | ||
)); | ||
writer.addQuad( | ||
namedNode('http://example.org/cartoons#Mammy'), | ||
namedNode('http://example.org/cartoons#hasPets'), | ||
writer.list([ | ||
namedNode('http://example.org/cartoons#Tom'), | ||
namedNode('http://example.org/cartoons#Jerry'), | ||
]) | ||
); | ||
writer.end((error, result) => console.log(result)); | ||
``` | ||
Be careful to use the output of `blank` and `list` | ||
**only once** and **only as argument to `addTriple`** of the same writer, | ||
as return values of these functions are unspecified. | ||
## Storing | ||
@@ -300,111 +259,56 @@ | ||
``` 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'); | ||
```JavaScript | ||
const store = N3.Store(); | ||
store.addQuad( | ||
namedNode('http://ex.org/Pluto'), | ||
namedNode('http://ex.org/type'), | ||
namedNode('http://ex.org/Dog') | ||
); | ||
store.addQuad( | ||
namedNode('http://ex.org/Mickey'), | ||
namedNode('http://ex.org/type'), | ||
namedNode('http://ex.org/Mouse') | ||
); | ||
var mickey = store.getTriples('http://ex.org/Mickey', null, null)[0]; | ||
console.log(mickey.subject, mickey.predicate, mickey.object, '.'); | ||
const mickey = store.getQuads(namedNode('http://ex.org/Mickey'), null, null)[0]; | ||
console.log(mickey); | ||
``` | ||
### Addition and deletion of triples/quads | ||
### Addition and deletion of quads | ||
The store provides the following manipulation methods | ||
([documentation](http://rubenverborgh.github.io/N3.js/docs/N3Store.html)): | ||
- `addTriple` to insert one triple/quad | ||
- `addTriples` to insert an array of triples/quads | ||
- `addPrefix` to register a prefix (facilitating lookup) | ||
- `addPrefixes` to register an array of prefixes | ||
- `removeTriple` to remove one triple/quad | ||
- `removeTriples` to remove an array of triples/quads | ||
([documentation](http://rdfjs.github.io/N3.js/docs/N3Store.html)): | ||
- `addQuad` to insert one quad | ||
- `addQuads` to insert an array of quads | ||
- `removeQuad` to remove one quad | ||
- `removeQuads` to remove an array of quads | ||
- `createBlankNode` returns an unused blank node identifier | ||
### Searching triples/quads or entities | ||
### Searching quads or entities | ||
The store provides the following search methods | ||
([documentation](http://rubenverborgh.github.io/N3.js/docs/N3Store.html)): | ||
- `getTriples` returns an array of triples/quads matching the given pattern | ||
- `countTriples` counts the number of triples/quads matching the given pattern | ||
- `forEach` executes a callback on all matching triples/quads | ||
- `every` returns whether a callback on matching triples/quads always returns true | ||
- `some` returns whether a callback on matching triples/quads returns true at least once | ||
- `getSubjects` returns an array of unique subjects occurring in matching triples | ||
- `forSubjects` executes a callback on unique subjects occurring in matching triples | ||
- `getPredicates` returns an array of unique predicates occurring in matching triple | ||
- `forPredicates` executes a callback on unique predicates occurring in matching triples | ||
- `getObjects` returns an array of unique objects occurring in matching triple | ||
- `forObjects` executes a callback on unique objects occurring in matching triples | ||
- `getGraphs` returns an array of unique graphs occurring in matching triple | ||
- `forGraphs` executes a callback on unique graphs occurring in matching triples | ||
([documentation](http://rdfjs.github.io/N3.js/docs/N3Store.html)): | ||
- `getQuads` returns an array of quads matching the given pattern | ||
- `countQuads` counts the number of quads matching the given pattern | ||
- `forEach` executes a callback on all matching quads | ||
- `every` returns whether a callback on matching quads always returns true | ||
- `some` returns whether a callback on matching quads returns true at least once | ||
- `getSubjects` returns an array of unique subjects occurring in matching quads | ||
- `forSubjects` executes a callback on unique subjects occurring in matching quads | ||
- `getPredicates` returns an array of unique predicates occurring in matching quad | ||
- `forPredicates` executes a callback on unique predicates occurring in matching quads | ||
- `getObjects` returns an array of unique objects occurring in matching quad | ||
- `forObjects` executes a callback on unique objects occurring in matching quads | ||
- `getGraphs` returns an array of unique graphs occurring in matching quad | ||
- `forGraphs` executes a callback on unique graphs occurring in matching quads | ||
All of the above methods also have a variant with the `byIRI` suffix | ||
(e.g., `getTriplesByIRI`), | ||
which skips prefix expansion and is thus faster. | ||
## Utility | ||
`N3.Util` offers helpers for IRI and literal representations. | ||
<br> | ||
As IRIs are most common, they are represented as simple strings: | ||
``` js | ||
var N3Util = N3.Util; | ||
N3Util.isIRI('http://example.org/cartoons#Mickey'); // true | ||
``` | ||
**Literals** are represented as double quoted strings: | ||
``` js | ||
N3Util.isLiteral('"Mickey Mouse"'); // true | ||
N3Util.getLiteralValue('"Mickey Mouse"'); // 'Mickey Mouse' | ||
N3Util.isLiteral('"Mickey Mouse"@en'); // true | ||
N3Util.getLiteralLanguage('"Mickey Mouse"@en'); // 'en' | ||
N3Util.isLiteral('"3"^^http://www.w3.org/2001/XMLSchema#integer'); // true | ||
N3Util.getLiteralType('"3"^^http://www.w3.org/2001/XMLSchema#integer'); // 'http://www.w3.org/2001/XMLSchema#integer' | ||
N3Util.isLiteral('"http://example.org/"'); // true | ||
N3Util.getLiteralValue('"http://example.org/"'); // 'http://example.org/' | ||
``` | ||
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/TriG syntax: | ||
``` js | ||
N3Util.isLiteral('"This word is "quoted"!"'); // true | ||
N3Util.isLiteral('"3"^^http://www.w3.org/2001/XMLSchema#integer'); // true | ||
``` | ||
The above string represents the string _This word is "quoted"!_, | ||
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 | ||
new N3.Parser().parse('<a> <b> "This word is \\"quoted\\"!".', console.log); | ||
// { subject: 'a', predicate: 'b', object: '"This word is "quoted"!"' } | ||
``` | ||
Literals can be created with `createLiteral`: | ||
``` js | ||
N3Util.createLiteral('My text', 'en-gb'); | ||
N3Util.createLiteral('123', 'http://www.w3.org/2001/XMLSchema#integer'); | ||
N3Util.createLiteral(123); | ||
N3Util.createLiteral(false); | ||
``` | ||
**Blank nodes** start with `_:`, and can be tested for as follows: | ||
``` js | ||
N3Util.isBlank('_:b1'); // true | ||
N3Util.isIRI('_:b1'); // false | ||
N3Util.isLiteral('_:b1'); // false | ||
``` | ||
**Prefixed names** can be tested and expanded: | ||
``` js | ||
var prefixes = { rdfs: 'http://www.w3.org/2000/01/rdf-schema#' }; | ||
N3Util.isPrefixedName('rdfs:label'); // true; | ||
N3Util.expandPrefixedName('rdfs:label', prefixes); // http://www.w3.org/2000/01/rdf-schema#label | ||
``` | ||
## Compatibility | ||
### Specifications | ||
### Format 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) | ||
- [RDF 1.1 Turtle](https://www.w3.org/TR/turtle/) | ||
– [EARL report](https://raw.githubusercontent.com/rdfjs/N3.js/earl/n3js-earl-report-turtle.ttl) | ||
- [RDF 1.1 TriG](https://www.w3.org/TR/trig/) | ||
– [EARL report](https://raw.githubusercontent.com/rdfjs/N3.js/earl/n3js-earl-report-trig.ttl) | ||
- [RDF 1.1 N-Triples](https://www.w3.org/TR/n-triples/) | ||
– [EARL report](https://raw.githubusercontent.com/rdfjs/N3.js/earl/n3js-earl-report-ntriples.ttl) | ||
- [RDF 1.1 N-Quads](https://www.w3.org/TR/n-quads/) | ||
– [EARL report](https://raw.githubusercontent.com/rdfjs/N3.js/earl/n3js-earl-report-nquads.ttl) | ||
@@ -416,7 +320,34 @@ In addition, the N3.js parser also supports [Notation3 (N3)](https://www.w3.org/TeamSubmission/n3/) (no official specification yet). | ||
## License, status and contributions | ||
The N3.js library is copyrighted by [Ruben Verborgh](http://ruben.verborgh.org/) | ||
and released under the [MIT License](https://github.com/RubenVerborgh/N3.js/blob/master/LICENSE.md). | ||
### Interface specifications | ||
The N3.js submodules are compatible with the following [RDF.js](http://rdf.js.org) interfaces: | ||
- `N3.DataFactory` implements | ||
[`DataFactory`](http://rdf.js.org/#datafactory-interface) | ||
- the terms it creates implement [`Term`](http://rdf.js.org/#term-interface) | ||
and one of | ||
[`NamedNode`](http://rdf.js.org/#namednode-interface), | ||
[`BlankNode`](http://rdf.js.org/#blanknode-interface), | ||
[`Literal`](http://rdf.js.org/#litereal-interface), | ||
[`Variable`](http://rdf.js.org/#variable-interface), | ||
[`DefaultGraph`](http://rdf.js.org/#defaultgraph-interface) | ||
- the triples/quads it creates implement | ||
[`Triple`](http://rdf.js.org/#triple-interface) | ||
and | ||
[`Quad`](http://rdf.js.org/#quad-interface) | ||
- `N3.StreamParser` implements | ||
[`Stream`](http://rdf.js.org/#stream-interface) | ||
and | ||
[`Sink`](http://rdf.js.org/#sink-interface) | ||
- `N3.StreamWriter` implements | ||
[`Stream`](http://rdf.js.org/#stream-interface) | ||
and | ||
[`Sink`](http://rdf.js.org/#sink-interface) | ||
- `N3.Store` implements | ||
[`Sink`](http://rdf.js.org/#stream-interface) | ||
## License and contributions | ||
The N3.js library is copyrighted by [Ruben Verborgh](https://ruben.verborgh.org/) | ||
and released under the [MIT License](https://github.com/rdfjs/N3.js/blob/master/LICENSE.md). | ||
Contributions are welcome, and bug reports or pull requests are always helpful. | ||
If you plan to implement a larger feature, it's best to contact me first. |
124913
11
13
2672
345