Comparing version 0.10.0 to 0.11.0
@@ -60,3 +60,3 @@ // **N3Lexer** tokenizes N3 documents. | ||
_blank: /^_:((?:[0-9A-Z_a-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])(?:\.?[\-0-9A-Z_a-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u037f-\u1fff\u200c\u200d\u203f\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]|[\ud800-\udb7f][\udc00-\udfff])*)(?:[ \t]+|(?=\.?[,;:\s#()\[\]\{\}"'<]))/, | ||
_number: /^[\-+]?(?:\d+\.?\d*([eE](?:[\-\+])?\d+)|\d*\.?\d+)(?=[.,;:\s#()\[\]\{\}"'<])/, | ||
_number: /^[\-+]?(?:\d+\.?\d*([eE](?:[\-\+])?\d+)|\d*\.?\d+)(?=\.?[,;:\s#()\[\]\{\}"'<])/, | ||
_boolean: /^(?:true|false)(?=[.,;\s#()\[\]\{\}"'<])/, | ||
@@ -114,3 +114,3 @@ _keyword: /^@[a-z]+(?=[\s#<])/i, | ||
else if (input[1] === '^') { | ||
this._prevTokenType = '^^'; | ||
this._previousMarker = '^^'; | ||
// Move to type IRI or prefixed name | ||
@@ -150,3 +150,3 @@ input = input.substr(2); | ||
// Try to find a blank node. Since it can contain (but not end with) a dot, | ||
// we always need a non-dot character before deciding it is a prefixed name. | ||
// we always need a non-dot character before deciding it is a blank node. | ||
// Therefore, try inserting a space if we're at the end of the input. | ||
@@ -190,3 +190,3 @@ if ((match = this._blank.exec(input)) || | ||
// Try to find a language code | ||
if (this._prevTokenType === 'literal' && (match = this._langcode.exec(input))) | ||
if (this._previousMarker === 'literal' && (match = this._langcode.exec(input))) | ||
type = 'langcode', value = match[1]; | ||
@@ -219,4 +219,7 @@ // Try to find a keyword | ||
case '-': | ||
// Try to find a number | ||
if (match = this._number.exec(input)) { | ||
// Try to find a number. Since it can contain (but not end with) a dot, | ||
// we always need a non-dot character before deciding it is a number. | ||
// Therefore, try inserting a space if we're at the end of the input. | ||
if (match = this._number.exec(input) || | ||
inputFinished && (match = this._number.exec(input + ' '))) { | ||
type = 'literal'; | ||
@@ -292,3 +295,3 @@ value = '"' + match[0] + '"^^http://www.w3.org/2001/XMLSchema#' + | ||
// Try to find a prefix | ||
if ((this._prevTokenType === '@prefix' || this._prevTokenType === 'PREFIX') && | ||
if ((this._previousMarker === '@prefix' || this._previousMarker === 'PREFIX') && | ||
(match = this._prefix.exec(input))) | ||
@@ -305,3 +308,3 @@ type = 'prefix', value = match[1] || ''; | ||
// A type token is special: it can only be emitted after an IRI or prefixed name is read | ||
if (this._prevTokenType === '^^') { | ||
if (this._previousMarker === '^^') { | ||
switch (type) { | ||
@@ -326,5 +329,6 @@ case 'prefixed': type = 'type'; break; | ||
// Emit the parsed token | ||
callback(null, { line: line, type: type, value: value, prefix: prefix }); | ||
this._prevTokenType = type; | ||
var token = { line: line, type: type, value: value, prefix: prefix }; | ||
callback(null, token); | ||
this.previousToken = token; | ||
this._previousMarker = type; | ||
// Advance to next part to tokenize | ||
@@ -368,3 +372,9 @@ input = input.substr(matchLength || match[0].length, input.length); | ||
this._input = null; | ||
return new Error('Unexpected "' + issue + '" on line ' + this._line + '.'); | ||
var err = new Error('Unexpected "' + issue + '" on line ' + this._line + '.'); | ||
err.context = { | ||
token: undefined, | ||
line: this._line, | ||
previousToken: this.previousToken, | ||
}; | ||
return err; | ||
}, | ||
@@ -412,2 +422,3 @@ | ||
}); | ||
input.on('error', callback); | ||
} | ||
@@ -414,0 +425,0 @@ }, |
@@ -777,3 +777,9 @@ // **N3Parser** parses N3 documents. | ||
_error: function (message, token) { | ||
this._callback(new Error(message + ' on line ' + token.line + '.')); | ||
var err = new Error(message + ' on line ' + token.line + '.'); | ||
err.context = { | ||
token: token, | ||
line: token.line, | ||
previousToken: this._lexer.previousToken, | ||
}; | ||
this._callback(err); | ||
}, | ||
@@ -780,0 +786,0 @@ |
@@ -17,9 +17,15 @@ // **N3StreamParser** parses an N3 stream into a triple stream. | ||
var self = this, parser = new N3Parser(options), onData, onEnd; | ||
parser.parse( | ||
// Pass dummy stream to obtain `data` and `end` callbacks | ||
{ on: function (event, cb) { event === 'data' ? (onData = cb) : (onEnd = cb); } }, | ||
// Handle triples by pushing them down the pipeline | ||
function (error, t) { error && self.emit('error', error) || t && self.push(t); }, | ||
// Emit prefixes through the `prefix` event | ||
function (prefix, uri) { self.emit('prefix', prefix, uri); }); | ||
// Pass dummy stream to obtain `data` and `end` callbacks | ||
parser.parse({ | ||
on: function (event, cb) { | ||
switch (event) { | ||
case 'data': onData = cb; break; | ||
case 'end': onEnd = cb; break; | ||
} | ||
}, | ||
}, | ||
// Handle triples by pushing them down the pipeline | ||
function (error, t) { error && self.emit('error', error) || t && self.push(t); }, | ||
// Emit prefixes through the `prefix` event | ||
function (prefix, uri) { self.emit('prefix', prefix, uri); }); | ||
@@ -26,0 +32,0 @@ // Implement Transform methods through parser callbacks |
@@ -97,14 +97,23 @@ // **N3Writer** writes N3 documents. | ||
_writeTripleLine: function (subject, predicate, object, graph, done) { | ||
// Don't use prefixes | ||
// Write the triple without prefixes | ||
delete this._prefixMatch; | ||
// Write the triple | ||
try { | ||
this._write(this._encodeIriOrBlankNode(subject) + ' ' + | ||
this._encodeIriOrBlankNode(predicate) + ' ' + | ||
this._encodeObject(object) + | ||
(graph ? ' ' + this._encodeIriOrBlankNode(graph) + '.\n' : '.\n'), done); | ||
} | ||
try { this._write(this.tripleToString(subject, predicate, object, graph), done); } | ||
catch (error) { done && done(error); } | ||
}, | ||
// ### `tripleToString` serializes a triple or quad as a string | ||
tripleToString: function (subject, predicate, object, graph) { | ||
return this._encodeIriOrBlankNode(subject) + ' ' + | ||
this._encodeIriOrBlankNode(predicate) + ' ' + | ||
this._encodeObject(object) + | ||
(graph ? ' ' + this._encodeIriOrBlankNode(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); | ||
}, this).join(''); | ||
}, | ||
// ### `_encodeIriOrBlankNode` represents an IRI or blank node | ||
@@ -111,0 +120,0 @@ _encodeIriOrBlankNode: function (entity) { |
{ | ||
"name": "n3", | ||
"version": "0.10.0", | ||
"version": "0.11.0", | ||
"description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.", | ||
@@ -20,4 +20,4 @@ "author": "Ruben Verborgh <ruben.verborgh@gmail.com>", | ||
"async": "^2.0.1", | ||
"browserify": "^13.1.0", | ||
"chai": "^3.5.0", | ||
"browserify": "^14.4.0", | ||
"chai": "^4.0.2", | ||
"chai-things": "^0.2.0", | ||
@@ -28,15 +28,14 @@ "colors": "^1.1.2", | ||
"docco": "^0.7.0", | ||
"eslint": "^3.13.1", | ||
"eslint": "^4.1.1", | ||
"mocha": "^3.0.2", | ||
"nyc": "^10.1.2", | ||
"pre-commit": "^1.1.3", | ||
"nyc": "^11.0.3", | ||
"pre-commit": "^1.2.2", | ||
"request": "^2.74.0", | ||
"uglify-js": "^2.7.3" | ||
"uglify-js": "^3.0.24" | ||
}, | ||
"scripts": { | ||
"test": "mocha", | ||
"test": "nyc mocha", | ||
"lint": "eslint lib perf test spec", | ||
"browser": "node browser/build-browser-versions", | ||
"cover": "nyc -- mocha -R dot --timeout 1000", | ||
"coveralls": "nyc --reporter=text-lcov -- mocha -R dot --timeout 1000 | coveralls", | ||
"coveralls": "nyc --reporter=text-lcov mocha | coveralls", | ||
"spec": "node spec/turtle-spec && node spec/trig-spec && node spec/ntriples-spec && node spec/nquads-spec", | ||
@@ -43,0 +42,0 @@ "spec-clean": "rm -r spec/turtle spec/trig", |
@@ -456,3 +456,9 @@ var N3Lexer = require('../N3').Lexer; | ||
it('should tokenize a stream with split number', | ||
it('should tokenize a stream ending with a digit and a dot', | ||
shouldTokenize(streamOf('1', '.'), | ||
{ type: 'literal', value: '"1"^^http://www.w3.org/2001/XMLSchema#integer', line: 1 }, | ||
{ type: '.', line: 1 }, | ||
{ type: 'eof', line: 1 })); | ||
it('should tokenize a stream containing a decimal without leading digit', | ||
shouldTokenize(streamOf('.', '1 '), | ||
@@ -462,2 +468,7 @@ { type: 'literal', value: '".1"^^http://www.w3.org/2001/XMLSchema#decimal', line: 1 }, | ||
it('should tokenize a stream containing a decimal with leading digit', | ||
shouldTokenize(streamOf('1.', '1 '), | ||
{ type: 'literal', value: '"1.1"^^http://www.w3.org/2001/XMLSchema#decimal', line: 1 }, | ||
{ type: 'eof', line: 1 })); | ||
it('should immediately signal an error if a linebreak occurs anywhere outside a triple-quoted literal', | ||
@@ -724,8 +735,16 @@ shouldNotTokenize(streamOf('abc\n', null), 'Unexpected "abc" on line 1.')); | ||
describe('passing data after the stream has been finished', function () { | ||
var tokens = [], stream = new EventEmitter(), lexer = new N3Lexer(); | ||
lexer.tokenize(stream, function (error, token) { !error && tokens.push(token); }); | ||
stream.emit('data', '<a> '); | ||
stream.emit('end'); | ||
stream.emit('data', '<b> '); | ||
stream.emit('end'); | ||
var tokens = [], error; | ||
before(function () { | ||
var stream = new EventEmitter(), lexer = new N3Lexer(); | ||
lexer.tokenize(stream, function (err, token) { | ||
if (err) | ||
error = err; | ||
else | ||
tokens.push(token); | ||
}); | ||
stream.emit('data', '<a> '); | ||
stream.emit('end'); | ||
stream.emit('data', '<b> '); | ||
stream.emit('end'); | ||
}); | ||
@@ -735,12 +754,24 @@ it('parses only the first chunk (plus EOF)', function () { | ||
}); | ||
it('does not emit an error', function () { | ||
expect(error).to.not.exist; | ||
}); | ||
}); | ||
describe('passing data after an error has occurred', function () { | ||
var tokens = [], stream = new EventEmitter(), lexer = new N3Lexer(); | ||
lexer.tokenize(stream, function (error, token) { !error && tokens.push(token); }); | ||
stream.emit('data', '<a> '); | ||
stream.emit('data', ' error '); | ||
stream.emit('end'); | ||
stream.emit('data', '<b> '); | ||
stream.emit('end'); | ||
describe('passing data after a syntax error', function () { | ||
var tokens = [], error; | ||
before(function () { | ||
var stream = new EventEmitter(), lexer = new N3Lexer(); | ||
lexer.tokenize(stream, function (err, token) { | ||
if (err) | ||
error = err; | ||
else | ||
tokens.push(token); | ||
}); | ||
stream.emit('data', '<a> '); | ||
stream.emit('data', ' error '); | ||
stream.emit('end'); | ||
stream.emit('data', '<b> '); | ||
stream.emit('end'); | ||
}); | ||
@@ -750,4 +781,28 @@ it('parses only the first chunk', function () { | ||
}); | ||
it('emits the error', function () { | ||
expect(error).to.exist; | ||
expect(error).to.have.property('message', 'Unexpected "error" on line 1.'); | ||
}); | ||
}); | ||
describe('when the stream errors', function () { | ||
var tokens = [], error; | ||
before(function () { | ||
var stream = new EventEmitter(), lexer = new N3Lexer(); | ||
lexer.tokenize(stream, function (err, token) { | ||
if (err) | ||
error = err; | ||
else | ||
tokens.push(token); | ||
}); | ||
stream.emit('error', new Error('my error')); | ||
}); | ||
it('passes the error', function () { | ||
expect(error).to.exist; | ||
expect(error).to.have.property('message', 'my error'); | ||
}); | ||
}); | ||
describe('called with a string and without callback', function () { | ||
@@ -754,0 +809,0 @@ var lexer = new N3Lexer(), |
@@ -30,4 +30,7 @@ var N3StreamParser = require('../N3').StreamParser; | ||
it('should parse decimals that are split across chunks in the stream', | ||
shouldParse('<sub> <pred> 11.2 .'.match(/.{1,2}/g), 1)); | ||
it("doesn't parse an invalid stream", | ||
shouldNotParse(['z.'], 'Unexpected "z." on line 1.')); | ||
shouldNotParse(['z.'], 'Unexpected "z." on line 1.'), { token: undefined, line: 1, previousToken: undefined }); | ||
@@ -57,3 +60,3 @@ it('emits "prefix" events', | ||
function shouldNotParse(chunks, expectedMessage) { | ||
function shouldNotParse(chunks, expectedMessage, expectedContext) { | ||
return function (done) { | ||
@@ -68,2 +71,3 @@ var inputStream = new ArrayReader(chunks), | ||
error.message.should.equal(expectedMessage); | ||
if (expectedContext) error.context.should.deep.equal(expectedContext); | ||
done(); | ||
@@ -70,0 +74,0 @@ }); |
@@ -19,2 +19,20 @@ var N3Writer = require('../N3').Writer; | ||
describe('An N3Writer instance', function () { | ||
it('should serialize a single triple', function () { | ||
var writer = N3Writer(); | ||
writer.tripleToString('a', 'b', 'c').should.equal('<a> <b> <c>.\n'); | ||
}); | ||
it('should serialize a single quad', function () { | ||
var writer = N3Writer(); | ||
writer.tripleToString('a', 'b', 'c', 'g').should.equal('<a> <b> <c> <g>.\n'); | ||
}); | ||
it('should serialize an array of triples', function () { | ||
var writer = N3Writer(); | ||
var triples = [{ subject: 'a', predicate: 'b', object: 'c' }, | ||
{ subject: 'd', predicate: 'e', object: 'f' }]; | ||
writer.triplesToString(triples).should.equal('<a> <b> <c>.\n<d> <e> <f>.\n'); | ||
}); | ||
it('should serialize 0 triples', | ||
@@ -526,4 +544,4 @@ shouldSerialize('')); | ||
it('should not write an invalid literal in N-Quads mode', function (done) { | ||
var writer = N3Writer({ format: 'N-Triples' }); | ||
writer.addTriple('a', 'b', '"c', function (error) { | ||
var writer = N3Writer({ format: 'N-Quads' }); | ||
writer.addTriple('a', 'b', '"c', 'g', function (error) { | ||
error.should.be.an.instanceof(Error); | ||
@@ -535,2 +553,3 @@ error.should.have.property('message', 'Invalid literal: "c'); | ||
it('should end when the end option is not set', function (done) { | ||
@@ -537,0 +556,0 @@ var outputStream = new QuickStream(), writer = N3Writer(outputStream, {}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
405700
7419