Make labels behave like block-scoped variables

Action and predicate code can now see variables defined in expressions
"above" them.

Based on a pull request by Bryon Vandiver (@asterick):

  https://github.com/pegjs/pegjs/pull/180

Fixes #316.
redux
David Majda 9 years ago
parent 73795a65cc
commit fb5f6c6ee9

@ -231,7 +231,7 @@ function generateBytecode(ast) {
[op.SILENT_FAILS_ON],
generate(expression, {
sp: context.sp + 1,
env: { },
env: objects.clone(context.env),
action: null
}),
[op.SILENT_FAILS_OFF],
@ -317,7 +317,7 @@ function generateBytecode(ast) {
return buildSequence(
generate(alternatives[0], {
sp: context.sp,
env: { },
env: objects.clone(context.env),
action: null
}),
alternatives.length > 1
@ -337,7 +337,7 @@ function generateBytecode(ast) {
},
action: function(node, context) {
var env = { },
var env = objects.clone(context.env),
emitCall = node.expression.type !== "sequence"
|| node.expression.elements.length === 0,
expressionCode = generate(node.expression, {
@ -425,11 +425,13 @@ function generateBytecode(ast) {
},
labeled: function(node, context) {
var env = objects.clone(context.env);
context.env[node.label] = context.sp + 1;
return generate(node.expression, {
sp: context.sp,
env: { },
env: env,
action: null
});
},
@ -439,7 +441,7 @@ function generateBytecode(ast) {
[op.PUSH_CURR_POS],
generate(node.expression, {
sp: context.sp + 1,
env: { },
env: objects.clone(context.env),
action: null
}),
buildCondition(
@ -462,7 +464,7 @@ function generateBytecode(ast) {
return buildSequence(
generate(node.expression, {
sp: context.sp,
env: { },
env: objects.clone(context.env),
action: null
}),
buildCondition(
@ -476,7 +478,7 @@ function generateBytecode(ast) {
zero_or_more: function(node, context) {
var expressionCode = generate(node.expression, {
sp: context.sp + 1,
env: { },
env: objects.clone(context.env),
action: null
});
@ -491,7 +493,7 @@ function generateBytecode(ast) {
one_or_more: function(node, context) {
var expressionCode = generate(node.expression, {
sp: context.sp + 1,
env: { },
env: objects.clone(context.env),
action: null
});

@ -440,6 +440,105 @@ describe("generated parser behavior", function() {
expect(parser).toParse("abc");
});
it("can access label variables from preceding labeled elements in an outside sequence (group)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" &{ return a === "a" && b === "b" && c === "c"; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["d", undefined]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (optional)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" &{ return a === "a" && b === "b" && c === "c"; })?',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["d", undefined]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (zero or more)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" &{ return a === "a" && b === "b" && c === "c"; })*',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", [["d", undefined]]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (one or more)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" &{ return a === "a" && b === "b" && c === "c"; })+',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", [["d", undefined]]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (text)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" $("d" &{ return a === "a" && b === "b" && c === "c"; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", "d"]);
});
it("can access label variables from preceding labeled elements in an outside sequence (positive simple predicate)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" &("d" &{ return a === "a" && b === "b" && c === "c"; }) "d"',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", undefined, "d"]);
});
it("can access label variables from preceding labeled elements in an outside sequence (negative simple predicate)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" !("d" &{ return a === "a" && b === "b" && c === "c"; }) "e"',
options
);
expect(parser).toParse("abce", ["a", "b", "c", undefined, "e"]);
});
it("can access label variables from preceding labeled elements in an outside sequence (label)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" d:("d" &{ return a === "a" && b === "b" && c === "c"; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["d", undefined]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (sequence)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" "e" "f" &{ return a === "a" && b === "b" && c === "c"; })',
options
);
expect(parser).toParse("abcdef", ["a", "b", "c", ["d", "e", "f", undefined]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (action)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" (d:("d" &{ return a === "a" && b === "b" && c === "c"; }) { return d; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["d", undefined]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (choice)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" / "e" / "f" &{ return a === "a" && b === "b" && c === "c"; })',
options
);
expect(parser).toParse("abcf", ["a", "b", "c", ["f", undefined]]);
});
});
describe("initializer variables & functions", function() {
@ -532,6 +631,96 @@ describe("generated parser behavior", function() {
expect(parser).toParse("abc");
});
it("can access label variables from preceding labeled elements in an outside sequence (optional)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" !{ return a !== "a" || b !== "b" || c !== "c"; })?',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["d", undefined]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (zero or more)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" !{ return a !== "a" || b !== "b" || c !== "c"; })*',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", [["d", undefined]]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (one or more)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" !{ return a !== "a" || b !== "b" || c !== "c"; })+',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", [["d", undefined]]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (text)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" $("d" !{ return a !== "a" || b !== "b" || c !== "c"; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", "d"]);
});
it("can access label variables from preceding labeled elements in an outside sequence (positive simple predicate)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" &("d" !{ return a !== "a" || b !== "b" || c !== "c"; }) "d"',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", undefined, "d"]);
});
it("can access label variables from preceding labeled elements in an outside sequence (negative simple predicate)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" !("d" !{ return a !== "a" || b !== "b" || c !== "c"; }) "e"',
options
);
expect(parser).toParse("abce", ["a", "b", "c", undefined, "e"]);
});
it("can access label variables from preceding labeled elements in an outside sequence (label)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" d:("d" !{ return a !== "a" || b !== "b" || c !== "c"; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["d", undefined]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (sequence)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" "e" "f" !{ return a !== "a" || b !== "b" || c !== "c"; })',
options
);
expect(parser).toParse("abcdef", ["a", "b", "c", ["d", "e", "f", undefined]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (action)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" (d:("d" !{ return a !== "a" || b !== "b" || c !== "c"; }) { return d; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["d", undefined]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (choice)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" / "e" / "f" !{ return a !== "a" || b !== "b" || c !== "c"; })',
options
);
expect(parser).toParse("abcf", ["a", "b", "c", ["f", undefined]]);
});
});
describe("initializer variables & functions", function() {
@ -820,6 +1009,105 @@ describe("generated parser behavior", function() {
expect(parser).toParse("abc", ["a", "b", "c"]);
});
it("can access label variables from preceding labeled elements in an outside sequence (group)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" { return [a, b, c]; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["a", "b", "c"]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (optional)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" { return [a, b, c]; })?',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["a", "b", "c"]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (zero or more)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" { return [a, b, c]; })*',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", [["a", "b", "c"]]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (one or more)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" { return [a, b, c]; })+',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", [["a", "b", "c"]]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (text)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" $("d" { return [a, b, c]; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", "d"]);
});
it("can access label variables from preceding labeled elements in an outside sequence (positive simple predicate)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" &("d" { return [a, b, c]; }) "d"',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", undefined, "d"]);
});
it("can access label variables from preceding labeled elements in an outside sequence (negative simple predicate)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" !("d" { return [a, b, c]; }) "e"',
options
);
expect(parser).toParse("abce", ["a", "b", "c", undefined, "e"]);
});
it("can access label variables from preceding labeled elements in an outside sequence (label)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" d:("d" { return [a, b, c]; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["a", "b", "c"]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (sequence)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" "e" ("f" { return [a, b, c]; }))',
options
);
expect(parser).toParse("abcdef", ["a", "b", "c", ["d", "e", ["a", "b", "c"]]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (action)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" (d:("d" { return [a, b, c]; }) { return d; })',
options
);
expect(parser).toParse("abcd", ["a", "b", "c", ["a", "b", "c"]]);
});
it("can access label variables from preceding labeled elements in an outside sequence (choice)", function() {
var parser = PEG.buildParser(
'start = a:"a" b:"b" c:"c" ("d" / "e" / "f" { return [a, b, c]; })',
options
);
expect(parser).toParse("abcf", ["a", "b", "c", ["a", "b", "c"]]);
});
});
describe("initializer variables & functions", function() {

Loading…
Cancel
Save