From b4bf49443ac0c5728bed55bf516dd89ebf3a0d2c Mon Sep 17 00:00:00 2001 From: David Majda Date: Fri, 21 May 2010 18:54:52 +0200 Subject: [PATCH] AST refactoring 5/6: Make AST classless --- lib/compiler.js | 71 +----------------- lib/metagrammar.js | 63 +++++++++++----- lib/metagrammar.pegjs | 63 +++++++++++----- test/metagrammar-test.js | 151 ++++++++++++++++++++++++++++----------- 4 files changed, 200 insertions(+), 148 deletions(-) diff --git a/lib/compiler.js b/lib/compiler.js index af98494..13dd3e9 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -153,75 +153,6 @@ PEG.Grammar.GrammarError = function(message) { PEG.Grammar.GrammarError.prototype = Error.prototype; -/* ===== PEG.Grammar.* ===== */ - -PEG.Grammar.Rule = function(name, displayName, expression) { - this.type = "rule"; - this.name = name; - this.displayName = displayName; - this.expression = expression; -}; - -PEG.Grammar.Choice = function(alternatives) { - this.type = "choice"; - this.alternatives = alternatives; -}; - -PEG.Grammar.Sequence = function(elements) { - this.type = "sequence"; - this.elements = elements; -}; - -PEG.Grammar.AndPredicate = function(expression) { - this.type = "and_predicate"; - this.expression = expression; -}; - -PEG.Grammar.NotPredicate = function(expression) { - this.type = "not_predicate"; - this.expression = expression; -}; - -PEG.Grammar.Optional = function(expression) { - this.type = "optional"; - this.expression = expression; -}; - -PEG.Grammar.ZeroOrMore = function(expression) { - this.type = "zero_or_more"; - this.expression = expression; -}; - -PEG.Grammar.OneOrMore = function(expression) { - this.type = "one_or_more"; - this.expression = expression; -}; - -PEG.Grammar.Action = function(expression, action) { - this.type = "action"; - this.expression = expression; - this.action = action; -}; - -PEG.Grammar.RuleRef = function(name) { - this.type = "rule_ref"; - this.name = name; -}; - -PEG.Grammar.Literal = function(value) { - this.type = "literal"; - this.value = value; -}; - -PEG.Grammar.Any = function() { - this.type = "any"; -}; - -PEG.Grammar.Class = function(characters) { - this.type = "class"; - this.characters = characters; -}; - /* ===== PEG.Compiler ===== */ PEG.Compiler = { @@ -715,7 +646,7 @@ PEG.Compiler = { var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); - if (node.expression instanceof PEG.Grammar.Sequence) { + if (node.expression.type === "sequence") { var params = PEG.ArrayUtils.map( PEG.ArrayUtils.range(1, node.expression.elements.length + 1), function(n) { return "$" + n; } diff --git a/lib/metagrammar.js b/lib/metagrammar.js index c91845f..ac02aa0 100644 --- a/lib/metagrammar.js +++ b/lib/metagrammar.js @@ -154,7 +154,12 @@ PEG.grammarParser = (function(){ } var result5 = result6 !== null ? (function($1, $2, $3, $4) { - return new PEG.Grammar.Rule($1, $2 !== "" ? $2 : null, $4); + return { + type: "rule", + name: $1, + displayName: $2 !== "" ? $2 : null, + expression: $4 + }; }).apply(null, result6) : null; @@ -247,12 +252,18 @@ PEG.grammarParser = (function(){ } var result14 = result15 !== null ? (function($1, $2) { - return $2.length > 0 - ? new PEG.Grammar.Choice([$1].concat(PEG.ArrayUtils.map( + if ($2.length > 0) { + var alternatives = [$1].concat(PEG.ArrayUtils.map( $2, function(element) { return element[1]; } - ))) - : $1; + )); + return { + type: "choice", + alternatives: alternatives + } + } else { + return $1; + } }).apply(null, result15) : null; @@ -297,10 +308,17 @@ PEG.grammarParser = (function(){ } var result25 = result26 !== null ? (function($1, $2) { - return new PEG.Grammar.Action( - $1.length != 1 ? new PEG.Grammar.Sequence($1) : $1[0], - $2 - ); + var expression = $1.length != 1 + ? { + type: "sequence", + elements: $1 + } + : $1[0]; + return { + type: "action", + expression: expression, + action: $2 + }; }).apply(null, result26) : null; if (result25 !== null) { @@ -313,7 +331,14 @@ PEG.grammarParser = (function(){ var result24 = this._parse_prefixed(context); } var result22 = result23 !== null - ? (function($1) { return $1.length != 1 ? new PEG.Grammar.Sequence($1) : $1[0]; })(result23) + ? (function($1) { + return $1.length != 1 + ? { + type: "sequence", + elements: $1 + } + : $1[0]; + })(result23) : null; if (result22 !== null) { var result21 = result22; @@ -357,7 +382,7 @@ PEG.grammarParser = (function(){ this._pos = savedPos6; } var result36 = result37 !== null - ? (function($1, $2) { return new PEG.Grammar.AndPredicate($2); }).apply(null, result37) + ? (function($1, $2) { return { type: "and_predicate", expression: $2 }; }).apply(null, result37) : null; if (result36 !== null) { var result30 = result36; @@ -377,7 +402,7 @@ PEG.grammarParser = (function(){ this._pos = savedPos5; } var result32 = result33 !== null - ? (function($1, $2) { return new PEG.Grammar.NotPredicate($2); }).apply(null, result33) + ? (function($1, $2) { return { type: "not_predicate", expression: $2 }; }).apply(null, result33) : null; if (result32 !== null) { var result30 = result32; @@ -426,7 +451,7 @@ PEG.grammarParser = (function(){ this._pos = savedPos9; } var result50 = result51 !== null - ? (function($1, $2) { return new PEG.Grammar.Optional($1); }).apply(null, result51) + ? (function($1, $2) { return { type: "optional", expression: $1}; }).apply(null, result51) : null; if (result50 !== null) { var result40 = result50; @@ -446,7 +471,7 @@ PEG.grammarParser = (function(){ this._pos = savedPos8; } var result46 = result47 !== null - ? (function($1, $2) { return new PEG.Grammar.ZeroOrMore($1); }).apply(null, result47) + ? (function($1, $2) { return { type: "zero_or_more", expression: $1}; }).apply(null, result47) : null; if (result46 !== null) { var result40 = result46; @@ -466,7 +491,7 @@ PEG.grammarParser = (function(){ this._pos = savedPos7; } var result42 = result43 !== null - ? (function($1, $2) { return new PEG.Grammar.OneOrMore($1); }).apply(null, result43) + ? (function($1, $2) { return { type: "one_or_more", expression: $1}; }).apply(null, result43) : null; if (result42 !== null) { var result40 = result42; @@ -557,28 +582,28 @@ PEG.grammarParser = (function(){ this._pos = savedPos11; } var result66 = result67 !== null - ? (function($1, $2) { return new PEG.Grammar.RuleRef($1); }).apply(null, result67) + ? (function($1, $2) { return { type: "rule_ref", name: $1 }; }).apply(null, result67) : null; if (result66 !== null) { var result54 = result66; } else { var result65 = this._parse_literal(context); var result64 = result65 !== null - ? (function($1) { return new PEG.Grammar.Literal($1); })(result65) + ? (function($1) { return { type: "literal", value: $1 }; })(result65) : null; if (result64 !== null) { var result54 = result64; } else { var result63 = this._parse_dot(context); var result62 = result63 !== null - ? (function($1) { return new PEG.Grammar.Any(); })(result63) + ? (function($1) { return { type: "any" }; })(result63) : null; if (result62 !== null) { var result54 = result62; } else { var result61 = this._parse_class(context); var result60 = result61 !== null - ? (function($1) { return new PEG.Grammar.Class($1); })(result61) + ? (function($1) { return { type: "class", characters: $1 }; })(result61) : null; if (result60 !== null) { var result54 = result60; diff --git a/lib/metagrammar.pegjs b/lib/metagrammar.pegjs index 56b63e5..6903414 100644 --- a/lib/metagrammar.pegjs +++ b/lib/metagrammar.pegjs @@ -5,45 +5,70 @@ grammar: __ rule+ { } rule: identifier (literal / "") colon expression { - return new PEG.Grammar.Rule($1, $2 !== "" ? $2 : null, $4); + return { + type: "rule", + name: $1, + displayName: $2 !== "" ? $2 : null, + expression: $4 + }; } expression: choice choice: sequence (slash sequence)* { - return $2.length > 0 - ? new PEG.Grammar.Choice([$1].concat(PEG.ArrayUtils.map( + if ($2.length > 0) { + var alternatives = [$1].concat(PEG.ArrayUtils.map( $2, function(element) { return element[1]; } - ))) - : $1; + )); + return { + type: "choice", + alternatives: alternatives + } + } else { + return $1; + } } sequence : prefixed* action { - return new PEG.Grammar.Action( - $1.length != 1 ? new PEG.Grammar.Sequence($1) : $1[0], - $2 - ); + var expression = $1.length != 1 + ? { + type: "sequence", + elements: $1 + } + : $1[0]; + return { + type: "action", + expression: expression, + action: $2 + }; + } + / prefixed* { + return $1.length != 1 + ? { + type: "sequence", + elements: $1 + } + : $1[0]; } - / prefixed* { return $1.length != 1 ? new PEG.Grammar.Sequence($1) : $1[0]; } prefixed - : and suffixed { return new PEG.Grammar.AndPredicate($2); } - / not suffixed { return new PEG.Grammar.NotPredicate($2); } + : and suffixed { return { type: "and_predicate", expression: $2 }; } + / not suffixed { return { type: "not_predicate", expression: $2 }; } / suffixed suffixed - : primary question { return new PEG.Grammar.Optional($1); } - / primary star { return new PEG.Grammar.ZeroOrMore($1); } - / primary plus { return new PEG.Grammar.OneOrMore($1); } + : primary question { return { type: "optional", expression: $1}; } + / primary star { return { type: "zero_or_more", expression: $1}; } + / primary plus { return { type: "one_or_more", expression: $1}; } / primary primary - : identifier !(( literal / "") colon) { return new PEG.Grammar.RuleRef($1); } - / literal { return new PEG.Grammar.Literal($1); } - / dot { return new PEG.Grammar.Any(); } - / class { return new PEG.Grammar.Class($1); } + : identifier !(( literal / "") colon) { return { type: "rule_ref", name: $1 }; } + / literal { return { type: "literal", value: $1 }; } + / dot { return { type: "any" }; } + / class { return { type: "class", characters: $1 }; } / lparen expression rparen { return $2; } /* "Lexical" elements */ diff --git a/test/metagrammar-test.js b/test/metagrammar-test.js index 8fea427..8e1ef34 100644 --- a/test/metagrammar-test.js +++ b/test/metagrammar-test.js @@ -17,53 +17,129 @@ global.grammarParserDoesNotParse = function(input) { module("Grammar Parser"); with (PEG.Grammar) { - var literalAbcd = new Literal("abcd"); - var literalEfgh = new Literal("efgh"); - var literalIjkl = new Literal("ijkl"); + function rule(name, displayName, expression) { + return { + type: "rule", + name: name, + displayName: displayName, + expression: expression + }; + } + + function choice(alternatives) { + return { + type: "choice", + alternatives: alternatives + }; + } + + function sequence(elements) { + return { + type: "sequence", + elements: elements + }; + } + + function nodeWithExpressionConstructor(type) { + return function(expression) { + return { + type: type, + expression: expression + }; + } + } + + var andPredicate = nodeWithExpressionConstructor("and_predicate"); + var notPredicate = nodeWithExpressionConstructor("not_predicate"); + + var optional = nodeWithExpressionConstructor("optional"); + var zeroOrMore = nodeWithExpressionConstructor("zero_or_more"); + var oneOrMore = nodeWithExpressionConstructor("one_or_more"); + + function action(expression, action) { + return { + type: "action", + expression: expression, + action: action + }; + }; + + function ruleRef(name) { + return { + type: "rule_ref", + name: name + }; + } - var optional = new Optional(literalAbcd); + function literal(value) { + return { + type: "literal", + value: value + }; + } + + function any() { + return { type: "any" }; + } + + function klass(characters) { + return { + type: "class", + characters: characters + }; + } - var notAbcd = new NotPredicate(literalAbcd); - var notEfgh = new NotPredicate(literalEfgh); - var notIjkl = new NotPredicate(literalIjkl); + var literalAbcd = literal("abcd"); + var literalEfgh = literal("efgh"); + var literalIjkl = literal("ijkl"); - var sequenceEmpty = new Sequence([]); - var sequenceNots = new Sequence([notAbcd, notEfgh, notIjkl]); - var sequenceLiterals = new Sequence([literalAbcd, literalEfgh, literalIjkl]); + var optionalLiteral = optional(literalAbcd); + + var notAbcd = notPredicate(literalAbcd); + var notEfgh = notPredicate(literalEfgh); + var notIjkl = notPredicate(literalIjkl); + + var sequenceEmpty = sequence([]); + var sequenceNots = sequence([notAbcd, notEfgh, notIjkl]); + var sequenceLiterals = sequence([literalAbcd, literalEfgh, literalIjkl]); + + var choiceLiterals = choice([literalAbcd, literalEfgh, literalIjkl]); function oneRuleGrammar(expression) { - return { start: new PEG.Grammar.Rule("start", null, expression) }; + return { start: rule("start", null, expression) }; } - var simpleGrammar = oneRuleGrammar(new Literal("abcd")); + var simpleGrammar = oneRuleGrammar(literal("abcd")); function identifierGrammar(identifier) { - return oneRuleGrammar(new PEG.Grammar.RuleRef(identifier)); + return oneRuleGrammar(ruleRef(identifier)); } + var literal_ = literal function literalGrammar(literal) { - return oneRuleGrammar(new PEG.Grammar.Literal(literal)); + return oneRuleGrammar(literal_(literal)); } function classGrammar(chars) { - return oneRuleGrammar(new PEG.Grammar.Class(chars)); + return oneRuleGrammar(klass(chars)); } - var anyGrammar = oneRuleGrammar(new Any()); + var anyGrammar = oneRuleGrammar(any()); + var action_ = action; function actionGrammar(action) { - return oneRuleGrammar(new PEG.Grammar.Action(new PEG.Grammar.Literal("a"), action)); + return oneRuleGrammar(action_(literal("a"), action)); } /* Canonical grammar is "a: \"abcd\";\nb: \"efgh\";\nc: \"ijkl\";". */ test("parses grammar", function() { - grammarParserParses('a: "abcd"', { a: new Rule("a", null, literalAbcd) }); + grammarParserParses('a: "abcd"', { a: rule("a", null, literalAbcd) }); grammarParserParses( 'a: "abcd"\nb: "efgh"\nc: "ijkl"', { - a: new Rule("a", null, literalAbcd), - b: new Rule("b", null, literalEfgh), - c: new Rule("c", null, literalIjkl) + a: rule("a", null, literalAbcd), + b: rule("b", null, literalEfgh), + c: rule("c", null, literalIjkl) } ); }); @@ -72,17 +148,12 @@ with (PEG.Grammar) { test("parses rule", function() { grammarParserParses( 'start: "abcd" / "efgh" / "ijkl"', - oneRuleGrammar(new Choice([literalAbcd, literalEfgh, literalIjkl])) + oneRuleGrammar(choiceLiterals) ); grammarParserParses( 'start "start rule": "abcd" / "efgh" / "ijkl"', { - start: - new Rule( - "start", - "start rule", - new Choice([literalAbcd, literalEfgh, literalIjkl]) - ) + start: rule("start", "start rule", choiceLiterals) } ); }); @@ -91,7 +162,7 @@ with (PEG.Grammar) { test("parses expression", function() { grammarParserParses( 'start: "abcd" / "efgh" / "ijkl"', - oneRuleGrammar(new Choice([literalAbcd, literalEfgh, literalIjkl])) + oneRuleGrammar(choiceLiterals) ); }); @@ -103,7 +174,7 @@ with (PEG.Grammar) { ); grammarParserParses( 'start: "abcd" "efgh" "ijkl" / "abcd" "efgh" "ijkl" / "abcd" "efgh" "ijkl"', - oneRuleGrammar(new Choice([ + oneRuleGrammar(choice([ sequenceLiterals, sequenceLiterals, sequenceLiterals @@ -115,15 +186,15 @@ with (PEG.Grammar) { test("parses sequence", function() { grammarParserParses( 'start: { code }', - oneRuleGrammar(new Action(sequenceEmpty, " code ")) + oneRuleGrammar(action(sequenceEmpty, " code ")) ); grammarParserParses( 'start: !"abcd" { code }', - oneRuleGrammar(new Action(notAbcd, " code ")) + oneRuleGrammar(action(notAbcd, " code ")) ); grammarParserParses( 'start: !"abcd" !"efgh" !"ijkl" { code }', - oneRuleGrammar(new Action(sequenceNots, " code ")) + oneRuleGrammar(action(sequenceNots, " code ")) ); grammarParserParses('start: ', oneRuleGrammar(sequenceEmpty)); @@ -136,17 +207,17 @@ with (PEG.Grammar) { /* Canonical prefixed is "!\"abcd\"". */ test("parses prefixed", function() { - grammarParserParses('start: &"abcd"?', oneRuleGrammar(new AndPredicate(optional))); - grammarParserParses('start: !"abcd"?', oneRuleGrammar(new NotPredicate(optional))); - grammarParserParses('start: "abcd"?', oneRuleGrammar(optional)); + grammarParserParses('start: &"abcd"?', oneRuleGrammar(andPredicate(optionalLiteral))); + grammarParserParses('start: !"abcd"?', oneRuleGrammar(notPredicate(optionalLiteral))); + grammarParserParses('start: "abcd"?', oneRuleGrammar(optionalLiteral)); }); /* Canonical suffixed is "\"abcd\"?". */ test("parses suffixed", function() { - grammarParserParses('start: "abcd"?', oneRuleGrammar(optional)); - grammarParserParses('start: "abcd"*', oneRuleGrammar(new ZeroOrMore(literalAbcd))); - grammarParserParses('start: "abcd"+', oneRuleGrammar(new OneOrMore(literalAbcd))); - grammarParserParses('start: "abcd"', literalGrammar("abcd")); + grammarParserParses('start: "abcd"?', oneRuleGrammar(optionalLiteral)); + grammarParserParses('start: "abcd"*', oneRuleGrammar(zeroOrMore(literalAbcd))); + grammarParserParses('start: "abcd"+', oneRuleGrammar(oneOrMore(literalAbcd))); + grammarParserParses('start: "abcd"', literalGrammar("abcd")); }); /* Canonical primary is "\"abcd\"". */