Skip to content

Add basic plugin functionality #106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
dignifiedquire opened this issue Sep 16, 2012 · 4 comments
Closed

Add basic plugin functionality #106

dignifiedquire opened this issue Sep 16, 2012 · 4 comments
Labels
Milestone

Comments

@dignifiedquire
Copy link

This is the issue for creating the proposed plugin system discussed in #100.

@dmajda
Copy link
Contributor

dmajda commented Jan 13, 2013

I decided to deviate from the last proposed design. I will describe the new design here (with some comments about the differences) and push its implementation in few minutes. See the commits for details.

The proper documentation of the plugin API will come in the 0.9 cycle. (I have other high-priority work to do on PEG.js and I plan to do a big documentation overhaul in 0.9 anyway.)

To understand the context the design was created in, read through the discussion in #100.

New Design

Requirements

  1. Plugins can replace the grammar parser.
  2. Plugins can add, remove and replace compiler passes without knowing any specifics (e.g. name) about them.
  3. The list of used plugins can be specified for each buildParser call.
  4. It is possible to load the plugins using a CLI option.
  5. Plugins contain as little boilerplate code as possible.
  6. Bonus: Plugins can take options.
  7. Bonus: Get rid of appliedPassNames (an ugly thing).

Compared to the last design, I added a requirement that plugins should not need to know any specifics of existing compiler passes to operate. This would avoid the need to make the passes part of the public API. I also removed the need to specify the list of plugins globally (for all buildParser calls). I don't see a big demand for this + it simplifies things.

Solution

  1. PEG.passes and PEG.parser would stay as they are, but they would be just accessors, not extension points. There would be no PEG.appliedPasses.
  2. There would be a new plugins option for PEG.buildParser in which user would be able to specify an array of plugins to use (empty by default).
  3. Inside PEG.buildParser, PEG.js would walk trough plugins from the plugins option one by one and call the use method on each, passing two arguments with a possibility to modify them:
    • config — configuration object containing used parser (in the parser property) and used compiler passes (in the passes property)
    • options — options passed by user

This would give the plugin the opportunity to (1) replace the grammar parser, (2) add, remove and replace compiler passes, (3) see the values of all options, allowing user to specify some plugin-specific ones.

The passes would be divided into three stages (check, transform and generate) and specified as an object with these stages as keys and arrays of passes as values. The names of stages would stay fixed across PEG.js versions (except major releases), while the list of passes would be subject of change. This approach would allow plugins to add/replace passes in any stage without knowing what passes it constitutes from, freeing them from dependence on specific pass names.

Once plugins finish their work, PEG.js would use the parser from the parser property of the config object to parse the grammar and passes from the passes property of the config object to generate the code from it.

Compared to the last design, I removed the the ability to specify options globally and split passes into stages. Both are reactions to changed requirements. Moreover I decoupled the parser/pass config from the options specified by user. I think these are rather low-level things and they should not be exposed as PEG.buildParser options.

dmajda added a commit that referenced this issue Jan 13, 2013
The |passes| parameter will allow to pass the list of passes from
|PEG.buildParser|. This will be used by plugins. The old way via setting
the |appliedPassNames| property is removed.

Implements part of GH-106.
dmajda added a commit that referenced this issue Jan 13, 2013
The |plugins| option allows users to use plugins that change how PEG.js
operates.

A plugin is any JavaScript object with a |use| method. After the user
calls |PEG.buildParser|, this method is called for each plugin with the
following two parameters:

  * PEG.js config that describes used grammar parser and compiler
    passes used to generate the parser

  * options passed by user to |PEG.buildParser|

The plugin is expected to change the config as needed, possibly based on
the options passed by user. It can e.g. change the used grammar parser,
change the compiler passes (including adding its own), etc. This way it
can extend PEG.js in a flexible way.

Implements part of GH-106.
dmajda added a commit that referenced this issue Jan 13, 2013
The compiler passes are now split into three stages:

  * check -- passes that check for various error conditions

  * transform -- passes that transform the AST (e.g. to perform
    optimizations)

  * generate -- passes that are related to code generation

Splitting the passes into stages is important for plugins. For example,
if a plugin wants to add a new optimization pass, it can add it at the
end of the "transform" stage without any knowledge about other passes it
contains. Similarly, if it wants to generate something else than the
default code generator does from the AST, it can just replace all passes
in the "generate" stage by its own one(s).

More generally, the stages make it possible to write plugins that do not
depend on names and actions of specific passes (which I consider
internal and subject of change), just on the definition of stages (which
I consider a public API with to which semver rules apply).

Implements part of GH-106.
dmajda added a commit that referenced this issue Jan 13, 2013
@dmajda dmajda closed this as completed Jan 13, 2013
@dmajda dmajda mentioned this issue Feb 26, 2016
@mnpenner
Copy link

Is this plugin API documented anywhere? I want to use a plugin to inject an "initializer".

@ming-codes
Copy link

I'm looking for that plugin API doc too :(

@futagoza
Copy link
Member

The plugin modules have to export an object, with one function named use:

export function use( config, options ) {
    const { parser, passes } = config;
    // your code, no return...
}

The plugins can modify config and options, which means they can change the parser, add or remove passes and change the options that will get passed to the compiler.

@futagoza futagoza mentioned this issue Nov 15, 2017
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants