diff --git a/lib/parser.js b/lib/parser.js index a641ece..a86badc 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -70,15 +70,10 @@ module.exports = (function() { ? { type: "choice", alternatives: buildList(first, rest, 3) } : first; }, - peg$c11 = function(first, rest, code) { - var expression = rest.length > 0 - ? { type: "sequence", elements: buildList(first, rest, 1) } - : first; - return { - type: "action", - expression: expression, - code: code - }; + peg$c11 = function(expression, code) { + return code !== null + ? { type: "action", expression: expression, code: code[1] } + : expression; }, peg$c12 = function(first, rest) { return rest.length > 0 @@ -748,7 +743,7 @@ module.exports = (function() { var s0, s1, s2, s3, s4, s5, s6, s7; s0 = peg$currPos; - s1 = peg$parseSequenceExpression(); + s1 = peg$parseActionExpression(); if (s1 !== peg$FAILED) { s2 = []; s3 = peg$currPos; @@ -764,7 +759,7 @@ module.exports = (function() { if (s5 !== peg$FAILED) { s6 = peg$parse__(); if (s6 !== peg$FAILED) { - s7 = peg$parseSequenceExpression(); + s7 = peg$parseActionExpression(); if (s7 !== peg$FAILED) { s4 = [s4, s5, s6, s7]; s3 = s4; @@ -799,7 +794,7 @@ module.exports = (function() { if (s5 !== peg$FAILED) { s6 = peg$parse__(); if (s6 !== peg$FAILED) { - s7 = peg$parseSequenceExpression(); + s7 = peg$parseActionExpression(); if (s7 !== peg$FAILED) { s4 = [s4, s5, s6, s7]; s3 = s4; @@ -836,6 +831,46 @@ module.exports = (function() { return s0; } + function peg$parseActionExpression() { + var s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = peg$parseSequenceExpression(); + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + s3 = peg$parse__(); + if (s3 !== peg$FAILED) { + s4 = peg$parseCodeBlock(); + if (s4 !== peg$FAILED) { + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$c0; + } + } else { + peg$currPos = s2; + s2 = peg$c0; + } + if (s2 === peg$FAILED) { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c11(s1, s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c0; + } + } else { + peg$currPos = s0; + s0 = peg$c0; + } + + return s0; + } + function peg$parseSequenceExpression() { var s0, s1, s2, s3, s4, s5; @@ -877,21 +912,9 @@ module.exports = (function() { } } if (s2 !== peg$FAILED) { - s3 = peg$parse__(); - if (s3 !== peg$FAILED) { - s4 = peg$parseCodeBlock(); - if (s4 !== peg$FAILED) { - peg$reportedPos = s0; - s1 = peg$c11(s1, s2, s4); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$c0; - } - } else { - peg$currPos = s0; - s0 = peg$c0; - } + peg$reportedPos = s0; + s1 = peg$c12(s1, s2); + s0 = s1; } else { peg$currPos = s0; s0 = peg$c0; @@ -900,57 +923,6 @@ module.exports = (function() { peg$currPos = s0; s0 = peg$c0; } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = peg$parseLabeledExpression(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$currPos; - s4 = peg$parse__(); - if (s4 !== peg$FAILED) { - s5 = peg$parseLabeledExpression(); - if (s5 !== peg$FAILED) { - s4 = [s4, s5]; - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$c0; - } - } else { - peg$currPos = s3; - s3 = peg$c0; - } - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$currPos; - s4 = peg$parse__(); - if (s4 !== peg$FAILED) { - s5 = peg$parseLabeledExpression(); - if (s5 !== peg$FAILED) { - s4 = [s4, s5]; - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$c0; - } - } else { - peg$currPos = s3; - s3 = peg$c0; - } - } - if (s2 !== peg$FAILED) { - peg$reportedPos = s0; - s1 = peg$c12(s1, s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$c0; - } - } else { - peg$currPos = s0; - s0 = peg$c0; - } - } return s0; } diff --git a/spec/parser.spec.js b/spec/parser.spec.js index b0b1bde..0561845 100644 --- a/spec/parser.spec.js +++ b/spec/parser.spec.js @@ -24,6 +24,8 @@ describe("PEG.js grammar parser", function() { type: "sequence", elements: [labeledAbcd, labeledEfgh, labeledIjkl] }, + actionOnAbcd = { type: "action", expression: literalAbcd, code: " code " }, + actionOnSequence = { type: "action", expression: sequenceOfLiterals, code: " code " }, choiceOfLiterals = { type: "choice", alternatives: [literalAbcd, literalEfgh, literalIjkl] @@ -234,52 +236,41 @@ describe("PEG.js grammar parser", function() { /* Canonical ChoiceExpression is "\"abcd\" / \"efgh\" / \"ijkl\"". */ it("parses ChoiceExpression", function() { - expect('start = "abcd" "efgh" "ijkl"').toParseAs( - oneRuleGrammar(sequenceOfLiterals) + expect('start = "abcd" { code }').toParseAs( + oneRuleGrammar(actionOnAbcd) ); expect( - 'start = "abcd" "efgh" "ijkl" / "abcd" "efgh" "ijkl" / "abcd" "efgh" "ijkl"' + 'start = "abcd" { code } / "abcd" { code } / "abcd" { code }' ).toParseAs(oneRuleGrammar({ type: "choice", - alternatives: [sequenceOfLiterals, sequenceOfLiterals, sequenceOfLiterals] + alternatives: [actionOnAbcd, actionOnAbcd, actionOnAbcd] })); expect( - 'start = "abcd" "efgh" "ijkl"\n/ "abcd" "efgh" "ijkl"\n/ "abcd" "efgh" "ijkl"' + 'start = "abcd" { code }\n/ "abcd" { code }\n/ "abcd" { code }' ).toParseAs(oneRuleGrammar({ type: "choice", - alternatives: [sequenceOfLiterals, sequenceOfLiterals, sequenceOfLiterals] + alternatives: [actionOnAbcd, actionOnAbcd, actionOnAbcd] })); expect( - 'start = "abcd" "efgh" "ijkl" /\n"abcd" "efgh" "ijkl" /\n"abcd" "efgh" "ijkl"' + 'start = "abcd" { code } /\n"abcd" { code } /\n"abcd" { code }' ).toParseAs(oneRuleGrammar({ type: "choice", - alternatives: [sequenceOfLiterals, sequenceOfLiterals, sequenceOfLiterals] + alternatives: [actionOnAbcd, actionOnAbcd, actionOnAbcd] })); }); - /* Canonical SequenceExpression is "\"abcd\" \"efgh\" \"ijkl\"". */ - it("parses SequenceExpression", function() { - expect('start = a:"abcd" { code }').toParseAs( - oneRuleGrammar({ type: "action", expression: labeledAbcd, code: " code " }) - ); - expect('start = a:"abcd"\n{ code }').toParseAs( - oneRuleGrammar({ type: "action", expression: labeledAbcd, code: " code " }) - ); - expect('start = a:"abcd" b:"efgh" c:"ijkl" { code }').toParseAs( - oneRuleGrammar({ - type: "action", - expression: sequenceOfLabeleds, - code: " code " - }) + /* Canonical ActionExpression is "\"abcd\" { code }". */ + it("parses ActionExpression", function() { + expect('start = "abcd" "efgh" "ijkl" { code }').toParseAs( + oneRuleGrammar(actionOnSequence) ); - expect('start = a:"abcd"\nb:"efgh"\nc:"ijkl" { code }').toParseAs( - oneRuleGrammar({ - type: "action", - expression: sequenceOfLabeleds, - code: " code " - }) + expect('start = "abcd" "efgh" "ijkl"\n{ code }').toParseAs( + oneRuleGrammar(actionOnSequence) ); + }); + /* Canonical SequenceExpression is "\"abcd\" \"efgh\" \"ijkl\"". */ + it("parses SequenceExpression", function() { expect('start = a:"abcd"').toParseAs( oneRuleGrammar(labeledAbcd) ); diff --git a/src/parser.pegjs b/src/parser.pegjs index cffb07a..0ae36fc 100644 --- a/src/parser.pegjs +++ b/src/parser.pegjs @@ -70,24 +70,21 @@ Expression = ChoiceExpression ChoiceExpression - = first:SequenceExpression rest:(__ "/" __ SequenceExpression)* { + = first:ActionExpression rest:(__ "/" __ ActionExpression)* { return rest.length > 0 ? { type: "choice", alternatives: buildList(first, rest, 3) } : first; } -SequenceExpression - = first:LabeledExpression rest:(__ LabeledExpression)* __ code:CodeBlock { - var expression = rest.length > 0 - ? { type: "sequence", elements: buildList(first, rest, 1) } - : first; - return { - type: "action", - expression: expression, - code: code - }; +ActionExpression + = expression:SequenceExpression code:(__ CodeBlock)? { + return code !== null + ? { type: "action", expression: expression, code: code[1] } + : expression; } - / first:LabeledExpression rest:(__ LabeledExpression)* { + +SequenceExpression + = first:LabeledExpression rest:(__ LabeledExpression)* { return rest.length > 0 ? { type: "sequence", elements: buildList(first, rest, 1) } : first;