diff --git a/spec/api/plugin-api.spec.js b/spec/api/plugin-api.spec.js new file mode 100644 index 0000000..72ea702 --- /dev/null +++ b/spec/api/plugin-api.spec.js @@ -0,0 +1,156 @@ +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 the 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 the options", function() { + var buildParserOptions = { foo: 42 }, + plugin = { + use: function(config, options) { + expect(options).toEqual(buildParserOptions); + } + }; + + PEG.buildParser(grammar, buildParserOptions); + }); + + it("can replace the parser", function() { + var plugin = { + use: function(config, options) { + var parser = PEG.buildParser([ + 'start = .* {', + ' return {', + ' type: "grammar",', + ' rules: [', + ' {', + ' type: "rule",', + ' 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 the 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"); + }); + }); +}); diff --git a/spec/index.html b/spec/index.html index c931c11..bd0d668 100644 --- a/spec/index.html +++ b/spec/index.html @@ -14,6 +14,7 @@ +