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.
This commit is contained in:
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…
Reference in a new issue