From 1b2279e026648e00370ece5db91fdea620f1741c Mon Sep 17 00:00:00 2001 From: David Majda Date: Thu, 7 Nov 2013 16:57:27 +0100 Subject: [PATCH] Error handling: Make predicates always return |undefined| After making the |?| operator return |null| instead of an empty string in the previous commit, empty strings were still returned from predicates. This didn't make much sense. Return value of a predicate is unimportant (if you have one in hand, you already know the predicate succeeded) and one could even argue that predicates shouldn't return any value at all. The closest thing to "return no value" in JavaScript is returning |undefined|, so I decided to make predicates return exactly that. Implements part of #198. --- README.md | 12 ++++---- lib/compiler/passes/generate-bytecode.js | 16 +++++----- lib/parser.js | 2 +- .../compiler/passes/generate-bytecode.spec.js | 12 ++++---- spec/generated-parser.spec.js | 30 +++++++++---------- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 6d15897..eea37ca 100644 --- a/README.md +++ b/README.md @@ -295,13 +295,13 @@ otherwise return `null`. #### & *expression* -Try to match the expression. If the match succeeds, just return an empty string -and do not advance the parser position, otherwise consider the match failed. +Try to match the expression. If the match succeeds, just return `undefined` and +do not advance the parser position, otherwise consider the match failed. #### ! *expression* -Try to match the expression. If the match does not succeed, just return an empty -string and do not advance the parser position, otherwise consider the match +Try to match the expression. If the match does not succeed, just return +`undefined` and do not advance the parser position, otherwise consider the match failed. #### & { *predicate* } @@ -310,7 +310,7 @@ The predicate is a piece of JavaScript code that is executed as if it was inside a function. It gets the match results of labeled expressions in preceding expression as its arguments. It should return some JavaScript value using the `return` statement. If the returned value evaluates to `true` in boolean -context, just return an empty string and do not advance the parser position; +context, just return `undefined` and do not advance the parser position; otherwise consider the match failed. The code inside the predicate can access all variables and functions defined in @@ -332,7 +332,7 @@ The predicate is a piece of JavaScript code that is executed as if it was inside a function. It gets the match results of labeled expressions in preceding expression as its arguments. It should return some JavaScript value using the `return` statement. If the returned value evaluates to `false` in boolean -context, just return an empty string and do not advance the parser position; +context, just return `undefined` and do not advance the parser position; otherwise consider the match failed. The code inside the predicate can access all variables and functions defined in diff --git a/lib/compiler/passes/generate-bytecode.js b/lib/compiler/passes/generate-bytecode.js index 9709f9e..9f13c7b 100644 --- a/lib/compiler/passes/generate-bytecode.js +++ b/lib/compiler/passes/generate-bytecode.js @@ -213,8 +213,8 @@ module.exports = function(ast, options) { } function buildSimplePredicate(expression, negative, context) { - var emptyStringIndex = addConst('""'), - failedIndex = addConst('peg$FAILED'); + var undefinedIndex = addConst('void 0'), + failedIndex = addConst('peg$FAILED'); return buildSequence( [op.PUSH_CURR_POS], @@ -230,7 +230,7 @@ module.exports = function(ast, options) { buildSequence( [op.POP], [negative ? op.POP : op.POP_CURR_POS], - [op.PUSH, emptyStringIndex] + [op.PUSH, undefinedIndex] ), buildSequence( [op.POP], @@ -242,9 +242,9 @@ module.exports = function(ast, options) { } function buildSemanticPredicate(code, negative, context) { - var functionIndex = addFunctionConst(utils.keys(context.env), code), - emptyStringIndex = addConst('""'), - failedIndex = addConst('peg$FAILED'); + var functionIndex = addFunctionConst(utils.keys(context.env), code), + undefinedIndex = addConst('void 0'), + failedIndex = addConst('peg$FAILED'); return buildSequence( [op.REPORT_CURR_POS], @@ -253,11 +253,11 @@ module.exports = function(ast, options) { [op.IF], buildSequence( [op.POP], - [op.PUSH, negative ? failedIndex : emptyStringIndex] + [op.PUSH, negative ? failedIndex : undefinedIndex] ), buildSequence( [op.POP], - [op.PUSH, negative ? emptyStringIndex : failedIndex] + [op.PUSH, negative ? undefinedIndex : failedIndex] ) ) ); diff --git a/lib/parser.js b/lib/parser.js index e10921e..65ee165 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -197,7 +197,7 @@ module.exports = (function() { expression: expression }; }, - peg$c18 = "", + peg$c18 = void 0, peg$c19 = function(name) { return { type: "rule_ref", diff --git a/spec/compiler/passes/generate-bytecode.spec.js b/spec/compiler/passes/generate-bytecode.spec.js index d9a1e4e..84ed6b6 100644 --- a/spec/compiler/passes/generate-bytecode.spec.js +++ b/spec/compiler/passes/generate-bytecode.spec.js @@ -272,7 +272,7 @@ describe("compiler pass |generateBytecode|", function() { it("defines correct constants", function() { expect(pass).toChangeAST(grammar, constsDetails([ - '""', + 'void 0', 'peg$FAILED', '"a"', '{ type: "literal", value: "a", description: "\\"a\\"" }' @@ -301,7 +301,7 @@ describe("compiler pass |generateBytecode|", function() { it("defines correct constants", function() { expect(pass).toChangeAST(grammar, constsDetails([ - '""', + 'void 0', 'peg$FAILED', '"a"', '{ type: "literal", value: "a", description: "\\"a\\"" }' @@ -328,7 +328,7 @@ describe("compiler pass |generateBytecode|", function() { it("defines correct constants", function() { expect(pass).toChangeAST( grammar, - constsDetails(['function() { code }', '""', 'peg$FAILED']) + constsDetails(['function() { code }', 'void 0', 'peg$FAILED']) ); }); }); @@ -380,7 +380,7 @@ describe("compiler pass |generateBytecode|", function() { '"c"', '{ type: "literal", value: "c", description: "\\"c\\"" }', 'function(a, b, c) { code }', - '""' + 'void 0' ])); }); }); @@ -405,7 +405,7 @@ describe("compiler pass |generateBytecode|", function() { it("defines correct constants", function() { expect(pass).toChangeAST( grammar, - constsDetails(['function() { code }', '""', 'peg$FAILED']) + constsDetails(['function() { code }', 'void 0', 'peg$FAILED']) ); }); }); @@ -457,7 +457,7 @@ describe("compiler pass |generateBytecode|", function() { '"c"', '{ type: "literal", value: "c", description: "\\"c\\"" }', 'function(a, b, c) { code }', - '""' + 'void 0' ])); }); }); diff --git a/spec/generated-parser.spec.js b/spec/generated-parser.spec.js index c0fcff1..8abbb73 100644 --- a/spec/generated-parser.spec.js +++ b/spec/generated-parser.spec.js @@ -397,14 +397,14 @@ describe("generated parser", function() { it("matches correctly", function() { var parser = PEG.buildParser('start = &"a" "a"', options); - expect(parser).toParse("a", ["", "a"]); + expect(parser).toParse("a", [undefined, "a"]); expect(parser).toFailToParse("b"); }); it("does not advance position on success", function() { var parser = PEG.buildParser('start = &"a" "a"', options); - expect(parser).toParse("a", ["", "a"]); + expect(parser).toParse("a", [undefined, "a"]); }); it("does not influence expected strings on failure", function() { @@ -418,7 +418,7 @@ describe("generated parser", function() { it("matches correctly", function() { var parser = PEG.buildParser('start = !"a" "b"', options); - expect(parser).toParse("b", ["", "b"]); + expect(parser).toParse("b", [undefined, "b"]); expect(parser).toFailToParse("a"); }); @@ -439,7 +439,7 @@ describe("generated parser", function() { it("causes successful match by returning |true|", function() { var parser = PEG.buildParser('start = &{ return true; }', options); - expect(parser).toParse("", ""); + expect(parser).toParse("", undefined); }); it("causes match failure by returning |false|", function() { @@ -454,7 +454,7 @@ describe("generated parser", function() { options ); - expect(parser).toParse("a", ["a", ""]); + expect(parser).toParse("a", ["a", undefined]); }); it("can use the |text| function", function() { @@ -463,7 +463,7 @@ describe("generated parser", function() { options ); - expect(parser).toParse("a", ["a", ""]); + expect(parser).toParse("a", ["a", undefined]); }); it("can use the |offset| function to get the current parse position", function() { @@ -472,7 +472,7 @@ describe("generated parser", function() { options ); - expect(parser).toParse("a", ["a", ""]); + expect(parser).toParse("a", ["a", undefined]); }); it("can use the |line| and |column| functions to get the current line and column", function() { @@ -504,7 +504,7 @@ describe("generated parser", function() { 'start = "a" &{ return v === 42; }' ].join("\n"), options); - expect(parser).toParse("a", ["a", ""]); + expect(parser).toParse("a", ["a", undefined]); }); it("can use functions defined in the initializer", function() { @@ -513,7 +513,7 @@ describe("generated parser", function() { 'start = "a" &{ return f() === 42; }' ].join("\n"), options); - expect(parser).toParse("a", ["a", ""]); + expect(parser).toParse("a", ["a", undefined]); }); it("can use options passed to the parser", function() { @@ -530,7 +530,7 @@ describe("generated parser", function() { it("causes successful match by returning |false|", function() { var parser = PEG.buildParser('start = !{ return false; }', options); - expect(parser).toParse("", ""); + expect(parser).toParse("", undefined); }); it("causes match failure by returning |true|", function() { @@ -545,7 +545,7 @@ describe("generated parser", function() { options ); - expect(parser).toParse("a", ["a", ""]); + expect(parser).toParse("a", ["a", undefined]); }); it("can use the |text| function", function() { @@ -554,7 +554,7 @@ describe("generated parser", function() { options ); - expect(parser).toParse("a", ["a", ""]); + expect(parser).toParse("a", ["a", undefined]); }); it("can use the |offset| function to get the current parse position", function() { @@ -563,7 +563,7 @@ describe("generated parser", function() { options ); - expect(parser).toParse("a", ["a", ""]); + expect(parser).toParse("a", ["a", undefined]); }); it("can use the |line| and |column| functions to get the current line and column", function() { @@ -595,7 +595,7 @@ describe("generated parser", function() { 'start = "a" !{ return v !== 42; }' ].join("\n"), options); - expect(parser).toParse("a", ["a", ""]); + expect(parser).toParse("a", ["a", undefined]); }); it("can use functions defined in the initializer", function() { @@ -604,7 +604,7 @@ describe("generated parser", function() { 'start = "a" !{ return f() !== 42; }' ].join("\n"), options); - expect(parser).toParse("a", ["a", ""]); + expect(parser).toParse("a", ["a", undefined]); }); it("can use options passed to the parser", function() {