Compiler plugin design #592
Replies: 4 comments 4 replies
|
I believe introducing a DSL for compiler plugins on top of the Ballerina language adds unnecessary complexity. I'm also +1 on starting with Option 1 (statically linked). It will allow us to better understand the real-world use cases and requirements for compiler plugins, which will ultimately help shape the design of Ballerina transform functions. For Option 2 (plugin path), I suggest using the following project structure: /compiler-plugin/<plugin_name>.bal |
|
In jBallerina, compiler plugins allow the following three tasks:
Based on these use cases, we came up with three kinds of compiler plugins in jBallerina:
You can find more details in the following links:
@heshanpadmasiri, in your proposal, you are only supporting code mutations. Is that intentional? |
|
Potential issue with a forward pass only implementation (which I am still in favour of) is adding imports and referencing symbols in those imports in compiler plugins. I don't know if we do this already but this would require some gymnastics in the compiler plugin's code. Issue here is you are trying to refer to a symbol that don't exists in the compiler env. |
Uh oh!
There was an error while loading. Please reload this page.
Objectives
Non-objectives
In jBallerina, we used SPI-based dynamic loading of code provided by a module, allowing it to perform AST mutations and provide diagnostics. While Go also has the ability to load dynamic libraries, this is only supported on Unix systems (ref). Thus, we can't use this on Windows or in the browser.
With this in mind, I think there are three options to consider.
Statically linked Go functions for a given library.
Each library that needs this support can declare a function that takes a compiler context and
ast.Pkgand returns anast.Pkg. Using the compiler context, it can provide diagnostics; since it returns the AST, it can perform arbitrary mutations on the AST. Essentially, this is very similar to how desugar is implemented (and most inline with how current jBallerina compiler plugins work). For the initial release, given that we only allow this capability for packages we pack in the distribution, such ashttp, this should be straightforward. Each package that needs this can register the function with the project API, and if the package has an import, it will call the registered function before desugar.Define these transforms using a restricted subset of Ballerina.
Each of these packages can define a
.balfile with a specific name (for example,build.bal; however, user code could already have such a file, so maybe this needs to be in a specific path, such as<project-root>/build/build.bal) that supports only a subset of the language library (and perhaps only a subset of the language, so we can avoid the need to desugar it). We will expose the AST and compiler context viaexternfunctions. Then, after generating the package AST for user code (sansbuild.bal), we'll pass the package AST as an argument tobuild.baland interpret it.Important
Ideally, I don't want to expose the AST and compiler context as data to the Ballerina side. This would introduce a lot of complexity in syncing data structures between the Go and Ballerina sides. Instead, I would like to expose mutation/data-access functions and handles.
Of these options, I don't think 3 gives us anything that 2 can't give us, other than perhaps cleaner syntax. I gravitate towards starting with 1; given that, for some time, we only need to care about libraries we pack into the distribution itself, and with time, we can move towards 2. This should reduce the time we need to get an initial working version (1 needs very little work from both the project API and frontend), and later, when we do 2, we can have a very clear idea of the minimal set of APIs we need to support.
All reactions