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.
redux
David Majda 11 years ago
parent 86769a6c5c
commit 1b2279e026

@ -295,13 +295,13 @@ otherwise return `null`.
#### & *expression* #### & *expression*
Try to match the expression. If the match succeeds, just return an empty string Try to match the expression. If the match succeeds, just return `undefined` and
and do not advance the parser position, otherwise consider the match failed. do not advance the parser position, otherwise consider the match failed.
#### ! *expression* #### ! *expression*
Try to match the expression. If the match does not succeed, just return an empty Try to match the expression. If the match does not succeed, just return
string and do not advance the parser position, otherwise consider the match `undefined` and do not advance the parser position, otherwise consider the match
failed. failed.
#### & { *predicate* } #### & { *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 a function. It gets the match results of labeled expressions in preceding
expression as its arguments. It should return some JavaScript value using the expression as its arguments. It should return some JavaScript value using the
`return` statement. If the returned value evaluates to `true` in boolean `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. otherwise consider the match failed.
The code inside the predicate can access all variables and functions defined in 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 a function. It gets the match results of labeled expressions in preceding
expression as its arguments. It should return some JavaScript value using the expression as its arguments. It should return some JavaScript value using the
`return` statement. If the returned value evaluates to `false` in boolean `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. otherwise consider the match failed.
The code inside the predicate can access all variables and functions defined in The code inside the predicate can access all variables and functions defined in

@ -213,8 +213,8 @@ module.exports = function(ast, options) {
} }
function buildSimplePredicate(expression, negative, context) { function buildSimplePredicate(expression, negative, context) {
var emptyStringIndex = addConst('""'), var undefinedIndex = addConst('void 0'),
failedIndex = addConst('peg$FAILED'); failedIndex = addConst('peg$FAILED');
return buildSequence( return buildSequence(
[op.PUSH_CURR_POS], [op.PUSH_CURR_POS],
@ -230,7 +230,7 @@ module.exports = function(ast, options) {
buildSequence( buildSequence(
[op.POP], [op.POP],
[negative ? op.POP : op.POP_CURR_POS], [negative ? op.POP : op.POP_CURR_POS],
[op.PUSH, emptyStringIndex] [op.PUSH, undefinedIndex]
), ),
buildSequence( buildSequence(
[op.POP], [op.POP],
@ -242,9 +242,9 @@ module.exports = function(ast, options) {
} }
function buildSemanticPredicate(code, negative, context) { function buildSemanticPredicate(code, negative, context) {
var functionIndex = addFunctionConst(utils.keys(context.env), code), var functionIndex = addFunctionConst(utils.keys(context.env), code),
emptyStringIndex = addConst('""'), undefinedIndex = addConst('void 0'),
failedIndex = addConst('peg$FAILED'); failedIndex = addConst('peg$FAILED');
return buildSequence( return buildSequence(
[op.REPORT_CURR_POS], [op.REPORT_CURR_POS],
@ -253,11 +253,11 @@ module.exports = function(ast, options) {
[op.IF], [op.IF],
buildSequence( buildSequence(
[op.POP], [op.POP],
[op.PUSH, negative ? failedIndex : emptyStringIndex] [op.PUSH, negative ? failedIndex : undefinedIndex]
), ),
buildSequence( buildSequence(
[op.POP], [op.POP],
[op.PUSH, negative ? emptyStringIndex : failedIndex] [op.PUSH, negative ? undefinedIndex : failedIndex]
) )
) )
); );

@ -197,7 +197,7 @@ module.exports = (function() {
expression: expression expression: expression
}; };
}, },
peg$c18 = "", peg$c18 = void 0,
peg$c19 = function(name) { peg$c19 = function(name) {
return { return {
type: "rule_ref", type: "rule_ref",

@ -272,7 +272,7 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'""', 'void 0',
'peg$FAILED', 'peg$FAILED',
'"a"', '"a"',
'{ type: "literal", value: "a", description: "\\"a\\"" }' '{ type: "literal", value: "a", description: "\\"a\\"" }'
@ -301,7 +301,7 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'""', 'void 0',
'peg$FAILED', 'peg$FAILED',
'"a"', '"a"',
'{ type: "literal", value: "a", description: "\\"a\\"" }' '{ type: "literal", value: "a", description: "\\"a\\"" }'
@ -328,7 +328,7 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST( expect(pass).toChangeAST(
grammar, grammar,
constsDetails(['function() { code }', '""', 'peg$FAILED']) constsDetails(['function() { code }', 'void 0', 'peg$FAILED'])
); );
}); });
}); });
@ -380,7 +380,7 @@ describe("compiler pass |generateBytecode|", function() {
'"c"', '"c"',
'{ type: "literal", value: "c", description: "\\"c\\"" }', '{ type: "literal", value: "c", description: "\\"c\\"" }',
'function(a, b, c) { code }', 'function(a, b, c) { code }',
'""' 'void 0'
])); ]));
}); });
}); });
@ -405,7 +405,7 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST( expect(pass).toChangeAST(
grammar, grammar,
constsDetails(['function() { code }', '""', 'peg$FAILED']) constsDetails(['function() { code }', 'void 0', 'peg$FAILED'])
); );
}); });
}); });
@ -457,7 +457,7 @@ describe("compiler pass |generateBytecode|", function() {
'"c"', '"c"',
'{ type: "literal", value: "c", description: "\\"c\\"" }', '{ type: "literal", value: "c", description: "\\"c\\"" }',
'function(a, b, c) { code }', 'function(a, b, c) { code }',
'""' 'void 0'
])); ]));
}); });
}); });

@ -397,14 +397,14 @@ describe("generated parser", function() {
it("matches correctly", function() { it("matches correctly", function() {
var parser = PEG.buildParser('start = &"a" "a"', options); var parser = PEG.buildParser('start = &"a" "a"', options);
expect(parser).toParse("a", ["", "a"]); expect(parser).toParse("a", [undefined, "a"]);
expect(parser).toFailToParse("b"); expect(parser).toFailToParse("b");
}); });
it("does not advance position on success", function() { it("does not advance position on success", function() {
var parser = PEG.buildParser('start = &"a" "a"', options); 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() { it("does not influence expected strings on failure", function() {
@ -418,7 +418,7 @@ describe("generated parser", function() {
it("matches correctly", function() { it("matches correctly", function() {
var parser = PEG.buildParser('start = !"a" "b"', options); var parser = PEG.buildParser('start = !"a" "b"', options);
expect(parser).toParse("b", ["", "b"]); expect(parser).toParse("b", [undefined, "b"]);
expect(parser).toFailToParse("a"); expect(parser).toFailToParse("a");
}); });
@ -439,7 +439,7 @@ describe("generated parser", function() {
it("causes successful match by returning |true|", function() { it("causes successful match by returning |true|", function() {
var parser = PEG.buildParser('start = &{ return true; }', options); var parser = PEG.buildParser('start = &{ return true; }', options);
expect(parser).toParse("", ""); expect(parser).toParse("", undefined);
}); });
it("causes match failure by returning |false|", function() { it("causes match failure by returning |false|", function() {
@ -454,7 +454,7 @@ describe("generated parser", function() {
options options
); );
expect(parser).toParse("a", ["a", ""]); expect(parser).toParse("a", ["a", undefined]);
}); });
it("can use the |text| function", function() { it("can use the |text| function", function() {
@ -463,7 +463,7 @@ describe("generated parser", function() {
options 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() { it("can use the |offset| function to get the current parse position", function() {
@ -472,7 +472,7 @@ describe("generated parser", function() {
options 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() { 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; }' 'start = "a" &{ return v === 42; }'
].join("\n"), options); ].join("\n"), options);
expect(parser).toParse("a", ["a", ""]); expect(parser).toParse("a", ["a", undefined]);
}); });
it("can use functions defined in the initializer", function() { it("can use functions defined in the initializer", function() {
@ -513,7 +513,7 @@ describe("generated parser", function() {
'start = "a" &{ return f() === 42; }' 'start = "a" &{ return f() === 42; }'
].join("\n"), options); ].join("\n"), options);
expect(parser).toParse("a", ["a", ""]); expect(parser).toParse("a", ["a", undefined]);
}); });
it("can use options passed to the parser", function() { 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() { it("causes successful match by returning |false|", function() {
var parser = PEG.buildParser('start = !{ return false; }', options); var parser = PEG.buildParser('start = !{ return false; }', options);
expect(parser).toParse("", ""); expect(parser).toParse("", undefined);
}); });
it("causes match failure by returning |true|", function() { it("causes match failure by returning |true|", function() {
@ -545,7 +545,7 @@ describe("generated parser", function() {
options options
); );
expect(parser).toParse("a", ["a", ""]); expect(parser).toParse("a", ["a", undefined]);
}); });
it("can use the |text| function", function() { it("can use the |text| function", function() {
@ -554,7 +554,7 @@ describe("generated parser", function() {
options 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() { it("can use the |offset| function to get the current parse position", function() {
@ -563,7 +563,7 @@ describe("generated parser", function() {
options 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() { 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; }' 'start = "a" !{ return v !== 42; }'
].join("\n"), options); ].join("\n"), options);
expect(parser).toParse("a", ["a", ""]); expect(parser).toParse("a", ["a", undefined]);
}); });
it("can use functions defined in the initializer", function() { it("can use functions defined in the initializer", function() {
@ -604,7 +604,7 @@ describe("generated parser", function() {
'start = "a" !{ return f() !== 42; }' 'start = "a" !{ return f() !== 42; }'
].join("\n"), options); ].join("\n"), options);
expect(parser).toParse("a", ["a", ""]); expect(parser).toParse("a", ["a", undefined]);
}); });
it("can use options passed to the parser", function() { it("can use options passed to the parser", function() {

Loading…
Cancel
Save