@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
167 lines • 7.21 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FlowrAnalyzerBuilder = void 0;
const config_1 = require("../config");
const flowr_analyzer_1 = require("./flowr-analyzer");
const engines_1 = require("../engines");
const assert_1 = require("../util/assert");
const flowr_analyzer_context_1 = require("./context/flowr-analyzer-context");
const flowr_analyzer_cache_1 = require("./cache/flowr-analyzer-cache");
const flowr_analyzer_plugin_defaults_1 = require("./plugins/flowr-analyzer-plugin-defaults");
const plugin_registry_1 = require("./plugins/plugin-registry");
/**
* Builder for the {@link FlowrAnalyzer}, use it to configure all analysis aspects before creating the analyzer instance
* with {@link FlowrAnalyzerBuilder#build|`.build()`} or {@link FlowrAnalyzerBuilder#buildSync|`.buildSync()`}.
*
* You can add new files and folders to analyze using the {@link FlowrAnalyzer#addRequest|`.addRequest()`} method on the resulting analyzer.
* @example Let's create an analyzer for a single R script file:
*
* ```ts
* const analyzer = new FlowrAnalyzerBuilder()
* .setParser(new TreeSitterExecutor())
* .buildSync()
* .addRequest('file:///path/to/script.R')
*
* ```
*
* If you now want to get the dataflow information for the file, you can do this:
*
* ```ts
* const dfInfo = await analyzer.dataflow();
* console.log(dfInfo);
* ```
* @see https://github.com/flowr-analysis/flowr/wiki/Analyzer
*/
class FlowrAnalyzerBuilder {
flowrConfig = config_1.FlowrConfig.default();
parser;
input;
plugins = new Map();
/**
* Creates a new builder for the {@link FlowrAnalyzer}.
* By default, the standard set of plugins as returned by {@link FlowrAnalyzerPluginDefaults} are registered.
* @param withDefaultPlugins - Whether to register the default plugins upon creation. Default is `true`.
* @see {@link FlowrAnalyzerPluginDefaults} - for the default plugin set.
* @see {@link FlowrAnalyzerBuilder#registerPlugins} - to add more plugins.
* @see {@link FlowrAnalyzerBuilder#unregisterPlugins} - to remove plugins.
*/
constructor(withDefaultPlugins = true) {
if (withDefaultPlugins) {
this.registerPlugins(...(0, flowr_analyzer_plugin_defaults_1.FlowrAnalyzerPluginDefaults)());
}
}
/**
* Apply an amendment to the configuration the builder currently holds.
* This is mostly intended for more complex logic to transform the config.
* Please consider using {@link FlowrAnalyzerBuilder.configure} to set/amend individual values
* Per default, the value returned by {@link FlowrConfig.default} is used.
* @param func - Receives the current configuration of the builder and allows for amendment.
*/
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
amendConfig(func) {
this.flowrConfig = config_1.FlowrConfig.amend(this.flowrConfig, func);
return this;
}
/**
* Overwrite the configuration used by the resulting analyzer.
* @param config - The new configuration.
*/
setConfig(config) {
this.flowrConfig = config;
return this;
}
/**
* Set a specific value in the configuration used by the resulting analyzer.
*/
configure(key, value) {
config_1.FlowrConfig.setInConfigInPlace(this.flowrConfig, key, value);
return this;
}
/**
* Set the parser instance used by the analyzer.
* This is an alternative to {@link FlowrAnalyzerBuilder#setEngine} if you already have a parser instance.
* Please be aware, that if you want to parallelize multiple analyzers, there should be separate parser instances.
*/
setParser(parser) {
this.parser = parser;
return this;
}
/**
* Set the engine and hence the parser that will be used by the analyzer.
* This is an alternative to {@link FlowrAnalyzerBuilder#setParser} if you do not have a parser instance at hand.
*/
setEngine(engine) {
this.flowrConfig.defaultEngine = engine;
return this;
}
/**
* Additional parameters for the analyses.
* @param input - The input.
*/
setInput(input) {
this.input = input;
return this;
}
/**
* Register one or multiple additional plugins.
* For the default plugin set, please refer to {@link FlowrAnalyzerPluginDefaults}, they can be registered
* by passing `true` to the {@link FlowrAnalyzerBuilder} constructor.
* @param plugin - One or multiple plugins to register.
* @see {@link FlowrAnalyzerBuilder#unregisterPlugins} to remove plugins.
*/
registerPlugins(...plugin) {
for (const p of plugin) {
const s = (0, plugin_registry_1.makePlugin)(p);
const g = this.plugins.get(s.type) ?? [];
g.push(s);
this.plugins.set(s.type, g);
}
return this;
}
/**
* Remove one or multiple plugins.
* @see {@link FlowrAnalyzerBuilder#registerPlugins} to add plugins.
*/
unregisterPlugins(...plugin) {
for (const p of plugin) {
const name = typeof p === 'string' ? p : p.name;
for (const [type, plugins] of this.plugins) {
this.plugins.set(type, plugins.filter((pl) => pl.name !== name));
}
}
return this;
}
/**
* Create the {@link FlowrAnalyzer} instance using the given information.
* Please note that the only reason this is `async` is that if no parser is set,
* we need to retrieve the default engine instance which is an async operation.
* If you have already initialized the engine (e.g., with {@link TreeSitterExecutor#initTreeSitter}),
* you can use the synchronous version {@link FlowrAnalyzerBuilder#buildSync} instead.
*/
async build() {
if (!this.parser) {
const engines = await (0, engines_1.retrieveEngineInstances)(this.flowrConfig, true);
this.parser = engines.engines[engines.default];
}
return this.buildSync();
}
/**
* Synchronous version of {@link FlowrAnalyzerBuilder#build}, please only use this if you have set the parser using
* {@link FlowrAnalyzerBuilder#setParser} before, otherwise an error will be thrown.
*/
buildSync() {
(0, assert_1.guard)(this.parser !== undefined, 'No parser set, please use the setParser or setEngine method to set a parser before building the analyzer');
const context = new flowr_analyzer_context_1.FlowrAnalyzerContext(this.flowrConfig, this.plugins);
const cache = flowr_analyzer_cache_1.FlowrAnalyzerCache.create({
parser: this.parser,
context,
...(this.input ?? {})
});
const analyzer = new flowr_analyzer_1.FlowrAnalyzer(this.parser, context, cache);
// we do it here to save time later if the analyzer is to be duplicated
context.resolvePreAnalysis();
return analyzer;
}
}
exports.FlowrAnalyzerBuilder = FlowrAnalyzerBuilder;
//# sourceMappingURL=flowr-analyzer-builder.js.map