@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
175 lines (131 loc) • 9.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.explainWritingCode = explainWritingCode;
const shell_1 = require("../../../r-bridge/shell");
const flowr_analyzer_1 = require("../../../project/flowr-analyzer");
const doc_code_1 = require("../../doc-util/doc-code");
const pipeline_executor_1 = require("../../../core/pipeline-executor");
const retriever_1 = require("../../../r-bridge/retriever");
const doc_structure_1 = require("../../doc-util/doc-structure");
const tree_sitter_executor_1 = require("../../../r-bridge/lang-4.x/tree-sitter/tree-sitter-executor");
const flowr_analyzer_builder_1 = require("../../../project/flowr-analyzer-builder");
async function staticSliceExample() {
const analyzer = await new flowr_analyzer_builder_1.FlowrAnalyzerBuilder()
.setEngine('tree-sitter')
.build();
analyzer.addRequest('x <- 1\ny <- x\nx');
const result = await analyzer.query([
{
type: 'static-slice',
criteria: ['3@x']
}
]);
//console.log(result['static-slice']);
return result;
}
/**
* Explain how to write code using flowR
*/
function explainWritingCode(_shell, ctx) {
return `_flowR_ can be used as a ${ctx.linkPage('flowr:npm', 'module')} and offers several main classes and interfaces that are interesting for extension writers
(see the ${ctx.linkPage('flowr:vscode', 'Visual Studio Code extension')} or the ${ctx.linkPage('wiki/Core')} wiki page for more information).
### Creating Analyses with _flowR_
Nowadays, instances of the ${ctx.link(flowr_analyzer_1.FlowrAnalyzer)} should be used as central frontend to get analysis results from _flowR_.
For example, a program slice can be created like this:
${ctx.code(staticSliceExample, { dropLinesEnd: 2, dropLinesStart: 1, hideDefinedAt: true })}
For more information, please have a look at the ${ctx.linkPage('wiki/Analyzer')} wiki page, which explains how to construct and use the ${ctx.link(flowr_analyzer_1.FlowrAnalyzer)} in more detail.
To work with specific perspectives, you can also consult the respective pages like the ${ctx.linkPage('wiki/Dataflow Graph')} or the ${ctx.linkPage('wiki/Abstract Interpretation')} wiki pages.
### The Pipeline Executor (Low-Level Interface)
Once, in the beginning, _flowR_ was meant to produce a dataflow graph merely to provide *program slices*.
However, with continuous updates, the ${ctx.linkPage('wiki/Dataflow Graph')} repeatedly proves to be the more interesting part.
With this, we restructured _flowR_'s originally *hardcoded* pipeline to be far more flexible.
Now, it can be theoretically extended or replaced with arbitrary steps, optional steps, and what we call 'decorations' of these steps.
In short, a slicing pipeline using the ${ctx.link(pipeline_executor_1.PipelineExecutor)} looks like this:
${(0, doc_code_1.codeBlock)('ts', `
const slicer = new ${pipeline_executor_1.PipelineExecutor.name}(DEFAULT_SLICING_PIPELINE, {
parser: new ${shell_1.RShell.name}(),
request: ${retriever_1.requestFromInput.name}('x <- 1\\nx + 1'),
criterion: ['2@x']
})
const slice = await slicer.allRemainingSteps()
// console.log(slice.reconstruct.code)
`)}
${(0, doc_structure_1.details)('More Information', `
If you compare this, with what you would have done with the old (and removed) \`SteppingSlicer\`,
this essentially just requires you to replace the \`SteppingSlicer\` with the ${ctx.link(pipeline_executor_1.PipelineExecutor)}
and to pass the ${ctx.link('DEFAULT_SLICING_PIPELINE')} as the first argument.
The ${ctx.link(pipeline_executor_1.PipelineExecutor)}...
1. Provides structures to investigate the results of all intermediate steps
2. Can be executed step-by-step
3. Can repeat steps (e.g., to calculate multiple slices on the same input)
See the in-code documentation for more information.
`)}
### Using the ${ctx.link(shell_1.RShell)} to Interact with R
The ${ctx.link(shell_1.RShell)} class allows interfacing with the \`R\` ecosystem installed on the host system.
Please have a look at ${ctx.linkPage('wiki/Engines', 'flowR\'s Engines')} for more information on alternatives (for example, the ${ctx.link(tree_sitter_executor_1.TreeSitterExecutor)}).
${(0, doc_structure_1.block)({
type: 'IMPORTANT',
content: `
Each ${ctx.link(shell_1.RShell)} controls a new instance of the R interpreter,
make sure to call ${(0, doc_code_1.codeInline)(ctx.linkM(shell_1.RShell, 'close', { codeFont: false, realNameWrapper: 'i' }) + '()')} when you are done.`
})}
You can start a new "session" simply by constructing a new object with ${(0, doc_code_1.codeInline)('new ' + ctx.link(shell_1.RShell, { codeFont: false }) + '()')}.
However, there are several options that may be of interest
(e.g., to automatically revive the shell in case of errors or to control the name location of the R process on the system).
With a shell object (let's call it \`shell\`), you can execute R code by using ${ctx.linkM(shell_1.RShell, 'sendCommand', { realNameWrapper: 'i' })},
for example ${(0, doc_code_1.codeInline)('shell.' + ctx.linkM(shell_1.RShell, 'sendCommand', { codeFont: false, hideClass: true }) + '("1 + 1")')}.
However, this does not return anything, so if you want to collect the output of your command, use
${ctx.linkM(shell_1.RShell, 'sendCommandWithOutput', { realNameWrapper: 'i' })} instead.
Besides that, the command ${ctx.linkM(shell_1.RShell, 'tryToInjectHomeLibPath')} may be of interest, as it enables all libraries available on the host system.
### Generate Statistics (No longer a Focus of flowR)
<details>
<summary>Adding a New Feature to Extract</summary>
In this example, we construct a new feature to extract, with the name "*example*".
Whenever this name appears, you may substitute this with whatever name fits your feature best (as long as the name is unique).
1. **Create a new file in \`src/statistics/features/supported\`**\\
Create the file \`example.ts\`, and add its export to the \`index.ts\` file in the same directory (if not done automatically).
2. **Create the basic structure**\\
To get a better feel of what a feature must have, let's look
at the basic structure (of course, due to TypeScript syntax,
there are other ways to achieve the same goal):
\`\`\`ts
const initialExampleInfo = {
/* whatever start value is good for you */
someCounter: 0
}
export type ExampleInfo = Writable<typeof initialExampleInfo>
export const example: Feature<ExampleInfo> = {
name: 'Example Feature',
description: 'A longer example description',
process(existing: ExampleInfo, input: FeatureProcessorInput): ExampleInfo {
/* perform analysis on the input */
return existing
},
initialValue: initialExampleInfo
}
\`\`\`
The \`initialExampleInfo\` type holds the initial values for each counter that you want to maintain during the feature extraction (they will usually be initialized with 0). The resulting \`ExampleInfo\` type holds the structure of the data that is to be counted. Due to the vast amount of data processed, information like the name and location of a function call is not stored here, but instead written to disk (see below).
Every new feature must be of the \`Feature<Info>\` type, with \`Info\` referring to a \`FeatureInfo\` (like \`ExampleInfo\` in this example). Next to a \`name\` and a \`description\`, each Feature must provide:
- a processor that extracts the information from the input, adding it to the existing information.
- a function returning the initial value of the information (in this case, \`initialExampleInfo\`).
3. **Add it to the feature-mapping**\\
Now, in the \`feature.ts\` file in \`src/statistics/features\`, add your feature to the \`ALL_FEATURES\` object.
Now, we want to extract something. For the *example* feature created in the previous steps, we choose to count the amount of \`COMMENT\` tokens.
So we define a corresponding [XPath](https://developer.mozilla.org/en-US/docs/Web/XPath) query:
\`\`\`ts
const commentQuery: Query = xpath.parse('//COMMENT')
\`\`\`
Within our feature's \`process\` function, running the query is as simple as:
\`\`\`ts
const comments = commentQuery.select({ node: input.parsedRAst })
\`\`\`
Now we could do a lot of further processing, but for simplicity, we only record every comment found this way:
\`\`\`ts
appendStatisticsFile(example.name, 'comments', comments, input.filepath)
\`\`\`
We use \`example.name\` to avoid duplication with the name that we’ve assigned to the feature. It corresponds to the name of the folder in the statistics output.
\`'comments'\` refers to a freely chosen (but unique) name, that will be used as the name for the output file within the folder. The \`comments\` variable holds the result of the query, which is an array of nodes. Finally, we pass the \`filepath\` of the file that was analyzed (if known), so that it can be added to the statistics file (as additional information).
</details>
`;
}
//# sourceMappingURL=doc-writing-code.js.map