Comparing version 0.4.1 to 0.4.2
@@ -144,3 +144,4 @@ // **N3Parser** parses N3 documents. | ||
_readPredicate: function (token) { | ||
switch (token.type) { | ||
var type = token.type; | ||
switch (type) { | ||
case 'IRI': | ||
@@ -169,5 +170,5 @@ case 'abbreviation': | ||
if (this._predicate === null) | ||
return this._error('Unexpected ' + token.type, token); | ||
return this._error('Unexpected ' + type, token); | ||
this._subject = null; | ||
return this._readBlankNodeTail(token); | ||
return type === ']' ? this._readBlankNodeTail(token) : this._readPunctuation(token); | ||
case ';': | ||
@@ -245,3 +246,3 @@ // Extra semicolons can be safely ignored | ||
if (token.type !== ']') | ||
return this._readPunctuation(token); | ||
return this._readBlankNodePunctuation(token); | ||
@@ -253,3 +254,3 @@ // Store blank node triple. | ||
object: this._object, | ||
graph: this._graph || '' }); | ||
graph: this._graph || '' }); | ||
@@ -452,6 +453,29 @@ // Restore parent triple that contains the blank node. | ||
object: this._object, | ||
graph: graph || '' }); | ||
graph: graph || '' }); | ||
return next; | ||
}, | ||
// ### `_readBlankNodePunctuation` reads punctuation in a blank node | ||
_readBlankNodePunctuation: function (token) { | ||
var next; | ||
switch (token.type) { | ||
// Semicolon means the subject is shared; predicate and object are different. | ||
case ';': | ||
next = this._readPredicate; | ||
break; | ||
// Comma means both the subject and predicate are shared; the object is different. | ||
case ',': | ||
next = this._readObject; | ||
break; | ||
default: | ||
return this._error('Expected punctuation to follow "' + this._object + '"', token); | ||
} | ||
// A triple has been completed now, so return it. | ||
this._callback(null, { subject: this._subject, | ||
predicate: this._predicate, | ||
object: this._object, | ||
graph: this._graph || '' }); | ||
return next; | ||
}, | ||
// ### `_readQuadPunctuation` reads punctuation after a quad. | ||
@@ -458,0 +482,0 @@ _readQuadPunctuation: function (token) { |
// **N3Util** provides N3 utility functions | ||
var XsdString = 'http://www.w3.org/2001/XMLSchema#string'; | ||
var Xsd = 'http://www.w3.org/2001/XMLSchema#'; | ||
var XsdString = Xsd + 'string'; | ||
var XsdInteger = Xsd + 'integer'; | ||
var XsdDecimal = Xsd + 'decimal'; | ||
var XsdBoolean = Xsd + 'boolean'; | ||
var RdfLangString = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString'; | ||
@@ -56,8 +60,7 @@ | ||
expandPrefixedName: function (prefixedName, prefixes) { | ||
var match = /(?:^|"\^\^)([^:\/#"'\^_]*):[^\/]/.exec(prefixedName); | ||
if (!match) return prefixedName; | ||
var prefix = match[1], base = prefixes[prefix], index = match.index; | ||
var match = /(?:^|"\^\^)([^:\/#"'\^_]*):[^\/]*$/.exec(prefixedName), prefix, base, index; | ||
if (match) | ||
prefix = match[1], base = prefixes[prefix], index = match.index; | ||
if (base === undefined) | ||
throw new Error('Unknown prefix: ' + prefix); | ||
return prefixedName; | ||
@@ -69,2 +72,28 @@ // The match index is non-zero when expanding a literal's type. | ||
}, | ||
// 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 : XsdDecimal; | ||
break; | ||
} | ||
default: | ||
return '"' + value + '"'; | ||
} | ||
} | ||
return '"' + value + | ||
(/^[a-z]+(-[a-z0-9]+)*$/i.test(modifier) ? '"@' + modifier.toLowerCase() | ||
: '"^^' + modifier); | ||
}, | ||
}; | ||
@@ -71,0 +100,0 @@ |
@@ -111,3 +111,4 @@ // **N3Writer** writes N3 documents. | ||
var prefixMatch = this._prefixRegex.exec(iri); | ||
return prefixMatch ? this._prefixIRIs[prefixMatch[1]] + prefixMatch[2] : '<' + iri + '>'; | ||
return !prefixMatch ? '<' + iri + '>' : | ||
(!prefixMatch[1] ? iri : this._prefixIRIs[prefixMatch[1]] + prefixMatch[2]); | ||
}, | ||
@@ -189,9 +190,9 @@ | ||
// Add all useful prefixes | ||
var hasPrefixes = false; | ||
var prefixIRIs = this._prefixIRIs, hasPrefixes = false; | ||
for (var prefix in prefixes) { | ||
// Verify whether the prefix can be used and does not exist yet | ||
var iri = prefixes[prefix]; | ||
if (/[#\/]$/.test(iri) && this._prefixIRIs[iri] !== (prefix += ':')) { | ||
if (/[#\/]$/.test(iri) && prefixIRIs[iri] !== (prefix += ':')) { | ||
hasPrefixes = true; | ||
this._prefixIRIs[iri] = prefix; | ||
prefixIRIs[iri] = prefix; | ||
// Finish a possible pending triple | ||
@@ -208,7 +209,10 @@ if (this._subject !== null) { | ||
if (hasPrefixes) { | ||
var prefixIRIs = ''; | ||
for (var prefixIRI in this._prefixIRIs) | ||
prefixIRIs += prefixIRIs ? '|' + prefixIRI : prefixIRI; | ||
prefixIRIs = prefixIRIs.replace(/[\]\/\(\)\*\+\?\.\\\$]/g, '\\$&'); | ||
this._prefixRegex = new RegExp('^(' + prefixIRIs + ')([a-zA-Z][\\-_a-zA-Z0-9]*)$'); | ||
var IRIlist = '', prefixList = ''; | ||
for (var prefixIRI in prefixIRIs) { | ||
IRIlist += IRIlist ? '|' + prefixIRI : prefixIRI; | ||
prefixList += (prefixList ? '|' : '') + prefixIRIs[prefixIRI]; | ||
} | ||
IRIlist = IRIlist.replace(/[\]\/\(\)\*\+\?\.\\\$]/g, '\\$&'); | ||
this._prefixRegex = new RegExp('^(?:' + prefixList + ')[^\/]*$|' + | ||
'^(' + IRIlist + ')([a-zA-Z][\\-_a-zA-Z0-9]*)$'); | ||
} | ||
@@ -219,3 +223,3 @@ // End a prefix block with a newline | ||
// ### `_prefixRegex` matches an IRI that begins with one of the added prefixes | ||
// ### `_prefixRegex` matches a prefixed name or IRI that begins with one of the added prefixes | ||
_prefixRegex: /$0^/, | ||
@@ -222,0 +226,0 @@ |
{ | ||
"name": "n3", | ||
"version": "0.4.1", | ||
"version": "0.4.2", | ||
"description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.", | ||
@@ -5,0 +5,0 @@ "author": "Ruben Verborgh <ruben.verborgh@gmail.com>", |
@@ -86,2 +86,8 @@ # Lightning fast, asynchronous, streaming RDF for JavaScript | ||
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: | ||
@@ -299,2 +305,10 @@ ``` js | ||
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: | ||
@@ -301,0 +315,0 @@ ``` js |
@@ -186,3 +186,17 @@ var N3Parser = require('../N3').Parser; | ||
it('should parse a multi-statement blank node', | ||
it('should not parse a blank node with only a semicolon', | ||
shouldNotParse('<a> <b> [;].', | ||
'Unexpected ] at line 1.')); | ||
it('should parse a blank node with a trailing semicolon', | ||
shouldParse('<a> <b> [ <u> <v>; ].', | ||
['a', 'b', '_:b0'], | ||
['_:b0', 'u', 'v'])); | ||
it('should parse a blank node with multiple trailing semicolons', | ||
shouldParse('<a> <b> [ <u> <v>;;; ].', | ||
['a', 'b', '_:b0'], | ||
['_:b0', 'u', 'v'])); | ||
it('should parse a multi-predicate blank node', | ||
shouldParse('<a> <b> [ <u> <v>; <w> <z> ].', | ||
@@ -193,2 +207,14 @@ ['a', 'b', '_:b0'], | ||
it('should parse a multi-predicate blank node with multiple semicolons', | ||
shouldParse('<a> <b> [ <u> <v>;;; <w> <z> ].', | ||
['a', 'b', '_:b0'], | ||
['_:b0', 'u', 'v'], | ||
['_:b0', 'w', 'z'])); | ||
it('should parse a multi-object blank node', | ||
shouldParse('<a> <b> [ <u> <v>, <z> ].', | ||
['a', 'b', '_:b0'], | ||
['_:b0', 'u', 'v'], | ||
['_:b0', 'u', 'z'])); | ||
it('should parse a multi-statement blank node ending with a literal', | ||
@@ -212,6 +238,2 @@ shouldParse('<a> <b> [ <u> <v>; <w> "z" ].', | ||
it('should not parse a blank node with only a semicolon', | ||
shouldNotParse('<a> <b> [;].', | ||
'Unexpected ] at line 1.')); | ||
it('should parse a multi-statement blank node with trailing semicolon', | ||
@@ -235,2 +257,6 @@ shouldParse('<a> <b> [ <u> <v>; <w> <z>; ].', | ||
it('should not parse an invalid blank node', | ||
shouldNotParse('[ <a> <b> .', | ||
'Expected punctuation to follow "b" at line 1.')); | ||
it('should parse statements with an empty list in the subject', | ||
@@ -237,0 +263,0 @@ shouldParse('() <a> <b>.', |
@@ -275,4 +275,4 @@ var N3Util = require('../N3').Util; | ||
it('does not expand a prefixed name if the prefix is missing', function () { | ||
N3Util.expandPrefixedName.bind(null, 'a:Test', { 'b': 'http://ex.org/#' }).should.throw('Unknown prefix: a'); | ||
it('does not expand a prefixed name if the prefix is unknown', function () { | ||
N3Util.expandPrefixedName('a:Test', { 'b': 'http://ex.org/#' }).should.equal('a:Test'); | ||
}); | ||
@@ -284,2 +284,62 @@ | ||
}); | ||
describe('createIRI', function () { | ||
it('converts a plain IRI', function () { | ||
N3Util.createIRI('http://ex.org/foo#bar').should.equal('http://ex.org/foo#bar'); | ||
}); | ||
it('converts a literal', function () { | ||
N3Util.createIRI('"http://ex.org/foo#bar"^^uri:type').should.equal('http://ex.org/foo#bar'); | ||
}); | ||
it('converts null', function () { | ||
expect(N3Util.createIRI(null)).to.be.null; | ||
}); | ||
}); | ||
describe('createLiteral', function () { | ||
it('converts the empty string', function () { | ||
N3Util.createLiteral('').should.equal('""'); | ||
}); | ||
it('converts the empty string with a language', function () { | ||
N3Util.createLiteral('', 'en-GB').should.equal('""@en-gb'); | ||
}); | ||
it('converts the empty string with a type', function () { | ||
N3Util.createLiteral('', 'http://ex.org/type').should.equal('""^^http://ex.org/type'); | ||
}); | ||
it('converts a non-empty string', function () { | ||
N3Util.createLiteral('abc').should.equal('"abc"'); | ||
}); | ||
it('converts a non-empty string with a language', function () { | ||
N3Util.createLiteral('abc', 'en-GB').should.equal('"abc"@en-gb'); | ||
}); | ||
it('converts a non-empty string with a type', function () { | ||
N3Util.createLiteral('abc', 'http://ex.org/type').should.equal('"abc"^^http://ex.org/type'); | ||
}); | ||
it('converts an integer', function () { | ||
N3Util.createLiteral(123).should.equal('"123"^^http://www.w3.org/2001/XMLSchema#integer'); | ||
}); | ||
it('converts a decimal', function () { | ||
N3Util.createLiteral(2.3).should.equal('"2.3"^^http://www.w3.org/2001/XMLSchema#decimal'); | ||
}); | ||
it('converts infinity', function () { | ||
N3Util.createLiteral(Infinity).should.equal('"Infinity"'); | ||
}); | ||
it('converts false', function () { | ||
N3Util.createLiteral(false).should.equal('"false"^^http://www.w3.org/2001/XMLSchema#boolean'); | ||
}); | ||
it('converts true', function () { | ||
N3Util.createLiteral(true).should.equal('"true"^^http://www.w3.org/2001/XMLSchema#boolean'); | ||
}); | ||
}); | ||
}); |
@@ -5,3 +5,3 @@ var N3Writer = require('../N3').Writer; | ||
describe('N3Parser', function () { | ||
describe('N3Writer', function () { | ||
describe('The N3Writer module', function () { | ||
@@ -137,2 +137,9 @@ it('should be a function', function () { | ||
it('should expand prefixes when possible', | ||
shouldSerialize({ prefixes: { a: 'http://a.org/', b: 'http://a.org/b#' } }, | ||
[['a:bc', 'b:ef', 'c:bhi']], | ||
'@prefix a: <http://a.org/>.\n' + | ||
'@prefix b: <http://a.org/b#>.\n\n' + | ||
'a:bc b:ef <c:bhi>.\n')); | ||
it('should not repeat the same subjects', | ||
@@ -139,0 +146,0 @@ shouldSerialize([['abc', 'def', 'ghi'], |
Sorry, the diff of this file is not supported yet
235030
4601
375