diff --git a/lib/compiler.js b/lib/compiler.js index 0f8675b..b761fb1 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -200,11 +200,9 @@ PEG.Grammar.Rule.prototype = { getName: function() { return this._name; } }; -PEG.Grammar.Literal = function(value) { this._value = value; }; - -PEG.Grammar.Class = function(characters) { this._characters = characters; }; - -PEG.Grammar.Any = function() {}; +PEG.Grammar.Choice = function(alternatives) { + this._alternatives = alternatives; +}; PEG.Grammar.Sequence = function(elements) { this._elements = elements; }; @@ -212,16 +210,6 @@ PEG.Grammar.Sequence.prototype = { getElements: function() { return this._elements; } }; -PEG.Grammar.Choice = function(alternatives) { - this._alternatives = alternatives; -}; - -PEG.Grammar.Optional = function(expression) { this._expression = expression; }; - -PEG.Grammar.ZeroOrMore = function(expression) { this._expression = expression; }; - -PEG.Grammar.OneOrMore = function(expression) { this._expression = expression; }; - PEG.Grammar.AndPredicate = function(expression) { this._expression = expression; }; @@ -230,13 +218,25 @@ PEG.Grammar.NotPredicate = function(expression) { this._expression = expression; }; -PEG.Grammar.RuleRef = function(name) { this._name = name; }; +PEG.Grammar.Optional = function(expression) { this._expression = expression; }; + +PEG.Grammar.ZeroOrMore = function(expression) { this._expression = expression; }; + +PEG.Grammar.OneOrMore = function(expression) { this._expression = expression; }; PEG.Grammar.Action = function(expression, action) { this._expression = expression; this._action = action; }; +PEG.Grammar.RuleRef = function(name) { this._name = name; }; + +PEG.Grammar.Literal = function(value) { this._value = value; }; + +PEG.Grammar.Any = function() {}; + +PEG.Grammar.Class = function(characters) { this._characters = characters; }; + /* ===== Referenced Rule Existence Checks ===== */ PEG.Grammar.extendNodes("checkReferencedRulesExist", { @@ -245,9 +245,12 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", { this._expression.checkReferencedRulesExist(grammar); }, - Literal: nop, - Class: nop, - Any: nop, + Choice: + function(grammar) { + PEG.ArrayUtils.each(this._alternatives, function(alternative) { + alternative.checkReferencedRulesExist(grammar); + }); + }, Sequence: function(grammar) { @@ -256,12 +259,11 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", { }); }, - Choice: - function(grammar) { - PEG.ArrayUtils.each(this._alternatives, function(alternative) { - alternative.checkReferencedRulesExist(grammar); - }); - }, + AndPredicate: + function(grammar) { this._expression.checkReferencedRulesExist(grammar); }, + + NotPredicate: + function(grammar) { this._expression.checkReferencedRulesExist(grammar); }, Optional: function(grammar) { this._expression.checkReferencedRulesExist(grammar); }, @@ -272,10 +274,7 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", { OneOrMore: function(grammar) { this._expression.checkReferencedRulesExist(grammar); }, - AndPredicate: - function(grammar) { this._expression.checkReferencedRulesExist(grammar); }, - - NotPredicate: + Action: function(grammar) { this._expression.checkReferencedRulesExist(grammar); }, RuleRef: @@ -287,8 +286,9 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", { } }, - Action: - function(grammar) { this._expression.checkReferencedRulesExist(grammar); } + Literal: nop, + Any: nop, + Class: nop }); @@ -300,9 +300,12 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", { this._expression.checkNoLeftRecursion(grammar, appliedRules.concat(this._name)); }, - Literal: nop, - Class: nop, - Any: nop, + Choice: + function(grammar, appliedRules) { + PEG.ArrayUtils.each(this._alternatives, function(alternative) { + alternative.checkNoLeftRecursion(grammar, appliedRules); + }); + }, Sequence: function(grammar, appliedRules) { @@ -311,34 +314,32 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", { } }, - Choice: + AndPredicate: function(grammar, appliedRules) { - PEG.ArrayUtils.each(this._alternatives, function(alternative) { - alternative.checkNoLeftRecursion(grammar, appliedRules); - }); + this._expression.checkNoLeftRecursion(grammar, appliedRules); }, - Optional: + NotPredicate: function(grammar, appliedRules) { this._expression.checkNoLeftRecursion(grammar, appliedRules); }, - ZeroOrMore: + Optional: function(grammar, appliedRules) { this._expression.checkNoLeftRecursion(grammar, appliedRules); }, - OneOrMore: + ZeroOrMore: function(grammar, appliedRules) { this._expression.checkNoLeftRecursion(grammar, appliedRules); }, - AndPredicate: + OneOrMore: function(grammar, appliedRules) { this._expression.checkNoLeftRecursion(grammar, appliedRules); }, - NotPredicate: + Action: function(grammar, appliedRules) { this._expression.checkNoLeftRecursion(grammar, appliedRules); }, @@ -351,10 +352,9 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", { grammar[this._name].checkNoLeftRecursion(grammar, appliedRules); }, - Action: - function(grammar, appliedRules) { - this._expression.checkNoLeftRecursion(grammar, appliedRules); - } + Literal: nop, + Any: nop, + Class: nop }); /* ===== PEG.Compiler ===== */ @@ -754,69 +754,31 @@ PEG.Grammar.Rule.prototype.compile = function() { * |null|. */ -PEG.Grammar.Literal.prototype.compile = function(resultVar) { - return PEG.Compiler.formatCode( - "if (this._input.substr(this._pos, ${length}) === ${value|string}) {", - " var ${resultVar} = ${value|string};", - " this._pos += ${length};", - "} else {", - " var ${resultVar} = null;", - " if (context.reportMatchFailures) {", - " this._matchFailed(this._quoteString(${value|string}));", - " }", - "}", - { - value: this._value, - length: this._value.length, - resultVar: resultVar - } +PEG.Grammar.Choice.prototype.compile = function(resultVar) { + var code = PEG.Compiler.formatCode( + "var ${resultVar} = null;", + { resultVar: resultVar } ); -}; -PEG.Grammar.Class.prototype.compile = function(resultVar) { - /* - * Stupid IE considers regexps /[]/ and /[^]/ syntactically invalid, so we - * translate them into euqivalents it can handle. - */ - if (this._characters === "") { - var regexp = "/^(?!)/"; - } else if (this._characters === "^") { - var regexp = "/^[\\S\\s]/"; - } else { - var regexp = "/^[" + this._characters + "]/"; + for (var i = this._alternatives.length - 1; i >= 0; i--) { + var alternativeResultVar = PEG.Compiler.generateUniqueIdentifier("result"); + code = PEG.Compiler.formatCode( + "${alternativeCode}", + "if (${alternativeResultVar} !== null) {", + " var ${resultVar} = ${alternativeResultVar};", + "} else {", + " ${code};", + "}", + { + alternativeCode: this._alternatives[i].compile(alternativeResultVar), + alternativeResultVar: alternativeResultVar, + code: code, + resultVar: resultVar + } + ); } - return PEG.Compiler.formatCode( - "if (this._input.substr(this._pos).match(${regexp}) !== null) {", - " var ${resultVar} = this._input.charAt(this._pos);", - " this._pos++;", - "} else {", - " var ${resultVar} = null;", - " if (context.reportMatchFailures) {", - " this._matchFailed('[' + ${characters|string} + ']');", - " }", - "}", - { - characters: this._characters, - regexp: regexp, - resultVar: resultVar - } - ); -}; - -PEG.Grammar.Any.prototype.compile = function(resultVar) { - return PEG.Compiler.formatCode( - "if (this._input.length > this._pos) {", - " var ${resultVar} = this._input.charAt(this._pos);", - " this._pos++;", - "} else {", - " var ${resultVar} = null;", - " if (context.reportMatchFailures) {", - " this._matchFailed('any character');", - " }", - "}", - { resultVar: resultVar } - ); + return code; }; PEG.Grammar.Sequence.prototype.compile = function(resultVar) { @@ -863,31 +825,58 @@ PEG.Grammar.Sequence.prototype.compile = function(resultVar) { ); }; -PEG.Grammar.Choice.prototype.compile = function(resultVar) { - var code = PEG.Compiler.formatCode( - "var ${resultVar} = null;", - { resultVar: resultVar } +PEG.Grammar.AndPredicate.prototype.compile = function(resultVar) { + var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos"); + var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar"); + var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); + + return PEG.Compiler.formatCode( + "var ${savedPosVar} = this._pos;", + "var ${savedReportMatchFailuresVar} = context.reportMatchFailures;", + "context.reportMatchFailures = false;", + "${expressionCode}", + "context.reportMatchFailures = ${savedReportMatchFailuresVar};", + "if (${expressionResultVar} !== null) {", + " var ${resultVar} = '';", + " this._pos = ${savedPosVar};", + "} else {", + " var ${resultVar} = null;", + "}", + { + expressionCode: this._expression.compile(expressionResultVar), + expressionResultVar: expressionResultVar, + savedPosVar: savedPosVar, + savedReportMatchFailuresVar: savedReportMatchFailuresVar, + resultVar: resultVar + } ); +}; - for (var i = this._alternatives.length - 1; i >= 0; i--) { - var alternativeResultVar = PEG.Compiler.generateUniqueIdentifier("result"); - code = PEG.Compiler.formatCode( - "${alternativeCode}", - "if (${alternativeResultVar} !== null) {", - " var ${resultVar} = ${alternativeResultVar};", - "} else {", - " ${code};", - "}", - { - alternativeCode: this._alternatives[i].compile(alternativeResultVar), - alternativeResultVar: alternativeResultVar, - code: code, - resultVar: resultVar - } - ); - } +PEG.Grammar.NotPredicate.prototype.compile = function(resultVar) { + var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos"); + var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar"); + var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); - return code; + return PEG.Compiler.formatCode( + "var ${savedPosVar} = this._pos;", + "var ${savedReportMatchFailuresVar} = context.reportMatchFailures;", + "context.reportMatchFailures = false;", + "${expressionCode}", + "context.reportMatchFailures = ${savedReportMatchFailuresVar};", + "if (${expressionResultVar} === null) {", + " var ${resultVar} = '';", + "} else {", + " var ${resultVar} = null;", + " this._pos = ${savedPosVar};", + "}", + { + expressionCode: this._expression.compile(expressionResultVar), + expressionResultVar: expressionResultVar, + savedPosVar: savedPosVar, + savedReportMatchFailuresVar: savedReportMatchFailuresVar, + resultVar: resultVar + } + ); }; PEG.Grammar.Optional.prototype.compile = function(resultVar) { @@ -944,70 +933,6 @@ PEG.Grammar.OneOrMore.prototype.compile = function(resultVar) { ); }; -PEG.Grammar.NotPredicate.prototype.compile = function(resultVar) { - var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos"); - var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar"); - var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); - - return PEG.Compiler.formatCode( - "var ${savedPosVar} = this._pos;", - "var ${savedReportMatchFailuresVar} = context.reportMatchFailures;", - "context.reportMatchFailures = false;", - "${expressionCode}", - "context.reportMatchFailures = ${savedReportMatchFailuresVar};", - "if (${expressionResultVar} === null) {", - " var ${resultVar} = '';", - "} else {", - " var ${resultVar} = null;", - " this._pos = ${savedPosVar};", - "}", - { - expressionCode: this._expression.compile(expressionResultVar), - expressionResultVar: expressionResultVar, - savedPosVar: savedPosVar, - savedReportMatchFailuresVar: savedReportMatchFailuresVar, - resultVar: resultVar - } - ); -}; - -PEG.Grammar.AndPredicate.prototype.compile = function(resultVar) { - var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos"); - var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar"); - var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); - - return PEG.Compiler.formatCode( - "var ${savedPosVar} = this._pos;", - "var ${savedReportMatchFailuresVar} = context.reportMatchFailures;", - "context.reportMatchFailures = false;", - "${expressionCode}", - "context.reportMatchFailures = ${savedReportMatchFailuresVar};", - "if (${expressionResultVar} !== null) {", - " var ${resultVar} = '';", - " this._pos = ${savedPosVar};", - "} else {", - " var ${resultVar} = null;", - "}", - { - expressionCode: this._expression.compile(expressionResultVar), - expressionResultVar: expressionResultVar, - savedPosVar: savedPosVar, - savedReportMatchFailuresVar: savedReportMatchFailuresVar, - resultVar: resultVar - } - ); -}; - -PEG.Grammar.RuleRef.prototype.compile = function(resultVar) { - return PEG.Compiler.formatCode( - "var ${resultVar} = this.${ruleMethod}(context);", - { - ruleMethod: "_parse_" + this._name, - resultVar: resultVar - } - ); -}; - PEG.Grammar.Action.prototype.compile = function(resultVar) { /* * In case of sequences, we splat their elements into function arguments one @@ -1058,4 +983,80 @@ PEG.Grammar.Action.prototype.compile = function(resultVar) { ); }; +PEG.Grammar.RuleRef.prototype.compile = function(resultVar) { + return PEG.Compiler.formatCode( + "var ${resultVar} = this.${ruleMethod}(context);", + { + ruleMethod: "_parse_" + this._name, + resultVar: resultVar + } + ); +}; + +PEG.Grammar.Literal.prototype.compile = function(resultVar) { + return PEG.Compiler.formatCode( + "if (this._input.substr(this._pos, ${length}) === ${value|string}) {", + " var ${resultVar} = ${value|string};", + " this._pos += ${length};", + "} else {", + " var ${resultVar} = null;", + " if (context.reportMatchFailures) {", + " this._matchFailed(this._quoteString(${value|string}));", + " }", + "}", + { + value: this._value, + length: this._value.length, + resultVar: resultVar + } + ); +}; + +PEG.Grammar.Any.prototype.compile = function(resultVar) { + return PEG.Compiler.formatCode( + "if (this._input.length > this._pos) {", + " var ${resultVar} = this._input.charAt(this._pos);", + " this._pos++;", + "} else {", + " var ${resultVar} = null;", + " if (context.reportMatchFailures) {", + " this._matchFailed('any character');", + " }", + "}", + { resultVar: resultVar } + ); +}; + + +PEG.Grammar.Class.prototype.compile = function(resultVar) { + /* + * Stupid IE considers regexps /[]/ and /[^]/ syntactically invalid, so we + * translate them into euqivalents it can handle. + */ + if (this._characters === "") { + var regexp = "/^(?!)/"; + } else if (this._characters === "^") { + var regexp = "/^[\\S\\s]/"; + } else { + var regexp = "/^[" + this._characters + "]/"; + } + + return PEG.Compiler.formatCode( + "if (this._input.substr(this._pos).match(${regexp}) !== null) {", + " var ${resultVar} = this._input.charAt(this._pos);", + " this._pos++;", + "} else {", + " var ${resultVar} = null;", + " if (context.reportMatchFailures) {", + " this._matchFailed('[' + ${characters|string} + ']');", + " }", + "}", + { + characters: this._characters, + regexp: regexp, + resultVar: resultVar + } + ); +}; + })(); diff --git a/test/compiler-test.js b/test/compiler-test.js index 654ec58..45447f7 100644 --- a/test/compiler-test.js +++ b/test/compiler-test.js @@ -202,15 +202,15 @@ test("buildParser reports missing start rule", function() { test("buildParser reports missing referenced rules", function() { var grammars = [ 'start: missing', - 'start: missing "a" "b"', - 'start: "a" "b" missing', 'start: missing / "a" / "b"', 'start: "a" / "b" / missing', + 'start: missing "a" "b"', + 'start: "a" "b" missing', + 'start: &missing', + 'start: !missing', 'start: missing?', 'start: missing*', 'start: missing+', - 'start: &missing', - 'start: !missing', 'start: missing { }' ]; @@ -227,14 +227,14 @@ test("buildParser reports left recursion", function() { var grammars = [ /* Direct */ 'start: start', - 'start: start "a" "b"', 'start: start / "a" / "b"', 'start: "a" / "b" / start', + 'start: start "a" "b"', + 'start: &start', + 'start: !start', 'start: start?', 'start: start*', 'start: start+', - 'start: &start', - 'start: !start', 'start: start { }', /* Indirect */ @@ -259,69 +259,14 @@ test("buildParser allows custom start rule", function() { module("Generated Parser"); -test("literals", function() { - var parser = PEG.buildParser('start: "abcd"'); - parses(parser, "abcd", "abcd"); - doesNotParse(parser, ""); - doesNotParse(parser, "abc"); - doesNotParse(parser, "abcde"); - doesNotParse(parser, "efgh"); - - /* - * Test that the parsing position moves forward after successful parsing of - * a literal. - */ - var posTestParser = PEG.buildParser('start: "a" "b"'); - parses(posTestParser, "ab", ["a", "b"]); -}); - -test("anys", function() { - var parser = PEG.buildParser('start: .'); +test("choices", function() { + var parser = PEG.buildParser('start: "a" / "b" / "c"'); parses(parser, "a", "a"); + parses(parser, "b", "b"); + parses(parser, "c", "c"); doesNotParse(parser, ""); doesNotParse(parser, "ab"); - - /* - * Test that the parsing position moves forward after successful parsing of - * an any. - */ - var posTestParser = PEG.buildParser('start: . .'); - parses(posTestParser, "ab", ["a", "b"]); -}); - -test("classes", function() { - var emptyClassParser = PEG.buildParser('start: []'); - doesNotParse(emptyClassParser, ""); - doesNotParse(emptyClassParser, "a"); - doesNotParse(emptyClassParser, "ab"); - - var nonEmptyClassParser = PEG.buildParser('start: [ab-d]'); - parses(nonEmptyClassParser, "a", "a"); - parses(nonEmptyClassParser, "b", "b"); - parses(nonEmptyClassParser, "c", "c"); - parses(nonEmptyClassParser, "d", "d"); - doesNotParse(nonEmptyClassParser, ""); - doesNotParse(nonEmptyClassParser, "ab"); - - var invertedEmptyClassParser = PEG.buildParser('start: [^]'); - doesNotParse(invertedEmptyClassParser, ""); - parses(invertedEmptyClassParser, "a", "a"); - doesNotParse(invertedEmptyClassParser, "ab"); - - var invertedNonEmptyClassParser = PEG.buildParser('start: [^ab-d]'); - doesNotParse(invertedNonEmptyClassParser, "a", "a"); - doesNotParse(invertedNonEmptyClassParser, "b", "b"); - doesNotParse(invertedNonEmptyClassParser, "c", "c"); - doesNotParse(invertedNonEmptyClassParser, "d", "d"); - doesNotParse(invertedNonEmptyClassParser, ""); - doesNotParse(invertedNonEmptyClassParser, "ab"); - - /* - * Test that the parsing position moves forward after successful parsing of - * a class. - */ - var posTestParser = PEG.buildParser('start: [ab-d] [ab-d]'); - parses(posTestParser, "ab", ["a", "b"]); + doesNotParse(parser, "d"); }); test("sequences", function() { @@ -344,36 +289,6 @@ test("sequences", function() { parses(posTestParser, "a", "a"); }); -test("choices", function() { - var parser = PEG.buildParser('start: "a" / "b" / "c"'); - parses(parser, "a", "a"); - parses(parser, "b", "b"); - parses(parser, "c", "c"); - doesNotParse(parser, ""); - doesNotParse(parser, "ab"); - doesNotParse(parser, "d"); -}); - -test("optional expressions", function() { - var parser = PEG.buildParser('start: "a"?'); - parses(parser, "", ""); - parses(parser, "a", "a"); -}); - -test("zero or more expressions", function() { - var parser = PEG.buildParser('start: "a"*'); - parses(parser, "", []); - parses(parser, "a", ["a"]); - parses(parser, "aaa", ["a", "a", "a"]); -}); - -test("one or more expressions", function() { - var parser = PEG.buildParser('start: "a"+'); - doesNotParse(parser, ""); - parses(parser, "a", ["a"]); - parses(parser, "aaa", ["a", "a", "a"]); -}); - test("and predicate", function() { var parser = PEG.buildParser('start: "a" &"b" "b"'); parses(parser, "ab", ["a", "", "b"]); @@ -398,14 +313,24 @@ test("not predicate", function() { parses(posTestParser, "ac", ["a", "", "c"]); }); -test("rule references", function() { - var parser = PEG.buildParser([ - 'start: static / dynamic', - 'static: "C" / "C++" / "Java" / "C#"', - 'dynamic: "Ruby" / "Python" / "JavaScript"' - ].join("\n")); - parses(parser, "Java", "Java"); - parses(parser, "Python", "Python"); +test("optional expressions", function() { + var parser = PEG.buildParser('start: "a"?'); + parses(parser, "", ""); + parses(parser, "a", "a"); +}); + +test("zero or more expressions", function() { + var parser = PEG.buildParser('start: "a"*'); + parses(parser, "", []); + parses(parser, "a", ["a"]); + parses(parser, "aaa", ["a", "a", "a"]); +}); + +test("one or more expressions", function() { + var parser = PEG.buildParser('start: "a"+'); + doesNotParse(parser, ""); + parses(parser, "a", ["a"]); + parses(parser, "aaa", ["a", "a", "a"]); }); test("actions", function() { @@ -438,6 +363,81 @@ test("actions", function() { parses(variablesParser, "abcdefghij", "ABCDEFGHIJ"); }); +test("rule references", function() { + var parser = PEG.buildParser([ + 'start: static / dynamic', + 'static: "C" / "C++" / "Java" / "C#"', + 'dynamic: "Ruby" / "Python" / "JavaScript"' + ].join("\n")); + parses(parser, "Java", "Java"); + parses(parser, "Python", "Python"); +}); + +test("literals", function() { + var parser = PEG.buildParser('start: "abcd"'); + parses(parser, "abcd", "abcd"); + doesNotParse(parser, ""); + doesNotParse(parser, "abc"); + doesNotParse(parser, "abcde"); + doesNotParse(parser, "efgh"); + + /* + * Test that the parsing position moves forward after successful parsing of + * a literal. + */ + var posTestParser = PEG.buildParser('start: "a" "b"'); + parses(posTestParser, "ab", ["a", "b"]); +}); + +test("anys", function() { + var parser = PEG.buildParser('start: .'); + parses(parser, "a", "a"); + doesNotParse(parser, ""); + doesNotParse(parser, "ab"); + + /* + * Test that the parsing position moves forward after successful parsing of + * an any. + */ + var posTestParser = PEG.buildParser('start: . .'); + parses(posTestParser, "ab", ["a", "b"]); +}); + +test("classes", function() { + var emptyClassParser = PEG.buildParser('start: []'); + doesNotParse(emptyClassParser, ""); + doesNotParse(emptyClassParser, "a"); + doesNotParse(emptyClassParser, "ab"); + + var nonEmptyClassParser = PEG.buildParser('start: [ab-d]'); + parses(nonEmptyClassParser, "a", "a"); + parses(nonEmptyClassParser, "b", "b"); + parses(nonEmptyClassParser, "c", "c"); + parses(nonEmptyClassParser, "d", "d"); + doesNotParse(nonEmptyClassParser, ""); + doesNotParse(nonEmptyClassParser, "ab"); + + var invertedEmptyClassParser = PEG.buildParser('start: [^]'); + doesNotParse(invertedEmptyClassParser, ""); + parses(invertedEmptyClassParser, "a", "a"); + doesNotParse(invertedEmptyClassParser, "ab"); + + var invertedNonEmptyClassParser = PEG.buildParser('start: [^ab-d]'); + doesNotParse(invertedNonEmptyClassParser, "a", "a"); + doesNotParse(invertedNonEmptyClassParser, "b", "b"); + doesNotParse(invertedNonEmptyClassParser, "c", "c"); + doesNotParse(invertedNonEmptyClassParser, "d", "d"); + doesNotParse(invertedNonEmptyClassParser, ""); + doesNotParse(invertedNonEmptyClassParser, "ab"); + + /* + * Test that the parsing position moves forward after successful parsing of + * a class. + */ + var posTestParser = PEG.buildParser('start: [ab-d] [ab-d]'); + parses(posTestParser, "ab", ["a", "b"]); +}); + test("cache", function() { /* * Should trigger a codepath where the cache is used (for the "a" rule).