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*
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

@ -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]
)
)
);

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

@ -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'
]));
});
});

@ -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() {

Loading…
Cancel
Save