Rewrite handling of optional parameters

Instead of testing arguments.length to see whether an optional parameter
was passed to a function, compare its value to "undefined". This
approach has two advantages:

  * It is in line with handling of default parameters in ES6.

  * Optional parameters are actually spelled out in the parameter
    list.

There is also one important disadvantage, namely that it's impossible to
pass "undefined" as an optional parameter value. This required a small
change in two tests.

Additional notes:

  * Default parameter values are set in assignments immediately
    after the function header. This reflects the fact that these
    assignments really belong to the parameter list (which is where they
    are in ES6).

  * Parameter values are checked against "void 0" in places where
    "undefined" can potentially be redefiend.
redux
David Majda 8 years ago
parent f866712c90
commit da2378d887

@ -38,10 +38,12 @@ var compiler = {
* during the generation and some may protrude to the generated parser and * during the generation and some may protrude to the generated parser and
* cause its malfunction. * cause its malfunction.
*/ */
compile: function(ast, passes) { compile: function(ast, passes, options) {
var options = arguments.length > 2 ? objects.clone(arguments[2]) : {}, options = options !== void 0 ? options : {};
stage;
var stage;
options = objects.clone(options);
objects.defaults(options, { objects.defaults(options, {
allowedStartRules: [ast.rules[0].name], allowedStartRules: [ast.rules[0].name],
cache: false, cache: false,

@ -437,13 +437,12 @@ function generateJS(ast, options) {
return code; return code;
}, },
pop: function() { pop: function(n) {
var n, values; var values;
if (arguments.length === 0) { if (n === void 0) {
return s(this.sp--); return s(this.sp--);
} else { } else {
n = arguments[0];
values = arrays.map(arrays.range(this.sp - n + 1, this.sp + 1), s); values = arrays.map(arrays.range(this.sp - n + 1, this.sp + 1), s);
this.sp -= n; this.sp -= n;
@ -851,9 +850,10 @@ function generateJS(ast, options) {
} }
parts.push([ parts.push([
' function peg$parse(input) {', ' function peg$parse(input, options) {',
' var options = arguments.length > 1 ? arguments[1] : {},', ' options = options !== void 0 ? options : {};',
' parser = this,', '',
' var parser = this,',
'', '',
' peg$FAILED = {},', ' peg$FAILED = {},',
'' ''

@ -28,9 +28,10 @@ module.exports = (function() {
peg$subclass(peg$SyntaxError, Error); peg$subclass(peg$SyntaxError, Error);
function peg$parse(input) { function peg$parse(input, options) {
var options = arguments.length > 1 ? arguments[1] : {}, options = options !== void 0 ? options : {};
parser = this,
var parser = this,
peg$FAILED = {}, peg$FAILED = {},

@ -22,7 +22,9 @@ var PEG = {
* errors are detected during the generation and some may protrude to the * errors are detected during the generation and some may protrude to the
* generated parser and cause its malfunction. * generated parser and cause its malfunction.
*/ */
buildParser: function(grammar) { buildParser: function(grammar, options) {
options = options !== void 0 ? options : {};
function convertPasses(passes) { function convertPasses(passes) {
var converted = {}, stage; var converted = {}, stage;
@ -35,8 +37,9 @@ var PEG = {
return converted; return converted;
} }
var options = arguments.length > 1 ? objects.clone(arguments[1]) : {}, options = objects.clone(options);
plugins = "plugins" in options ? options.plugins : [],
var plugins = "plugins" in options ? options.plugins : [],
config = { config = {
parser: this.parser, parser: this.parser,
passes: convertPasses(this.compiler.passes) passes: convertPasses(this.compiler.passes)

@ -34,14 +34,15 @@ describe("generated parser behavior", function() {
beforeEach(function() { beforeEach(function() {
this.addMatchers({ this.addMatchers({
toParse: function(input, expected) { toParse: function(input, expected, options) {
var options = arguments.length > 2 ? arguments[2] : {}, options = options !== undefined ? options : {};
result;
var result;
try { try {
result = this.actual.parse(input, options); result = this.actual.parse(input, options);
if (arguments.length > 1) { if (expected !== undefined) {
this.message = function() { this.message = function() {
return "Expected " + jasmine.pp(input) + " " return "Expected " + jasmine.pp(input) + " "
+ "with options " + jasmine.pp(options) + " " + "with options " + jasmine.pp(options) + " "
@ -58,7 +59,7 @@ describe("generated parser behavior", function() {
this.message = function() { this.message = function() {
return "Expected " + jasmine.pp(input) + " " return "Expected " + jasmine.pp(input) + " "
+ "with options " + jasmine.pp(options) + " " + "with options " + jasmine.pp(options) + " "
+ "to parse" + (arguments.length > 1 ? " as " + jasmine.pp(expected) : "") + ", " + "to parse" + (expected !== undefined ? " as " + jasmine.pp(expected) : "") + ", "
+ "but it failed to parse with message " + "but it failed to parse with message "
+ jasmine.pp(e.message) + "."; + jasmine.pp(e.message) + ".";
}; };
@ -67,9 +68,10 @@ describe("generated parser behavior", function() {
} }
}, },
toFailToParse: function(input, details) { toFailToParse: function(input, details, options) {
var options = arguments.length > 2 ? arguments[2] : {}, options = options !== undefined ? options : {};
result;
var result;
try { try {
result = this.actual.parse(input, options); result = this.actual.parse(input, options);
@ -417,9 +419,14 @@ describe("generated parser behavior", function() {
describe("positive semantic predicate", function() { describe("positive semantic predicate", function() {
describe("when the code returns a truthy value", function() { describe("when the code returns a truthy value", function() {
it("returns |undefined|", function() { it("returns |undefined|", function() {
var parser = PEG.buildParser('start = &{ return true; }', options); /*
* The |""| is needed so that the parser doesn't return just
* |undefined| which we can't compare against in |toParse| due to the
* way optional parameters work.
*/
var parser = PEG.buildParser('start = &{ return true; } ""', options);
expect(parser).toParse("", undefined); expect(parser).toParse("", [undefined, ""]);
}); });
}); });
@ -617,9 +624,14 @@ describe("generated parser behavior", function() {
describe("negative semantic predicate", function() { describe("negative semantic predicate", function() {
describe("when the code returns a falsey value", function() { describe("when the code returns a falsey value", function() {
it("returns |undefined|", function() { it("returns |undefined|", function() {
var parser = PEG.buildParser('start = !{ return false; }', options); /*
* The |""| is needed so that the parser doesn't return just
* |undefined| which we can't compare against in |toParse| due to the
* way optional parameters work.
*/
var parser = PEG.buildParser('start = !{ return false; } ""', options);
expect(parser).toParse("", undefined); expect(parser).toParse("", [undefined, ""]);
}); });
}); });

@ -4,7 +4,9 @@
beforeEach(function() { beforeEach(function() {
this.addMatchers({ this.addMatchers({
toChangeAST: function(grammar, details) { toChangeAST: function(grammar, details, options) {
options = options !== undefined ? options : {};
function matchDetails(value, details) { function matchDetails(value, details) {
function isArray(value) { function isArray(value) {
return Object.prototype.toString.apply(value) === "[object Array]"; return Object.prototype.toString.apply(value) === "[object Array]";
@ -42,8 +44,7 @@ beforeEach(function() {
} }
} }
var options = arguments.length > 2 ? arguments[2] : {}, var ast = PEG.parser.parse(grammar);
ast = PEG.parser.parse(grammar);
this.actual(ast, options); this.actual(ast, options);

Loading…
Cancel
Save