Use Error objects.
This commit is contained in:
parent
9165b20d53
commit
e14d8f3720
@ -143,10 +143,7 @@ function number(n) {
|
||||
} else if (typeof(n) === 'number') {
|
||||
return n;
|
||||
} else {
|
||||
throw {
|
||||
error: "RuntimeError",
|
||||
message: "color functions take numbers as parameters"
|
||||
};
|
||||
throw new Error('Color functions take numbers as parameters.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,8 @@ var carto = {
|
||||
Parser: require('./parser').Parser,
|
||||
Renderer: require('./renderer').Renderer,
|
||||
tree: require('./tree'),
|
||||
|
||||
// @TODO
|
||||
writeError: function(ctx, options) {
|
||||
var message = '';
|
||||
var extract = ctx.extract;
|
||||
|
@ -171,26 +171,24 @@ carto.Parser = function Parser(env) {
|
||||
}
|
||||
}
|
||||
|
||||
function errorMessage(message, i) {
|
||||
if (typeof i === 'undefined') i = furthest;
|
||||
lines = input.split('\n');
|
||||
line = (input.slice(0, i).match(/\n/g) || '').length + 1;
|
||||
|
||||
for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++; }
|
||||
|
||||
var error = new Error((message || 'Syntax Error') + ' on line ' + line);
|
||||
return _(error).extend({
|
||||
name: 'ParseError',
|
||||
// Make an error object from a passed set of properties.
|
||||
// Accepted properties:
|
||||
// - `message`: Text of the error message.
|
||||
// - `filename`: Filename where the error occurred.
|
||||
// - `index`: Char. index where the error occurred.
|
||||
function makeError(err) {
|
||||
_(err).defaults({
|
||||
index: furthest,
|
||||
filename: env.filename,
|
||||
line: line,
|
||||
index: i,
|
||||
column: column,
|
||||
extract: [
|
||||
lines[line - 2],
|
||||
lines[line - 1],
|
||||
lines[line]
|
||||
]
|
||||
message: 'Parse error.',
|
||||
line: 0,
|
||||
column: -1
|
||||
});
|
||||
err.line = (input.slice(0, err.index).match(/\n/g) || '').length + 1;
|
||||
for (var n = err.index; n >= 0 && input.charAt(n) !== '\n'; n--) {
|
||||
err.column++;
|
||||
};
|
||||
return new Error(_('<%=filename%>:<%=line%>:<%=column%> <%=message%>').template(err));
|
||||
}
|
||||
|
||||
this.env = env = env || {};
|
||||
@ -266,10 +264,9 @@ carto.Parser = function Parser(env) {
|
||||
}
|
||||
if (level > 0) {
|
||||
// TODO: make invalid instead
|
||||
throw _(new Error('Missing closing `}`')).extend({
|
||||
index: i,
|
||||
line: 0,
|
||||
filename: env.filename
|
||||
throw makeError({
|
||||
message:'Missing closing `}`',
|
||||
index:i
|
||||
});
|
||||
}
|
||||
|
||||
@ -283,42 +280,18 @@ carto.Parser = function Parser(env) {
|
||||
root = new tree.Ruleset([], $(this.parsers.primary));
|
||||
root.root = true;
|
||||
|
||||
root.getLine = function(index) {
|
||||
return index ? (input.slice(0, index).match(/\n/g) || '').length : null;
|
||||
};
|
||||
|
||||
root.makeError = function(e) {
|
||||
lines = input.split('\n');
|
||||
line = root.getLine(e.index);
|
||||
|
||||
for (var n = e.index, column = -1;
|
||||
n >= 0 && input.charAt(n) !== '\n';
|
||||
n--) { column++ }
|
||||
|
||||
return _(new Error(e.message)).extend({
|
||||
type: e.type,
|
||||
filename: e.filename,
|
||||
index: e.index,
|
||||
line: typeof(line) === 'number' ? line + 1 : null,
|
||||
column: column,
|
||||
extract: [
|
||||
lines[line - 1],
|
||||
lines[line],
|
||||
lines[line + 1]
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Get an array of Ruleset objects, flattened
|
||||
// and sorted according to specificitySort
|
||||
root.toList = (function() {
|
||||
var line, lines, column, _ = require('underscore')._;
|
||||
return function(env) {
|
||||
env.error = function(e) {
|
||||
if (!env.errors) env.errors = [];
|
||||
env.errors.push(root.makeError(e));
|
||||
if (!env.errors) env.errors = new Error('');
|
||||
if (env.errors.message) {
|
||||
env.errors.message += '\n' + makeError(e).message;
|
||||
} else {
|
||||
env.errors.message = makeError(e).message;
|
||||
}
|
||||
};
|
||||
env.errors = [];
|
||||
env.frames = env.frames || [];
|
||||
|
||||
// call populates Invalid-caused errors
|
||||
@ -353,7 +326,10 @@ carto.Parser = function Parser(env) {
|
||||
// showing the line where the parse error occured.
|
||||
// We split it up into two parts (the part which parsed,
|
||||
// and the part which didn't), so we can color them differently.
|
||||
if (i < input.length - 1) throw errorMessage('Parse error', i);
|
||||
if (i < input.length - 1) throw makeError({
|
||||
message:'Parse error.',
|
||||
index:i
|
||||
});
|
||||
|
||||
return root;
|
||||
},
|
||||
@ -639,7 +615,7 @@ carto.Parser = function Parser(env) {
|
||||
$('(') && (args = $(this.entities.arguments)) && $(')');
|
||||
|
||||
if (elements.length > 0 && ($(';') || peek('}'))) {
|
||||
throw 'Calls are not yet supported';
|
||||
throw new Error('Calls are not yet supported.');
|
||||
return new tree.mixin.Call(elements, args, index);
|
||||
}
|
||||
},
|
||||
@ -680,7 +656,7 @@ carto.Parser = function Parser(env) {
|
||||
if (value = $(this.expression)) {
|
||||
params.push({ name: param.name, value: value });
|
||||
} else {
|
||||
throw new Error('Expected value');
|
||||
throw new Error('Expected value.');
|
||||
}
|
||||
} else {
|
||||
params.push({ name: param.name });
|
||||
@ -695,7 +671,7 @@ carto.Parser = function Parser(env) {
|
||||
ruleset = $(this.block);
|
||||
|
||||
if (ruleset) {
|
||||
throw 'Definitions should not exist here';
|
||||
throw new Error('Definitions should not exist here.');
|
||||
return new tree.mixin.Definition(name, params, ruleset);
|
||||
}
|
||||
}
|
||||
@ -781,7 +757,10 @@ carto.Parser = function Parser(env) {
|
||||
filters.add(f);
|
||||
conditions++;
|
||||
} else if (attachment) {
|
||||
throw errorMessage('Encountered second attachment name', i - 1);
|
||||
throw makeError({
|
||||
message:'Encountered second attachment name.',
|
||||
index:i - 1
|
||||
});
|
||||
} else {
|
||||
attachment = a;
|
||||
}
|
||||
|
@ -17,8 +17,7 @@ carto.Renderer.prototype.render = function render(m, callback) {
|
||||
env = _(this.env).defaults({
|
||||
benchmark: false,
|
||||
validation_data: false,
|
||||
effects: [],
|
||||
errors: []
|
||||
effects: []
|
||||
});
|
||||
|
||||
var output = [];
|
||||
@ -73,7 +72,7 @@ carto.Renderer.prototype.render = function render(m, callback) {
|
||||
}
|
||||
|
||||
// Exit on errors.
|
||||
if (env.errors.length) return callback(env.errors);
|
||||
if (env.errors) return callback(env.errors);
|
||||
|
||||
if (!map_properties.srs)
|
||||
map_properties.srs = 'srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over"';
|
||||
|
@ -170,7 +170,7 @@ tree.Ruleset.prototype = {
|
||||
} else if (rule instanceof tree.Rule) {
|
||||
rules.push(rule);
|
||||
} else if (rule instanceof tree.Invalid) {
|
||||
env.errors.push(rule);
|
||||
env.error(rule);
|
||||
}
|
||||
}
|
||||
|
||||
|
40
test/errorhandling.test.js
Normal file
40
test/errorhandling.test.js
Normal file
@ -0,0 +1,40 @@
|
||||
var path = require('path'),
|
||||
sys = require('sys'),
|
||||
assert = require('assert'),
|
||||
fs = require('fs');
|
||||
|
||||
var carto = require('../lib/carto');
|
||||
var tree = require('../lib/carto/tree');
|
||||
var helper = require('./support/helper');
|
||||
|
||||
helper.files('errorhandling', 'mml', function(file) {
|
||||
exports['errorhandling ' + path.basename(file)] = function(beforeExit) {
|
||||
var completed = false;
|
||||
var renderResult;
|
||||
var mml = helper.mml(file);
|
||||
new carto.Renderer({
|
||||
paths: [ path.dirname(file) ],
|
||||
data_dir: path.join(__dirname, '../data'),
|
||||
local_data_dir: path.join(__dirname, 'rendering'),
|
||||
filename: file
|
||||
}).render(mml, function (err) {
|
||||
var result = helper.resultFile(file);
|
||||
var output = err.message;
|
||||
// @TODO for some reason, fs.readFile includes an additional \n
|
||||
// at the end of read files. Determine why.
|
||||
fs.readFile(helper.resultFile(file), 'utf8', function(err, data) {
|
||||
assert.deepEqual(output, data.substr(0, data.length - 1));
|
||||
});
|
||||
});
|
||||
|
||||
beforeExit(function() {
|
||||
/*
|
||||
if (!completed && renderResult) {
|
||||
console.warn(helper.stylize('renderer produced:', 'bold'));
|
||||
console.warn(renderResult);
|
||||
}
|
||||
assert.ok(completed, 'Rendering finished.');
|
||||
*/
|
||||
});
|
||||
}
|
||||
});
|
@ -1 +1 @@
|
||||
[{"message":"Unrecognized selector: polygonopacity","index":41,"type":"syntax","filename":"[absolute path]","line":3,"column":2,"extract":[" polygon-fill: #f00;"," polygonopacity: 0.5;","}"]}]
|
||||
invalid_property.mss:3:2 Unrecognized rule: polygonopacity
|
||||
|
@ -1 +1 @@
|
||||
[{"message":"Invalid value for polygon-opacity, a valid float is expected. #ff0000 was given.","index":19,"type":"syntax","filename":"[absolute path]","line":2,"column":2,"extract":["#world[zoom=5] {"," polygon-opacity: #f00;","}"]}]
|
||||
invalid_value.mss:2:2 Invalid value for polygon-opacity, a valid float is expected. #ff0000 was given.
|
||||
|
@ -1 +1,3 @@
|
||||
[{"message":"variable @something is undefined","index":33,"type":"runtime","filename":"[absolute path]","line":2,"column":16,"extract":["#world[zoom=5] {"," polygon-fill: @something;","}"]}]
|
||||
undefined_variable.mss:2:16 variable @something is undefined
|
||||
undefined_variable.mss:3:14 variable @something is undefined
|
||||
undefined_variable.mss:4:22 variable @something is undefined
|
||||
|
Loading…
Reference in New Issue
Block a user