@ -2,8 +2,19 @@
module ( "PEG.compiler" ) ;
test ( "choices" , function ( ) {
var parser = PEG . buildParser ( 'start = "a" / "b" / "c"' ) ;
function testWithVaryingTrackLineAndColumn ( name , callback ) {
test (
name + " (with trackLineAndColumn: false) " ,
function ( ) { callback ( { trackLineAndColumn : false } ) ; }
) ;
test (
name + " (with trackLineAndColumn: true) " ,
function ( ) { callback ( { trackLineAndColumn : true } ) ; }
) ;
}
testWithVaryingTrackLineAndColumn ( "choices" , function ( options ) {
var parser = PEG . buildParser ( 'start = "a" / "b" / "c"' , options ) ;
parses ( parser , "a" , "a" ) ;
parses ( parser , "b" , "b" ) ;
parses ( parser , "c" , "c" ) ;
@ -12,12 +23,12 @@ test("choices", function() {
doesNotParse ( parser , "d" ) ;
} ) ;
test ( "sequences" , function ( ) {
var emptySequenceParser = PEG . buildParser ( 'start = ' );
test WithVaryingTrackLineAndColumn ( "sequences" , function ( options ) {
var emptySequenceParser = PEG . buildParser ( 'start = ' , options );
parses ( emptySequenceParser , "" , [ ] ) ;
doesNotParse ( emptySequenceParser , "abc" ) ;
var nonEmptySequenceParser = PEG . buildParser ( 'start = "a" "b" "c"' );
var nonEmptySequenceParser = PEG . buildParser ( 'start = "a" "b" "c"' , options );
parses ( nonEmptySequenceParser , "abc" , [ "a" , "b" , "c" ] ) ;
doesNotParse ( nonEmptySequenceParser , "" ) ;
doesNotParse ( nonEmptySequenceParser , "ab" ) ;
@ -28,18 +39,18 @@ 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"' , options );
parses ( posTestParser , "a" , "a" ) ;
} ) ;
test ( "labels" , function ( ) {
var parser = PEG . buildParser ( 'start = label:"a"' );
test WithVaryingTrackLineAndColumn ( "labels" , function ( options ) {
var parser = PEG . buildParser ( 'start = label:"a"' , options );
parses ( parser , "a" , "a" ) ;
doesNotParse ( parser , "b" ) ;
} ) ;
test ( "simple and" , function ( ) {
var parser = PEG . buildParser ( 'start = "a" &"b" "b"' );
test WithVaryingTrackLineAndColumn ( "simple and" , function ( options ) {
var parser = PEG . buildParser ( 'start = "a" &"b" "b"' , options );
parses ( parser , "ab" , [ "a" , "" , "b" ] ) ;
doesNotParse ( parser , "ac" ) ;
@ -49,8 +60,8 @@ test("simple and", function() {
* /
} ) ;
test ( "simple not" , function ( ) {
var parser = PEG . buildParser ( 'start = "a" !"b"' );
test WithVaryingTrackLineAndColumn ( "simple not" , function ( options ) {
var parser = PEG . buildParser ( 'start = "a" !"b"' , options );
parses ( parser , "a" , [ "a" , "" ] ) ;
doesNotParse ( parser , "ab" ) ;
@ -62,15 +73,24 @@ test("simple not", function() {
parses ( posTestParser , "ac" , [ "a" , "" , "c" ] ) ;
} ) ;
test ( "semantic and" , function ( ) {
var acceptingParser = PEG . buildParser ( 'start = "a" &{ return true; } "b"' ) ;
test ( "semantic and (with trackLineAndColumn: false)" , function ( ) {
var options = { trackLineAndColumn : false } ;
var acceptingParser = PEG . buildParser (
'start = "a" &{ return true; } "b"' ,
options
) ;
parses ( acceptingParser , "ab" , [ "a" , "" , "b" ] ) ;
var rejectingParser = PEG . buildParser ( 'start = "a" &{ return false; } "b"' ) ;
var rejectingParser = PEG . buildParser (
'start = "a" &{ return false; } "b"' ,
options
) ;
doesNotParse ( rejectingParser , "ab" ) ;
var singleElementUnlabeledParser = PEG . buildParser (
'start = "a" &{ return arguments.length === 1 && offset === 1; }'
'start = "a" &{ return arguments.length === 1 && offset === 1; }' ,
options
) ;
parses ( singleElementUnlabeledParser , "a" , [ "a" , "" ] ) ;
@ -80,11 +100,12 @@ test("semantic and", function() {
' && offset === 1' ,
' && a === "a";' ,
' }'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( singleElementLabeledParser , "a" , [ "a" , "" ] ) ;
var multiElementUnlabeledParser = PEG . buildParser (
'start = "a" "b" "c" &{ return arguments.length === 1 && offset === 3; }'
'start = "a" "b" "c" &{ return arguments.length === 1 && offset === 3; }' ,
options
) ;
parses ( multiElementUnlabeledParser , "abc" , [ "a" , "b" , "c" , "" ] ) ;
@ -95,14 +116,14 @@ test("semantic and", function() {
' && a === "a"' ,
' && c === "c";' ,
' }'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( multiElementLabeledParser , "abc" , [ "a" , "b" , "c" , "" ] ) ;
var innerElementsUnlabeledParser = PEG . buildParser ( [
'start = "a"' ,
' ("b" "c" "d" &{ return arguments.length === 1 && offset === 4; })' ,
' "e"'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( innerElementsUnlabeledParser , "abcde" , [ "a" , [ "b" , "c" , "d" , "" ] , "e" ] ) ;
var innerElementsLabeledParser = PEG . buildParser ( [
@ -116,19 +137,138 @@ test("semantic and", function() {
' }' ,
' )' ,
' "e"'
] . join ( "\n" ) ) ;
] . join ( "\n" ) , options ) ;
parses ( innerElementsLabeledParser , "abcde" , [ "a" , [ "b" , "c" , "d" , "" ] , "e" ] ) ;
} ) ;
test ( "semantic and (with trackLineAndColumn: true)" , function ( ) {
var options = { trackLineAndColumn : true } ;
var acceptingParser = PEG . buildParser (
'start = "a" &{ return true; } "b"' ,
options
) ;
parses ( acceptingParser , "ab" , [ "a" , "" , "b" ] ) ;
var rejectingParser = PEG . buildParser (
'start = "a" &{ return false; } "b"' ,
options
) ;
doesNotParse ( rejectingParser , "ab" ) ;
var singleElementUnlabeledParser = PEG . buildParser ( [
'start = "a" &{' ,
' return arguments.length === 3' ,
' && offset === 1' ,
' && line === 1' ,
' && column === 2;' ,
' }'
] . join ( "\n" ) , options ) ;
parses ( singleElementUnlabeledParser , "a" , [ "a" , "" ] ) ;
var singleElementLabeledParser = PEG . buildParser ( [
'start = a:"a" &{' ,
' return arguments.length === 4' ,
' && offset === 1' ,
' && line === 1' ,
' && column === 2' ,
' && a === "a";' ,
' }'
] . join ( "\n" ) , options ) ;
parses ( singleElementLabeledParser , "a" , [ "a" , "" ] ) ;
var multiElementUnlabeledParser = PEG . buildParser ( [
'start = "a" "b" "c" &{' ,
' return arguments.length === 3' ,
' && offset === 3' ,
' && line === 1' ,
' && column === 4;' ,
' }'
] . join ( "\n" ) , options ) ;
parses ( multiElementUnlabeledParser , "abc" , [ "a" , "b" , "c" , "" ] ) ;
var multiElementLabeledParser = PEG . buildParser ( [
'start = a:"a" "b" c:"c" &{' ,
' return arguments.length === 5' ,
' && offset === 3' ,
' && line === 1' ,
' && column === 4' ,
' && a === "a"' ,
' && c === "c";' ,
' }'
] . join ( "\n" ) , options ) ;
parses ( multiElementLabeledParser , "abc" , [ "a" , "b" , "c" , "" ] ) ;
var innerElementsUnlabeledParser = PEG . buildParser ( [
'start = "a"' ,
' (' ,
' "b" "c" "d" &{' ,
' return arguments.length === 3' ,
' && offset === 4' ,
' && line === 1' ,
' && column === 5;' ,
' }' ,
' )' ,
' "e"'
] . join ( "\n" ) , options ) ;
parses ( innerElementsUnlabeledParser , "abcde" , [ "a" , [ "b" , "c" , "d" , "" ] , "e" ] ) ;
var innerElementsLabeledParser = PEG . buildParser ( [
'start = "a"' ,
' (' ,
' b:"b" "c" d:"d" &{' ,
' return arguments.length === 5' ,
' && offset === 4' ,
' && line === 1' ,
' && column === 5' ,
' && b === "b"' ,
' && d === "d";' ,
' }' ,
' )' ,
' "e"'
] . join ( "\n" ) , options ) ;
parses ( innerElementsLabeledParser , "abcde" , [ "a" , [ "b" , "c" , "d" , "" ] , "e" ] ) ;
var digitsParser = PEG . buildParser ( [
'{ var result; }' ,
'start = line (nl+ line)* { return result; }' ,
'line = thing (" "+ thing)*' ,
'thing = digit / mark' ,
'digit = [0-9]' ,
'mark = &{ result = [line, column]; return true; } "x"' ,
'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")'
] . join ( "\n" ) , options ) ;
parses ( digitsParser , "1\n2\n\n3\n\n\n4 5 x" , [ 7 , 5 ] ) ;
/* Non-Unix newlines */
parses ( digitsParser , "1\rx" , [ 2 , 1 ] ) ; // Old Mac
parses ( digitsParser , "1\r\nx" , [ 2 , 1 ] ) ; // Windows
parses ( digitsParser , "1\n\rx" , [ 3 , 1 ] ) ; // mismatched
/* Strange newlines */
parses ( digitsParser , "1\u2028x" , [ 2 , 1 ] ) ; // line separator
parses ( digitsParser , "1\u2029x" , [ 2 , 1 ] ) ; // paragraph separator
} ) ;
test ( "semantic not" , function ( ) {
var acceptingParser = PEG . buildParser ( 'start = "a" !{ return false; } "b"' ) ;
test ( "semantic not (with trackLineAndColumn: false)" , function ( ) {
var options = { trackLineAndColumn : false } ;
var acceptingParser = PEG . buildParser (
'start = "a" !{ return false; } "b"' ,
options
) ;
parses ( acceptingParser , "ab" , [ "a" , "" , "b" ] ) ;
var rejectingParser = PEG . buildParser ( 'start = "a" !{ return true; } "b"' ) ;
var rejectingParser = PEG . buildParser (
'start = "a" !{ return true; } "b"' ,
options
) ;
doesNotParse ( rejectingParser , "ab" ) ;
var singleElementUnlabeledParser = PEG . buildParser (
'start = "a" !{ return arguments.length !== 1 || offset !== 1; }'
'start = "a" !{ return arguments.length !== 1 || offset !== 1; }' ,
options
) ;
parses ( singleElementUnlabeledParser , "a" , [ "a" , "" ] ) ;
@ -138,11 +278,12 @@ test("semantic not", function() {
' || offset !== 1' ,
' || a !== "a";' ,
' }'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( singleElementLabeledParser , "a" , [ "a" , "" ] ) ;
var multiElementUnlabeledParser = PEG . buildParser (
'start = "a" "b" "c" !{ return arguments.length !== 1 || offset !== 3; }'
'start = "a" "b" "c" !{ return arguments.length !== 1 || offset !== 3; }' ,
options
) ;
parses ( multiElementUnlabeledParser , "abc" , [ "a" , "b" , "c" , "" ] ) ;
@ -153,14 +294,14 @@ test("semantic not", function() {
' || a !== "a"' ,
' || c !== "c";' ,
' }'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( multiElementLabeledParser , "abc" , [ "a" , "b" , "c" , "" ] ) ;
var innerElementsUnlabeledParser = PEG . buildParser ( [
'start = "a"' ,
' ("b" "c" "d" !{ return arguments.length !== 1 || offset !== 4; })' ,
' "e"'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( innerElementsUnlabeledParser , "abcde" , [ "a" , [ "b" , "c" , "d" , "" ] , "e" ] ) ;
var innerElementsLabeledParser = PEG . buildParser ( [
@ -174,53 +315,170 @@ test("semantic not", function() {
' }' ,
' )' ,
' "e"'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( innerElementsLabeledParser , "abcde" , [ "a" , [ "b" , "c" , "d" , "" ] , "e" ] ) ;
} ) ;
test ( "optional expressions" , function ( ) {
var parser = PEG . buildParser ( 'start = "a"?' ) ;
test ( "semantic not (with trackLineAndColumn: true)" , function ( ) {
var options = { trackLineAndColumn : true } ;
var acceptingParser = PEG . buildParser (
'start = "a" !{ return false; } "b"' ,
options
) ;
parses ( acceptingParser , "ab" , [ "a" , "" , "b" ] ) ;
var rejectingParser = PEG . buildParser (
'start = "a" !{ return true; } "b"' ,
options
) ;
doesNotParse ( rejectingParser , "ab" ) ;
var singleElementUnlabeledParser = PEG . buildParser ( [
'start = "a" !{' ,
' return arguments.length !== 3' ,
' || offset !== 1' ,
' || line !== 1' ,
' || column !== 2;' ,
' }'
] . join ( "\n" ) , options ) ;
parses ( singleElementUnlabeledParser , "a" , [ "a" , "" ] ) ;
var singleElementLabeledParser = PEG . buildParser ( [
'start = a:"a" !{' ,
' return arguments.length !== 4' ,
' || offset !== 1' ,
' || line !== 1' ,
' || column !== 2' ,
' || a !== "a";' ,
' }'
] . join ( "\n" ) , options ) ;
parses ( singleElementLabeledParser , "a" , [ "a" , "" ] ) ;
var multiElementUnlabeledParser = PEG . buildParser ( [
'start = "a" "b" "c" !{' ,
' return arguments.length !== 3' ,
' || offset !== 3' ,
' || line !== 1' ,
' || column !== 4;' ,
' }'
] . join ( "\n" ) , options ) ;
parses ( multiElementUnlabeledParser , "abc" , [ "a" , "b" , "c" , "" ] ) ;
var multiElementLabeledParser = PEG . buildParser ( [
'start = a:"a" "b" c:"c" !{' ,
' return arguments.length !== 5' ,
' || offset !== 3' ,
' || line !== 1' ,
' || column !== 4' ,
' || a !== "a"' ,
' || c !== "c";' ,
' }'
] . join ( "\n" ) , options ) ;
parses ( multiElementLabeledParser , "abc" , [ "a" , "b" , "c" , "" ] ) ;
var innerElementsUnlabeledParser = PEG . buildParser ( [
'start = "a"' ,
' (' ,
' "b" "c" "d" !{' ,
' return arguments.length !== 3' ,
' || offset !== 4' ,
' || line !== 1' ,
' || column !== 5;' ,
' }' ,
' )' ,
' "e"'
] . join ( "\n" ) , options ) ;
parses ( innerElementsUnlabeledParser , "abcde" , [ "a" , [ "b" , "c" , "d" , "" ] , "e" ] ) ;
var innerElementsLabeledParser = PEG . buildParser ( [
'start = "a"' ,
' (' ,
' b:"b" "c" d:"d" !{' ,
' return arguments.length !== 5' ,
' || offset !== 4' ,
' || line !== 1' ,
' || column !== 5' ,
' || b !== "b"' ,
' || d !== "d";' ,
' }' ,
' )' ,
' "e"'
] . join ( "\n" ) , options ) ;
parses ( innerElementsLabeledParser , "abcde" , [ "a" , [ "b" , "c" , "d" , "" ] , "e" ] ) ;
var digitsParser = PEG . buildParser ( [
'{ var result; }' ,
'start = line (nl+ line)* { return result; }' ,
'line = thing (" "+ thing)*' ,
'thing = digit / mark' ,
'digit = [0-9]' ,
'mark = !{ result = [line, column]; return false; } "x"' ,
'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")'
] . join ( "\n" ) , options ) ;
parses ( digitsParser , "1\n2\n\n3\n\n\n4 5 x" , [ 7 , 5 ] ) ;
/* Non-Unix newlines */
parses ( digitsParser , "1\rx" , [ 2 , 1 ] ) ; // Old Mac
parses ( digitsParser , "1\r\nx" , [ 2 , 1 ] ) ; // Windows
parses ( digitsParser , "1\n\rx" , [ 3 , 1 ] ) ; // mismatched
/* Strange newlines */
parses ( digitsParser , "1\u2028x" , [ 2 , 1 ] ) ; // line separator
parses ( digitsParser , "1\u2029x" , [ 2 , 1 ] ) ; // paragraph separator
} ) ;
testWithVaryingTrackLineAndColumn ( "optional expressions" , function ( options ) {
var parser = PEG . buildParser ( 'start = "a"?' , options ) ;
parses ( parser , "" , "" ) ;
parses ( parser , "a" , "a" ) ;
} ) ;
test ( "zero or more expressions" , function ( ) {
var parser = PEG . buildParser ( 'start = "a"*' ) ;
test WithVaryingTrackLineAndColumn ( "zero or more expressions" , function ( options ) {
var parser = PEG . buildParser ( 'start = "a"*' , options );
parses ( parser , "" , [ ] ) ;
parses ( parser , "a" , [ "a" ] ) ;
parses ( parser , "aaa" , [ "a" , "a" , "a" ] ) ;
} ) ;
test ( "one or more expressions" , function ( ) {
var parser = PEG . buildParser ( 'start = "a"+' ) ;
test WithVaryingTrackLineAndColumn ( "one or more expressions" , function ( options ) {
var parser = PEG . buildParser ( 'start = "a"+' , options );
doesNotParse ( parser , "" ) ;
parses ( parser , "a" , [ "a" ] ) ;
parses ( parser , "aaa" , [ "a" , "a" , "a" ] ) ;
} ) ;
test ( "actions" , function ( ) {
test ( "actions (with trackLineAndColumn: false)" , function ( ) {
var options = { trackLineAndColumn : false } ;
var singleElementUnlabeledParser = PEG . buildParser (
'start = "a" { return arguments.length; }'
'start = "a" { return arguments.length; }' ,
options
) ;
parses ( singleElementUnlabeledParser , "a" , 1 ) ;
var singleElementLabeledParser = PEG . buildParser (
'start = a:"a" { return [arguments.length, offset, a]; }'
'start = a:"a" { return [arguments.length, offset, a]; }' ,
options
) ;
parses ( singleElementLabeledParser , "a" , [ 2 , 0 , "a" ] ) ;
var multiElementUnlabeledParser = PEG . buildParser (
'start = "a" "b" "c" { return arguments.length; }'
'start = "a" "b" "c" { return arguments.length; }' ,
options
) ;
parses ( multiElementUnlabeledParser , "abc" , 1 ) ;
var multiElementLabeledParser = PEG . buildParser (
'start = a:"a" "b" c:"c" { return [arguments.length, offset, a, c]; }'
'start = a:"a" "b" c:"c" { return [arguments.length, offset, a, c]; }' ,
options
) ;
parses ( multiElementLabeledParser , "abc" , [ 3 , 0 , "a" , "c" ] ) ;
var innerElementsUnlabeledParser = PEG . buildParser (
'start = "a" ("b" "c" "d" { return arguments.length; }) "e"'
'start = "a" ("b" "c" "d" { return arguments.length; }) "e"' ,
options
) ;
parses ( innerElementsUnlabeledParser , "abcde" , [ "a" , 1 , "e" ] ) ;
@ -228,77 +486,175 @@ test("actions", function() {
'start = "a"' ,
' (b:"b" "c" d:"d" { return [arguments.length, offset, b, d]; })' ,
' "e"'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( innerElementsLabeledParser , "abcde" , [ "a" , [ 3 , 1 , "b" , "d" ] , "e" ] ) ;
/ *
* Test that the parsing position returns after successfull parsing of the
* action expression and action returning | null | .
* /
var posTestParser = PEG . buildParser ( 'start = "a" { return null; } / "a"' ) ;
var posTestParser = PEG . buildParser (
'start = "a" { return null; } / "a"' ,
options
) ;
parses ( posTestParser , "a" , "a" ) ;
/* 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"); }' ,
options
) ;
doesNotParse ( notAMatchParser , "b" ) ;
} ) ;
test ( "initializer" , function ( ) {
test ( "actions (with trackLineAndColumn: true)" , function ( ) {
var options = { trackLineAndColumn : true } ;
var singleElementUnlabeledParser = PEG . buildParser (
'start = "a" { return arguments.length; }' ,
options
) ;
parses ( singleElementUnlabeledParser , "a" , 3 ) ;
var singleElementLabeledParser = PEG . buildParser (
'start = a:"a" { return [arguments.length, offset, line, column, a]; }' ,
options
) ;
parses ( singleElementLabeledParser , "a" , [ 4 , 0 , 1 , 1 , "a" ] ) ;
var multiElementUnlabeledParser = PEG . buildParser (
'start = "a" "b" "c" { return arguments.length; }' ,
options
) ;
parses ( multiElementUnlabeledParser , "abc" , 3 ) ;
var multiElementLabeledParser = PEG . buildParser ( [
'start = a:"a" "b" c:"c" {' ,
' return [arguments.length, offset, line, column, a, c];' ,
'}'
] . join ( "\n" ) , options ) ;
parses ( multiElementLabeledParser , "abc" , [ 5 , 0 , 1 , 1 , "a" , "c" ] ) ;
var innerElementsUnlabeledParser = PEG . buildParser (
'start = "a" ("b" "c" "d" { return arguments.length; }) "e"' ,
options
) ;
parses ( innerElementsUnlabeledParser , "abcde" , [ "a" , 3 , "e" ] ) ;
var innerElementsLabeledParser = PEG . buildParser ( [
'start = "a"' ,
' (' ,
' b:"b" "c" d:"d" {' ,
' return [arguments.length, offset, line, column, b, d];' ,
' }' ,
' )' ,
' "e"'
] . join ( "\n" ) , options ) ;
parses (
innerElementsLabeledParser ,
"abcde" ,
[ "a" , [ 5 , 1 , 1 , 2 , "b" , "d" ] , "e" ]
) ;
/ *
* Test that the parsing position returns after successfull parsing of the
* action expression and action returning | null | .
* /
var posTestParser = PEG . buildParser (
'start = "a" { return null; } / "a"' ,
options
) ;
parses ( posTestParser , "a" , "a" ) ;
/* 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"); }' ,
options
) ;
doesNotParse ( notAMatchParser , "b" ) ;
var numbersParser = PEG . buildParser ( [
'{ var result; }' ,
'start = line (nl+ line)* { return result; }' ,
'line = thing (" "+ thing)*' ,
'thing = digit / mark' ,
'digit = [0-9]' ,
'mark = "x" { result = [line, column]; }' ,
'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")'
] . join ( "\n" ) , options ) ;
parses ( numbersParser , "1\n2\n\n3\n\n\n4 5 x" , [ 7 , 5 ] ) ;
/* Non-Unix newlines */
parses ( numbersParser , "1\rx" , [ 2 , 1 ] ) ; // Old Mac
parses ( numbersParser , "1\r\nx" , [ 2 , 1 ] ) ; // Windows
parses ( numbersParser , "1\n\rx" , [ 3 , 1 ] ) ; // mismatched
/* Strange newlines */
parses ( numbersParser , "1\u2028x" , [ 2 , 1 ] ) ; // line separator
parses ( numbersParser , "1\u2029x" , [ 2 , 1 ] ) ; // paragraph separator
} ) ;
testWithVaryingTrackLineAndColumn ( "initializer" , function ( options ) {
var variableInActionParser = PEG . buildParser (
'{ a = 42; }; start = "a" { return a; }'
'{ a = 42; }; start = "a" { return a; }' ,
options
) ;
parses ( variableInActionParser , "a" , 42 ) ;
var functionInActionParser = PEG . buildParser (
'{ function f() { return 42; } }; start = "a" { return f(); }'
'{ function f() { return 42; } }; start = "a" { return f(); }' ,
options
) ;
parses ( functionInActionParser , "a" , 42 ) ;
var variableInSemanticAndParser = PEG . buildParser (
'{ a = 42; }; start = "a" &{ return a === 42; }'
'{ a = 42; }; start = "a" &{ return a === 42; }' ,
options
) ;
parses ( variableInSemanticAndParser , "a" , [ "a" , "" ] ) ;
var functionInSemanticAndParser = PEG . buildParser (
'{ function f() { return 42; } }; start = "a" &{ return f() === 42; }'
'{ function f() { return 42; } }; start = "a" &{ return f() === 42; }' ,
options
) ;
parses ( functionInSemanticAndParser , "a" , [ "a" , "" ] ) ;
var variableInSemanticNotParser = PEG . buildParser (
'{ a = 42; }; start = "a" !{ return a !== 42; }'
'{ a = 42; }; start = "a" !{ return a !== 42; }' ,
options
) ;
parses ( variableInSemanticNotParser , "a" , [ "a" , "" ] ) ;
var functionInSemanticNotParser = PEG . buildParser (
'{ function f() { return 42; } }; start = "a" !{ return f() !== 42; }'
'{ function f() { return 42; } }; start = "a" !{ return f() !== 42; }' ,
options
) ;
parses ( functionInSemanticNotParser , "a" , [ "a" , "" ] ) ;
} ) ;
test ( "rule references" , function ( ) {
test WithVaryingTrackLineAndColumn ( "rule references" , function ( options ) {
var parser = PEG . buildParser ( [
'start = static / dynamic' ,
'static = "C" / "C++" / "Java" / "C#"' ,
'dynamic = "Ruby" / "Python" / "JavaScript"'
] . join ( "\n" ) ) ;
] . join ( "\n" ) , options );
parses ( parser , "Java" , "Java" ) ;
parses ( parser , "Python" , "Python" ) ;
} ) ;
test ( "literals" , function ( ) {
var zeroCharParser = PEG . buildParser ( 'start = ""' ) ;
test WithVaryingTrackLineAndColumn ( "literals" , function ( options ) {
var zeroCharParser = PEG . buildParser ( 'start = ""' , options );
parses ( zeroCharParser , "" , "" ) ;
doesNotParse ( zeroCharParser , "a" ) ;
var oneCharCaseSensitiveParser = PEG . buildParser ( 'start = "a"' ) ;
var oneCharCaseSensitiveParser = PEG . buildParser ( 'start = "a"' , options );
parses ( oneCharCaseSensitiveParser , "a" , "a" ) ;
doesNotParse ( oneCharCaseSensitiveParser , "" ) ;
doesNotParse ( oneCharCaseSensitiveParser , "A" ) ;
doesNotParse ( oneCharCaseSensitiveParser , "b" ) ;
var multiCharCaseSensitiveParser = PEG . buildParser ( 'start = "abcd"' ) ;
var multiCharCaseSensitiveParser = PEG . buildParser ( 'start = "abcd"' , options );
parses ( multiCharCaseSensitiveParser , "abcd" , "abcd" ) ;
doesNotParse ( multiCharCaseSensitiveParser , "" ) ;
doesNotParse ( multiCharCaseSensitiveParser , "abc" ) ;
@ -306,13 +662,16 @@ test("literals", function() {
doesNotParse ( multiCharCaseSensitiveParser , "ABCD" ) ;
doesNotParse ( multiCharCaseSensitiveParser , "efgh" ) ;
var oneCharCaseInsensitiveParser = PEG . buildParser ( 'start = "a"i' );
var oneCharCaseInsensitiveParser = PEG . buildParser ( 'start = "a"i' , options );
parses ( oneCharCaseInsensitiveParser , "a" , "a" ) ;
parses ( oneCharCaseInsensitiveParser , "A" , "A" ) ;
doesNotParse ( oneCharCaseInsensitiveParser , "" ) ;
doesNotParse ( oneCharCaseInsensitiveParser , "b" ) ;
var multiCharCaseInsensitiveParser = PEG . buildParser ( 'start = "abcd"i' ) ;
var multiCharCaseInsensitiveParser = PEG . buildParser (
'start = "abcd"i' ,
options
) ;
parses ( multiCharCaseInsensitiveParser , "abcd" , "abcd" ) ;
parses ( multiCharCaseInsensitiveParser , "ABCD" , "ABCD" ) ;
doesNotParse ( multiCharCaseInsensitiveParser , "" ) ;
@ -324,12 +683,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"' , options );
parses ( posTestParser , "ab" , [ "a" , "b" ] ) ;
} ) ;
test ( "anys" , function ( ) {
var parser = PEG . buildParser ( 'start = .' );
test WithVaryingTrackLineAndColumn ( "anys" , function ( options ) {
var parser = PEG . buildParser ( 'start = .' , options );
parses ( parser , "a" , "a" ) ;
doesNotParse ( parser , "" ) ;
doesNotParse ( parser , "ab" ) ;
@ -338,22 +697,25 @@ 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 = . .' , options );
parses ( posTestParser , "ab" , [ "a" , "b" ] ) ;
} ) ;
test ( "classes" , function ( ) {
var emptyClassParser = PEG . buildParser ( 'start = []' );
test WithVaryingTrackLineAndColumn ( "classes" , function ( options ) {
var emptyClassParser = PEG . buildParser ( 'start = []' , options );
doesNotParse ( emptyClassParser , "" ) ;
doesNotParse ( emptyClassParser , "a" ) ;
doesNotParse ( emptyClassParser , "ab" ) ;
var invertedEmptyClassParser = PEG . buildParser ( 'start = [^]' );
var invertedEmptyClassParser = PEG . buildParser ( 'start = [^]' , options );
doesNotParse ( invertedEmptyClassParser , "" ) ;
parses ( invertedEmptyClassParser , "a" , "a" ) ;
doesNotParse ( invertedEmptyClassParser , "ab" ) ;
var nonEmptyCaseSensitiveClassParser = PEG . buildParser ( 'start = [ab-d]' ) ;
var nonEmptyCaseSensitiveClassParser = PEG . buildParser (
'start = [ab-d]' ,
options
) ;
parses ( nonEmptyCaseSensitiveClassParser , "a" , "a" ) ;
parses ( nonEmptyCaseSensitiveClassParser , "b" , "b" ) ;
parses ( nonEmptyCaseSensitiveClassParser , "c" , "c" ) ;
@ -366,7 +728,10 @@ test("classes", function() {
doesNotParse ( nonEmptyCaseSensitiveClassParser , "e" ) ;
doesNotParse ( nonEmptyCaseSensitiveClassParser , "ab" ) ;
var invertedNonEmptyCaseSensitiveClassParser = PEG . buildParser ( 'start = [^ab-d]' ) ;
var invertedNonEmptyCaseSensitiveClassParser = PEG . buildParser (
'start = [^ab-d]' ,
options
) ;
parses ( invertedNonEmptyCaseSensitiveClassParser , "A" , "A" ) ;
parses ( invertedNonEmptyCaseSensitiveClassParser , "B" , "B" ) ;
parses ( invertedNonEmptyCaseSensitiveClassParser , "C" , "C" ) ;
@ -379,7 +744,10 @@ test("classes", function() {
doesNotParse ( invertedNonEmptyCaseSensitiveClassParser , "" ) ;
doesNotParse ( invertedNonEmptyCaseSensitiveClassParser , "ab" ) ;
var nonEmptyCaseInsensitiveClassParser = PEG . buildParser ( 'start = [ab-d]i' ) ;
var nonEmptyCaseInsensitiveClassParser = PEG . buildParser (
'start = [ab-d]i' ,
options
) ;
parses ( nonEmptyCaseInsensitiveClassParser , "a" , "a" ) ;
parses ( nonEmptyCaseInsensitiveClassParser , "b" , "b" ) ;
parses ( nonEmptyCaseInsensitiveClassParser , "c" , "c" ) ;
@ -392,7 +760,10 @@ test("classes", function() {
doesNotParse ( nonEmptyCaseInsensitiveClassParser , "e" ) ;
doesNotParse ( nonEmptyCaseInsensitiveClassParser , "ab" ) ;
var invertedNonEmptyCaseInsensitiveClassParser = PEG . buildParser ( 'start = [^ab-d]i' ) ;
var invertedNonEmptyCaseInsensitiveClassParser = PEG . buildParser (
'start = [^ab-d]i' ,
options
) ;
parses ( invertedNonEmptyCaseInsensitiveClassParser , "e" , "e" ) ;
doesNotParse ( invertedNonEmptyCaseInsensitiveClassParser , "a" , "a" ) ;
doesNotParse ( invertedNonEmptyCaseInsensitiveClassParser , "b" , "b" ) ;
@ -409,11 +780,11 @@ 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]' , options );
parses ( posTestParser , "ab" , [ "a" , "b" ] ) ;
} ) ;
test ( "cache" , function ( ) {
test WithVaryingTrackLineAndColumn ( "cache" , function ( options ) {
/ *
* Should trigger a codepath where the cache is used ( for the "a" rule ) .
* /
@ -422,19 +793,19 @@ test("cache", function() {
'a = "a"' ,
'b = "b"' ,
'c = "c"'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( parser , "ac" , [ "a" , "c" ] ) ;
} ) ;
test ( "indempotence" , function ( ) {
var parser1 = PEG . buildParser ( 'start = "abcd"' );
var parser2 = PEG . buildParser ( 'start = "abcd"' );
test WithVaryingTrackLineAndColumn ( "indempotence" , function ( options ) {
var parser1 = PEG . buildParser ( 'start = "abcd"' , options );
var parser2 = PEG . buildParser ( 'start = "abcd"' , options );
strictEqual ( parser1 . toSource ( ) , parser2 . toSource ( ) ) ;
} ) ;
test ( "error details" , function ( ) {
var literalParser = PEG . buildParser ( 'start = "abcd"' );
test WithVaryingTrackLineAndColumn ( "error details" , function ( options ) {
var literalParser = PEG . buildParser ( 'start = "abcd"' , options );
doesNotParseWithDetails (
literalParser ,
"" ,
@ -457,7 +828,7 @@ test("error details", function() {
'Expected end of input but "e" found.'
) ;
var classParser = PEG . buildParser ( 'start = [a-d]' );
var classParser = PEG . buildParser ( 'start = [a-d]' , options );
doesNotParseWithDetails (
classParser ,
"" ,
@ -465,7 +836,7 @@ test("error details", function() {
null ,
'Expected [a-d] but end of input found.'
) ;
var negativeClassParser = PEG . buildParser ( 'start = [^a-d]' );
var negativeClassParser = PEG . buildParser ( 'start = [^a-d]' , options );
doesNotParseWithDetails (
negativeClassParser ,
"" ,
@ -474,7 +845,7 @@ test("error details", function() {
'Expected [^a-d] but end of input found.'
) ;
var anyParser = PEG . buildParser ( 'start = .' );
var anyParser = PEG . buildParser ( 'start = .' , options );
doesNotParseWithDetails (
anyParser ,
"" ,
@ -483,7 +854,10 @@ test("error details", function() {
'Expected any character but end of input found.'
) ;
var namedRuleWithLiteralParser = PEG . buildParser ( 'start "digit" = [0-9]' ) ;
var namedRuleWithLiteralParser = PEG . buildParser (
'start "digit" = [0-9]' ,
options
) ;
doesNotParseWithDetails (
namedRuleWithLiteralParser ,
"a" ,
@ -492,7 +866,7 @@ test("error details", function() {
'Expected digit but "a" found.'
) ;
var namedRuleWithAnyParser = PEG . buildParser ( 'start "whatever" = .' );
var namedRuleWithAnyParser = PEG . buildParser ( 'start "whatever" = .' , options );
doesNotParseWithDetails (
namedRuleWithAnyParser ,
"" ,
@ -504,7 +878,7 @@ test("error details", function() {
var namedRuleWithNamedRuleParser = PEG . buildParser ( [
'start "digits" = digit+' ,
'digit "digit" = [0-9]'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
doesNotParseWithDetails (
namedRuleWithNamedRuleParser ,
"" ,
@ -513,7 +887,7 @@ test("error details", function() {
'Expected digits but end of input found.'
) ;
var choiceParser1 = PEG . buildParser ( 'start = "a" / "b" / "c"' );
var choiceParser1 = PEG . buildParser ( 'start = "a" / "b" / "c"' , options );
doesNotParseWithDetails (
choiceParser1 ,
"def" ,
@ -522,7 +896,7 @@ test("error details", function() {
'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"' , options );
doesNotParseWithDetails (
choiceParser2 ,
"abd" ,
@ -531,7 +905,7 @@ test("error details", function() {
'Expected "c" but "d" found.'
) ;
var simpleNotParser = PEG . buildParser ( 'start = !"a" "b"' );
var simpleNotParser = PEG . buildParser ( 'start = !"a" "b"' , options );
doesNotParseWithDetails (
simpleNotParser ,
"c" ,
@ -540,7 +914,7 @@ test("error details", function() {
'Expected "b" but "c" found.'
) ;
var simpleAndParser = PEG . buildParser ( 'start = &"a" [a-b]' );
var simpleAndParser = PEG . buildParser ( 'start = &"a" [a-b]' , options );
doesNotParseWithDetails (
simpleAndParser ,
"c" ,
@ -549,7 +923,7 @@ test("error details", function() {
'Expected end of input but "c" found.'
) ;
var emptyParser = PEG . buildParser ( 'start = ' );
var emptyParser = PEG . buildParser ( 'start = ' , options );
doesNotParseWithDetails (
emptyParser ,
"something" ,
@ -558,7 +932,7 @@ test("error details", function() {
'Expected end of input but "s" found.'
) ;
var duplicateErrorParser = PEG . buildParser ( 'start = "a" / "a"' );
var duplicateErrorParser = PEG . buildParser ( 'start = "a" / "a"' , options );
doesNotParseWithDetails (
duplicateErrorParser ,
"" ,
@ -567,7 +941,7 @@ test("error details", function() {
'Expected "a" but end of input found.'
) ;
var unsortedErrorsParser = PEG . buildParser ( 'start = "b" / "a"' );
var unsortedErrorsParser = PEG . buildParser ( 'start = "b" / "a"' , options );
doesNotParseWithDetails (
unsortedErrorsParser ,
"" ,
@ -577,8 +951,8 @@ test("error details", function() {
) ;
} ) ;
test ( "error positions" , function ( ) {
var simpleParser = PEG . buildParser ( 'start = "a"' );
test WithVaryingTrackLineAndColumn ( "error positions" , function ( options ) {
var simpleParser = PEG . buildParser ( 'start = "a"' , options );
/* Regular match failure */
doesNotParseWithPos ( simpleParser , "b" , 0 , 1 , 1 ) ;
@ -590,7 +964,7 @@ test("error positions", function() {
'start = line (("\\r" / "\\n" / "\\u2028" / "\\u2029")+ line)*' ,
'line = digits (" "+ digits)*' ,
'digits = digits:[0-9]+ { return digits.join(""); }'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
doesNotParseWithPos ( digitsParser , "1\n2\n\n3\n\n\n4 5 x" , 13 , 7 , 5 ) ;
@ -604,11 +978,11 @@ test("error positions", function() {
doesNotParseWithPos ( digitsParser , "1\u2029x" , 2 , 2 , 1 ) ; // paragraph separator
} ) ;
test ( "start rule" , function ( ) {
test WithVaryingTrackLineAndColumn ( "start rule" , function ( options ) {
var parser = PEG . buildParser ( [
'a = .* { return "alpha"; }' ,
'b = .* { return "beta"; }'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
/* Default start rule = the first one */
parses ( parser , "whatever" , "alpha" ) ;
@ -631,7 +1005,7 @@ test("start rule", function() {
* http : //en.wikipedia.org/w/index.php?title=Parsing_expression_grammar&oldid=335106938.
* /
test ( "arithmetics" , function ( ) {
test WithVaryingTrackLineAndColumn ( "arithmetics" , function ( options ) {
/ *
* Value ← [ 0 - 9 ] + / ' ( ' E x p r ' ) '
* Product ← Value ( ( '*' / '/' ) Value ) *
@ -658,7 +1032,7 @@ test("arithmetics", function() {
' }' ,
'Value = digits:[0-9]+ { return parseInt(digits.join("")); }' ,
' / "(" expr:Expr ")" { return expr; }'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
/* Test "value" rule. */
parses ( parser , "0" , 0 ) ;
@ -686,7 +1060,7 @@ test("arithmetics", function() {
parses ( parser , "(1+2)*(3+4)" , ( 1 + 2 ) * ( 3 + 4 ) ) ;
} ) ;
test ( "non-context-free language" , function ( ) {
test WithVaryingTrackLineAndColumn ( "non-context-free language" , function ( options ) {
/ * T h e f o l l o w i n g p a r s i n g e x p r e s s i o n g r a m m a r d e s c r i b e s t h e c l a s s i c
* non - context - free language { a ^ n b ^ n c ^ n : n >= 1 } :
*
@ -698,7 +1072,7 @@ test("non-context-free language", function() {
'S = &(A "c") a:"a"+ B:B !("a" / "b" / "c") { return a.join("") + B; }' ,
'A = a:"a" A:A? b:"b" { return a + A + b; }' ,
'B = b:"b" B:B? c:"c" { return b + B + c; }'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( parser , "abc" , "abc" ) ;
parses ( parser , "aaabbbccc" , "aaabbbccc" ) ;
@ -710,7 +1084,7 @@ test("non-context-free language", function() {
doesNotParse ( parser , "aaabbbcccc" ) ;
} ) ;
test ( "nested comments" , function ( ) {
test WithVaryingTrackLineAndColumn ( "nested comments" , function ( options ) {
/ *
* Begin ← "(*"
* End ← "*)"
@ -725,7 +1099,7 @@ test("nested comments", function() {
'Z = .' ,
'Begin = "(*"' ,
'End = "*)"'
] . join ( "\n" ) );
] . join ( "\n" ) , options );
parses ( parser , "(**)" , "(**)" ) ;
parses ( parser , "(*abc*)" , "(*abc*)" ) ;