@ -1,147 +1,179 @@
"use strict" ;
le t chai = require ( "chai" ) ;
le t parser = require ( "../../../lib/parser" ) ;
cons t chai = require ( "chai" ) ;
cons t parser = require ( "../../../lib/parser" ) ;
le t expect = chai . expect ;
cons t expect = chai . expect ;
// better diagnostics for deep eq failure
chai . config . truncateThreshold = 0 ;
describe ( "PEG.js grammar parser" , function ( ) {
let literalAbcd = { type : "literal" , value : "abcd" , ignoreCase : false } ;
let literalEfgh = { type : "literal" , value : "efgh" , ignoreCase : false } ;
let literalIjkl = { type : "literal" , value : "ijkl" , ignoreCase : false } ;
let literalMnop = { type : "literal" , value : "mnop" , ignoreCase : false } ;
let semanticAnd = { type : "semantic_and" , code : " code " } ;
let semanticNot = { type : "semantic_not" , code : " code " } ;
let optional = { type : "optional" , expression : literalAbcd } ;
let zeroOrMore = { type : "zero_or_more" , expression : literalAbcd } ;
let oneOrMore = { type : "one_or_more" , expression : literalAbcd } ;
let textOptional = { type : "text" , expression : optional } ;
let simpleNotAbcd = { type : "simple_not" , expression : literalAbcd } ;
let simpleAndOptional = { type : "simple_and" , expression : optional } ;
let simpleNotOptional = { type : "simple_not" , expression : optional } ;
let labeledAbcd = { type : "labeled" , label : "a" , expression : literalAbcd } ;
let labeledEfgh = { type : "labeled" , label : "b" , expression : literalEfgh } ;
let labeledIjkl = { type : "labeled" , label : "c" , expression : literalIjkl } ;
let labeledMnop = { type : "labeled" , label : "d" , expression : literalMnop } ;
let labeledSimpleNot = { type : "labeled" , label : "a" , expression : simpleNotAbcd } ;
let sequence = {
const literalAbcd = { type : "literal" , value : "abcd" , ignoreCase : false } ;
const literalEfgh = { type : "literal" , value : "efgh" , ignoreCase : false } ;
const literalIjkl = { type : "literal" , value : "ijkl" , ignoreCase : false } ;
const literalMnop = { type : "literal" , value : "mnop" , ignoreCase : false } ;
const semanticAnd = { type : "semantic_and" , code : " code " } ;
const semanticNot = { type : "semantic_not" , code : " code " } ;
const optional = { type : "optional" , expression : literalAbcd } ;
const zeroOrMore = { type : "zero_or_more" , expression : literalAbcd } ;
const oneOrMore = { type : "one_or_more" , expression : literalAbcd } ;
const textOptional = { type : "text" , expression : optional } ;
const simpleNotAbcd = { type : "simple_not" , expression : literalAbcd } ;
const simpleAndOptional = { type : "simple_and" , expression : optional } ;
const simpleNotOptional = { type : "simple_not" , expression : optional } ;
const labeledAbcd = { type : "labeled" , label : "a" , expression : literalAbcd } ;
const labeledEfgh = { type : "labeled" , label : "b" , expression : literalEfgh } ;
const labeledIjkl = { type : "labeled" , label : "c" , expression : literalIjkl } ;
const labeledMnop = { type : "labeled" , label : "d" , expression : literalMnop } ;
const labeledSimpleNot = { type : "labeled" , label : "a" , expression : simpleNotAbcd } ;
const sequence = {
type : "sequence" ,
elements : [ literalAbcd , literalEfgh , literalIjkl ]
} ;
le t sequence2 = {
cons t sequence2 = {
type : "sequence" ,
elements : [ labeledAbcd , labeledEfgh ]
} ;
le t sequence4 = {
cons t sequence4 = {
type : "sequence" ,
elements : [ labeledAbcd , labeledEfgh , labeledIjkl , labeledMnop ]
} ;
le t groupLabeled = { type : "group" , expression : labeledAbcd } ;
le t groupSequence = { type : "group" , expression : sequence } ;
le t actionAbcd = { type : "action" , expression : literalAbcd , code : " code " } ;
le t actionEfgh = { type : "action" , expression : literalEfgh , code : " code " } ;
le t actionIjkl = { type : "action" , expression : literalIjkl , code : " code " } ;
le t actionMnop = { type : "action" , expression : literalMnop , code : " code " } ;
le t actionSequence = { type : "action" , expression : sequence , code : " code " } ;
le t choice = {
cons t groupLabeled = { type : "group" , expression : labeledAbcd } ;
cons t groupSequence = { type : "group" , expression : sequence } ;
cons t actionAbcd = { type : "action" , expression : literalAbcd , code : " code " } ;
cons t actionEfgh = { type : "action" , expression : literalEfgh , code : " code " } ;
cons t actionIjkl = { type : "action" , expression : literalIjkl , code : " code " } ;
cons t actionMnop = { type : "action" , expression : literalMnop , code : " code " } ;
cons t actionSequence = { type : "action" , expression : sequence , code : " code " } ;
cons t choice = {
type : "choice" ,
alternatives : [ literalAbcd , literalEfgh , literalIjkl ]
} ;
le t choice2 = {
cons t choice2 = {
type : "choice" ,
alternatives : [ actionAbcd , actionEfgh ]
} ;
le t choice4 = {
cons t choice4 = {
type : "choice" ,
alternatives : [ actionAbcd , actionEfgh , actionIjkl , actionMnop ]
} ;
le t named = { type : "named" , name : "start rule" , expression : literalAbcd } ;
le t ruleA = { type : "rule" , name : "a" , expression : literalAbcd } ;
le t ruleB = { type : "rule" , name : "b" , expression : literalEfgh } ;
le t ruleC = { type : "rule" , name : "c" , expression : literalIjkl } ;
le t ruleStart = { type : "rule" , name : "start" , expression : literalAbcd } ;
le t initializer = { type : "initializer" , code : " code " } ;
cons t named = { type : "named" , name : "start rule" , expression : literalAbcd } ;
cons t ruleA = { type : "rule" , name : "a" , expression : literalAbcd } ;
cons t ruleB = { type : "rule" , name : "b" , expression : literalEfgh } ;
cons t ruleC = { type : "rule" , name : "c" , expression : literalIjkl } ;
cons t ruleStart = { type : "rule" , name : "start" , expression : literalAbcd } ;
cons t initializer = { type : "initializer" , code : " code " } ;
function oneRuleGrammar ( expression ) {
return {
type : "grammar" ,
initializer : null ,
rules : [ { type : "rule" , name : "start" , expression : expression } ]
} ;
}
function actionGrammar ( code ) {
return oneRuleGrammar (
{ type : "action" , expression : literalAbcd , code : code }
) ;
}
function literalGrammar ( value , ignoreCase ) {
return oneRuleGrammar (
{ type : "literal" , value : value , ignoreCase : ignoreCase }
) ;
}
function classGrammar ( parts , inverted , ignoreCase ) {
return oneRuleGrammar ( {
type : "class" ,
parts : parts ,
inverted : inverted ,
ignoreCase : ignoreCase
} ) ;
}
function anyGrammar ( ) {
return oneRuleGrammar ( { type : "any" } ) ;
}
function ruleRefGrammar ( name ) {
return oneRuleGrammar ( { type : "rule_ref" , name : name } ) ;
}
le t trivialGrammar = literalGrammar ( "abcd" , false ) ;
le t twoRuleGrammar = {
cons t trivialGrammar = literalGrammar ( "abcd" , false ) ;
cons t twoRuleGrammar = {
type : "grammar" ,
initializer : null ,
rules : [ ruleA , ruleB ]
} ;
let stripLocation = ( function ( ) {
const stripLocation = ( function ( ) {
let strip ;
function buildVisitor ( functions ) {
return function ( node ) {
return functions [ node . type ] . apply ( null , arguments ) ;
} ;
}
function stripLeaf ( node ) {
delete node . location ;
}
function stripExpression ( node ) {
delete node . location ;
strip ( node . expression ) ;
}
function stripChildren ( property ) {
return function ( node ) {
delete node . location ;
node [ property ] . forEach ( strip ) ;
} ;
}
let strip = buildVisitor ( {
strip = buildVisitor ( {
grammar ( node ) {
delete node . location ;
if ( node . initializer ) {
strip ( node . initializer ) ;
}
node . rules . forEach ( strip ) ;
} ,
initializer : stripLeaf ,
@ -167,13 +199,16 @@ describe("PEG.js grammar parser", function() {
} ) ;
return strip ;
} ) ( ) ;
function helpers ( chai , utils ) {
let Assertion = chai . Assertion ;
const Assertion = chai . Assertion ;
Assertion . addMethod ( "parseAs" , function ( expected ) {
let result = parser . parse ( utils . flag ( this , "object" ) ) ;
const result = parser . parse ( utils . flag ( this , "object" ) ) ;
stripLocation ( result ) ;
@ -185,21 +220,29 @@ describe("PEG.js grammar parser", function() {
result ,
! utils . flag ( this , "negate" )
) ;
} ) ;
Assertion . addMethod ( "failToParse" , function ( props ) {
let passed , result ;
try {
result = parser . parse ( utils . flag ( this , "object" ) ) ;
passed = true ;
} catch ( e ) {
result = e ;
passed = false ;
}
if ( passed ) {
stripLocation ( result ) ;
}
this . assert (
@ -210,26 +253,36 @@ describe("PEG.js grammar parser", function() {
result
) ;
if ( ! passed && props !== undefined ) {
if ( ! passed && typeof props !== "undefined" ) {
Object . keys ( props ) . forEach ( key => {
new Assertion ( result ) . to . have . property ( key )
new Assertion ( result )
. to . have . property ( key )
. that . is . deep . equal ( props [ key ] ) ;
} ) ;
}
} ) ;
}
// Helper activation needs to put inside a |beforeEach| block because the
// helpers conflict with the ones in
// test/behavior/generated-parser-behavior.spec.js.
beforeEach ( function ( ) {
chai . use ( helpers ) ;
} ) ;
// Grammars without any rules are not accepted.
it ( "parses Rule+" , function ( ) {
expect ( "start = a" ) . to . parseAs ( ruleRefGrammar ( "a" ) ) ;
le t grammar = ruleRefGrammar ( "a" ) ;
cons t grammar = ruleRefGrammar ( "a" ) ;
grammar . initializer = {
"type" : "initializer" ,
"code" : ""
@ -238,10 +291,12 @@ describe("PEG.js grammar parser", function() {
expect ( "" ) . to . failToParse ( ) ;
expect ( "{}" ) . to . failToParse ( ) ;
} ) ;
// Canonical Grammar is "a = 'abcd'; b = 'efgh'; c = 'ijkl';".
it ( "parses Grammar" , function ( ) {
expect ( "\na = 'abcd';\n" ) . to . parseAs (
{ type : "grammar" , initializer : null , rules : [ ruleA ] }
) ;
@ -251,34 +306,42 @@ describe("PEG.js grammar parser", function() {
expect ( "\n{ code };\na = 'abcd';\n" ) . to . parseAs (
{ type : "grammar" , initializer : initializer , rules : [ ruleA ] }
) ;
} ) ;
// Canonical Initializer is "{ code }".
it ( "parses Initializer" , function ( ) {
expect ( "{ code };start = 'abcd'" ) . to . parseAs (
{ type : "grammar" , initializer : initializer , rules : [ ruleStart ] }
) ;
} ) ;
// Canonical Rule is "a = 'abcd';".
it ( "parses Rule" , function ( ) {
expect ( "start\n=\n'abcd';" ) . to . parseAs (
oneRuleGrammar ( literalAbcd )
) ;
expect ( "start\n'start rule'\n=\n'abcd';" ) . to . parseAs (
oneRuleGrammar ( named )
) ;
} ) ;
// Canonical Expression is "'abcd'".
it ( "parses Expression" , function ( ) {
expect ( "start = 'abcd' / 'efgh' / 'ijkl'" ) . to . parseAs (
oneRuleGrammar ( choice )
) ;
} ) ;
// Canonical ChoiceExpression is "'abcd' / 'efgh' / 'ijkl'".
it ( "parses ChoiceExpression" , function ( ) {
expect ( "start = 'abcd' { code }" ) . to . parseAs (
oneRuleGrammar ( actionAbcd )
) ;
@ -290,20 +353,24 @@ describe("PEG.js grammar parser", function() {
) . to . parseAs (
oneRuleGrammar ( choice4 )
) ;
} ) ;
// Canonical ActionExpression is "'abcd' { code }".
it ( "parses ActionExpression" , function ( ) {
expect ( "start = 'abcd' 'efgh' 'ijkl'" ) . to . parseAs (
oneRuleGrammar ( sequence )
) ;
expect ( "start = 'abcd' 'efgh' 'ijkl'\n{ code }" ) . to . parseAs (
oneRuleGrammar ( actionSequence )
) ;
} ) ;
// Canonical SequenceExpression is "'abcd' 'efgh' 'ijkl'".
it ( "parses SequenceExpression" , function ( ) {
expect ( "start = a:'abcd'" ) . to . parseAs (
oneRuleGrammar ( labeledAbcd )
) ;
@ -313,42 +380,54 @@ describe("PEG.js grammar parser", function() {
expect ( "start = a:'abcd'\nb:'efgh'\nc:'ijkl'\nd:'mnop'" ) . to . parseAs (
oneRuleGrammar ( sequence4 )
) ;
} ) ;
// Canonical LabeledExpression is "a:'abcd'".
it ( "parses LabeledExpression" , function ( ) {
expect ( "start = a\n:\n!'abcd'" ) . to . parseAs ( oneRuleGrammar ( labeledSimpleNot ) ) ;
expect ( "start = !'abcd'" ) . to . parseAs ( oneRuleGrammar ( simpleNotAbcd ) ) ;
} ) ;
// Canonical PrefixedExpression is "!'abcd'".
it ( "parses PrefixedExpression" , function ( ) {
expect ( "start = !\n'abcd'?" ) . to . parseAs ( oneRuleGrammar ( simpleNotOptional ) ) ;
expect ( "start = 'abcd'?" ) . to . parseAs ( oneRuleGrammar ( optional ) ) ;
} ) ;
// Canonical PrefixedOperator is "!".
it ( "parses PrefixedOperator" , function ( ) {
expect ( "start = $'abcd'?" ) . to . parseAs ( oneRuleGrammar ( textOptional ) ) ;
expect ( "start = &'abcd'?" ) . to . parseAs ( oneRuleGrammar ( simpleAndOptional ) ) ;
expect ( "start = !'abcd'?" ) . to . parseAs ( oneRuleGrammar ( simpleNotOptional ) ) ;
} ) ;
// Canonical SuffixedExpression is "'abcd'?".
it ( "parses SuffixedExpression" , function ( ) {
expect ( "start = 'abcd'\n?" ) . to . parseAs ( oneRuleGrammar ( optional ) ) ;
expect ( "start = 'abcd'" ) . to . parseAs ( oneRuleGrammar ( literalAbcd ) ) ;
} ) ;
// Canonical SuffixedOperator is "?".
it ( "parses SuffixedOperator" , function ( ) {
expect ( "start = 'abcd'?" ) . to . parseAs ( oneRuleGrammar ( optional ) ) ;
expect ( "start = 'abcd'*" ) . to . parseAs ( oneRuleGrammar ( zeroOrMore ) ) ;
expect ( "start = 'abcd'+" ) . to . parseAs ( oneRuleGrammar ( oneOrMore ) ) ;
} ) ;
// Canonical PrimaryExpression is "'abcd'".
it ( "parses PrimaryExpression" , function ( ) {
expect ( "start = 'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start = [a-d]" ) . to . parseAs ( classGrammar ( [ [ "a" , "d" ] ] , false , false ) ) ;
expect ( "start = ." ) . to . parseAs ( anyGrammar ( ) ) ;
@ -358,31 +437,39 @@ describe("PEG.js grammar parser", function() {
expect ( "start = (\na:'abcd'\n)" ) . to . parseAs ( oneRuleGrammar ( groupLabeled ) ) ;
expect ( "start = (\n'abcd' 'efgh' 'ijkl'\n)" ) . to . parseAs ( oneRuleGrammar ( groupSequence ) ) ;
expect ( "start = (\n'abcd'\n)" ) . to . parseAs ( trivialGrammar ) ;
} ) ;
// Canonical RuleReferenceExpression is "a".
it ( "parses RuleReferenceExpression" , function ( ) {
expect ( "start = a" ) . to . parseAs ( ruleRefGrammar ( "a" ) ) ;
expect ( "start = a\n=" ) . to . failToParse ( ) ;
expect ( "start = a\n'abcd'\n=" ) . to . failToParse ( ) ;
} ) ;
// Canonical SemanticPredicateExpression is "!{ code }".
it ( "parses SemanticPredicateExpression" , function ( ) {
expect ( "start = !\n{ code }" ) . to . parseAs ( oneRuleGrammar ( semanticNot ) ) ;
} ) ;
// Canonical SemanticPredicateOperator is "!".
it ( "parses SemanticPredicateOperator" , function ( ) {
expect ( "start = &{ code }" ) . to . parseAs ( oneRuleGrammar ( semanticAnd ) ) ;
expect ( "start = !{ code }" ) . to . parseAs ( oneRuleGrammar ( semanticNot ) ) ;
} ) ;
// The SourceCharacter rule is not tested.
// Canonical WhiteSpace is " ".
it ( "parses WhiteSpace" , function ( ) {
expect ( "start =\t'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =\v'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =\f'abcd'" ) . to . parseAs ( trivialGrammar ) ;
@ -390,99 +477,123 @@ describe("PEG.js grammar parser", function() {
expect ( "start =\u00A0'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =\uFEFF'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =\u1680'abcd'" ) . to . parseAs ( trivialGrammar ) ;
} ) ;
// Canonical LineTerminator is "\n".
it ( "parses LineTerminator" , function ( ) {
expect ( "start = '\n'" ) . to . failToParse ( ) ;
expect ( "start = '\r'" ) . to . failToParse ( ) ;
expect ( "start = '\u2028'" ) . to . failToParse ( ) ;
expect ( "start = '\u2029'" ) . to . failToParse ( ) ;
} ) ;
// Canonical LineTerminatorSequence is "\r\n".
it ( "parses LineTerminatorSequence" , function ( ) {
expect ( "start =\n'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =\r\n'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =\r'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =\u2028'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =\u2029'abcd'" ) . to . parseAs ( trivialGrammar ) ;
} ) ;
// Canonical Comment is "/* comment */".
it ( "parses Comment" , function ( ) {
expect ( "start =// comment\n'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =/* comment */'abcd'" ) . to . parseAs ( trivialGrammar ) ;
} ) ;
// Canonical MultiLineComment is "/* comment */".
it ( "parses MultiLineComment" , function ( ) {
expect ( "start =/**/'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =/*a*/'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =/*abc*/'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =/**/*/'abcd'" ) . to . failToParse ( ) ;
} ) ;
// Canonical MultiLineCommentNoLineTerminator is "/* comment */".
it ( "parses MultiLineCommentNoLineTerminator" , function ( ) {
expect ( "a = 'abcd'/**/\r\nb = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
expect ( "a = 'abcd'/*a*/\r\nb = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
expect ( "a = 'abcd'/*abc*/\r\nb = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
expect ( "a = 'abcd'/**/*/\r\nb = 'efgh'" ) . to . failToParse ( ) ;
expect ( "a = 'abcd'/*\n*/\r\nb = 'efgh'" ) . to . failToParse ( ) ;
} ) ;
// Canonical SingleLineComment is "// comment".
it ( "parses SingleLineComment" , function ( ) {
expect ( "start =//\n'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =//a\n'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =//abc\n'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =//\n@\n'abcd'" ) . to . failToParse ( ) ;
} ) ;
// Canonical Identifier is "a".
it ( "parses Identifier" , function ( ) {
expect ( "start = a:'abcd'" ) . to . parseAs ( oneRuleGrammar ( labeledAbcd ) ) ;
} ) ;
// Canonical IdentifierName is "a".
it ( "parses IdentifierName" , function ( ) {
expect ( "start = a" ) . to . parseAs ( ruleRefGrammar ( "a" ) ) ;
expect ( "start = ab" ) . to . parseAs ( ruleRefGrammar ( "ab" ) ) ;
expect ( "start = abcd" ) . to . parseAs ( ruleRefGrammar ( "abcd" ) ) ;
} ) ;
// Canonical IdentifierStart is "a".
it ( "parses IdentifierStart" , function ( ) {
expect ( "start = a" ) . to . parseAs ( ruleRefGrammar ( "a" ) ) ;
expect ( "start = $" ) . to . parseAs ( ruleRefGrammar ( "$" ) ) ;
expect ( "start = _" ) . to . parseAs ( ruleRefGrammar ( "_" ) ) ;
expect ( "start = \\u0061" ) . to . parseAs ( ruleRefGrammar ( "a" ) ) ;
} ) ;
// Canonical IdentifierPart is "a".
it ( "parses IdentifierPart" , function ( ) {
expect ( "start = aa" ) . to . parseAs ( ruleRefGrammar ( "aa" ) ) ;
expect ( "start = a\u0300" ) . to . parseAs ( ruleRefGrammar ( "a\u0300" ) ) ;
expect ( "start = a0" ) . to . parseAs ( ruleRefGrammar ( "a0" ) ) ;
expect ( "start = a\u203F" ) . to . parseAs ( ruleRefGrammar ( "a\u203F" ) ) ;
expect ( "start = a\u200C" ) . to . parseAs ( ruleRefGrammar ( "a\u200C" ) ) ;
expect ( "start = a\u200D" ) . to . parseAs ( ruleRefGrammar ( "a\u200D" ) ) ;
} ) ;
// Unicode rules and reserved word rules are not tested.
// Canonical LiteralMatcher is "'abcd'".
it ( "parses LiteralMatcher" , function ( ) {
expect ( "start = 'abcd'" ) . to . parseAs ( literalGrammar ( "abcd" , false ) ) ;
expect ( "start = 'abcd'i" ) . to . parseAs ( literalGrammar ( "abcd" , true ) ) ;
} ) ;
// Canonical StringLiteral is "'abcd'".
it ( "parses StringLiteral" , function ( ) {
expect ( "start = \"\"" ) . to . parseAs ( literalGrammar ( "" , false ) ) ;
expect ( "start = \"a\"" ) . to . parseAs ( literalGrammar ( "a" , false ) ) ;
expect ( "start = \"abc\"" ) . to . parseAs ( literalGrammar ( "abc" , false ) ) ;
@ -490,10 +601,12 @@ describe("PEG.js grammar parser", function() {
expect ( "start = ''" ) . to . parseAs ( literalGrammar ( "" , false ) ) ;
expect ( "start = 'a'" ) . to . parseAs ( literalGrammar ( "a" , false ) ) ;
expect ( "start = 'abc'" ) . to . parseAs ( literalGrammar ( "abc" , false ) ) ;
} ) ;
// Canonical DoubleStringCharacter is "a".
it ( "parses DoubleStringCharacter" , function ( ) {
expect ( "start = \"a\"" ) . to . parseAs ( literalGrammar ( "a" , false ) ) ;
expect ( "start = \"\\n\"" ) . to . parseAs ( literalGrammar ( "\n" , false ) ) ;
expect ( "start = \"\\\n\"" ) . to . parseAs ( literalGrammar ( "" , false ) ) ;
@ -501,10 +614,12 @@ describe("PEG.js grammar parser", function() {
expect ( "start = \"\"\"" ) . to . failToParse ( ) ;
expect ( "start = \"\\\"" ) . to . failToParse ( ) ;
expect ( "start = \"\n\"" ) . to . failToParse ( ) ;
} ) ;
// Canonical SingleStringCharacter is "a".
it ( "parses SingleStringCharacter" , function ( ) {
expect ( "start = 'a'" ) . to . parseAs ( literalGrammar ( "a" , false ) ) ;
expect ( "start = '\\n'" ) . to . parseAs ( literalGrammar ( "\n" , false ) ) ;
expect ( "start = '\\\n'" ) . to . parseAs ( literalGrammar ( "" , false ) ) ;
@ -512,10 +627,12 @@ describe("PEG.js grammar parser", function() {
expect ( "start = '''" ) . to . failToParse ( ) ;
expect ( "start = '\\'" ) . to . failToParse ( ) ;
expect ( "start = '\n'" ) . to . failToParse ( ) ;
} ) ;
// Canonical CharacterClassMatcher is "[a-d]".
it ( "parses CharacterClassMatcher" , function ( ) {
expect ( "start = []" ) . to . parseAs (
classGrammar ( [ ] , false , false )
) ;
@ -542,20 +659,24 @@ describe("PEG.js grammar parser", function() {
expect ( "start = [\\\n]" ) . to . parseAs (
classGrammar ( [ ] , false , false )
) ;
} ) ;
// Canonical ClassCharacterRange is "a-d".
it ( "parses ClassCharacterRange" , function ( ) {
expect ( "start = [a-d]" ) . to . parseAs ( classGrammar ( [ [ "a" , "d" ] ] , false , false ) ) ;
expect ( "start = [a-a]" ) . to . parseAs ( classGrammar ( [ [ "a" , "a" ] ] , false , false ) ) ;
expect ( "start = [b-a]" ) . to . failToParse ( {
message : "Invalid character range: b-a."
} ) ;
} ) ;
// Canonical ClassCharacter is "a".
it ( "parses ClassCharacter" , function ( ) {
expect ( "start = [a]" ) . to . parseAs ( classGrammar ( [ "a" ] , false , false ) ) ;
expect ( "start = [\\n]" ) . to . parseAs ( classGrammar ( [ "\n" ] , false , false ) ) ;
expect ( "start = [\\\n]" ) . to . parseAs ( classGrammar ( [ ] , false , false ) ) ;
@ -563,31 +684,39 @@ describe("PEG.js grammar parser", function() {
expect ( "start = []]" ) . to . failToParse ( ) ;
expect ( "start = [\\]" ) . to . failToParse ( ) ;
expect ( "start = [\n]" ) . to . failToParse ( ) ;
} ) ;
// Canonical LineContinuation is "\\\n".
it ( "parses LineContinuation" , function ( ) {
expect ( "start = '\\\r\n'" ) . to . parseAs ( literalGrammar ( "" , false ) ) ;
} ) ;
// Canonical EscapeSequence is "n".
it ( "parses EscapeSequence" , function ( ) {
expect ( "start = '\\n'" ) . to . parseAs ( literalGrammar ( "\n" , false ) ) ;
expect ( "start = '\\0'" ) . to . parseAs ( literalGrammar ( "\x00" , false ) ) ;
expect ( "start = '\\xFF'" ) . to . parseAs ( literalGrammar ( "\xFF" , false ) ) ;
expect ( "start = '\\uFFFF'" ) . to . parseAs ( literalGrammar ( "\uFFFF" , false ) ) ;
expect ( "start = '\\09'" ) . to . failToParse ( ) ;
} ) ;
// Canonical CharacterEscapeSequence is "n".
it ( "parses CharacterEscapeSequence" , function ( ) {
expect ( "start = '\\n'" ) . to . parseAs ( literalGrammar ( "\n" , false ) ) ;
expect ( "start = '\\a'" ) . to . parseAs ( literalGrammar ( "a" , false ) ) ;
} ) ;
// Canonical SingleEscapeCharacter is "n".
it ( "parses SingleEscapeCharacter" , function ( ) {
expect ( "start = '\\''" ) . to . parseAs ( literalGrammar ( "'" , false ) ) ;
expect ( "start = '\\\"'" ) . to . parseAs ( literalGrammar ( "\"" , false ) ) ;
expect ( "start = '\\\\'" ) . to . parseAs ( literalGrammar ( "\\" , false ) ) ;
@ -597,14 +726,17 @@ describe("PEG.js grammar parser", function() {
expect ( "start = '\\r'" ) . to . parseAs ( literalGrammar ( "\r" , false ) ) ;
expect ( "start = '\\t'" ) . to . parseAs ( literalGrammar ( "\t" , false ) ) ;
expect ( "start = '\\v'" ) . to . parseAs ( literalGrammar ( "\v" , false ) ) ;
} ) ;
// Canonical NonEscapeCharacter is "a".
it ( "parses NonEscapeCharacter" , function ( ) {
expect ( "start = '\\a'" ) . to . parseAs ( literalGrammar ( "a" , false ) ) ;
// The negative predicate is impossible to test with PEG.js grammar
// structure.
} ) ;
// The EscapeCharacter rule is impossible to test with PEG.js grammar
@ -612,28 +744,37 @@ describe("PEG.js grammar parser", function() {
// Canonical HexEscapeSequence is "xFF".
it ( "parses HexEscapeSequence" , function ( ) {
expect ( "start = '\\xFF'" ) . to . parseAs ( literalGrammar ( "\xFF" , false ) ) ;
} ) ;
// Canonical UnicodeEscapeSequence is "uFFFF".
it ( "parses UnicodeEscapeSequence" , function ( ) {
expect ( "start = '\\uFFFF'" ) . to . parseAs ( literalGrammar ( "\uFFFF" , false ) ) ;
} ) ;
// Digit rules are not tested.
// Canonical AnyMatcher is ".".
it ( "parses AnyMatcher" , function ( ) {
expect ( "start = ." ) . to . parseAs ( anyGrammar ( ) ) ;
} ) ;
// Canonical CodeBlock is "{ code }".
it ( "parses CodeBlock" , function ( ) {
expect ( "start = 'abcd' { code }" ) . to . parseAs ( actionGrammar ( " code " ) ) ;
} ) ;
// Canonical Code is " code ".
it ( "parses Code" , function ( ) {
expect ( "start = 'abcd' {a}" ) . to . parseAs ( actionGrammar ( "a" ) ) ;
expect ( "start = 'abcd' {abc}" ) . to . parseAs ( actionGrammar ( "abc" ) ) ;
expect ( "start = 'abcd' {{a}}" ) . to . parseAs ( actionGrammar ( "{a}" ) ) ;
@ -641,41 +782,51 @@ describe("PEG.js grammar parser", function() {
expect ( "start = 'abcd' {{}" ) . to . failToParse ( ) ;
expect ( "start = 'abcd' {}}" ) . to . failToParse ( ) ;
} ) ;
// Unicode character category rules and token rules are not tested.
// Canonical __ is "\n".
it ( "parses __" , function ( ) {
expect ( "start ='abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start = 'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =\r\n'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start =/* comment */'abcd'" ) . to . parseAs ( trivialGrammar ) ;
expect ( "start = 'abcd'" ) . to . parseAs ( trivialGrammar ) ;
} ) ;
// Canonical _ is " ".
it ( "parses _" , function ( ) {
expect ( "a = 'abcd'\r\nb = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
expect ( "a = 'abcd' \r\nb = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
expect ( "a = 'abcd'/* comment */\r\nb = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
expect ( "a = 'abcd' \r\nb = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
} ) ;
// Canonical EOS is ";".
it ( "parses EOS" , function ( ) {
expect ( "a = 'abcd'\n;b = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
expect ( "a = 'abcd' \r\nb = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
expect ( "a = 'abcd' // comment\r\nb = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
expect ( "a = 'abcd'\nb = 'efgh'" ) . to . parseAs ( twoRuleGrammar ) ;
} ) ;
// Canonical EOF is the end of input.
it ( "parses EOF" , function ( ) {
expect ( "start = 'abcd'\n" ) . to . parseAs ( trivialGrammar ) ;
} ) ;
it ( "reports unmatched brace" , function ( ) {
const text = "rule = \n 'x' { y \n z" ;
const errorLocation = {
start : { offset : 13 , line : 2 , column : 6 } ,
@ -685,5 +836,7 @@ describe("PEG.js grammar parser", function() {
. to . throw ( "Unbalanced brace." )
. with . property ( "location" )
. that . deep . equals ( errorLocation ) ;
} ) ;
} ) ;