da57118a43
Parsers can now be generated with support for tracing using the --trace CLI option or a boolean |trace| option to |PEG.buildParser|. This makes them trace their progress, which can be useful for debugging. Parsers generated with tracing support are called "tracing parsers". When a tracing parser executes, by default it traces the rules it enters and exits by writing messages to the console. For example, a parser built from this grammar: start = a / b a = "a" b = "b" will write this to the console when parsing input "b": 1:1 rule.enter start 1:1 rule.enter a 1:1 rule.fail a 1:1 rule.enter b 1:2 rule.match b 1:2 rule.match start You can customize tracing by passing a custom *tracer* to parser's |parse| method using the |tracer| option: parser.parse(input, { trace: tracer }); This will replace the built-in default tracer (which writes to the console) by the tracer you supplied. The tracer must be an object with a |trace| method. This method is called each time a tracing event happens. It takes one argument which is an object describing the tracing event. Currently, three events are supported: * rule.enter -- triggered when a rule is entered * rule.match -- triggered when a rule matches successfully * rule.fail -- triggered when a rule fails to match These events are triggered in nested pairs -- for each rule.enter event there is a matching rule.match or rule.fail event. The event object passed as an argument to |trace| contains these properties: * type -- event type * rule -- name of the rule the event is related to * offset -- parse position at the time of the event * line -- line at the time of the event * column -- column at the time of the event * result -- rule's match result (only for rule.match event) The whole tracing API is somewhat experimental (which is why it isn't documented properly yet) and I expect it will evolve over time as experience is gained. The default tracer is also somewhat bare-bones. I hope that PEG.js user community will develop more sophisticated tracers over time and I'll be able to integrate their best ideas into the default tracer.
158 lines
4.7 KiB
JavaScript
158 lines
4.7 KiB
JavaScript
describe("plugin API", function() {
|
|
beforeEach(function() {
|
|
this.addMatchers({
|
|
toBeObject: function() {
|
|
this.message = function() {
|
|
return "Expected " + jasmine.pp(this.actual) + " "
|
|
+ (this.isNot ? "not " : "")
|
|
+ "to be an object.";
|
|
};
|
|
|
|
return this.actual !== null && typeof this.actual === "object";
|
|
},
|
|
|
|
toBeArray: function() {
|
|
this.message = function() {
|
|
return "Expected " + jasmine.pp(this.actual) + " "
|
|
+ (this.isNot ? "not " : "")
|
|
+ "to be an array.";
|
|
};
|
|
|
|
return Object.prototype.toString.apply(this.actual) === "[object Array]";
|
|
},
|
|
|
|
toBeFunction: function() {
|
|
this.message = function() {
|
|
return "Expected " + jasmine.pp(this.actual) + " "
|
|
+ (this.isNot ? "not " : "")
|
|
+ "to be a function.";
|
|
};
|
|
|
|
return typeof this.actual === "function";
|
|
},
|
|
});
|
|
});
|
|
|
|
describe("use", function() {
|
|
var grammar = 'start = "a"';
|
|
|
|
it("is called for each plugin", function() {
|
|
var pluginsUsed = [false, false, false],
|
|
plugins = [
|
|
{ use: function() { pluginsUsed[0] = true; } },
|
|
{ use: function() { pluginsUsed[1] = true; } },
|
|
{ use: function() { pluginsUsed[2] = true; } }
|
|
];
|
|
|
|
PEG.buildParser(grammar, { plugins: plugins });
|
|
|
|
expect(pluginsUsed).toEqual([true, true, true]);
|
|
});
|
|
|
|
it("receives configuration", function() {
|
|
var plugin = {
|
|
use: function(config, options) {
|
|
var i;
|
|
|
|
expect(config).toBeObject();
|
|
|
|
expect(config.parser).toBeObject();
|
|
expect(config.parser.parse('start = "a"')).toBeObject();
|
|
|
|
expect(config.passes).toBeObject();
|
|
|
|
expect(config.passes.check).toBeArray();
|
|
for (i = 0; i < config.passes.check.length; i++) {
|
|
expect(config.passes.check[i]).toBeFunction();
|
|
}
|
|
|
|
expect(config.passes.transform).toBeArray();
|
|
for (i = 0; i < config.passes.transform.length; i++) {
|
|
expect(config.passes.transform[i]).toBeFunction();
|
|
}
|
|
|
|
expect(config.passes.generate).toBeArray();
|
|
for (i = 0; i < config.passes.generate.length; i++) {
|
|
expect(config.passes.generate[i]).toBeFunction();
|
|
}
|
|
}
|
|
};
|
|
|
|
PEG.buildParser(grammar, { plugins: [plugin] });
|
|
});
|
|
|
|
it("receives options", function() {
|
|
var buildParserOptions = { foo: 42 },
|
|
plugin = {
|
|
use: function(config, options) {
|
|
expect(options).toEqual(buildParserOptions);
|
|
}
|
|
};
|
|
|
|
PEG.buildParser(grammar, buildParserOptions);
|
|
});
|
|
|
|
it("can replace parser", function() {
|
|
var plugin = {
|
|
use: function(config, options) {
|
|
var parser = PEG.buildParser([
|
|
'start = .* {',
|
|
' return {',
|
|
' type: "grammar",',
|
|
' rules: [',
|
|
' {',
|
|
' type: "rule",',
|
|
' name: "start",',
|
|
' expression: { type: "literal", value: text(), ignoreCase: false }',
|
|
' }',
|
|
' ]',
|
|
' };',
|
|
'}'
|
|
].join("\n"));
|
|
|
|
config.parser = parser;
|
|
}
|
|
},
|
|
parser = PEG.buildParser('a', { plugins: [plugin] });
|
|
|
|
expect(parser.parse("a")).toBe("a");
|
|
});
|
|
|
|
it("can change compiler passes", function() {
|
|
var plugin = {
|
|
use: function(config, options) {
|
|
var pass = function(ast) {
|
|
ast.code = '({ parse: function() { return 42; } })';
|
|
};
|
|
|
|
config.passes.generate = [pass];
|
|
}
|
|
},
|
|
parser = PEG.buildParser(grammar, { plugins: [plugin] });
|
|
|
|
expect(parser.parse("a")).toBe(42);
|
|
});
|
|
|
|
it("can change options", function() {
|
|
var grammar = [
|
|
'a = "x"',
|
|
'b = "x"',
|
|
'c = "x"'
|
|
].join("\n"),
|
|
plugin = {
|
|
use: function(config, options) {
|
|
options.allowedStartRules = ["b", "c"];
|
|
}
|
|
},
|
|
parser = PEG.buildParser(grammar, {
|
|
allowedStartRules: ["a"],
|
|
plugins: [plugin]
|
|
});
|
|
|
|
expect(function() { parser.parse("x", { startRule: "a" }); }).toThrow();
|
|
expect(parser.parse("x", { startRule: "b" })).toBe("x");
|
|
expect(parser.parse("x", { startRule: "c" })).toBe("x");
|
|
});
|
|
});
|
|
});
|