@ -193,7 +193,7 @@ test("buildParser reports syntax errors in the grammar", function() {
test ( "buildParser reports missing start rule" , function ( ) {
throws (
function ( ) { PEG . buildParser ( 'notStart: "abcd"' ) ; } ,
function ( ) { PEG . buildParser ( 'notStart = "abcd"' ) ; } ,
PEG . GrammarError ,
{ message : "Missing \"start\" rule." }
) ;
@ -201,17 +201,17 @@ 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' ,
'start: !missing' ,
'start: missing?' ,
'start: missing*' ,
'start: missing+' ,
'start: missing { }'
'start = 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 { }'
] ;
PEG . ArrayUtils . each ( grammars , function ( grammar ) {
@ -226,19 +226,19 @@ test("buildParser reports missing referenced rules", function() {
test ( "buildParser reports left recursion" , function ( ) {
var grammars = [
/* Direct */
'start: start' ,
'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 / "a" / "b"' ,
'start = "a" / "b" / start' ,
'start = start "a" "b"' ,
'start = &start' ,
'start = !start' ,
'start = start?' ,
'start = start*' ,
'start = start+' ,
'start = start { }' ,
/* Indirect */
'start: stop\nstop: start'
'start = stop\nstop = start'
] ;
PEG . ArrayUtils . each ( grammars , function ( grammar ) {
@ -251,7 +251,7 @@ test("buildParser reports left recursion", function() {
} ) ;
test ( "buildParser allows custom start rule" , function ( ) {
var parser = PEG . buildParser ( 's: "abcd"' , "s" ) ;
var parser = PEG . buildParser ( 's = "abcd"' , "s" ) ;
parses ( parser , "abcd" , "abcd" ) ;
} ) ;
@ -260,7 +260,7 @@ test("buildParser allows custom start rule", function() {
module ( "Generated Parser" ) ;
test ( "choices" , function ( ) {
var parser = PEG . buildParser ( 'start: "a" / "b" / "c"' ) ;
var parser = PEG . buildParser ( 'start = "a" / "b" / "c"' ) ;
parses ( parser , "a" , "a" ) ;
parses ( parser , "b" , "b" ) ;
parses ( parser , "c" , "c" ) ;
@ -270,11 +270,11 @@ test("choices", function() {
} ) ;
test ( "sequences" , function ( ) {
var emptySequenceParser = PEG . buildParser ( 'start: ' ) ;
var emptySequenceParser = PEG . buildParser ( 'start = ' ) ;
parses ( emptySequenceParser , "" , [ ] ) ;
doesNotParse ( emptySequenceParser , "abc" ) ;
var nonEmptySequenceParser = PEG . buildParser ( 'start: "a" "b" "c"' ) ;
var nonEmptySequenceParser = PEG . buildParser ( 'start = "a" "b" "c"' ) ;
parses ( nonEmptySequenceParser , "abc" , [ "a" , "b" , "c" ] ) ;
doesNotParse ( nonEmptySequenceParser , "" ) ;
doesNotParse ( nonEmptySequenceParser , "ab" ) ;
@ -285,12 +285,12 @@ test("sequences", function() {
* Test that the parsing position returns after unsuccessful parsing of a
* sequence .
* /
var posTestParser = PEG . buildParser ( 'start: ("a" "b") / "a"' ) ;
var posTestParser = PEG . buildParser ( 'start = ("a" "b") / "a"' ) ;
parses ( posTestParser , "a" , "a" ) ;
} ) ;
test ( "and predicate" , function ( ) {
var parser = PEG . buildParser ( 'start: "a" &"b" "b"' ) ;
var parser = PEG . buildParser ( 'start = "a" &"b" "b"' ) ;
parses ( parser , "ab" , [ "a" , "" , "b" ] ) ;
doesNotParse ( parser , "ac" ) ;
@ -301,7 +301,7 @@ test("and predicate", function() {
} ) ;
test ( "not predicate" , function ( ) {
var parser = PEG . buildParser ( 'start: "a" !"b"' ) ;
var parser = PEG . buildParser ( 'start = "a" !"b"' ) ;
parses ( parser , "a" , [ "a" , "" ] ) ;
doesNotParse ( parser , "ab" ) ;
@ -309,25 +309,25 @@ test("not predicate", function() {
* Test that the parsing position returns after successful parsing of a
* predicate .
* /
var posTestParser = PEG . buildParser ( 'start: "a" !"b" "c"' ) ;
var posTestParser = PEG . buildParser ( 'start = "a" !"b" "c"' ) ;
parses ( posTestParser , "ac" , [ "a" , "" , "c" ] ) ;
} ) ;
test ( "optional expressions" , function ( ) {
var parser = PEG . buildParser ( 'start: "a"?' ) ;
var parser = PEG . buildParser ( 'start = "a"?' ) ;
parses ( parser , "" , "" ) ;
parses ( parser , "a" , "a" ) ;
} ) ;
test ( "zero or more expressions" , function ( ) {
var parser = PEG . buildParser ( 'start: "a"*' ) ;
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"+' ) ;
var parser = PEG . buildParser ( 'start = "a"+' ) ;
doesNotParse ( parser , "" ) ;
parses ( parser , "a" , [ "a" ] ) ;
parses ( parser , "aaa" , [ "a" , "a" , "a" ] ) ;
@ -335,46 +335,46 @@ test("one or more expressions", function() {
test ( "actions" , function ( ) {
var singleMatchParser = PEG . buildParser (
'start: "a" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
'start = "a" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
) ;
parses ( singleMatchParser , "a" , "A" ) ;
var multiMatchParser = PEG . buildParser (
'start: "a" "b" "c" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
'start = "a" "b" "c" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
) ;
parses ( multiMatchParser , "abc" , "ABC" ) ;
var innerMatchParser = PEG . buildParser (
'start: "a" ("b" "c" "d" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }) "e"'
'start = "a" ("b" "c" "d" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }) "e"'
) ;
parses ( innerMatchParser , "abcde" , [ "a" , "BCD" , "e" ] ) ;
/* Test that the action is not called when its expression does not match. */
var notAMatchParser = PEG . buildParser (
'start: "a" { ok(false, "action got called when it should not be"); }'
'start = "a" { ok(false, "action got called when it should not be"); }'
) ;
doesNotParse ( notAMatchParser , "b" ) ;
var variablesParser = PEG . buildParser ( [
'start: "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" {' ,
' return [$1, $2, $3, $4, $5, $6, $7, $8, $9, $10].join("").toUpperCase();' ,
' }'
'start = "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" {' ,
' return [$1, $2, $3, $4, $5, $6, $7, $8, $9, $10].join("").toUpperCase();' ,
' }'
] . join ( "\n" ) ) ;
parses ( variablesParser , "abcdefghij" , "ABCDEFGHIJ" ) ;
} ) ;
test ( "rule references" , function ( ) {
var parser = PEG . buildParser ( [
'start: static / dynamic' ,
'static: "C" / "C++" / "Java" / "C#"' ,
'dynamic: "Ruby" / "Python" / "JavaScript"'
'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"' ) ;
var parser = PEG . buildParser ( 'start = "abcd"' ) ;
parses ( parser , "abcd" , "abcd" ) ;
doesNotParse ( parser , "" ) ;
doesNotParse ( parser , "abc" ) ;
@ -385,12 +385,12 @@ test("literals", function() {
* Test that the parsing position moves forward after successful parsing of
* a literal .
* /
var posTestParser = PEG . buildParser ( 'start: "a" "b"' ) ;
var posTestParser = PEG . buildParser ( 'start = "a" "b"' ) ;
parses ( posTestParser , "ab" , [ "a" , "b" ] ) ;
} ) ;
test ( "anys" , function ( ) {
var parser = PEG . buildParser ( 'start: .' ) ;
var parser = PEG . buildParser ( 'start = .' ) ;
parses ( parser , "a" , "a" ) ;
doesNotParse ( parser , "" ) ;
doesNotParse ( parser , "ab" ) ;
@ -399,17 +399,17 @@ test("anys", function() {
* Test that the parsing position moves forward after successful parsing of
* an any .
* /
var posTestParser = PEG . buildParser ( 'start: . .' ) ;
var posTestParser = PEG . buildParser ( 'start = . .' ) ;
parses ( posTestParser , "ab" , [ "a" , "b" ] ) ;
} ) ;
test ( "classes" , function ( ) {
var emptyClassParser = PEG . buildParser ( 'start: []' ) ;
var emptyClassParser = PEG . buildParser ( 'start = []' ) ;
doesNotParse ( emptyClassParser , "" ) ;
doesNotParse ( emptyClassParser , "a" ) ;
doesNotParse ( emptyClassParser , "ab" ) ;
var nonEmptyClassParser = PEG . buildParser ( 'start: [ab-d]' ) ;
var nonEmptyClassParser = PEG . buildParser ( 'start = [ab-d]' ) ;
parses ( nonEmptyClassParser , "a" , "a" ) ;
parses ( nonEmptyClassParser , "b" , "b" ) ;
parses ( nonEmptyClassParser , "c" , "c" ) ;
@ -417,12 +417,12 @@ test("classes", function() {
doesNotParse ( nonEmptyClassParser , "" ) ;
doesNotParse ( nonEmptyClassParser , "ab" ) ;
var invertedEmptyClassParser = PEG . buildParser ( 'start: [^]' ) ;
var invertedEmptyClassParser = PEG . buildParser ( 'start = [^]' ) ;
doesNotParse ( invertedEmptyClassParser , "" ) ;
parses ( invertedEmptyClassParser , "a" , "a" ) ;
doesNotParse ( invertedEmptyClassParser , "ab" ) ;
var invertedNonEmptyClassParser = PEG . buildParser ( 'start: [^ab-d]' ) ;
var invertedNonEmptyClassParser = PEG . buildParser ( 'start = [^ab-d]' ) ;
doesNotParse ( invertedNonEmptyClassParser , "a" , "a" ) ;
doesNotParse ( invertedNonEmptyClassParser , "b" , "b" ) ;
doesNotParse ( invertedNonEmptyClassParser , "c" , "c" ) ;
@ -434,7 +434,7 @@ test("classes", function() {
* Test that the parsing position moves forward after successful parsing of
* a class .
* /
var posTestParser = PEG . buildParser ( 'start: [ab-d] [ab-d]' ) ;
var posTestParser = PEG . buildParser ( 'start = [ab-d] [ab-d]' ) ;
parses ( posTestParser , "ab" , [ "a" , "b" ] ) ;
} ) ;
@ -443,23 +443,23 @@ test("cache", function() {
* Should trigger a codepath where the cache is used ( for the "a" rule ) .
* /
var parser = PEG . buildParser ( [
'start: (a b) / (a c)' ,
'a: "a"' ,
'b: "b"' ,
'c: "c"'
'start = (a b) / (a c)' ,
'a = "a"' ,
'b = "b"' ,
'c = "c"'
] . join ( "\n" ) ) ;
parses ( parser , "ac" , [ "a" , "c" ] ) ;
} ) ;
test ( "indempotence" , function ( ) {
var parser1 = PEG . buildParser ( 'start: "abcd"' ) ;
var parser2 = PEG . buildParser ( 'start: "abcd"' ) ;
var parser1 = PEG . buildParser ( 'start = "abcd"' ) ;
var parser2 = PEG . buildParser ( 'start = "abcd"' ) ;
strictEqual ( parser1 . toSource ( ) , parser2 . toSource ( ) ) ;
} ) ;
test ( "error messages" , function ( ) {
var literalParser = PEG . buildParser ( 'start: "abcd"' ) ;
var literalParser = PEG . buildParser ( 'start = "abcd"' ) ;
doesNotParseWithMessage (
literalParser ,
"" ,
@ -476,34 +476,34 @@ test("error messages", function() {
'Expected end of input but "e" found.'
) ;
var classParser = PEG . buildParser ( 'start: [a-d]' ) ;
var classParser = PEG . buildParser ( 'start = [a-d]' ) ;
doesNotParseWithMessage (
classParser ,
"" ,
'Expected [a-d] but end of input found.'
) ;
var negativeClassParser = PEG . buildParser ( 'start: [^a-d]' ) ;
var negativeClassParser = PEG . buildParser ( 'start = [^a-d]' ) ;
doesNotParseWithMessage (
negativeClassParser ,
"" ,
'Expected [^a-d] but end of input found.'
) ;
var anyParser = PEG . buildParser ( 'start: .' ) ;
var anyParser = PEG . buildParser ( 'start = .' ) ;
doesNotParseWithMessage (
anyParser ,
"" ,
'Expected any character but end of input found.'
) ;
var namedRuleWithLiteralParser = PEG . buildParser ( 'start "digit": [0-9]' ) ;
var namedRuleWithLiteralParser = PEG . buildParser ( 'start "digit" = [0-9]' ) ;
doesNotParseWithMessage (
namedRuleWithLiteralParser ,
"a" ,
'Expected digit but "a" found.'
) ;
var namedRuleWithAnyParser = PEG . buildParser ( 'start "whatever": .' ) ;
var namedRuleWithAnyParser = PEG . buildParser ( 'start "whatever" = .' ) ;
doesNotParseWithMessage (
namedRuleWithAnyParser ,
"" ,
@ -511,8 +511,8 @@ test("error messages", function() {
) ;
var namedRuleWithNamedRuleParser = PEG . buildParser ( [
'start "digits": digit+' ,
'digit "digit": [0-9]'
'start "digits" = digit+' ,
'digit "digit" = [0-9]'
] . join ( "\n" ) ) ;
doesNotParseWithMessage (
namedRuleWithNamedRuleParser ,
@ -520,49 +520,49 @@ test("error messages", function() {
'Expected digits but end of input found.'
) ;
var choiceParser1 = PEG . buildParser ( 'start: "a" / "b" / "c"' ) ;
var choiceParser1 = PEG . buildParser ( 'start = "a" / "b" / "c"' ) ;
doesNotParseWithMessage (
choiceParser1 ,
"def" ,
'Expected "a", "b" or "c" but "d" found.'
) ;
var choiceParser2 = PEG . buildParser ( 'start: "a" "b" "c" / "a"' ) ;
var choiceParser2 = PEG . buildParser ( 'start = "a" "b" "c" / "a"' ) ;
doesNotParseWithMessage (
choiceParser2 ,
"abd" ,
'Expected "c" but "d" found.'
) ;
var notPredicateParser = PEG . buildParser ( 'start: !"a" "b"' ) ;
var notPredicateParser = PEG . buildParser ( 'start = !"a" "b"' ) ;
doesNotParseWithMessage (
notPredicateParser ,
"c" ,
'Expected "b" but "c" found.'
) ;
var andPredicateParser = PEG . buildParser ( 'start: &"a" [a-b]' ) ;
var andPredicateParser = PEG . buildParser ( 'start = &"a" [a-b]' ) ;
doesNotParseWithMessage (
andPredicateParser ,
"c" ,
'Expected end of input but "c" found.'
) ;
var emptyParser = PEG . buildParser ( 'start: ' ) ;
var emptyParser = PEG . buildParser ( 'start = ' ) ;
doesNotParseWithMessage (
emptyParser ,
"something" ,
'Expected end of input but "s" found.'
) ;
var duplicateErrorParser = PEG . buildParser ( 'start: "a" / "a"' ) ;
var duplicateErrorParser = PEG . buildParser ( 'start = "a" / "a"' ) ;
doesNotParseWithMessage (
duplicateErrorParser ,
"" ,
'Expected "a" but end of input found.'
) ;
var unsortedErrorsParser = PEG . buildParser ( 'start: "b" / "a"' ) ;
var unsortedErrorsParser = PEG . buildParser ( 'start = "b" / "a"' ) ;
doesNotParseWithMessage (
unsortedErrorsParser ,
"" ,
@ -572,9 +572,9 @@ test("error messages", function() {
test ( "error positions" , function ( ) {
var parser = PEG . buildParser ( [
'start: line (("\\r" / "\\n" / "\\u2028" / "\\u2029")+ line)*' ,
'line: digit (" "+ digit)*' ,
'digit: [0-9]+ { return $1.join(""); }'
'start = line (("\\r" / "\\n" / "\\u2028" / "\\u2029")+ line)*' ,
'line = digit (" "+ digit)*' ,
'digit = [0-9]+ { return $1.join(""); }'
] . join ( "\n" ) ) ;
doesNotParseWithPos ( parser , "a" , 1 , 1 ) ;
@ -603,9 +603,9 @@ test("arithmetics", function() {
* Expr โ Sum
* /
var parser = PEG . buildParser ( [
'Value : [0-9]+ { return parseInt($1.join("")); }' ,
'Value = [0-9]+ { return parseInt($1.join("")); }' ,
' / "(" Expr ")" { return $2; }' ,
'Product : Value (("*" / "/") Value)* {' ,
'Product = Value (("*" / "/") Value)* {' ,
' var result = $1;' ,
' for (var i = 0; i < $2.length; i++) {' ,
' if ($2[i][0] == "*") { result *= $2[i][1]; }' ,
@ -613,7 +613,7 @@ test("arithmetics", function() {
' }' ,
' return result;' ,
' }' ,
'Sum : Product (("+" / "-") Product)* {' ,
'Sum = Product (("+" / "-") Product)* {' ,
' var result = $1;' ,
' for (var i = 0; i < $2.length; i++) {' ,
' if ($2[i][0] == "+") { result += $2[i][1]; }' ,
@ -621,7 +621,7 @@ test("arithmetics", function() {
' }' ,
' return result;' ,
' }' ,
'Expr : Sum'
'Expr = Sum'
] . join ( "\n" ) , "Expr" ) ;
/* Test "value" rule. */
@ -659,9 +659,9 @@ test("non-context-free language", function() {
* B โ b B ? c
* /
var parser = PEG . buildParser ( [
'S: &(A "c") "a"+ B !("a" / "b" / "c") { return $2.join("") + $3; }' ,
'A: "a" A? "b" { return $1 + $2 + $3; }' ,
'B: "b" B? "c" { return $1 + $2 + $3; }' ,
'S = &(A "c") "a"+ B !("a" / "b" / "c") { return $2.join("") + $3; }' ,
'A = "a" A? "b" { return $1 + $2 + $3; }' ,
'B = "b" B? "c" { return $1 + $2 + $3; }' ,
] . join ( "\n" ) , "S" ) ;
parses ( parser , "abc" , "abc" ) ;
@ -683,12 +683,12 @@ test("nested comments", function() {
* Z โ any single character
* /
var parser = PEG . buildParser ( [
'Begin : "(*"' ,
'End : "*)"' ,
'C : Begin N* End { return $1 + $2.join("") + $3; }' ,
'N : C' ,
'Begin = "(*"' ,
'End = "*)"' ,
'C = Begin N* End { return $1 + $2.join("") + $3; }' ,
'N = C' ,
' / (!Begin !End Z) { return $3; }' ,
'Z : .'
'Z = .'
] . join ( "\n" ) , "C" ) ;
parses ( parser , "(**)" , "(**)" ) ;