PEG.js grammar: Disallow empty sequences

Empty sequences are useless and they only confused users. Let's disallow
them.
redux
David Majda 10 years ago committed by David Majda
parent 2005345976
commit df154daafb

@ -351,8 +351,6 @@ module.exports = function(ast) {
},
sequence: function(node, context) {
var emptyArrayIndex;
function buildElementsCode(elements, context) {
var processedCount, functionIndex;
@ -402,7 +400,6 @@ module.exports = function(ast) {
}
}
if (node.elements.length > 0) {
failedIndex = addConst('peg$FAILED');
return buildSequence(
@ -413,11 +410,6 @@ module.exports = function(ast) {
action: context.action
})
);
} else {
emptyArrayIndex = addConst('[]');
return [op.PUSH, emptyArrayIndex];
}
},
labeled: function(node, context) {

@ -676,10 +676,14 @@ module.exports = (function() {
s0 = peg$currPos;
s1 = [];
s2 = peg$parselabeled();
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parselabeled();
}
} else {
s1 = peg$c0;
}
if (s1 !== peg$FAILED) {
s2 = peg$parseaction();
if (s2 !== peg$FAILED) {
@ -698,10 +702,14 @@ module.exports = (function() {
s0 = peg$currPos;
s1 = [];
s2 = peg$parselabeled();
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parselabeled();
}
} else {
s1 = peg$c0;
}
if (s1 !== peg$FAILED) {
peg$reportedPos = s0;
s1 = peg$c8(s1);

@ -85,24 +85,25 @@ describe("compiler pass |generateBytecode|", function() {
describe("for action", function() {
describe("without labels", function() {
var grammar = 'start = { code }';
var grammar = 'start = "a" { code }';
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
1, // PUSH_CURR_POS
0, 0, // <expression>
14, 0, 2, 2, 18, 0, 19, 1, // <expression>
11, 6, 0, // IF_NOT_ERROR
20, 1, // * REPORT_SAVED_POS
22, 1, 1, 0, // CALL
22, 2, 1, 0, // CALL
5 // NIP
]));
});
it("defines correct constants", function() {
expect(pass).toChangeAST(
grammar,
constsDetails(['[]', 'function() { code }'])
);
expect(pass).toChangeAST(grammar, constsDetails([
'"a"',
'{ type: "literal", value: "a", description: "\\"a\\"" }',
'function() { code }'
]));
});
});
@ -171,21 +172,6 @@ describe("compiler pass |generateBytecode|", function() {
});
describe("for sequence", function() {
describe("empty", function() {
var grammar = 'start = ';
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
0, 0 // PUSH
]));
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails(['[]']));
});
});
describe("non-empty", function() {
var grammar = 'start = "a" "b" "c"';
it("generates correct bytecode", function() {
@ -223,7 +209,6 @@ describe("compiler pass |generateBytecode|", function() {
]));
});
});
});
describe("for labeled", function() {
it("generates correct bytecode", function() {

@ -393,13 +393,7 @@ describe("generated parser", function() {
});
describe("sequence matching", function() {
it("matches empty sequence correctly", function() {
var parser = PEG.buildParser('start = ', options);
expect(parser).toParse("", []);
});
it("matches non-empty sequence correctly", function() {
it("matches correctly", function() {
var parser = PEG.buildParser('start = "a" "b" "c"', options);
expect(parser).toParse("abc", ["a", "b", "c"]);
@ -845,9 +839,9 @@ describe("generated parser", function() {
describe("expectations reporting", function() {
it("reports expectations correctly with no alternative", function() {
var parser = PEG.buildParser('start = ', options);
var parser = PEG.buildParser('start = "a"', options);
expect(parser).toFailToParse("a", {
expect(parser).toFailToParse("ab", {
expected: [{ type: "end", description: "end of input" }]
});
});
@ -916,10 +910,10 @@ describe("generated parser", function() {
describe("message building", function() {
it("builds message correctly with no alternative", function() {
var parser = PEG.buildParser('start = ', options);
var parser = PEG.buildParser('start = "a"', options);
expect(parser).toFailToParse("a", {
message: 'Expected end of input but "a" found.'
expect(parser).toFailToParse("ab", {
message: 'Expected end of input but "b" found.'
});
});

@ -8,7 +8,6 @@ describe("PEG.js grammar parser", function() {
labeledAbcd = { type: "labeled", label: "a", expression: literalAbcd },
labeledEfgh = { type: "labeled", label: "b", expression: literalEfgh },
labeledIjkl = { type: "labeled", label: "c", expression: literalIjkl },
sequenceEmpty = { type: "sequence", elements: [] },
sequenceOfLiterals = {
type: "sequence",
elements: [literalAbcd, literalEfgh, literalIjkl]
@ -235,13 +234,6 @@ describe("PEG.js grammar parser", function() {
/* Canonical sequence is "\"abcd\" \"efgh\" \"ijkl\"". */
it("parses sequence", function() {
expect('start = { code }').toParseAs(
oneRuleGrammar({
type: "action",
expression: sequenceEmpty,
code: " code "
})
);
expect('start = a:"abcd" { code }').toParseAs(
oneRuleGrammar({ type: "action", expression: labeledAbcd, code: " code " })
);
@ -253,9 +245,6 @@ describe("PEG.js grammar parser", function() {
})
);
expect('start = ').toParseAs(
oneRuleGrammar(sequenceEmpty)
);
expect('start = a:"abcd"').toParseAs(
oneRuleGrammar(labeledAbcd)
);

@ -54,7 +54,7 @@ choice
}
sequence
= elements:labeled* code:action {
= elements:labeled+ code:action {
var expression = elements.length !== 1
? {
type: "sequence",
@ -67,7 +67,7 @@ sequence
code: code
};
}
/ elements:labeled* {
/ elements:labeled+ {
return elements.length !== 1
? {
type: "sequence",

Loading…
Cancel
Save