@eagleoutice/flowr 2.7.4 → 2.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -15
- package/cli/repl/commands/repl-commands.d.ts +2 -0
- package/cli/repl/commands/repl-commands.js +2 -0
- package/cli/repl/commands/repl-dataflow.d.ts +2 -0
- package/cli/repl/commands/repl-dataflow.js +38 -1
- package/cli/repl/core.js +22 -0
- package/config.d.ts +5 -0
- package/config.js +6 -0
- package/dataflow/graph/graph.js +2 -0
- package/documentation/wiki-analyzer.js +12 -0
- package/documentation/wiki-interface.js +3 -0
- package/documentation/wiki-query.js +1 -1
- package/package.json +5 -1
- package/project/context/flowr-analyzer-dependencies-context.d.ts +5 -1
- package/project/context/flowr-analyzer-functions-context.d.ts +16 -0
- package/project/context/flowr-analyzer-functions-context.js +6 -0
- package/project/context/flowr-analyzer-loading-order-context.d.ts +4 -4
- package/project/context/flowr-analyzer-loading-order-context.js +4 -0
- package/project/plugins/file-plugins/files/flowr-description-file.d.ts +18 -1
- package/project/plugins/file-plugins/files/flowr-description-file.js +70 -13
- package/project/plugins/loading-order-plugins/flowr-analyzer-loading-order-description-file-plugin.js +8 -3
- package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-description-file-plugin.js +5 -2
- package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-namespace-file-plugin.js +6 -1
- package/project/plugins/package-version-plugins/package.js +1 -1
- package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +12 -2
- package/queries/catalog/project-query/project-query-executor.js +12 -2
- package/queries/catalog/project-query/project-query-format.d.ts +13 -0
- package/queries/catalog/project-query/project-query-format.js +25 -2
- package/queries/query-print.js +8 -3
- package/util/mermaid/cfg.d.ts +3 -0
- package/util/mermaid/cfg.js +25 -0
- package/util/r-author.d.ts +39 -0
- package/util/r-author.js +194 -0
- package/util/simple-df/dfg-ascii.d.ts +5 -0
- package/util/simple-df/dfg-ascii.js +272 -0
- package/util/version.js +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ It offers a wide variety of features, for example:
|
|
|
24
24
|
|
|
25
25
|
```shell
|
|
26
26
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
27
|
-
flowR repl using flowR v2.7.
|
|
27
|
+
flowR repl using flowR v2.7.4, R grammar v14 (tree-sitter engine)
|
|
28
28
|
R> :query @linter "read.csv(\"/root/x.txt\")"
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -47,18 +47,18 @@ It offers a wide variety of features, for example:
|
|
|
47
47
|
╰ Path `/root/x.txt` at 1.1-23
|
|
48
48
|
╰ _Metadata_: <code>{"totalConsidered":1,"totalUnknown":0,"searchTimeMs":0,"processTimeMs":0}</code>
|
|
49
49
|
╰ **Unused Definitions** (unused-definitions):
|
|
50
|
-
╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":
|
|
50
|
+
╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":0}</code>
|
|
51
51
|
╰ **Naming Convention** (naming-convention):
|
|
52
52
|
╰ _Metadata_: <code>{"numMatches":0,"numBreak":0,"searchTimeMs":0,"processTimeMs":0}</code>
|
|
53
53
|
╰ **Network Functions** (network-functions):
|
|
54
54
|
╰ _Metadata_: <code>{"totalCalls":0,"totalFunctionDefinitions":0,"searchTimeMs":0,"processTimeMs":0}</code>
|
|
55
55
|
╰ **Dataframe Access Validation** (dataframe-access-validation):
|
|
56
|
-
╰ _Metadata_: <code>{"numOperations":0,"numAccesses":0,"totalAccessed":0,"searchTimeMs":0,"processTimeMs":
|
|
56
|
+
╰ _Metadata_: <code>{"numOperations":0,"numAccesses":0,"totalAccessed":0,"searchTimeMs":0,"processTimeMs":1}</code>
|
|
57
57
|
╰ **Dead Code** (dead-code):
|
|
58
58
|
╰ _Metadata_: <code>{"consideredNodes":5,"searchTimeMs":0,"processTimeMs":0}</code>
|
|
59
59
|
╰ **Useless Loops** (useless-loop):
|
|
60
60
|
╰ _Metadata_: <code>{"numOfUselessLoops":0,"searchTimeMs":0,"processTimeMs":0}</code>
|
|
61
|
-
[;3mAll queries together required ≈2 ms (1ms accuracy, total
|
|
61
|
+
[;3mAll queries together required ≈2 ms (1ms accuracy, total 2 ms)[0m[0m
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
|
|
@@ -82,7 +82,7 @@ It offers a wide variety of features, for example:
|
|
|
82
82
|
|
|
83
83
|
Query: **linter** (3 ms)\
|
|
84
84
|
╰ **Deprecated Functions** (deprecated-functions):\
|
|
85
|
-
╰ _Metadata_: <code>{"totalCalls":0,"totalFunctionDefinitions":0,"searchTimeMs":
|
|
85
|
+
╰ _Metadata_: <code>{"totalCalls":0,"totalFunctionDefinitions":0,"searchTimeMs":1,"processTimeMs":0}</code>\
|
|
86
86
|
╰ **File Path Validity** (file-path-validity):\
|
|
87
87
|
╰ certain:\
|
|
88
88
|
╰ Path `/root/x.txt` at 1.1-23\
|
|
@@ -92,7 +92,7 @@ It offers a wide variety of features, for example:
|
|
|
92
92
|
╰ **Absolute Paths** (absolute-file-paths):\
|
|
93
93
|
╰ certain:\
|
|
94
94
|
╰ Path `/root/x.txt` at 1.1-23\
|
|
95
|
-
╰ _Metadata_: <code>{"totalConsidered":1,"totalUnknown":0,"searchTimeMs":
|
|
95
|
+
╰ _Metadata_: <code>{"totalConsidered":1,"totalUnknown":0,"searchTimeMs":0,"processTimeMs":0}</code>\
|
|
96
96
|
╰ **Unused Definitions** (unused-definitions):\
|
|
97
97
|
╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":0}</code>\
|
|
98
98
|
╰ **Naming Convention** (naming-convention):\
|
|
@@ -105,11 +105,11 @@ It offers a wide variety of features, for example:
|
|
|
105
105
|
╰ _Metadata_: <code>{"consideredNodes":5,"searchTimeMs":0,"processTimeMs":0}</code>\
|
|
106
106
|
╰ **Useless Loops** (useless-loop):\
|
|
107
107
|
╰ _Metadata_: <code>{"numOfUselessLoops":0,"searchTimeMs":0,"processTimeMs":0}</code>\
|
|
108
|
-
_All queries together required ≈3 ms (1ms accuracy, total
|
|
108
|
+
_All queries together required ≈3 ms (1ms accuracy, total 3 ms)_
|
|
109
109
|
|
|
110
110
|
<details> <summary style="color:gray">Show Detailed Results as Json</summary>
|
|
111
111
|
|
|
112
|
-
The analysis required _3.
|
|
112
|
+
The analysis required _3.0 ms_ (including parsing and normalization and the query) within the generation environment.
|
|
113
113
|
|
|
114
114
|
In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
|
|
115
115
|
Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
|
|
@@ -126,7 +126,7 @@ It offers a wide variety of features, for example:
|
|
|
126
126
|
".meta": {
|
|
127
127
|
"totalCalls": 0,
|
|
128
128
|
"totalFunctionDefinitions": 0,
|
|
129
|
-
"searchTimeMs":
|
|
129
|
+
"searchTimeMs": 1,
|
|
130
130
|
"processTimeMs": 0
|
|
131
131
|
}
|
|
132
132
|
},
|
|
@@ -180,7 +180,7 @@ It offers a wide variety of features, for example:
|
|
|
180
180
|
".meta": {
|
|
181
181
|
"totalConsidered": 1,
|
|
182
182
|
"totalUnknown": 0,
|
|
183
|
-
"searchTimeMs":
|
|
183
|
+
"searchTimeMs": 0,
|
|
184
184
|
"processTimeMs": 0
|
|
185
185
|
}
|
|
186
186
|
},
|
|
@@ -307,7 +307,7 @@ It offers a wide variety of features, for example:
|
|
|
307
307
|
|
|
308
308
|
```shell
|
|
309
309
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
310
|
-
flowR repl using flowR v2.7.
|
|
310
|
+
flowR repl using flowR v2.7.4, R grammar v14 (tree-sitter engine)
|
|
311
311
|
R> :query @static-slice (11@sum) file://test/testfiles/example.R
|
|
312
312
|
```
|
|
313
313
|
|
|
@@ -321,7 +321,7 @@ It offers a wide variety of features, for example:
|
|
|
321
321
|
N <- 10
|
|
322
322
|
for(i in 1:(N-1)) sum <- sum + i + w
|
|
323
323
|
sum
|
|
324
|
-
[;3mAll queries together required ≈2 ms (1ms accuracy, total
|
|
324
|
+
[;3mAll queries together required ≈2 ms (1ms accuracy, total 3 ms)[0m[0m
|
|
325
325
|
```
|
|
326
326
|
|
|
327
327
|
|
|
@@ -355,7 +355,7 @@ It offers a wide variety of features, for example:
|
|
|
355
355
|
|
|
356
356
|
|
|
357
357
|
* 🚀 **fast data- and control-flow graphs**\
|
|
358
|
-
Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">
|
|
358
|
+
Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">117.6 ms</span></i> (as of Dec 22, 2025),
|
|
359
359
|
_flowR_ can analyze the data- and control-flow of the average real-world R script. See the [benchmarks](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark) for more information,
|
|
360
360
|
and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/Dataflow-Graph) for more details on the dataflow graph.
|
|
361
361
|
|
|
@@ -391,7 +391,7 @@ It offers a wide variety of features, for example:
|
|
|
391
391
|
|
|
392
392
|
```shell
|
|
393
393
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
394
|
-
flowR repl using flowR v2.7.
|
|
394
|
+
flowR repl using flowR v2.7.4, R grammar v14 (tree-sitter engine)
|
|
395
395
|
R> :dataflow* test/testfiles/example.R
|
|
396
396
|
```
|
|
397
397
|
|
|
@@ -696,7 +696,7 @@ It offers a wide variety of features, for example:
|
|
|
696
696
|
```
|
|
697
697
|
|
|
698
698
|
|
|
699
|
-
(The analysis required _2.
|
|
699
|
+
(The analysis required _2.7 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
|
|
700
700
|
|
|
701
701
|
|
|
702
702
|
|
|
@@ -16,6 +16,8 @@ declare const _commands: {
|
|
|
16
16
|
readonly 'dataflow*': ReplCodeCommand;
|
|
17
17
|
readonly dataflowsimple: ReplCodeCommand;
|
|
18
18
|
readonly 'dataflowsimple*': ReplCodeCommand;
|
|
19
|
+
readonly dataflowascii: ReplCodeCommand;
|
|
20
|
+
readonly dataflowsilent: ReplCodeCommand;
|
|
19
21
|
readonly controlflow: ReplCodeCommand;
|
|
20
22
|
readonly 'controlflow*': ReplCodeCommand;
|
|
21
23
|
readonly controlflowbb: ReplCodeCommand;
|
|
@@ -83,6 +83,8 @@ const _commands = {
|
|
|
83
83
|
'dataflow*': repl_dataflow_1.dataflowStarCommand,
|
|
84
84
|
'dataflowsimple': repl_dataflow_1.dataflowSimplifiedCommand,
|
|
85
85
|
'dataflowsimple*': repl_dataflow_1.dataflowSimpleStarCommand,
|
|
86
|
+
'dataflowascii': repl_dataflow_1.dataflowAsciiCommand,
|
|
87
|
+
'dataflowsilent': repl_dataflow_1.dataflowSilentCommand,
|
|
86
88
|
'controlflow': repl_cfg_1.controlflowCommand,
|
|
87
89
|
'controlflow*': repl_cfg_1.controlflowStarCommand,
|
|
88
90
|
'controlflowbb': repl_cfg_1.controlflowBbCommand,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ReplCodeCommand } from './repl-main';
|
|
2
2
|
export declare const dataflowCommand: ReplCodeCommand;
|
|
3
3
|
export declare const dataflowStarCommand: ReplCodeCommand;
|
|
4
|
+
export declare const dataflowAsciiCommand: ReplCodeCommand;
|
|
5
|
+
export declare const dataflowSilentCommand: ReplCodeCommand;
|
|
4
6
|
export declare const dataflowSimplifiedCommand: ReplCodeCommand;
|
|
5
7
|
export declare const dataflowSimpleStarCommand: ReplCodeCommand;
|
|
@@ -33,11 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.dataflowSimpleStarCommand = exports.dataflowSimplifiedCommand = exports.dataflowStarCommand = exports.dataflowCommand = void 0;
|
|
36
|
+
exports.dataflowSimpleStarCommand = exports.dataflowSimplifiedCommand = exports.dataflowSilentCommand = exports.dataflowAsciiCommand = exports.dataflowStarCommand = exports.dataflowCommand = void 0;
|
|
37
37
|
const retriever_1 = require("../../../r-bridge/retriever");
|
|
38
38
|
const dfg_1 = require("../../../util/mermaid/dfg");
|
|
39
39
|
const ansi_1 = require("../../../util/text/ansi");
|
|
40
40
|
const core_1 = require("../core");
|
|
41
|
+
const vertex_1 = require("../../../dataflow/graph/vertex");
|
|
42
|
+
const dfg_ascii_1 = require("../../../util/simple-df/dfg-ascii");
|
|
41
43
|
function formatInfo(out, type, meta) {
|
|
42
44
|
return out.formatter.format(`Copied ${type} to clipboard (dataflow: ${meta['.meta'].timing + 'ms'}).`, { color: 7 /* Colors.White */, effect: ansi_1.ColorEffect.Foreground, style: 3 /* FontStyles.Italic */ });
|
|
43
45
|
}
|
|
@@ -80,6 +82,41 @@ exports.dataflowStarCommand = {
|
|
|
80
82
|
catch { /* do nothing this is a service thing */ }
|
|
81
83
|
}
|
|
82
84
|
};
|
|
85
|
+
exports.dataflowAsciiCommand = {
|
|
86
|
+
description: 'Returns an ASCII representation of the dataflow graph',
|
|
87
|
+
isCodeCommand: true,
|
|
88
|
+
usageExample: ':dataflowascii',
|
|
89
|
+
aliases: ['df!'],
|
|
90
|
+
script: false,
|
|
91
|
+
argsParser: (args) => (0, core_1.handleString)(args),
|
|
92
|
+
fn: async ({ output, analyzer }) => {
|
|
93
|
+
const result = await analyzer.dataflow();
|
|
94
|
+
output.stdout((0, dfg_ascii_1.dfgToAscii)(result.graph));
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
exports.dataflowSilentCommand = {
|
|
98
|
+
description: 'Just calculates the DFG, but only prints summary info',
|
|
99
|
+
isCodeCommand: true,
|
|
100
|
+
usageExample: ':dataflowsilent',
|
|
101
|
+
aliases: ['d#', 'df#'],
|
|
102
|
+
script: false,
|
|
103
|
+
argsParser: (args) => (0, core_1.handleString)(args),
|
|
104
|
+
fn: async ({ output, analyzer }) => {
|
|
105
|
+
const result = await analyzer.dataflow();
|
|
106
|
+
const numOfEdges = Array.from(result.graph.edges().flatMap(e => e[1].entries())).length;
|
|
107
|
+
const numOfVertices = Array.from(result.graph.vertices(true)).length;
|
|
108
|
+
output.stdout(output.formatter.format(`Dataflow calculated in ${result['.meta'].timing}ms.`, { color: 7 /* Colors.White */, effect: ansi_1.ColorEffect.Foreground, style: 3 /* FontStyles.Italic */ }) + '\n' +
|
|
109
|
+
'Edges: ' + output.formatter.format(`${String(numOfEdges).padStart(12)}`, { color: 6 /* Colors.Cyan */, effect: ansi_1.ColorEffect.Foreground }) + '\n' +
|
|
110
|
+
// number of vertices and edges
|
|
111
|
+
'Vertices: ' + output.formatter.format(`${String(numOfVertices).padStart(12)}`, { color: 6 /* Colors.Cyan */, effect: ansi_1.ColorEffect.Foreground }));
|
|
112
|
+
const longestVertexType = Math.max(...Object.keys(vertex_1.VertexType).map(vt => vt.length));
|
|
113
|
+
for (const vertType of Object.values(vertex_1.VertexType)) {
|
|
114
|
+
const vertsOfType = Array.from(result.graph.verticesOfType(vertType));
|
|
115
|
+
const longVertexName = Object.entries(vertex_1.VertexType).find(([, v]) => v === vertType)?.[0] ?? vertType;
|
|
116
|
+
output.stdout(` - ${(longVertexName + ':').padEnd(longestVertexType + 1)} ` + output.formatter.format(`${String(vertsOfType.length).padStart(8)}`, { color: 6 /* Colors.Cyan */, effect: ansi_1.ColorEffect.Foreground }).padStart(9, ' '));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
83
120
|
exports.dataflowSimplifiedCommand = {
|
|
84
121
|
description: `Get mermaid code for the simplified dataflow graph, start with '${retriever_1.fileProtocol}' to indicate a file`,
|
|
85
122
|
isCodeCommand: true,
|
package/cli/repl/core.js
CHANGED
|
@@ -53,6 +53,7 @@ const os_1 = __importDefault(require("os"));
|
|
|
53
53
|
const path_1 = __importDefault(require("path"));
|
|
54
54
|
const fs_1 = __importDefault(require("fs"));
|
|
55
55
|
const args_1 = require("../../util/text/args");
|
|
56
|
+
const ansi_1 = require("../../util/text/ansi");
|
|
56
57
|
const repl_commands_1 = require("./commands/repl-commands");
|
|
57
58
|
const scripts_info_1 = require("../common/scripts-info");
|
|
58
59
|
const retriever_1 = require("../../r-bridge/retriever");
|
|
@@ -144,6 +145,7 @@ function handleString(code) {
|
|
|
144
145
|
};
|
|
145
146
|
}
|
|
146
147
|
async function replProcessStatement(output, statement, analyzer, allowRSessionAccess) {
|
|
148
|
+
const time = Date.now();
|
|
147
149
|
if (statement.startsWith(':')) {
|
|
148
150
|
const command = statement.slice(1).split(' ')[0].toLowerCase();
|
|
149
151
|
const processor = (0, repl_commands_1.getCommand)(command);
|
|
@@ -179,6 +181,26 @@ async function replProcessStatement(output, statement, analyzer, allowRSessionAc
|
|
|
179
181
|
else {
|
|
180
182
|
await (0, repl_execute_1.tryExecuteRShellCommand)({ output, analyzer, remainingLine: statement, allowRSessionAccess });
|
|
181
183
|
}
|
|
184
|
+
if (analyzer.inspectContext().config.repl.quickStats) {
|
|
185
|
+
try {
|
|
186
|
+
const duration = Date.now() - time;
|
|
187
|
+
console.log(output.formatter.format(`[REPL Stats] Processed in ${duration}ms`, {
|
|
188
|
+
style: 3 /* FontStyles.Italic */,
|
|
189
|
+
effect: ansi_1.ColorEffect.Foreground,
|
|
190
|
+
color: 7 /* Colors.White */
|
|
191
|
+
}));
|
|
192
|
+
const memoryUsage = process.memoryUsage();
|
|
193
|
+
const memoryUsageStr = Object.entries(memoryUsage).map(([key, value]) => `${key}: ${(value / 1024 / 1024).toFixed(2)} MB`).join(', ');
|
|
194
|
+
console.log(output.formatter.format(`[REPL Stats] Memory Usage: ${memoryUsageStr}`, {
|
|
195
|
+
style: 3 /* FontStyles.Italic */,
|
|
196
|
+
effect: ansi_1.ColorEffect.Foreground,
|
|
197
|
+
color: 7 /* Colors.White */
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// do nothing, this is just a nice-to-have
|
|
202
|
+
}
|
|
203
|
+
}
|
|
182
204
|
}
|
|
183
205
|
/**
|
|
184
206
|
* This function interprets the given `expr` as a REPL command (see {@link repl} for more on the semantics).
|
package/config.d.ts
CHANGED
|
@@ -98,6 +98,11 @@ export interface FlowrConfigOptions extends MergeableRecord {
|
|
|
98
98
|
};
|
|
99
99
|
};
|
|
100
100
|
};
|
|
101
|
+
/** Configuration options for the REPL */
|
|
102
|
+
readonly repl: {
|
|
103
|
+
/** Whether to show quick stats in the REPL after each evaluation */
|
|
104
|
+
quickStats: boolean;
|
|
105
|
+
};
|
|
101
106
|
readonly project: {
|
|
102
107
|
/** Whether to resolve unknown paths loaded by the r project disk when trying to source/analyze files */
|
|
103
108
|
resolveUnknownPathsOnDisk: boolean;
|
package/config.js
CHANGED
|
@@ -65,6 +65,9 @@ exports.defaultConfigOptions = {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
|
+
repl: {
|
|
69
|
+
quickStats: false
|
|
70
|
+
},
|
|
68
71
|
project: {
|
|
69
72
|
resolveUnknownPathsOnDisk: true
|
|
70
73
|
},
|
|
@@ -106,6 +109,9 @@ exports.flowrConfigFileSchema = joi_1.default.object({
|
|
|
106
109
|
}).optional().description('Do you want to overwrite (parts) of the builtin definition?')
|
|
107
110
|
}).optional().description('Semantics regarding how to handle the R environment.')
|
|
108
111
|
}).description('Configure language semantics and how flowR handles them.'),
|
|
112
|
+
repl: joi_1.default.object({
|
|
113
|
+
quickStats: joi_1.default.boolean().optional().description('Whether to show quick stats in the REPL after each evaluation.')
|
|
114
|
+
}).description('Configuration options for the REPL.'),
|
|
109
115
|
project: joi_1.default.object({
|
|
110
116
|
resolveUnknownPathsOnDisk: joi_1.default.boolean().optional().description('Whether to resolve unknown paths loaded by the r project disk when trying to source/analyze files.')
|
|
111
117
|
}).description('Project specific configuration options.'),
|
package/dataflow/graph/graph.js
CHANGED
|
@@ -308,6 +308,8 @@ class DataflowGraph {
|
|
|
308
308
|
}
|
|
309
309
|
else {
|
|
310
310
|
this.vertexInformation.set(reference.nodeId, { ...vertex, tag: vertex_1.VertexType.VariableDefinition });
|
|
311
|
+
this.types.set(vertex.tag, (this.types.get(vertex.tag) ?? []).filter(id => id !== reference.nodeId));
|
|
312
|
+
this.types.set(vertex_1.VertexType.VariableDefinition, (this.types.get(vertex_1.VertexType.VariableDefinition) ?? []).concat([reference.nodeId]));
|
|
311
313
|
}
|
|
312
314
|
}
|
|
313
315
|
/**
|
|
@@ -27,6 +27,7 @@ const doc_maker_1 = require("./wiki-mk/doc-maker");
|
|
|
27
27
|
const flowr_analyzer_rmd_file_plugin_1 = require("../project/plugins/file-plugins/notebooks/flowr-analyzer-rmd-file-plugin");
|
|
28
28
|
const flowr_analyzer_plugin_1 = require("../project/plugins/flowr-analyzer-plugin");
|
|
29
29
|
const flowr_analyzer_environment_context_1 = require("../project/context/flowr-analyzer-environment-context");
|
|
30
|
+
const flowr_analyzer_functions_context_1 = require("../project/context/flowr-analyzer-functions-context");
|
|
30
31
|
async function analyzerQuickExample() {
|
|
31
32
|
const analyzer = await new flowr_analyzer_builder_1.FlowrAnalyzerBuilder()
|
|
32
33
|
.setEngine('tree-sitter')
|
|
@@ -398,6 +399,17 @@ Probably the most important method is
|
|
|
398
399
|
${ctx.linkM(flowr_analyzer_dependencies_context_1.FlowrAnalyzerDependenciesContext, 'getDependency', { codeFont: true, realNameWrapper: 'i' })}
|
|
399
400
|
that allows you to query for a specific dependency by name.
|
|
400
401
|
|
|
402
|
+
${(0, doc_structure_1.section)('Functions Context', 3)}
|
|
403
|
+
|
|
404
|
+
The ${ctx.link(flowr_analyzer_dependencies_context_1.FlowrAnalyzerDependenciesContext)} also provides access to the associated
|
|
405
|
+
${ctx.link(flowr_analyzer_functions_context_1.FlowrAnalyzerFunctionsContext)} via its \`functionsContext\` attribute.
|
|
406
|
+
|
|
407
|
+
${ctx.hierarchy(flowr_analyzer_functions_context_1.FlowrAnalyzerFunctionsContext, { showImplSnippet: false })}
|
|
408
|
+
|
|
409
|
+
Probably the most important method is
|
|
410
|
+
${ctx.linkM(flowr_analyzer_functions_context_1.FlowrAnalyzerFunctionsContext, 'getFunctionInfo', { codeFont: true, realNameWrapper: 'i' })}
|
|
411
|
+
that allows you to query for a specific function by name.
|
|
412
|
+
|
|
401
413
|
${(0, doc_structure_1.section)('Environment Context', 3)}
|
|
402
414
|
|
|
403
415
|
Here is the structure of the ${ctx.link(flowr_analyzer_environment_context_1.FlowrAnalyzerEnvironmentContext)} that provides access to the built-in environment:
|
|
@@ -149,7 +149,7 @@ ${await (0, doc_query_1.showQuery)(shell, '', [{
|
|
|
149
149
|
const exampleCode = 'x + 1';
|
|
150
150
|
return `
|
|
151
151
|
This query returns the information about the analyzed project.
|
|
152
|
-
|
|
152
|
+
If present, it will incorporate plugins to, e.g., extract author and license information from R package DESCRIPTION files.
|
|
153
153
|
|
|
154
154
|
${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
|
|
155
155
|
type: 'project'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/flowr",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.5",
|
|
4
4
|
"description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
|
|
5
5
|
"types": "dist/src/index.d.ts",
|
|
6
6
|
"repository": {
|
|
@@ -172,11 +172,13 @@
|
|
|
172
172
|
"@types/command-line-args": "^5.2.3",
|
|
173
173
|
"@types/command-line-usage": "^5.0.4",
|
|
174
174
|
"@types/commonmark": "^0.27.10",
|
|
175
|
+
"@types/dagre": "^0.7.53",
|
|
175
176
|
"@types/n-readlines": "^1.0.6",
|
|
176
177
|
"@types/n3": "^1.26.0",
|
|
177
178
|
"@types/object-hash": "^3.0.6",
|
|
178
179
|
"@types/seedrandom": "^3.0.8",
|
|
179
180
|
"@types/semver": "^7.7.0",
|
|
181
|
+
"@types/spdx-expression-parse": "^3.0.5",
|
|
180
182
|
"@types/tmp": "^0.2.6",
|
|
181
183
|
"@types/ws": "^8.18.1",
|
|
182
184
|
"@typescript-eslint/eslint-plugin": "^8.40.0",
|
|
@@ -203,6 +205,7 @@
|
|
|
203
205
|
"command-line-args": "^6.0.1",
|
|
204
206
|
"command-line-usage": "^7.0.3",
|
|
205
207
|
"commonmark": "^0.31.2",
|
|
208
|
+
"dagre": "^0.8.5",
|
|
206
209
|
"gray-matter": "^4.0.3",
|
|
207
210
|
"joi": "^18.0.1",
|
|
208
211
|
"lz-string": "^1.5.0",
|
|
@@ -213,6 +216,7 @@
|
|
|
213
216
|
"rotating-file-stream": "^3.2.6",
|
|
214
217
|
"seedrandom": "^3.0.5",
|
|
215
218
|
"semver": "^7.7.1",
|
|
219
|
+
"spdx-expression-parse": "^4.0.0",
|
|
216
220
|
"tar": "^7.4.3",
|
|
217
221
|
"tmp": "^0.2.3",
|
|
218
222
|
"ts-essentials": "^10.1.1",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AbstractFlowrAnalyzerContext } from './abstract-flowr-analyzer-context';
|
|
2
2
|
import { FlowrAnalyzerPackageVersionsPlugin } from '../plugins/package-version-plugins/flowr-analyzer-package-versions-plugin';
|
|
3
3
|
import type { Package } from '../plugins/package-version-plugins/package';
|
|
4
|
-
import type { FlowrAnalyzerFunctionsContext } from './flowr-analyzer-functions-context';
|
|
4
|
+
import type { FlowrAnalyzerFunctionsContext, ReadOnlyFlowrAnalyzerFunctionsContext } from './flowr-analyzer-functions-context';
|
|
5
5
|
/**
|
|
6
6
|
* This is a read-only interface to the {@link FlowrAnalyzerDependenciesContext}.
|
|
7
7
|
* It prevents you from modifying the dependencies, but allows you to inspect them (which is probably what you want when using the {@link FlowrAnalyzer}).
|
|
@@ -12,6 +12,10 @@ export interface ReadOnlyFlowrAnalyzerDependenciesContext {
|
|
|
12
12
|
* The name of this context.
|
|
13
13
|
*/
|
|
14
14
|
readonly name: string;
|
|
15
|
+
/**
|
|
16
|
+
* The functions context associated with this dependencies-context.
|
|
17
|
+
*/
|
|
18
|
+
readonly functionsContext: ReadOnlyFlowrAnalyzerFunctionsContext;
|
|
15
19
|
/**
|
|
16
20
|
* Get the dependency with the given name, if it exists.
|
|
17
21
|
*
|
|
@@ -14,10 +14,26 @@ export interface FunctionInfo {
|
|
|
14
14
|
s3TypeDispatch?: string;
|
|
15
15
|
inferredType?: string;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* This is a read-only interface to the {@link FlowrAnalyzerFunctionsContext}.
|
|
19
|
+
* It prevents you from modifying the functions, but allows you to inspect them (which is probably what you want when using the {@link FlowrAnalyzer}).
|
|
20
|
+
* If you are a {@link FlowrAnalyzerPackageVersionsPlugin} and want to modify the functions, you can use the {@link FlowrAnalyzerFunctionsContext} directly.
|
|
21
|
+
*/
|
|
17
22
|
export interface ReadOnlyFlowrAnalyzerFunctionsContext {
|
|
18
23
|
readonly name: string;
|
|
24
|
+
/**
|
|
25
|
+
* Get the function information for the given function name and optional class name.
|
|
26
|
+
* @param name - The name of the function to get information for.
|
|
27
|
+
* @param className - The optional class name (e.g., for S3 generics).
|
|
28
|
+
*/
|
|
19
29
|
getFunctionInfo(name: string, className?: string): FunctionInfo | FunctionInfo[] | undefined;
|
|
20
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* This context is responsible for managing the functions identified in the project, including their origins, types, and other metadata.
|
|
33
|
+
* It works in conjunction with {@link FlowrAnalyzerPackageVersionsPlugin}s to gather and maintain this information.
|
|
34
|
+
*
|
|
35
|
+
* If you are interested in inspecting these functions, refer to {@link ReadOnlyFlowrAnalyzerFunctionsContext}.
|
|
36
|
+
*/
|
|
21
37
|
export declare class FlowrAnalyzerFunctionsContext extends AbstractFlowrAnalyzerContext<undefined, void, FlowrAnalyzerPackageVersionsPlugin> implements ReadOnlyFlowrAnalyzerFunctionsContext {
|
|
22
38
|
readonly name = "flowr-analyzer-functions-context";
|
|
23
39
|
private functionInfo;
|
|
@@ -10,6 +10,12 @@ var FunctionTypes;
|
|
|
10
10
|
FunctionTypes["ExportTypes"] = "exportTypes";
|
|
11
11
|
FunctionTypes["S3"] = "S3";
|
|
12
12
|
})(FunctionTypes || (exports.FunctionTypes = FunctionTypes = {}));
|
|
13
|
+
/**
|
|
14
|
+
* This context is responsible for managing the functions identified in the project, including their origins, types, and other metadata.
|
|
15
|
+
* It works in conjunction with {@link FlowrAnalyzerPackageVersionsPlugin}s to gather and maintain this information.
|
|
16
|
+
*
|
|
17
|
+
* If you are interested in inspecting these functions, refer to {@link ReadOnlyFlowrAnalyzerFunctionsContext}.
|
|
18
|
+
*/
|
|
13
19
|
class FlowrAnalyzerFunctionsContext extends abstract_flowr_analyzer_context_1.AbstractFlowrAnalyzerContext {
|
|
14
20
|
name = 'flowr-analyzer-functions-context';
|
|
15
21
|
functionInfo = new Map();
|
|
@@ -29,7 +29,7 @@ export interface ReadOnlyFlowrAnalyzerLoadingOrderContext {
|
|
|
29
29
|
/**
|
|
30
30
|
* Get the current guesses for the loading order, if any. These are populated by {@link FlowrAnalyzerLoadingOrderPlugin}s.
|
|
31
31
|
*/
|
|
32
|
-
currentGuesses(): readonly RParseRequest[][];
|
|
32
|
+
currentGuesses(): readonly (readonly RParseRequest[])[];
|
|
33
33
|
/**
|
|
34
34
|
* Get the current known loading order, if any. This is populated by {@link FlowrAnalyzerLoadingOrderPlugin}s if they have a source of identifying the order definitively.
|
|
35
35
|
*/
|
|
@@ -46,9 +46,9 @@ export declare class FlowrAnalyzerLoadingOrderContext extends AbstractFlowrAnaly
|
|
|
46
46
|
private rerunRequired;
|
|
47
47
|
constructor(ctx: FlowrAnalyzerContext, plugins: readonly FlowrAnalyzerLoadingOrderPlugin[] | undefined);
|
|
48
48
|
private knownOrder?;
|
|
49
|
-
private guesses;
|
|
49
|
+
private readonly guesses;
|
|
50
50
|
/** just the base collection of requests we know nothing about the order! */
|
|
51
|
-
private unordered;
|
|
51
|
+
private readonly unordered;
|
|
52
52
|
reset(): void;
|
|
53
53
|
/**
|
|
54
54
|
* Add one or multiple requests to the context.
|
|
@@ -69,7 +69,7 @@ export declare class FlowrAnalyzerLoadingOrderContext extends AbstractFlowrAnaly
|
|
|
69
69
|
* In case you have a certain order, use the `certain` flag to indicate this -- but please take care of *really* being certain!
|
|
70
70
|
*/
|
|
71
71
|
addGuess(guess: readonly RParseRequest[], certain?: boolean): void;
|
|
72
|
-
currentGuesses(): readonly RParseRequest[][];
|
|
72
|
+
currentGuesses(): readonly (readonly RParseRequest[])[];
|
|
73
73
|
currentKnownOrder(): readonly RParseRequest[] | undefined;
|
|
74
74
|
peekLoadingOrder(): readonly RParseRequest[] | undefined;
|
|
75
75
|
getUnorderedRequests(): readonly RParseRequest[];
|
|
@@ -65,12 +65,16 @@ class FlowrAnalyzerLoadingOrderContext extends abstract_flowr_analyzer_context_1
|
|
|
65
65
|
loadingOrderLog.warn(`Adding certain guess ${guess.map(g => g.request).join(', ')} after known order!`);
|
|
66
66
|
if (!(0, arrays_1.arrayEqual)(this.knownOrder, guess)) {
|
|
67
67
|
loadingOrderLog.error(`Certain guess ${guess.map(g => g.request).join(', ')} does not match known order ${this.knownOrder.map(g => g.request).join(', ')}`);
|
|
68
|
+
this.guesses.push(guess);
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
else {
|
|
71
72
|
this.knownOrder = guess;
|
|
72
73
|
}
|
|
73
74
|
}
|
|
75
|
+
else {
|
|
76
|
+
this.guesses.push(guess);
|
|
77
|
+
}
|
|
74
78
|
}
|
|
75
79
|
currentGuesses() {
|
|
76
80
|
return this.guesses;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { type FlowrFileProvider, type FileRole, FlowrFile } from '../../../context/flowr-file';
|
|
2
|
+
import type { Info } from 'spdx-expression-parse';
|
|
3
|
+
import type { RAuthorInfo } from '../../../../util/r-author';
|
|
4
|
+
import type { DeepReadonly } from 'ts-essentials';
|
|
2
5
|
export type DCF = Map<string, string[]>;
|
|
3
6
|
/**
|
|
4
7
|
* This decorates a text file and provides access to its content as a DCF (Debian Control File)-like structure.
|
|
5
8
|
*/
|
|
6
|
-
export declare class FlowrDescriptionFile extends FlowrFile<DCF
|
|
9
|
+
export declare class FlowrDescriptionFile extends FlowrFile<DeepReadonly<DCF>> {
|
|
7
10
|
private readonly wrapped;
|
|
8
11
|
/**
|
|
9
12
|
* Prefer the static {@link FlowrDescriptionFile.from} method to create instances of this class as it will not re-create if already a description file
|
|
@@ -19,4 +22,18 @@ export declare class FlowrDescriptionFile extends FlowrFile<DCF> {
|
|
|
19
22
|
* Description file lifter, this does not re-create if already a description file
|
|
20
23
|
*/
|
|
21
24
|
static from(file: FlowrFileProvider | FlowrDescriptionFile, role?: FileRole): FlowrDescriptionFile;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the parsed license information from the 'License' field in the DESCRIPTION file.
|
|
27
|
+
*/
|
|
28
|
+
license(): Info[] | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Returns the parsed authors from the `Authors@R` field in the DESCRIPTION file.
|
|
31
|
+
*/
|
|
32
|
+
authors(): RAuthorInfo[] | undefined;
|
|
22
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Parses the 'License' field from an R DESCRIPTION file into SPDX license expressions.
|
|
36
|
+
* @param licenseField - The 'License' field from the DESCRIPTION file as an array of strings.
|
|
37
|
+
* @returns An array of SPDX license information objects if parsing was successful.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseRLicenseField(...licenseField: string[]): Info[];
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.FlowrDescriptionFile = void 0;
|
|
7
|
+
exports.parseRLicenseField = parseRLicenseField;
|
|
4
8
|
const flowr_file_1 = require("../../../context/flowr-file");
|
|
9
|
+
const spdx_expression_parse_1 = __importDefault(require("spdx-expression-parse"));
|
|
10
|
+
const log_1 = require("../../../../util/log");
|
|
11
|
+
const r_author_1 = require("../../../../util/r-author");
|
|
12
|
+
const args_1 = require("../../../../util/text/args");
|
|
5
13
|
/**
|
|
6
14
|
* This decorates a text file and provides access to its content as a DCF (Debian Control File)-like structure.
|
|
7
15
|
*/
|
|
@@ -31,8 +39,65 @@ class FlowrDescriptionFile extends flowr_file_1.FlowrFile {
|
|
|
31
39
|
}
|
|
32
40
|
return file instanceof FlowrDescriptionFile ? file : new FlowrDescriptionFile(file);
|
|
33
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns the parsed license information from the 'License' field in the DESCRIPTION file.
|
|
44
|
+
*/
|
|
45
|
+
license() {
|
|
46
|
+
const licenses = this.content().get('License');
|
|
47
|
+
if (!licenses) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return parseRLicenseField(...licenses);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Returns the parsed authors from the `Authors@R` field in the DESCRIPTION file.
|
|
54
|
+
*/
|
|
55
|
+
authors() {
|
|
56
|
+
const authors = this.content().get('Authors@R');
|
|
57
|
+
return authors ? authors.flatMap(r_author_1.parseRAuthorString) : undefined;
|
|
58
|
+
}
|
|
34
59
|
}
|
|
35
60
|
exports.FlowrDescriptionFile = FlowrDescriptionFile;
|
|
61
|
+
function cleanUpDescLicense(licenseStr) {
|
|
62
|
+
// we have to replace '\s[|+&]\s' with ' OR ' or ' AND ' respectively
|
|
63
|
+
return licenseStr
|
|
64
|
+
.replaceAll(/\s*\|\s*/g, ' OR ')
|
|
65
|
+
.replaceAll(/\s*[&+,]\s*/g, ' AND ')
|
|
66
|
+
// we have to replace any variant of 'file LICENSE' with just LicenseRef-FILE
|
|
67
|
+
.replaceAll(/file(\s+|-)LICENSE/gi, 'LicenseRef-FILE');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parses the 'License' field from an R DESCRIPTION file into SPDX license expressions.
|
|
71
|
+
* @param licenseField - The 'License' field from the DESCRIPTION file as an array of strings.
|
|
72
|
+
* @returns An array of SPDX license information objects if parsing was successful.
|
|
73
|
+
*/
|
|
74
|
+
function parseRLicenseField(...licenseField) {
|
|
75
|
+
const licenses = [];
|
|
76
|
+
for (const licenseEntry of licenseField) {
|
|
77
|
+
const cleanedLicense = cleanUpDescLicense(licenseEntry);
|
|
78
|
+
try {
|
|
79
|
+
const parsed = (0, spdx_expression_parse_1.default)(cleanedLicense);
|
|
80
|
+
licenses.push(parsed);
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
log_1.log.warn(`Failed to parse license expression '${cleanedLicense}': ${e.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return licenses;
|
|
87
|
+
}
|
|
88
|
+
function emplaceDCF(key, val, result) {
|
|
89
|
+
if (!key) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
let values = [];
|
|
93
|
+
if (key.includes('@')) {
|
|
94
|
+
values = [val.trim()];
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
values = val ? cleanValues(val) : [];
|
|
98
|
+
}
|
|
99
|
+
result.set(key, values);
|
|
100
|
+
}
|
|
36
101
|
/**
|
|
37
102
|
* Parses the given file in the 'Debian Control Format'.
|
|
38
103
|
* @param file - The file to parse
|
|
@@ -49,27 +114,19 @@ function parseDCF(file) {
|
|
|
49
114
|
currentValue += '\n' + line.trim();
|
|
50
115
|
}
|
|
51
116
|
else {
|
|
52
|
-
|
|
53
|
-
const values = currentValue ? cleanValues(currentValue) : [];
|
|
54
|
-
result.set(currentKey, values);
|
|
55
|
-
}
|
|
117
|
+
emplaceDCF(currentKey, currentValue, result);
|
|
56
118
|
const [key, rest] = line.split(firstColonRegex).map(s => s.trim());
|
|
57
119
|
currentKey = key?.trim() ?? '';
|
|
58
120
|
currentValue = rest?.trim() ?? '';
|
|
59
121
|
}
|
|
60
122
|
}
|
|
61
|
-
|
|
62
|
-
const values = currentValue ? cleanValues(currentValue) : [];
|
|
63
|
-
result.set(currentKey, values);
|
|
64
|
-
}
|
|
123
|
+
emplaceDCF(currentKey, currentValue, result);
|
|
65
124
|
return result;
|
|
66
125
|
}
|
|
67
|
-
const
|
|
68
|
-
const cleanQuotesRegex = /'/g;
|
|
126
|
+
const splitRegex = /[\n\r]+/g;
|
|
69
127
|
function cleanValues(values) {
|
|
70
|
-
return values
|
|
71
|
-
.
|
|
72
|
-
.map(s => s.trim().replace(cleanQuotesRegex, ''))
|
|
128
|
+
return values.split(splitRegex).flatMap(l => (0, args_1.splitAtEscapeSensitive)(l, false, ','))
|
|
129
|
+
.map(s => s.trim())
|
|
73
130
|
.filter(s => s.length > 0);
|
|
74
131
|
}
|
|
75
132
|
//# sourceMappingURL=flowr-description-file.js.map
|