Improve indentation of variable declarations

Before this commit, continuation lines of multi-line values in variable
declaration initializers were aligned with the variable name:

  let foo = {
        a: 5,
        b: 6
      };

This was highly irregular, maintenance intensive, and made declarations
look different from assignments.

This commit changes the indentation to be more regular and similar to
assignments:

  let foo = {
    a: 5,
    b: 6
  };
redux
David Majda 8 years ago
parent b5eb1a0ec4
commit 7ca229a432

@ -62,9 +62,9 @@ $("#run").click(() => {
let runCount = parseInt($("#run-count").val(), 10);
let options = {
cache: $("#cache").is(":checked"),
optimize: $("#optimize").val()
};
cache: $("#cache").is(":checked"),
optimize: $("#optimize").val()
};
if (isNaN(runCount) || runCount <= 0) {
alert("Number of runs must be a positive integer.");

@ -10,22 +10,22 @@ let Runner = {
// Queue
let Q = {
functions: [],
add: function(f) {
this.functions.push(f);
},
run: function() {
if (this.functions.length > 0) {
this.functions.shift()();
// We can't use |arguments.callee| here because |this| would get
// messed-up in that case.
setTimeout(() => { Q.run(); }, 0);
}
}
};
functions: [],
add: function(f) {
this.functions.push(f);
},
run: function() {
if (this.functions.length > 0) {
this.functions.shift()();
// We can't use |arguments.callee| here because |this| would get
// messed-up in that case.
setTimeout(() => { Q.run(); }, 0);
}
}
};
// The benchmark itself is factored out into several functions (some of them
// generated), which are enqueued and run one by one using |setTimeout|. We

@ -348,10 +348,10 @@ function generateBytecode(ast) {
let emitCall = node.expression.type !== "sequence"
|| node.expression.elements.length === 0;
let expressionCode = generate(node.expression, {
sp: context.sp + (emitCall ? 1 : 0),
env: env,
action: node
});
sp: context.sp + (emitCall ? 1 : 0),
env: env,
action: node
});
let functionIndex = addFunctionConst(Object.keys(env), node.code);
return emitCall
@ -482,10 +482,10 @@ function generateBytecode(ast) {
zero_or_more: function(node, context) {
let expressionCode = generate(node.expression, {
sp: context.sp + 1,
env: cloneEnv(context.env),
action: null
});
sp: context.sp + 1,
env: cloneEnv(context.env),
action: null
});
return buildSequence(
[op.PUSH_EMPTY_ARRAY],
@ -497,10 +497,10 @@ function generateBytecode(ast) {
one_or_more: function(node, context) {
let expressionCode = generate(node.expression, {
sp: context.sp + 1,
env: cloneEnv(context.env),
action: null
});
sp: context.sp + 1,
env: cloneEnv(context.env),
action: null
});
return buildSequence(
[op.PUSH_EMPTY_ARRAY],
@ -569,30 +569,30 @@ function generateBytecode(ast) {
"class": function(node) {
let regexp = "/^["
+ (node.inverted ? "^" : "")
+ node.parts.map(part =>
Array.isArray(part)
? js.regexpClassEscape(part[0])
+ "-"
+ js.regexpClassEscape(part[1])
: js.regexpClassEscape(part)
).join("")
+ "]/" + (node.ignoreCase ? "i" : "");
+ (node.inverted ? "^" : "")
+ node.parts.map(part =>
Array.isArray(part)
? js.regexpClassEscape(part[0])
+ "-"
+ js.regexpClassEscape(part[1])
: js.regexpClassEscape(part)
).join("")
+ "]/" + (node.ignoreCase ? "i" : "");
let parts = "["
+ node.parts.map(part =>
Array.isArray(part)
? "[\"" + js.stringEscape(part[0]) + "\", \"" + js.stringEscape(part[1]) + "\"]"
: "\"" + js.stringEscape(part) + "\""
).join(", ")
+ "]";
+ node.parts.map(part =>
Array.isArray(part)
? "[\"" + js.stringEscape(part[0]) + "\", \"" + js.stringEscape(part[1]) + "\"]"
: "\"" + js.stringEscape(part) + "\""
).join(", ")
+ "]";
let regexpIndex = addConst(regexp);
let expectedIndex = addConst(
"peg$classExpectation("
+ parts + ", "
+ node.inverted + ", "
+ node.ignoreCase
+ ")"
);
"peg$classExpectation("
+ parts + ", "
+ node.inverted + ", "
+ node.ignoreCase
+ ")"
);
return buildCondition(
[op.MATCH_REGEXP, regexpIndex],

@ -411,41 +411,41 @@ function generateJS(ast, options) {
function s(i) { return "s" + i; } // |stack[i]| of the abstract machine
let stack = {
sp: -1,
maxSp: -1,
sp: -1,
maxSp: -1,
push: function(exprCode) {
let code = s(++this.sp) + " = " + exprCode + ";";
push: function(exprCode) {
let code = s(++this.sp) + " = " + exprCode + ";";
if (this.sp > this.maxSp) { this.maxSp = this.sp; }
if (this.sp > this.maxSp) { this.maxSp = this.sp; }
return code;
},
return code;
},
pop: function(n) {
if (n === undefined) {
return s(this.sp--);
} else {
let values = Array(n);
pop: function(n) {
if (n === undefined) {
return s(this.sp--);
} else {
let values = Array(n);
for (var i = 0; i < n; i++) {
values[i] = s(this.sp - n + 1 + i);
}
for (var i = 0; i < n; i++) {
values[i] = s(this.sp - n + 1 + i);
}
this.sp -= n;
this.sp -= n;
return values;
}
},
return values;
}
},
top: function() {
return s(this.sp);
},
top: function() {
return s(this.sp);
},
index: function(i) {
return s(this.sp - i);
}
};
index: function(i) {
return s(this.sp - i);
}
};
function compile(bc) {
let ip = 0;
@ -512,10 +512,10 @@ function generateJS(ast, options) {
let paramsLength = bc[ip + baseLength - 1];
let value = c(bc[ip + 1]) + "("
+ bc.slice(ip + baseLength, ip + baseLength + paramsLength).map(
p => stack.index(p)
).join(", ")
+ ")";
+ bc.slice(ip + baseLength, ip + baseLength + paramsLength).map(
p => stack.index(p)
).join(", ")
+ ")";
stack.pop(bc[ip + 2]);
parts.push(stack.push(value));
ip += baseLength + paramsLength;
@ -767,32 +767,32 @@ function generateJS(ast, options) {
"",
"peg$SyntaxError.buildMessage = function(expected, found) {",
" var DESCRIBE_EXPECTATION_FNS = {",
" literal: function(expectation) {",
" return \"\\\"\" + literalEscape(expectation.text) + \"\\\"\";",
" },",
"",
" \"class\": function(expectation) {",
" var escapedParts = expectation.parts.map(function(part) {",
" return Array.isArray(part)",
" ? classEscape(part[0]) + \"-\" + classEscape(part[1])",
" : classEscape(part);",
" });",
"",
" return \"[\" + (expectation.inverted ? \"^\" : \"\") + escapedParts + \"]\";",
" },",
"",
" any: function(expectation) {",
" return \"any character\";",
" },",
"",
" end: function(expectation) {",
" return \"end of input\";",
" },",
"",
" other: function(expectation) {",
" return expectation.description;",
" }",
" };",
" literal: function(expectation) {",
" return \"\\\"\" + literalEscape(expectation.text) + \"\\\"\";",
" },",
"",
" \"class\": function(expectation) {",
" var escapedParts = expectation.parts.map(function(part) {",
" return Array.isArray(part)",
" ? classEscape(part[0]) + \"-\" + classEscape(part[1])",
" : classEscape(part);",
" });",
"",
" return \"[\" + (expectation.inverted ? \"^\" : \"\") + escapedParts + \"]\";",
" },",
"",
" any: function(expectation) {",
" return \"any character\";",
" },",
"",
" end: function(expectation) {",
" return \"end of input\";",
" },",
"",
" other: function(expectation) {",
" return expectation.description;",
" }",
" };",
"",
" function hex(ch) {",
" return ch.charCodeAt(0).toString(16).toUpperCase();",
@ -935,10 +935,10 @@ function generateJS(ast, options) {
if (options.optimize === "size") {
let startRuleIndices = "{ "
+ options.allowedStartRules.map(
r => r + ": " + asts.indexOfRule(ast, r)
).join(", ")
+ " }";
+ options.allowedStartRules.map(
r => r + ": " + asts.indexOfRule(ast, r)
).join(", ")
+ " }";
let startRuleIndex = asts.indexOfRule(ast, options.allowedStartRules[0]);
parts.push([
@ -947,10 +947,10 @@ function generateJS(ast, options) {
].join("\n"));
} else {
let startRuleFunctions = "{ "
+ options.allowedStartRules.map(
r => r + ": peg$parse" + r
).join(", ")
+ " }";
+ options.allowedStartRules.map(
r => r + ": peg$parse" + r
).join(", ")
+ " }";
let startRuleFunction = "peg$parse" + options.allowedStartRules[0];
parts.push([
@ -984,10 +984,10 @@ function generateJS(ast, options) {
if (options.trace) {
if (options.optimize === "size") {
let ruleNames = "["
+ ast.rules.map(
r => "\"" + js.stringEscape(r.name) + "\""
).join(", ")
+ "]";
+ ast.rules.map(
r => "\"" + js.stringEscape(r.name) + "\""
).join(", ")
+ "]";
parts.push([
" var peg$ruleNames = " + ruleNames + ";",
@ -1272,10 +1272,10 @@ function generateJS(ast, options) {
let dependencyVars = Object.keys(options.dependencies);
let dependencyIds = dependencyIds.map(v => options.dependencies[v]);
let dependencies = "["
+ dependencyIds.map(
id => "\"" + js.stringEscape(id) + "\""
).join(", ")
+ "]";
+ dependencyIds.map(
id => "\"" + js.stringEscape(id) + "\""
).join(", ")
+ "]";
let params = dependencyVars.join(", ");
return [
@ -1310,13 +1310,13 @@ function generateJS(ast, options) {
let dependencyVars = Object.keys(options.dependencies);
let dependencyIds = dependencyIds.map(v => options.dependencies[v]);
let dependencies = "["
+ dependencyIds.map(
id => "\"" + js.stringEscape(id) + "\""
).join(", ")
+ "]";
+ dependencyIds.map(
id => "\"" + js.stringEscape(id) + "\""
).join(", ")
+ "]";
let requires = dependencyIds.map(
id => "require(\"" + js.stringEscape(id) + "\")"
).join(", ");
id => "require(\"" + js.stringEscape(id) + "\")"
).join(", ");
let params = dependencyVars.join(", ");
parts.push([

@ -26,39 +26,39 @@ let visitor = {
}
const DEFAULT_FUNCTIONS = {
grammar: function(node) {
let extraArgs = Array.prototype.slice.call(arguments, 1);
grammar: function(node) {
let extraArgs = Array.prototype.slice.call(arguments, 1);
if (node.initializer) {
visit.apply(null, [node.initializer].concat(extraArgs));
}
if (node.initializer) {
visit.apply(null, [node.initializer].concat(extraArgs));
}
node.rules.forEach(rule => {
visit.apply(null, [rule].concat(extraArgs));
});
},
node.rules.forEach(rule => {
visit.apply(null, [rule].concat(extraArgs));
});
},
initializer: visitNop,
rule: visitExpression,
named: visitExpression,
choice: visitChildren("alternatives"),
action: visitExpression,
sequence: visitChildren("elements"),
labeled: visitExpression,
text: visitExpression,
simple_and: visitExpression,
simple_not: visitExpression,
optional: visitExpression,
zero_or_more: visitExpression,
one_or_more: visitExpression,
group: visitExpression,
semantic_and: visitNop,
semantic_not: visitNop,
rule_ref: visitNop,
literal: visitNop,
"class": visitNop,
any: visitNop
};
initializer: visitNop,
rule: visitExpression,
named: visitExpression,
choice: visitChildren("alternatives"),
action: visitExpression,
sequence: visitChildren("elements"),
labeled: visitExpression,
text: visitExpression,
simple_and: visitExpression,
simple_not: visitExpression,
optional: visitExpression,
zero_or_more: visitExpression,
one_or_more: visitExpression,
group: visitExpression,
semantic_and: visitNop,
semantic_not: visitNop,
rule_ref: visitNop,
literal: visitNop,
"class": visitNop,
any: visitNop
};
Object.keys(DEFAULT_FUNCTIONS).forEach(type => {
if (!functions.hasOwnProperty(type)) {

@ -28,32 +28,32 @@ peg$subclass(peg$SyntaxError, Error);
peg$SyntaxError.buildMessage = function(expected, found) {
var DESCRIBE_EXPECTATION_FNS = {
literal: function(expectation) {
return "\"" + literalEscape(expectation.text) + "\"";
},
"class": function(expectation) {
var escapedParts = expectation.parts.map(function(part) {
return Array.isArray(part)
? classEscape(part[0]) + "-" + classEscape(part[1])
: classEscape(part);
});
return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";
},
any: function(expectation) {
return "any character";
},
end: function(expectation) {
return "end of input";
},
other: function(expectation) {
return expectation.description;
}
};
literal: function(expectation) {
return "\"" + literalEscape(expectation.text) + "\"";
},
"class": function(expectation) {
var escapedParts = expectation.parts.map(function(part) {
return Array.isArray(part)
? classEscape(part[0]) + "-" + classEscape(part[1])
: classEscape(part);
});
return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";
},
any: function(expectation) {
return "any character";
},
end: function(expectation) {
return "end of input";
},
other: function(expectation) {
return expectation.description;
}
};
function hex(ch) {
return ch.charCodeAt(0).toString(16).toUpperCase();

@ -33,9 +33,9 @@ let peg = {
let plugins = "plugins" in options ? options.plugins : [];
let config = {
parser: peg.parser,
passes: convertPasses(peg.compiler.passes)
};
parser: peg.parser,
passes: convertPasses(peg.compiler.passes)
};
plugins.forEach(p => { p.use(config, options); });

@ -21,10 +21,10 @@ describe("generated parser API", function() {
describe("start rule", function() {
let parser = peg.generate([
"a = 'x' { return 'a'; }",
"b = 'x' { return 'b'; }",
"c = 'x' { return 'c'; }"
].join("\n"), { allowedStartRules: ["b", "c"] });
"a = 'x' { return 'a'; }",
"b = 'x' { return 'b'; }",
"c = 'x' { return 'c'; }"
].join("\n"), { allowedStartRules: ["b", "c"] });
describe("when |startRule| is not set", function() {
it("starts parsing from the first allowed rule", function() {
@ -48,10 +48,10 @@ describe("generated parser API", function() {
describe("tracing", function() {
let parser = peg.generate([
"start = a / b",
"a = 'a'",
"b = 'b'"
].join("\n"), { trace: true });
"start = a / b",
"a = 'a'",
"b = 'b'"
].join("\n"), { trace: true });
describe("default tracer", function() {
it("traces using console.log (if console is defined)", function() {

@ -21,10 +21,10 @@ describe("PEG.js API", function() {
describe("allowed start rules", function() {
let grammar = [
"a = 'x'",
"b = 'x'",
"c = 'x'"
].join("\n");
"a = 'x'",
"b = 'x'",
"c = 'x'"
].join("\n");
// The |allowedStartRules| option is implemented separately for each
// optimization mode, so we need to test it in both.
@ -82,10 +82,10 @@ describe("PEG.js API", function() {
describe("intermediate results caching", function() {
let grammar = [
"{ var n = 0; }",
"start = (a 'b') / (a 'c') { return n; }",
"a = 'a' { n++; }"
].join("\n");
"{ var n = 0; }",
"start = (a 'b') / (a 'c') { return n; }",
"a = 'a' { n++; }"
].join("\n");
describe("when |cache| is not set", function() {
it("generated parser doesn't cache intermediate parse results", function() {

@ -40,10 +40,10 @@ describe("plugin API", function() {
it("is called for each plugin", function() {
let pluginsUsed = [false, false, false];
let plugins = [
{ use: function() { pluginsUsed[0] = true; } },
{ use: function() { pluginsUsed[1] = true; } },
{ use: function() { pluginsUsed[2] = true; } }
];
{ use: function() { pluginsUsed[0] = true; } },
{ use: function() { pluginsUsed[1] = true; } },
{ use: function() { pluginsUsed[2] = true; } }
];
peg.generate(grammar, { plugins: plugins });
@ -52,40 +52,40 @@ describe("plugin API", function() {
it("receives configuration", function() {
let plugin = {
use: function(config) {
expect(config).toBeObject();
use: function(config) {
expect(config).toBeObject();
expect(config.parser).toBeObject();
expect(config.parser.parse("start = 'a'")).toBeObject();
expect(config.parser).toBeObject();
expect(config.parser.parse("start = 'a'")).toBeObject();
expect(config.passes).toBeObject();
expect(config.passes).toBeObject();
expect(config.passes.check).toBeArray();
config.passes.check.forEach(pass => {
expect(pass).toBeFunction();
});
expect(config.passes.check).toBeArray();
config.passes.check.forEach(pass => {
expect(pass).toBeFunction();
});
expect(config.passes.transform).toBeArray();
config.passes.transform.forEach(pass => {
expect(pass).toBeFunction();
});
expect(config.passes.transform).toBeArray();
config.passes.transform.forEach(pass => {
expect(pass).toBeFunction();
});
expect(config.passes.generate).toBeArray();
config.passes.generate.forEach(pass => {
expect(pass).toBeFunction();
});
}
};
expect(config.passes.generate).toBeArray();
config.passes.generate.forEach(pass => {
expect(pass).toBeFunction();
});
}
};
peg.generate(grammar, { plugins: [plugin] });
});
it("receives options", function() {
let plugin = {
use: function(config, options) {
expect(options).toEqual(generateOptions);
}
};
use: function(config, options) {
expect(options).toEqual(generateOptions);
}
};
let generateOptions = { plugins: [plugin], foo: 42 };
peg.generate(grammar, generateOptions);
@ -93,25 +93,25 @@ describe("plugin API", function() {
it("can replace parser", function() {
let plugin = {
use: function(config) {
let parser = peg.generate([
"start = .* {",
" return {",
" type: 'grammar',",
" rules: [",
" {",
" type: 'rule',",
" name: 'start',",
" expression: { type: 'literal', value: text(), ignoreCase: false }",
" }",
" ]",
" };",
"}"
].join("\n"));
config.parser = parser;
}
};
use: function(config) {
let parser = peg.generate([
"start = .* {",
" return {",
" type: 'grammar',",
" rules: [",
" {",
" type: 'rule',",
" name: 'start',",
" expression: { type: 'literal', value: text(), ignoreCase: false }",
" }",
" ]",
" };",
"}"
].join("\n"));
config.parser = parser;
}
};
let parser = peg.generate("a", { plugins: [plugin] });
expect(parser.parse("a")).toBe("a");
@ -119,14 +119,14 @@ describe("plugin API", function() {
it("can change compiler passes", function() {
let plugin = {
use: function(config) {
let pass = ast => {
ast.code = "({ parse: function() { return 42; } })";
};
config.passes.generate = [pass];
}
use: function(config) {
let pass = ast => {
ast.code = "({ parse: function() { return 42; } })";
};
config.passes.generate = [pass];
}
};
let parser = peg.generate(grammar, { plugins: [plugin] });
expect(parser.parse("a")).toBe(42);
@ -134,19 +134,19 @@ describe("plugin API", function() {
it("can change options", function() {
let grammar = [
"a = 'x'",
"b = 'x'",
"c = 'x'"
].join("\n");
"a = 'x'",
"b = 'x'",
"c = 'x'"
].join("\n");
let plugin = {
use: function(config, options) {
options.allowedStartRules = ["b", "c"];
}
};
use: function(config, options) {
options.allowedStartRules = ["b", "c"];
}
};
let parser = peg.generate(grammar, {
allowedStartRules: ["a"],
plugins: [plugin]
});
allowedStartRules: ["a"],
plugins: [plugin]
});
expect(() => { parser.parse("x", { startRule: "a" }); }).toThrow();
expect(parser.parse("x", { startRule: "b" })).toBe("x");

@ -18,15 +18,15 @@ describe("generated parser behavior", function() {
}
let optionsVariants = [
{ cache: false, optimize: "speed", trace: false },
{ cache: false, optimize: "speed", trace: true },
{ cache: false, optimize: "size", trace: false },
{ cache: false, optimize: "size", trace: true },
{ cache: true, optimize: "speed", trace: false },
{ cache: true, optimize: "speed", trace: true },
{ cache: true, optimize: "size", trace: false },
{ cache: true, optimize: "size", trace: true }
];
{ cache: false, optimize: "speed", trace: false },
{ cache: false, optimize: "speed", trace: true },
{ cache: false, optimize: "size", trace: false },
{ cache: false, optimize: "size", trace: true },
{ cache: true, optimize: "speed", trace: false },
{ cache: true, optimize: "speed", trace: true },
{ cache: true, optimize: "size", trace: false },
{ cache: true, optimize: "size", trace: true }
];
optionsVariants.forEach(variant => {
describe(
@ -131,9 +131,9 @@ describe("generated parser behavior", function() {
describe("initializer", function() {
it("executes the code before parsing starts", function() {
let parser = peg.generate([
"{ var result = 42; }",
"start = 'a' { return result; }"
].join("\n"), options);
"{ var result = 42; }",
"start = 'a' { return result; }"
].join("\n"), options);
expect(parser).toParse("a", 42);
});
@ -141,9 +141,9 @@ describe("generated parser behavior", function() {
describe("available variables and functions", function() {
it("|options| contains options", function() {
let parser = peg.generate([
"{ var result = options; }",
"start = 'a' { return result; }"
].join("\n"), options);
"{ var result = options; }",
"start = 'a' { return result; }"
].join("\n"), options);
expect(parser).toParse("a", { a: 42 }, { a: 42 });
});
@ -154,20 +154,20 @@ describe("generated parser behavior", function() {
if (options.cache) {
it("caches rule match results", function() {
let parser = peg.generate([
"{ var n = 0; }",
"start = (a 'b') / (a 'c') { return n; }",
"a = 'a' { n++; }"
].join("\n"), options);
"{ var n = 0; }",
"start = (a 'b') / (a 'c') { return n; }",
"a = 'a' { n++; }"
].join("\n"), options);
expect(parser).toParse("ac", 1);
});
} else {
it("doesn't cache rule match results", function() {
let parser = peg.generate([
"{ var n = 0; }",
"start = (a 'b') / (a 'c') { return n; }",
"a = 'a' { n++; }"
].join("\n"), options);
"{ var n = 0; }",
"start = (a 'b') / (a 'c') { return n; }",
"a = 'a' { n++; }"
].join("\n"), options);
expect(parser).toParse("ac", 2);
});
@ -387,9 +387,9 @@ describe("generated parser behavior", function() {
describe("when referenced rule's expression matches", function() {
it("returns its result", function() {
let parser = peg.generate([
"start = a",
"a = 'a'"
].join("\n"), options);
"start = a",
"a = 'a'"
].join("\n"), options);
expect(parser).toParse("a", "a");
});
@ -398,9 +398,9 @@ describe("generated parser behavior", function() {
describe("when referenced rule's expression doesn't match", function() {
it("reports match failure", function() {
let parser = peg.generate([
"start = a",
"a = 'a'"
].join("\n"), options);
"start = a",
"a = 'a'"
].join("\n"), options);
expect(parser).toFailToParse("b");
});
@ -431,78 +431,78 @@ describe("generated parser behavior", function() {
describe("in containing sequence", function() {
it("can access variables defined by preceding labeled elements", function() {
let parser = peg.generate(
"start = a:'a' &{ return a === 'a'; }",
options
);
"start = a:'a' &{ return a === 'a'; }",
options
);
expect(parser).toParse("a");
});
it("cannot access variable defined by labeled predicate element", function() {
let parser = peg.generate(
"start = 'a' b:&{ return b === undefined; } 'c'",
options
);
"start = 'a' b:&{ return b === undefined; } 'c'",
options
);
expect(parser).toFailToParse("ac");
});
it("cannot access variables defined by following labeled elements", function() {
let parser = peg.generate(
"start = &{ return a === 'a'; } a:'a'",
options
);
"start = &{ return a === 'a'; } a:'a'",
options
);
expect(parser).toFailToParse("a");
});
it("cannot access variables defined by subexpressions", function() {
let testcases = [
{
grammar: "start = (a:'a') &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')? &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')* &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')+ &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = $(a:'a') &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = &(a:'a') 'a' &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = !(a:'a') 'b' &{ return a === 'a'; }",
input: "b"
},
{
grammar: "start = b:(a:'a') &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = ('a' b:'b' 'c') &{ return b === 'b'; }",
input: "abc"
},
{
grammar: "start = (a:'a' { return a; }) &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = ('a' / b:'b' / 'c') &{ return b === 'b'; }",
input: "b"
}
];
{
grammar: "start = (a:'a') &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')? &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')* &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')+ &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = $(a:'a') &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = &(a:'a') 'a' &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = !(a:'a') 'b' &{ return a === 'a'; }",
input: "b"
},
{
grammar: "start = b:(a:'a') &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = ('a' b:'b' 'c') &{ return b === 'b'; }",
input: "abc"
},
{
grammar: "start = (a:'a' { return a; }) &{ return a === 'a'; }",
input: "a"
},
{
grammar: "start = ('a' / b:'b' / 'c') &{ return b === 'b'; }",
input: "b"
}
];
testcases.forEach(testcase => {
let parser = peg.generate(testcase.grammar, options);
@ -514,27 +514,27 @@ describe("generated parser behavior", function() {
describe("in outer sequence", function() {
it("can access variables defined by preceding labeled elements", function() {
let parser = peg.generate(
"start = a:'a' ('b' &{ return a === 'a'; })",
options
);
"start = a:'a' ('b' &{ return a === 'a'; })",
options
);
expect(parser).toParse("ab");
});
it("cannot access variable defined by labeled predicate element", function() {
let parser = peg.generate(
"start = 'a' b:('b' &{ return b === undefined; }) 'c'",
options
);
"start = 'a' b:('b' &{ return b === undefined; }) 'c'",
options
);
expect(parser).toFailToParse("abc");
});
it("cannot access variables defined by following labeled elements", function() {
let parser = peg.generate(
"start = ('a' &{ return b === 'b'; }) b:'b'",
options
);
"start = ('a' &{ return b === 'b'; }) b:'b'",
options
);
expect(parser).toFailToParse("ab");
});
@ -544,18 +544,18 @@ describe("generated parser behavior", function() {
describe("initializer variables & functions", function() {
it("can access variables defined in the initializer", function() {
let parser = peg.generate([
"{ var v = 42 }",
"start = &{ return v === 42; }"
].join("\n"), options);
"{ var v = 42 }",
"start = &{ return v === 42; }"
].join("\n"), options);
expect(parser).toParse("");
});
it("can access functions defined in the initializer", function() {
let parser = peg.generate([
"{ function f() { return 42; } }",
"start = &{ return f() === 42; }"
].join("\n"), options);
"{ function f() { return 42; } }",
"start = &{ return f() === 42; }"
].join("\n"), options);
expect(parser).toParse("");
});
@ -564,23 +564,23 @@ describe("generated parser behavior", function() {
describe("available variables & functions", function() {
it("|options| contains options", function() {
let parser = peg.generate([
"{ var result; }",
"start = &{ result = options; return true; } { return result; }"
].join("\n"), options);
"{ var result; }",
"start = &{ result = options; return true; } { return result; }"
].join("\n"), options);
expect(parser).toParse("", { a: 42 }, { a: 42 });
});
it("|location| returns current location info", function() {
let parser = peg.generate([
"{ var result; }",
"start = line (nl+ line)* { return result; }",
"line = thing (' '+ thing)*",
"thing = digit / mark",
"digit = [0-9]",
"mark = &{ result = location(); return true; } 'x'",
"nl = '\\r'? '\\n'"
].join("\n"), options);
"{ var result; }",
"start = line (nl+ line)* { return result; }",
"line = thing (' '+ thing)*",
"thing = digit / mark",
"digit = [0-9]",
"mark = &{ result = location(); return true; } 'x'",
"nl = '\\r'? '\\n'"
].join("\n"), options);
expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", {
start: { offset: 13, line: 7, column: 5 },
@ -624,78 +624,78 @@ describe("generated parser behavior", function() {
describe("in containing sequence", function() {
it("can access variables defined by preceding labeled elements", function() {
let parser = peg.generate(
"start = a:'a' !{ return a !== 'a'; }",
options
);
"start = a:'a' !{ return a !== 'a'; }",
options
);
expect(parser).toParse("a");
});
it("cannot access variable defined by labeled predicate element", function() {
let parser = peg.generate(
"start = 'a' b:!{ return b !== undefined; } 'c'",
options
);
"start = 'a' b:!{ return b !== undefined; } 'c'",
options
);
expect(parser).toFailToParse("ac");
});
it("cannot access variables defined by following labeled elements", function() {
let parser = peg.generate(
"start = !{ return a !== 'a'; } a:'a'",
options
);
"start = !{ return a !== 'a'; } a:'a'",
options
);
expect(parser).toFailToParse("a");
});
it("cannot access variables defined by subexpressions", function() {
let testcases = [
{
grammar: "start = (a:'a') !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')? !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')* !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')+ !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = $(a:'a') !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = &(a:'a') 'a' !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = !(a:'a') 'b' !{ return a !== 'a'; }",
input: "b"
},
{
grammar: "start = b:(a:'a') !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = ('a' b:'b' 'c') !{ return b !== 'b'; }",
input: "abc"
},
{
grammar: "start = (a:'a' { return a; }) !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = ('a' / b:'b' / 'c') !{ return b !== 'b'; }",
input: "b"
}
];
{
grammar: "start = (a:'a') !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')? !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')* !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = (a:'a')+ !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = $(a:'a') !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = &(a:'a') 'a' !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = !(a:'a') 'b' !{ return a !== 'a'; }",
input: "b"
},
{
grammar: "start = b:(a:'a') !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = ('a' b:'b' 'c') !{ return b !== 'b'; }",
input: "abc"
},
{
grammar: "start = (a:'a' { return a; }) !{ return a !== 'a'; }",
input: "a"
},
{
grammar: "start = ('a' / b:'b' / 'c') !{ return b !== 'b'; }",
input: "b"
}
];
testcases.forEach(testcase => {
let parser = peg.generate(testcase.grammar, options);
@ -707,27 +707,27 @@ describe("generated parser behavior", function() {
describe("in outer sequence", function() {
it("can access variables defined by preceding labeled elements", function() {
let parser = peg.generate(
"start = a:'a' ('b' !{ return a !== 'a'; })",
options
);
"start = a:'a' ('b' !{ return a !== 'a'; })",
options
);
expect(parser).toParse("ab");
});
it("cannot access variable defined by labeled predicate element", function() {
let parser = peg.generate(
"start = 'a' b:('b' !{ return b !== undefined; }) 'c'",
options
);
"start = 'a' b:('b' !{ return b !== undefined; }) 'c'",
options
);
expect(parser).toFailToParse("abc");
});
it("cannot access variables defined by following labeled elements", function() {
let parser = peg.generate(
"start = ('a' !{ return b !== 'b'; }) b:'b'",
options
);
"start = ('a' !{ return b !== 'b'; }) b:'b'",
options
);
expect(parser).toFailToParse("ab");
});
@ -737,18 +737,18 @@ describe("generated parser behavior", function() {
describe("initializer variables & functions", function() {
it("can access variables defined in the initializer", function() {
let parser = peg.generate([
"{ var v = 42 }",
"start = !{ return v !== 42; }"
].join("\n"), options);
"{ var v = 42 }",
"start = !{ return v !== 42; }"
].join("\n"), options);
expect(parser).toParse("");
});
it("can access functions defined in the initializer", function() {
let parser = peg.generate([
"{ function f() { return 42; } }",
"start = !{ return f() !== 42; }"
].join("\n"), options);
"{ function f() { return 42; } }",
"start = !{ return f() !== 42; }"
].join("\n"), options);
expect(parser).toParse("");
});
@ -757,23 +757,23 @@ describe("generated parser behavior", function() {
describe("available variables & functions", function() {
it("|options| contains options", function() {
let parser = peg.generate([
"{ var result; }",
"start = !{ result = options; return false; } { return result; }"
].join("\n"), options);
"{ var result; }",
"start = !{ result = options; return false; } { return result; }"
].join("\n"), options);
expect(parser).toParse("", { a: 42 }, { a: 42 });
});
it("|location| returns current location info", function() {
let parser = peg.generate([
"{ var result; }",
"start = line (nl+ line)* { return result; }",
"line = thing (' '+ thing)*",
"thing = digit / mark",
"digit = [0-9]",
"mark = !{ result = location(); return false; } 'x'",
"nl = '\\r'? '\\n'"
].join("\n"), options);
"{ var result; }",
"start = line (nl+ line)* { return result; }",
"line = thing (' '+ thing)*",
"thing = digit / mark",
"digit = [0-9]",
"mark = !{ result = location(); return false; } 'x'",
"nl = '\\r'? '\\n'"
].join("\n"), options);
expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", {
start: { offset: 13, line: 7, column: 5 },
@ -1010,60 +1010,60 @@ describe("generated parser behavior", function() {
it("can access variables defined by labeled sequence elements", function() {
let parser = peg.generate(
"start = a:'a' b:'b' c:'c' { return [a, b, c]; }",
options
);
"start = a:'a' b:'b' c:'c' { return [a, b, c]; }",
options
);
expect(parser).toParse("abc", ["a", "b", "c"]);
});
it("cannot access variables defined by subexpressions", function() {
let testcases = [
{
grammar: "start = (a:'a') { return a; }",
input: "a"
},
{
grammar: "start = (a:'a')? { return a; }",
input: "a"
},
{
grammar: "start = (a:'a')* { return a; }",
input: "a"
},
{
grammar: "start = (a:'a')+ { return a; }",
input: "a"
},
{
grammar: "start = $(a:'a') { return a; }",
input: "a"
},
{
grammar: "start = &(a:'a') 'a' { return a; }",
input: "a"
},
{
grammar: "start = !(a:'a') 'b' { return a; }",
input: "b"
},
{
grammar: "start = b:(a:'a') { return a; }",
input: "a"
},
{
grammar: "start = ('a' b:'b' 'c') { return b; }",
input: "abc"
},
{
grammar: "start = (a:'a' { return a; }) { return a; }",
input: "a"
},
{