You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
6.6 KiB
JavaScript
217 lines
6.6 KiB
JavaScript
8 years ago
|
"use strict";
|
||
|
|
||
7 years ago
|
const chai = require( "chai" );
|
||
6 years ago
|
const peg = require( "pegjs" );
|
||
7 years ago
|
const sinon = require( "sinon" );
|
||
|
|
||
|
const expect = chai.expect;
|
||
|
|
||
|
describe( "generated parser API", function () {
|
||
|
|
||
|
describe( "parse", function () {
|
||
|
|
||
|
it( "parses input", function () {
|
||
|
|
||
|
const parser = peg.generate( "start = 'a'" );
|
||
|
expect( parser.parse( "a" ) ).to.equal( "a" );
|
||
|
|
||
|
} );
|
||
|
|
||
|
it( "throws an exception on syntax error", function () {
|
||
|
|
||
|
const parser = peg.generate( "start = 'a'" );
|
||
|
expect( () => {
|
||
|
|
||
|
parser.parse( "b" );
|
||
|
|
||
|
} ).to.throw();
|
||
|
|
||
|
} );
|
||
|
|
||
|
describe( "start rule", function () {
|
||
|
|
||
|
const parser = peg.generate( `
|
||
|
|
||
|
a = 'x' { return 'a'; }
|
||
|
b = 'x' { return 'b'; }
|
||
|
c = 'x' { return 'c'; }
|
||
|
|
||
|
`, { allowedStartRules: [ "b", "c" ] } );
|
||
|
|
||
|
describe( "when |startRule| is not set", function () {
|
||
|
|
||
|
it( "starts parsing from the first allowed rule", function () {
|
||
|
|
||
|
expect( parser.parse( "x" ) ).to.equal( "b" );
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
describe( "when |startRule| is set to an allowed rule", function () {
|
||
|
|
||
|
it( "starts parsing from specified rule", function () {
|
||
|
|
||
|
expect( parser.parse( "x", { startRule: "b" } ) ).to.equal( "b" );
|
||
|
expect( parser.parse( "x", { startRule: "c" } ) ).to.equal( "c" );
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
describe( "when |startRule| is set to a disallowed start rule", function () {
|
||
|
|
||
|
it( "throws an exception", function () {
|
||
|
|
||
|
expect( () => {
|
||
|
|
||
|
parser.parse( "x", { startRule: "a" } );
|
||
|
|
||
|
} ).to.throw();
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
describe( "tracing", function () {
|
||
|
|
||
|
const parser = peg.generate( `
|
||
|
|
||
|
start = a / b
|
||
|
a = 'a'
|
||
|
b = 'b'
|
||
|
|
||
|
`, { trace: true } );
|
||
|
|
||
|
describe( "default tracer", function () {
|
||
|
|
||
|
it( "traces using console.log (if console is defined)", function () {
|
||
|
|
||
|
const messages = [
|
||
|
"1:1-1:1 rule.enter start",
|
||
|
"1:1-1:1 rule.enter a",
|
||
|
"1:1-1:1 rule.fail a",
|
||
|
"1:1-1:1 rule.enter b",
|
||
|
"1:1-1:2 rule.match b",
|
||
|
"1:1-1:2 rule.match start"
|
||
|
];
|
||
|
|
||
|
if ( typeof console === "object" ) sinon.stub( console, "log" );
|
||
|
|
||
|
try {
|
||
|
|
||
|
parser.parse( "b" );
|
||
|
|
||
|
if ( typeof console === "object" ) {
|
||
|
|
||
|
expect( console.log.callCount ).to.equal( messages.length );
|
||
|
messages.forEach( ( message, index ) => {
|
||
|
|
||
|
const call = console.log.getCall( index );
|
||
|
expect( call.calledWithExactly( message ) ).to.equal( true );
|
||
|
|
||
|
} );
|
||
|
|
||
|
}
|
||
|
|
||
|
} finally {
|
||
|
|
||
|
if ( typeof console === "object" ) console.log.restore();
|
||
|
|
||
|
}
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
describe( "custom tracers", function () {
|
||
|
|
||
|
describe( "trace", function () {
|
||
|
|
||
|
it( "receives tracing events", function () {
|
||
|
|
||
|
const events = [
|
||
|
{
|
||
|
type: "rule.enter",
|
||
|
rule: "start",
|
||
|
location: {
|
||
|
start: { offset: 0, line: 1, column: 1 },
|
||
|
end: { offset: 0, line: 1, column: 1 }
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
type: "rule.enter",
|
||
|
rule: "a",
|
||
|
location: {
|
||
|
start: { offset: 0, line: 1, column: 1 },
|
||
|
end: { offset: 0, line: 1, column: 1 }
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
type: "rule.fail",
|
||
|
rule: "a",
|
||
|
location: {
|
||
|
start: { offset: 0, line: 1, column: 1 },
|
||
|
end: { offset: 0, line: 1, column: 1 }
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
type: "rule.enter",
|
||
|
rule: "b",
|
||
|
location: {
|
||
|
start: { offset: 0, line: 1, column: 1 },
|
||
|
end: { offset: 0, line: 1, column: 1 }
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
type: "rule.match",
|
||
|
rule: "b",
|
||
|
result: "b",
|
||
|
location: {
|
||
|
start: { offset: 0, line: 1, column: 1 },
|
||
|
end: { offset: 1, line: 1, column: 2 }
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
type: "rule.match",
|
||
|
rule: "start",
|
||
|
result: "b",
|
||
|
location: {
|
||
|
start: { offset: 0, line: 1, column: 1 },
|
||
|
end: { offset: 1, line: 1, column: 2 }
|
||
|
}
|
||
|
}
|
||
|
];
|
||
|
|
||
|
const tracer = { trace: sinon.spy() };
|
||
|
parser.parse( "b", { tracer: tracer } );
|
||
|
|
||
|
expect( tracer.trace.callCount ).to.equal( events.length );
|
||
|
events.forEach( ( event, index ) => {
|
||
|
|
||
|
const call = tracer.trace.getCall( index );
|
||
|
expect( call.calledWithExactly( event ) ).to.equal( true );
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
it( "accepts custom options", function () {
|
||
|
|
||
|
const parser = peg.generate( "start = 'a'" );
|
||
|
parser.parse( "a", { foo: 42 } );
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|
||
|
|
||
|
} );
|