From 21c6d9ccd387517ebc12e0cd8a52945a0e0e2800 Mon Sep 17 00:00:00 2001 From: David Majda Date: Sat, 11 Feb 2012 12:42:26 +0100 Subject: [PATCH] Add |offset| property to exceptions thrown by parsers Based on a patch by Marcin Stefaniuk (marcin@stefaniuk.info). --- README.md | 2 +- src/emitter.js | 4 +++- src/parser.js | 4 +++- test/compiler-test.js | 16 ++++++++-------- test/helpers.js | 3 ++- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 489a506..916963b 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ To get parser’s source code, call the `toSource` method on the parser. Using the Parser ---------------- -Using the generated parser is simple — just call its `parse` method and pass an input string as a parameter. The method will return a parse result (the exact value depends on the grammar used to build the parser) or throw an exception if the input is invalid. The exception will contain `line`, `column` and `message` properties with more details about the error. +Using the generated parser is simple — just call its `parse` method and pass an input string as a parameter. The method will return a parse result (the exact value depends on the grammar used to build the parser) or throw an exception if the input is invalid. The exception will contain `offset`, `line`, `column` and `message` properties with more details about the error. parser.parse("abba"); // returns ["a", "b", "b", "a"] diff --git a/src/emitter.js b/src/emitter.js index 2b27d67..f588356 100644 --- a/src/emitter.js +++ b/src/emitter.js @@ -443,6 +443,7 @@ PEG.compiler.emitter = function(ast) { ' var errorPosition = computeErrorPosition();', ' throw new this.SyntaxError(', ' buildErrorMessage(),', + ' Math.max(pos, rightmostFailuresPos),', ' errorPosition.line,', ' errorPosition.column', ' );', @@ -457,9 +458,10 @@ PEG.compiler.emitter = function(ast) { ' ', ' /* Thrown when a parser encounters a syntax error. */', ' ', - ' result.SyntaxError = function(message, line, column) {', + ' result.SyntaxError = function(message, offset, line, column) {', ' this.name = "SyntaxError";', ' this.message = message;', + ' this.offset = offset;', ' this.line = line;', ' this.column = column;', ' };', diff --git a/src/parser.js b/src/parser.js index fe6f080..91fe9d2 100644 --- a/src/parser.js +++ b/src/parser.js @@ -3598,6 +3598,7 @@ PEG.parser = (function(){ var errorPosition = computeErrorPosition(); throw new this.SyntaxError( buildErrorMessage(), + Math.max(pos, rightmostFailuresPos), errorPosition.line, errorPosition.column ); @@ -3612,9 +3613,10 @@ PEG.parser = (function(){ /* Thrown when a parser encounters a syntax error. */ - result.SyntaxError = function(message, line, column) { + result.SyntaxError = function(message, offset, line, column) { this.name = "SyntaxError"; this.message = message; + this.offset = offset; this.line = line; this.column = column; }; diff --git a/test/compiler-test.js b/test/compiler-test.js index 2e18428..e9e5a41 100644 --- a/test/compiler-test.js +++ b/test/compiler-test.js @@ -447,10 +447,10 @@ test("error positions", function() { var simpleParser = PEG.buildParser('start = "a"'); /* Regular match failure */ - doesNotParseWithPos(simpleParser, "b", 1, 1); + doesNotParseWithPos(simpleParser, "b", 0, 1, 1); /* Trailing input */ - doesNotParseWithPos(simpleParser, "ab", 1, 2); + doesNotParseWithPos(simpleParser, "ab", 1, 1, 2); var digitsParser = PEG.buildParser([ 'start = line (("\\r" / "\\n" / "\\u2028" / "\\u2029")+ line)*', @@ -458,16 +458,16 @@ test("error positions", function() { 'digits = digits:[0-9]+ { return digits.join(""); }' ].join("\n")); - doesNotParseWithPos(digitsParser, "1\n2\n\n3\n\n\n4 5 x", 7, 5); + doesNotParseWithPos(digitsParser, "1\n2\n\n3\n\n\n4 5 x", 13, 7, 5); /* Non-Unix newlines */ - doesNotParseWithPos(digitsParser, "1\rx", 2, 1); // Old Mac - doesNotParseWithPos(digitsParser, "1\r\nx", 2, 1); // Windows - doesNotParseWithPos(digitsParser, "1\n\rx", 3, 1); // mismatched + doesNotParseWithPos(digitsParser, "1\rx", 2, 2, 1); // Old Mac + doesNotParseWithPos(digitsParser, "1\r\nx", 3, 2, 1); // Windows + doesNotParseWithPos(digitsParser, "1\n\rx", 3, 3, 1); // mismatched /* Strange newlines */ - doesNotParseWithPos(digitsParser, "1\u2028x", 2, 1); // line separator - doesNotParseWithPos(digitsParser, "1\u2029x", 2, 1); // paragraph separator + doesNotParseWithPos(digitsParser, "1\u2028x", 2, 2, 1); // line separator + doesNotParseWithPos(digitsParser, "1\u2029x", 2, 2, 1); // paragraph separator }); test("start rule", function() { diff --git a/test/helpers.js b/test/helpers.js index 512a5a8..9b0a734 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -19,11 +19,12 @@ doesNotParseWithMessage = function(parser, input, message) { ); }; -doesNotParseWithPos = function(parser, input, line, column) { +doesNotParseWithPos = function(parser, input, offset, line, column) { raises( function() { parser.parse(input); }, function(e) { return e instanceof parser.SyntaxError + && e.offset === offset && e.line === line && e.column === column; }