From 69a0f769fc1e3cd751affce198a8248cda2859c2 Mon Sep 17 00:00:00 2001 From: David Majda Date: Fri, 18 Sep 2015 10:56:05 -0700 Subject: [PATCH] Use literal raw text in error messages Fixes #127. --- lib/compiler/passes/generate-bytecode.js | 4 +- lib/parser.js | 7 +- spec/api/plugin-api.spec.js | 2 +- .../compiler/passes/generate-bytecode.spec.js | 2 +- spec/unit/parser.spec.js | 87 ++++++++++--------- src/parser.pegjs | 1 + 6 files changed, 53 insertions(+), 50 deletions(-) diff --git a/lib/compiler/passes/generate-bytecode.js b/lib/compiler/passes/generate-bytecode.js index 75d9ead..1249e80 100644 --- a/lib/compiler/passes/generate-bytecode.js +++ b/lib/compiler/passes/generate-bytecode.js @@ -536,9 +536,7 @@ function generateBytecode(ast) { '{', 'type: "literal",', 'value: "' + js.stringEscape(node.value) + '",', - 'description: "' - + js.stringEscape('"' + js.stringEscape(node.value) + '"') - + '"', + 'description: "' + js.stringEscape(node.rawText) + '"', '}' ].join(' ')); diff --git a/lib/parser.js b/lib/parser.js index 70b6894..c8cdf5b 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -149,13 +149,13 @@ module.exports = (function() { peg$c36 = "\t", peg$c37 = { type: "literal", value: "\t", description: "\"\\t\"" }, peg$c38 = "\x0B", - peg$c39 = { type: "literal", value: "\x0B", description: "\"\\x0B\"" }, + peg$c39 = { type: "literal", value: "\x0B", description: "\"\\v\"" }, peg$c40 = "\f", peg$c41 = { type: "literal", value: "\f", description: "\"\\f\"" }, peg$c42 = " ", peg$c43 = { type: "literal", value: " ", description: "\" \"" }, peg$c44 = "\xA0", - peg$c45 = { type: "literal", value: "\xA0", description: "\"\\xA0\"" }, + peg$c45 = { type: "literal", value: "\xA0", description: "\"\\u00A0\"" }, peg$c46 = "\uFEFF", peg$c47 = { type: "literal", value: "\uFEFF", description: "\"\\uFEFF\"" }, peg$c48 = /^[\n\r\u2028\u2029]/, @@ -198,12 +198,13 @@ module.exports = (function() { type: "literal", value: value, ignoreCase: ignoreCase !== null, + rawText: text(), location: location() }; }, peg$c84 = { type: "other", description: "string" }, peg$c85 = "\"", - peg$c86 = { type: "literal", value: "\"", description: "\"\\\"\"" }, + peg$c86 = { type: "literal", value: "\"", description: "'\"'" }, peg$c87 = function(chars) { return chars.join(""); }, peg$c88 = "'", peg$c89 = { type: "literal", value: "'", description: "\"'\"" }, diff --git a/spec/api/plugin-api.spec.js b/spec/api/plugin-api.spec.js index a5cc208..129ea32 100644 --- a/spec/api/plugin-api.spec.js +++ b/spec/api/plugin-api.spec.js @@ -108,7 +108,7 @@ describe("plugin API", function() { ' {', ' type: "rule",', ' name: "start",', - ' expression: { type: "literal", value: text(), ignoreCase: false }', + ' expression: { type: "literal", value: text(), ignoreCase: false, rawText: text() }', ' }', ' ]', ' };', diff --git a/spec/unit/compiler/passes/generate-bytecode.spec.js b/spec/unit/compiler/passes/generate-bytecode.spec.js index 65fa445..616dd56 100644 --- a/spec/unit/compiler/passes/generate-bytecode.spec.js +++ b/spec/unit/compiler/passes/generate-bytecode.spec.js @@ -572,7 +572,7 @@ describe("compiler pass |generateBytecode|", function() { it("defines correct constants", function() { expect(pass).toChangeAST(grammar, constsDetails([ '"a"', - '{ type: "literal", value: "A", description: "\\"A\\"" }' + '{ type: "literal", value: "A", description: "\\"A\\"i" }' ])); }); }); diff --git a/spec/unit/parser.spec.js b/spec/unit/parser.spec.js index 4fba7a9..4102ffb 100644 --- a/spec/unit/parser.spec.js +++ b/spec/unit/parser.spec.js @@ -4,10 +4,10 @@ "use strict"; describe("PEG.js grammar parser", function() { - var literalAbcd = { type: "literal", value: "abcd", ignoreCase: false }, - literalEfgh = { type: "literal", value: "efgh", ignoreCase: false }, - literalIjkl = { type: "literal", value: "ijkl", ignoreCase: false }, - literalMnop = { type: "literal", value: "mnop", ignoreCase: false }, + var literalAbcd = { type: "literal", value: "abcd", ignoreCase: false, rawText: '"abcd"' }, + literalEfgh = { type: "literal", value: "efgh", ignoreCase: false, rawText: '"efgh"' }, + literalIjkl = { type: "literal", value: "ijkl", ignoreCase: false, rawText: '"ijkl"' }, + literalMnop = { type: "literal", value: "mnop", ignoreCase: false, rawText: '"mnop"' }, semanticAnd = { type: "semantic_and", code: " code " }, semanticNot = { type: "semantic_not", code: " code " }, optional = { type: "optional", expression: literalAbcd }, @@ -72,10 +72,13 @@ describe("PEG.js grammar parser", function() { ); } - function literalGrammar(value, ignoreCase) { - return oneRuleGrammar( - { type: "literal", value: value, ignoreCase: ignoreCase } - ); + function literalGrammar(value, ignoreCase, rawText) { + return oneRuleGrammar({ + type: "literal", + value: value, + ignoreCase: ignoreCase, + rawText: rawText + }); } function classGrammar(parts, inverted, ignoreCase, rawText) { @@ -96,7 +99,7 @@ describe("PEG.js grammar parser", function() { return oneRuleGrammar({ type: "rule_ref", name: name }); } - var trivialGrammar = literalGrammar("abcd", false), + var trivialGrammar = literalGrammar("abcd", false, '"abcd"'), twoRuleGrammar = { type: "grammar", initializer: null, @@ -488,26 +491,26 @@ describe("PEG.js grammar parser", function() { /* Canonical LiteralMatcher is "\"abcd\"". */ it("parses LiteralMatcher", function() { - expect('start = "abcd"' ).toParseAs(literalGrammar("abcd", false)); - expect('start = "abcd"i').toParseAs(literalGrammar("abcd", true)); + expect('start = "abcd"' ).toParseAs(literalGrammar("abcd", false, '"abcd"')); + expect('start = "abcd"i').toParseAs(literalGrammar("abcd", true, '"abcd"i')); }); /* Canonical StringLiteral is "\"abcd\"". */ it("parses StringLiteral", function() { - expect('start = ""' ).toParseAs(literalGrammar("", false)); - expect('start = "a"' ).toParseAs(literalGrammar("a", false)); - expect('start = "abc"').toParseAs(literalGrammar("abc", false)); + expect('start = ""' ).toParseAs(literalGrammar("", false, '""')); + expect('start = "a"' ).toParseAs(literalGrammar("a", false, '"a"')); + expect('start = "abc"').toParseAs(literalGrammar("abc", false, '"abc"')); - expect("start = ''" ).toParseAs(literalGrammar("", false)); - expect("start = 'a'" ).toParseAs(literalGrammar("a", false)); - expect("start = 'abc'").toParseAs(literalGrammar("abc", false)); + expect("start = ''" ).toParseAs(literalGrammar("", false, "''")); + expect("start = 'a'" ).toParseAs(literalGrammar("a", false, "'a'")); + expect("start = 'abc'").toParseAs(literalGrammar("abc", false, "'abc'")); }); /* Canonical DoubleStringCharacter is "a". */ it("parses DoubleStringCharacter", function() { - expect('start = "a"' ).toParseAs(literalGrammar("a", false)); - expect('start = "\\n"' ).toParseAs(literalGrammar("\n", false)); - expect('start = "\\\n"').toParseAs(literalGrammar("", false)); + expect('start = "a"' ).toParseAs(literalGrammar("a", false, '"a"')); + expect('start = "\\n"' ).toParseAs(literalGrammar("\n", false, '"\\n"')); + expect('start = "\\\n"').toParseAs(literalGrammar("", false, '"\\\n"')); expect('start = """' ).toFailToParse(); expect('start = "\\"').toFailToParse(); @@ -516,9 +519,9 @@ describe("PEG.js grammar parser", function() { /* Canonical SingleStringCharacter is "a". */ it("parses SingleStringCharacter", function() { - expect("start = 'a'" ).toParseAs(literalGrammar("a", false)); - expect("start = '\\n'" ).toParseAs(literalGrammar("\n", false)); - expect("start = '\\\n'").toParseAs(literalGrammar("", false)); + expect("start = 'a'" ).toParseAs(literalGrammar("a", false, "'a'")); + expect("start = '\\n'" ).toParseAs(literalGrammar("\n", false, "'\\n'")); + expect("start = '\\\n'").toParseAs(literalGrammar("", false, "'\\\n'")); expect("start = '''" ).toFailToParse(); expect("start = '\\'").toFailToParse(); @@ -589,41 +592,41 @@ describe("PEG.js grammar parser", function() { /* Canonical LineContinuation is "\\\n". */ it("parses LineContinuation", function() { - expect('start = "\\\r\n"').toParseAs(literalGrammar("", false)); + expect('start = "\\\r\n"').toParseAs(literalGrammar("", false, '"\\\r\n"')); }); /* Canonical EscapeSequence is "n". */ it("parses EscapeSequence", function() { - expect('start = "\\n"' ).toParseAs(literalGrammar("\n", false)); - expect('start = "\\0"' ).toParseAs(literalGrammar("\x00", false)); - expect('start = "\\xFF"' ).toParseAs(literalGrammar("\xFF", false)); - expect('start = "\\uFFFF"').toParseAs(literalGrammar("\uFFFF", false)); + expect('start = "\\n"' ).toParseAs(literalGrammar("\n", false, '"\\n"')); + expect('start = "\\0"' ).toParseAs(literalGrammar("\x00", false, '"\\0"')); + expect('start = "\\xFF"' ).toParseAs(literalGrammar("\xFF", false, '"\\xFF"')); + expect('start = "\\uFFFF"').toParseAs(literalGrammar("\uFFFF", false, '"\\uFFFF"')); expect('start = "\\09"').toFailToParse(); }); /* Canonical CharacterEscapeSequence is "n". */ it("parses CharacterEscapeSequence", function() { - expect('start = "\\n"').toParseAs(literalGrammar("\n", false)); - expect('start = "\\a"').toParseAs(literalGrammar("a", false)); + expect('start = "\\n"').toParseAs(literalGrammar("\n", false, '"\\n"')); + expect('start = "\\a"').toParseAs(literalGrammar("a", false, '"\\a"')); }); /* Canonical SingleEscapeCharacter is "n". */ it("parses SingleEscapeCharacter", function() { - expect('start = "\\\'"').toParseAs(literalGrammar("'", false)); - expect('start = "\\""' ).toParseAs(literalGrammar('"', false)); - expect('start = "\\\\"').toParseAs(literalGrammar("\\", false)); - expect('start = "\\b"' ).toParseAs(literalGrammar("\b", false)); - expect('start = "\\f"' ).toParseAs(literalGrammar("\f", false)); - expect('start = "\\n"' ).toParseAs(literalGrammar("\n", false)); - expect('start = "\\r"' ).toParseAs(literalGrammar("\r", false)); - expect('start = "\\t"' ).toParseAs(literalGrammar("\t", false)); - expect('start = "\\v"' ).toParseAs(literalGrammar("\x0B", false)); // no "\v" in IE + expect('start = "\\\'"').toParseAs(literalGrammar("'", false, '"\\\'"')); + expect('start = "\\""' ).toParseAs(literalGrammar('"', false, '"\\""')); + expect('start = "\\\\"').toParseAs(literalGrammar("\\", false, '"\\\\"')); + expect('start = "\\b"' ).toParseAs(literalGrammar("\b", false, '"\\b"')); + expect('start = "\\f"' ).toParseAs(literalGrammar("\f", false, '"\\f"')); + expect('start = "\\n"' ).toParseAs(literalGrammar("\n", false, '"\\n"')); + expect('start = "\\r"' ).toParseAs(literalGrammar("\r", false, '"\\r"')); + expect('start = "\\t"' ).toParseAs(literalGrammar("\t", false, '"\\t"')); + expect('start = "\\v"' ).toParseAs(literalGrammar("\x0B", false, '"\\v"')); // no "\v" in IE }); /* Canonical NonEscapeCharacter is "a". */ it("parses NonEscapeCharacter", function() { - expect('start = "\\a"').toParseAs(literalGrammar("a", false)); + expect('start = "\\a"').toParseAs(literalGrammar("a", false, '"\\a"')); /* * The negative predicate is impossible to test with PEG.js grammar @@ -638,12 +641,12 @@ describe("PEG.js grammar parser", function() { /* Canonical HexEscapeSequence is "xFF". */ it("parses HexEscapeSequence", function() { - expect('start = "\\xFF"').toParseAs(literalGrammar("\xFF", false)); + expect('start = "\\xFF"').toParseAs(literalGrammar("\xFF", false, '"\\xFF"')); }); /* Canonical UnicodeEscapeSequence is "uFFFF". */ it("parses UnicodeEscapeSequence", function() { - expect('start = "\\uFFFF"').toParseAs(literalGrammar("\uFFFF", false)); + expect('start = "\\uFFFF"').toParseAs(literalGrammar("\uFFFF", false, '"\\uFFFF"')); }); /* Digit rules are not tested. */ diff --git a/src/parser.pegjs b/src/parser.pegjs index e2554c7..59edf87 100644 --- a/src/parser.pegjs +++ b/src/parser.pegjs @@ -345,6 +345,7 @@ LiteralMatcher "literal" type: "literal", value: value, ignoreCase: ignoreCase !== null, + rawText: text(), location: location() }; }