Skip to content

Commit

Permalink
Add support for line and column number context for errors: json5#86
Browse files Browse the repository at this point in the history
  • Loading branch information
amb26 committed Dec 9, 2015
1 parent c4494d3 commit e4ba49e
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 12 deletions.
33 changes: 25 additions & 8 deletions lib/json5.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ JSON5.parse = (function () {
// We are defining the function inside of another function to avoid creating
// global variables.

var at, // The index of the current character
ch, // The current character
var at, // The index of the current character
lineNumber, // The current line number
columnNumber, // The current column number
ch, // The current character
escapee = {
"'": "'",
'"': '"',
Expand All @@ -43,14 +45,22 @@ JSON5.parse = (function () {
],
text,

renderChar = function (chr) {
return chr === '' ? 'EOF' : "'" + chr + "'";
},

error = function (m) {

// Call error when something is wrong.

var error = new SyntaxError();
error.message = m;
// beginning of message suffix to agree with that provided by Gecko - see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
error.message = m + " at line " + lineNumber + " column " + columnNumber + " of the JSON5 data. Still to read: " + JSON.stringify(text.substring(at - 1, at + 19));
error.at = at;
error.text = text;
// These two property names have been chosen to agree with the ones in Gecko, the only popular
// environment which seems to supply this info on JSON.parse
error.lineNumber = lineNumber;
error.columnNumber = columnNumber;
throw error;
},

Expand All @@ -59,14 +69,19 @@ JSON5.parse = (function () {
// If a c parameter is provided, verify that it matches the current character.

if (c && c !== ch) {
error("Expected '" + c + "' instead of '" + ch + "'");
error("Expected " + renderChar(c) + " instead of " + renderChar(ch));
}

// Get the next character. When there are no more characters,
// return the empty string.

ch = text.charAt(at);
at += 1;
++ at;
++ columnNumber;
if (ch === '\n') {
++ lineNumber;
columnNumber = 0;
}
return ch;
},

Expand Down Expand Up @@ -94,7 +109,7 @@ JSON5.parse = (function () {
if ((ch !== '_' && ch !== '$') &&
(ch < 'a' || ch > 'z') &&
(ch < 'A' || ch > 'Z')) {
error("Bad identifier");
error("Bad identifier as unquoted key");
}

// Subsequent characters can contain digits.
Expand Down Expand Up @@ -375,7 +390,7 @@ JSON5.parse = (function () {
next( 'N' );
return NaN;
}
error("Unexpected '" + ch + "'");
error("Unexpected " + renderChar(ch));
},

value, // Place holder for the value function.
Expand Down Expand Up @@ -487,6 +502,8 @@ JSON5.parse = (function () {

text = String(source);
at = 0;
lineNumber = 1;
columnNumber = 1;
ch = ' ';
result = value();
white();
Expand Down
6 changes: 6 additions & 0 deletions test/parse-cases/arrays/no-comma-array.errorSpec
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
at: 16,
lineNumber: 3,
columnNumber: 5,
message: "Expected ']' instead of 'f'"
}
6 changes: 6 additions & 0 deletions test/parse-cases/comments/top-level-block-comment.errorSpec
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
at: 77,
lineNumber: 4,
columnNumber: 3,
message: "Unexpected EOF"
}
2 changes: 1 addition & 1 deletion test/parse-cases/comments/top-level-block-comment.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
This should fail;
comments cannot be the top-level value.
comments cannot be the only top-level value.
*/
6 changes: 6 additions & 0 deletions test/parse-cases/comments/top-level-inline-comment.errorSpec
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
at: 66,
lineNumber: 1,
columnNumber: 67,
message: "Unexpected EOF"
}
2 changes: 1 addition & 1 deletion test/parse-cases/comments/top-level-inline-comment.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
// This should fail; comments cannot be the top-level value.
// This should fail; comments cannot be the only top-level value.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
at: 7,
lineNumber: 2,
columnNumber: 5,
message: "Bad identifier as unquoted key"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
at: 12,
lineNumber: 2,
columnNumber: 10,
message: "Expected ':' instead of '-'"
}
6 changes: 6 additions & 0 deletions test/parse-cases/objects/leading-comma-object.errorSpec
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
at: 7,
lineNumber: 2,
columnNumber: 5,
message: "Bad identifier as unquoted key"
}
6 changes: 6 additions & 0 deletions test/parse-cases/strings/no-comma-array.errorSpec
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
at: 16,
lineNumber: 3,
columNumber: 5,
message: "Expected ']' instead of 'f'"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
at: 5,
lineNumber: 2,
columnNumber: 0,
message: "Bad string"
}
46 changes: 44 additions & 2 deletions test/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,49 @@ var Path = require('path');
var dirsPath = Path.resolve(__dirname, 'parse-cases');
var dirs = FS.readdirSync(dirsPath);

var readErrorSpec = function (filePath) {
var specName = Path.basename(filePath, '.txt') + '.errorSpec';
var specPath = Path.join(Path.dirname(filePath), specName);
var specTxt;
try {
specTxt = FS.readFileSync(specPath); // note that existsSync has been deprecated
} catch (e) {}
if (specTxt) {
try {
return JSON5.parse(specTxt);
} catch (err) {
err.message = 'Error reading error specification file ' + specName + ': ' + err.message;
throw err;
}
}
};

var testParseJSON5 = function (filePath, str) {
var errorSpec = readErrorSpec(filePath);
var err;
try {
JSON5.parse(str);
} catch (e) {
err = e;
}
assert(err, 'Expected JSON5 parsing to fail.');
if (errorSpec) {
describe("Error fixture " + filePath, function () {
Object.keys(errorSpec).forEach(function (key) {
if (key === 'message') {
it('Expected error message\n' + err.message + '\nto start with ' + errorSpec.message, function () {
assert(err.message.indexOf(errorSpec.message) === 0);
});
} else {
it('Expected parse error field ' + key + ' to hold value ' + errorSpec[key], function () {
assert.equal(err[key], errorSpec[key]);
});
}
})
});
}
};

function createTest(fileName, dir) {
var ext = Path.extname(fileName);
var filePath = Path.join(dirsPath, dir, fileName);
Expand Down Expand Up @@ -66,8 +109,7 @@ function createTest(fileName, dir) {
case '.txt':
assert.throws(parseES5, // test validation
'Test case bug: expected ES5 parsing to fail.');
assert.throws(parseJSON5,
'Expected JSON5 parsing to fail.');
testParseJSON5(filePath, str);
break;
}
};
Expand Down

0 comments on commit e4ba49e

Please sign in to comment.