@eagleoutice/flowr 2.2.5 → 2.2.7

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 CHANGED
@@ -48,7 +48,7 @@ It offers a wide variety of features, for example:
48
48
 
49
49
  ```shell
50
50
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
51
- flowR repl using flowR v2.2.4, R v4.4.0 (r-shell engine)
51
+ flowR repl using flowR v2.2.5, R v4.4.0 (r-shell engine)
52
52
  R> :slicer test/testfiles/example.R --criterion "11@sum"
53
53
  ```
54
54
 
@@ -94,8 +94,8 @@ It offers a wide variety of features, for example:
94
94
  </details>
95
95
 
96
96
 
97
- * 🚀 **fast data and control-flow graphs**\
98
- Within just <i><span title="This measurement is automatically fetched from the latest benchmarks!">119.8 ms</i></span> (as of Feb 16, 2025),
97
+ * 🚀 **fast data- and control-flow graphs**\
98
+ Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">117.7 ms</i></span> (as of Feb 17, 2025),
99
99
  _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,
100
100
  and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/Dataflow-Graph) for more details on the dataflow graph.
101
101
 
@@ -131,7 +131,7 @@ It offers a wide variety of features, for example:
131
131
 
132
132
  ```shell
133
133
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
134
- flowR repl using flowR v2.2.4, R v4.4.0 (r-shell engine)
134
+ flowR repl using flowR v2.2.5, R v4.4.0 (r-shell engine)
135
135
  R> :dataflow* test/testfiles/example.R
136
136
  ```
137
137
 
@@ -375,7 +375,7 @@ It offers a wide variety of features, for example:
375
375
  52 -->|"argument"| 50
376
376
  ```
377
377
 
378
- (The analysis required _22.93 ms_ (including parse and normalize, using the [r-shell](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
378
+ (The analysis required _24.34 ms_ (including parse and normalize, using the [r-shell](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
379
379
 
380
380
 
381
381
 
@@ -390,11 +390,11 @@ It offers a wide variety of features, for example:
390
390
 
391
391
  If you want to use flowR and the features it provides, feel free to check out the:
392
392
 
393
- - [Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=code-inspect.vscode-flowr): provides access to flowR's capabilities directly in VS Code (also works in [vscode.dev](https://vscode.dev/))
393
+ - [Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=code-inspect.vscode-flowr): provides access to flowR directly in VS Code (or [vscode.dev](https://vscode.dev/))
394
394
  - [RStudio Addin](https://github.com/flowr-analysis/rstudio-addin-flowr): integrates flowR into [RStudio](https://posit.co/downloads/)
395
- - [R package](https://github.com/flowr-analysis/flowr-r-adapter): allows you to use flowR in your R scripts
395
+ - [R package](https://github.com/flowr-analysis/flowr-r-adapter): use flowR in your R scripts
396
396
  - [Docker image](https://hub.docker.com/r/eagleoutice/flowr): run flowR in a container, this also includes [flowR's server](https://github.com/flowr-analysis/flowr/wiki/Interface#communicating-with-the-server)
397
- - [NPM package](https://www.npmjs.com/package/@eagleoutice/flowr): include flowR in your TypeScript and JavaScript projects (e.g., used for the VS Code extension)
397
+ - [NPM package](https://www.npmjs.com/package/@eagleoutice/flowr): include flowR in your TypeScript and JavaScript projects
398
398
 
399
399
  ## ⭐ Getting Started
400
400
 
@@ -39,7 +39,6 @@ const default_pipelines_1 = require("../../../core/steps/pipeline/default-pipeli
39
39
  const retriever_1 = require("../../../r-bridge/retriever");
40
40
  const cfg_2 = require("../../../util/mermaid/cfg");
41
41
  const ansi_1 = require("../../../util/ansi");
42
- const clipboard = Promise.resolve().then(() => __importStar(require('clipboardy')));
43
42
  async function controlflow(parser, remainingLine) {
44
43
  return await (0, default_pipelines_1.createDataflowPipeline)(parser, {
45
44
  request: (0, retriever_1.requestFromInput)(remainingLine.trim())
@@ -62,7 +61,8 @@ exports.controlflowCommand = {
62
61
  const mermaid = (0, cfg_2.cfgToMermaid)(cfg, result.normalize);
63
62
  output.stdout(mermaid);
64
63
  try {
65
- (await clipboard).default.writeSync(mermaid);
64
+ const clipboard = await Promise.resolve().then(() => __importStar(require('clipboardy')));
65
+ clipboard.default.writeSync(mermaid);
66
66
  output.stdout(formatInfo(output, 'mermaid code'));
67
67
  }
68
68
  catch { /* do nothing this is a service thing */ }
@@ -79,7 +79,8 @@ exports.controlflowStarCommand = {
79
79
  const mermaid = (0, cfg_2.cfgToMermaidUrl)(cfg, result.normalize);
80
80
  output.stdout(mermaid);
81
81
  try {
82
- (await clipboard).default.writeSync(mermaid);
82
+ const clipboard = await Promise.resolve().then(() => __importStar(require('clipboardy')));
83
+ clipboard.default.writeSync(mermaid);
83
84
  output.stdout(formatInfo(output, 'mermaid url'));
84
85
  }
85
86
  catch { /* do nothing this is a service thing */ }
@@ -37,7 +37,6 @@ exports.dataflowStarCommand = exports.dataflowCommand = void 0;
37
37
  const default_pipelines_1 = require("../../../core/steps/pipeline/default-pipelines");
38
38
  const retriever_1 = require("../../../r-bridge/retriever");
39
39
  const dfg_1 = require("../../../util/mermaid/dfg");
40
- const clipboard = Promise.resolve().then(() => __importStar(require('clipboardy')));
41
40
  const ansi_1 = require("../../../util/ansi");
42
41
  /**
43
42
  * Obtain the dataflow graph using a known parser (such as the {@link RShell} or {@link TreeSitterExecutor}).
@@ -63,7 +62,8 @@ exports.dataflowCommand = {
63
62
  const mermaid = (0, dfg_1.graphToMermaid)({ graph: result.dataflow.graph, includeEnvironments: false }).string;
64
63
  output.stdout(mermaid);
65
64
  try {
66
- (await clipboard).default.writeSync(mermaid);
65
+ const clipboard = await Promise.resolve().then(() => __importStar(require('clipboardy')));
66
+ clipboard.default.writeSync(mermaid);
67
67
  output.stdout(formatInfo(output, 'mermaid code', result.dataflow['.meta'].timing));
68
68
  }
69
69
  catch { /* do nothing this is a service thing */ }
@@ -79,7 +79,8 @@ exports.dataflowStarCommand = {
79
79
  const mermaid = (0, dfg_1.graphToMermaidUrl)(result.dataflow.graph, false);
80
80
  output.stdout(mermaid);
81
81
  try {
82
- (await clipboard).default.writeSync(mermaid);
82
+ const clipboard = await Promise.resolve().then(() => __importStar(require('clipboardy')));
83
+ clipboard.default.writeSync(mermaid);
83
84
  output.stdout(formatInfo(output, 'mermaid url', result.dataflow['.meta'].timing));
84
85
  }
85
86
  catch { /* do nothing this is a service thing */ }
@@ -37,7 +37,6 @@ exports.normalizeStarCommand = exports.normalizeCommand = void 0;
37
37
  const default_pipelines_1 = require("../../../core/steps/pipeline/default-pipelines");
38
38
  const retriever_1 = require("../../../r-bridge/retriever");
39
39
  const ast_1 = require("../../../util/mermaid/ast");
40
- const clipboard = Promise.resolve().then(() => __importStar(require('clipboardy')));
41
40
  const ansi_1 = require("../../../util/ansi");
42
41
  async function normalize(parser, remainingLine) {
43
42
  return await (0, default_pipelines_1.createNormalizePipeline)(parser, {
@@ -60,7 +59,8 @@ exports.normalizeCommand = {
60
59
  const mermaid = (0, ast_1.normalizedAstToMermaid)(result.normalize.ast);
61
60
  output.stdout(mermaid);
62
61
  try {
63
- (await clipboard).default.writeSync(mermaid);
62
+ const clipboard = await Promise.resolve().then(() => __importStar(require('clipboardy')));
63
+ clipboard.default.writeSync(mermaid);
64
64
  output.stdout(formatInfo(output, 'mermaid url', result.normalize['.meta'].timing));
65
65
  }
66
66
  catch { /* do nothing this is a service thing */ }
@@ -76,7 +76,8 @@ exports.normalizeStarCommand = {
76
76
  const mermaid = (0, ast_1.normalizedAstToMermaidUrl)(result.normalize.ast);
77
77
  output.stdout(mermaid);
78
78
  try {
79
- (await clipboard).default.writeSync(mermaid);
79
+ const clipboard = await Promise.resolve().then(() => __importStar(require('clipboardy')));
80
+ clipboard.default.writeSync(mermaid);
80
81
  output.stdout(formatInfo(output, 'mermaid url', result.normalize['.meta'].timing));
81
82
  }
82
83
  catch { /* do nothing this is a service thing */ }
@@ -4,7 +4,7 @@ import type { Identifier, IdentifierDefinition } from './identifier';
4
4
  import { ReferenceType } from './identifier';
5
5
  import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
6
6
  import type { DataflowGraph } from '../graph/graph';
7
- import type { AstIdMap } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
7
+ import type { AstIdMap, RNodeWithParent } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
8
8
  /**
9
9
  * Resolves a given identifier name to a list of its possible definition location using R scoping and resolving rules.
10
10
  *
@@ -21,9 +21,32 @@ export declare function resolvesToBuiltInConstant(name: Identifier | undefined,
21
21
  export declare function resolveToConstants(name: Identifier | undefined, environment: REnvironmentInformation): unknown[] | undefined;
22
22
  export declare function getAliases(sourceIds: readonly NodeId[], dataflow: DataflowGraph, environment: REnvironmentInformation): NodeId[] | undefined;
23
23
  /** Please use {@link resolveValueOfVariable} */
24
- export declare function resolveToValues(identifier: Identifier | undefined, environment: REnvironmentInformation, idMap?: AstIdMap): unknown[] | undefined;
24
+ export declare function trackAliasInEnvironments(identifier: Identifier | undefined, use: REnvironmentInformation, idMap?: AstIdMap): unknown[] | undefined;
25
+ export declare function trackAliasesInGraph(id: NodeId, graph: DataflowGraph, idMap?: AstIdMap): unknown[] | undefined;
25
26
  /**
26
27
  * Convenience function using the variable resolver as specified within the configuration file
27
28
  * In the future we may want to have this set once at the start of the analysis
29
+ *
30
+ * @see {@link resolveIdToValue} - for a more general approach which "evaluates" a node based on value resolve
28
31
  */
29
32
  export declare function resolveValueOfVariable(identifier: Identifier | undefined, environment: REnvironmentInformation, idMap?: AstIdMap): unknown[] | undefined;
33
+ export interface ResolveInfo {
34
+ /** The current environment used for name resolution */
35
+ environment?: REnvironmentInformation;
36
+ /** The id map to resolve the node if given as an id */
37
+ idMap?: AstIdMap;
38
+ /** The graph to resolve in */
39
+ graph?: DataflowGraph;
40
+ /** Whether to track variables */
41
+ full?: boolean;
42
+ }
43
+ /**
44
+ * Generalized {@link resolveValueOfVariable} function which evaluates a node based on the value resolve
45
+ *
46
+ * @param id - The node id or node to resolve
47
+ * @param environment - The current environment used for name resolution
48
+ * @param graph - The graph to resolve in
49
+ * @param idMap - The id map to resolve the node if given as an id
50
+ * @param full - Whether to track variables
51
+ */
52
+ export declare function resolveIdToValue(id: NodeId | RNodeWithParent, { environment, graph, idMap, full }: ResolveInfo): unknown[] | undefined;
@@ -4,8 +4,10 @@ exports.resolveByName = resolveByName;
4
4
  exports.resolvesToBuiltInConstant = resolvesToBuiltInConstant;
5
5
  exports.resolveToConstants = resolveToConstants;
6
6
  exports.getAliases = getAliases;
7
- exports.resolveToValues = resolveToValues;
7
+ exports.trackAliasInEnvironments = trackAliasInEnvironments;
8
+ exports.trackAliasesInGraph = trackAliasesInGraph;
8
9
  exports.resolveValueOfVariable = resolveValueOfVariable;
10
+ exports.resolveIdToValue = resolveIdToValue;
9
11
  const environment_1 = require("./environment");
10
12
  const logic_1 = require("../../util/logic");
11
13
  const identifier_1 = require("./identifier");
@@ -14,6 +16,10 @@ const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id"
14
16
  const vertex_1 = require("../graph/vertex");
15
17
  const config_1 = require("../../config");
16
18
  const assert_1 = require("../../util/assert");
19
+ const type_1 = require("../../r-bridge/lang-4.x/ast/model/type");
20
+ const visiting_queue_1 = require("../../slicing/static/visiting-queue");
21
+ const fingerprint_1 = require("../../slicing/static/fingerprint");
22
+ const edge_1 = require("../graph/edge");
17
23
  const FunctionTargetTypes = identifier_1.ReferenceType.Function | identifier_1.ReferenceType.BuiltInFunction | identifier_1.ReferenceType.Unknown | identifier_1.ReferenceType.Argument | identifier_1.ReferenceType.Parameter;
18
24
  const VariableTargetTypes = identifier_1.ReferenceType.Variable | identifier_1.ReferenceType.Parameter | identifier_1.ReferenceType.Argument | identifier_1.ReferenceType.Unknown;
19
25
  const ConstantTargetTypes = identifier_1.ReferenceType.Constant | identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.Unknown;
@@ -149,11 +155,11 @@ function getAliases(sourceIds, dataflow, environment) {
149
155
  return [...definitions];
150
156
  }
151
157
  /** Please use {@link resolveValueOfVariable} */
152
- function resolveToValues(identifier, environment, idMap) {
158
+ function trackAliasInEnvironments(identifier, use, idMap) {
153
159
  if (identifier === undefined) {
154
160
  return undefined;
155
161
  }
156
- const defs = resolveByName(identifier, environment);
162
+ const defs = resolveByName(identifier, use);
157
163
  if (defs === undefined) {
158
164
  return undefined;
159
165
  }
@@ -183,17 +189,126 @@ function resolveToValues(identifier, environment, idMap) {
183
189
  }
184
190
  return values;
185
191
  }
192
+ function isNestedInLoop(node, ast) {
193
+ const parent = node?.info.parent;
194
+ if (node === undefined || !parent) {
195
+ return false;
196
+ }
197
+ const parentNode = ast.get(parent);
198
+ if (parentNode === undefined) {
199
+ return false;
200
+ }
201
+ if (parentNode.type === type_1.RType.WhileLoop || parentNode.type === type_1.RType.RepeatLoop) {
202
+ return true;
203
+ }
204
+ return isNestedInLoop(parentNode, ast);
205
+ }
206
+ function trackAliasesInGraph(id, graph, idMap) {
207
+ idMap ??= graph.idMap;
208
+ (0, assert_1.guard)(idMap !== undefined, 'The ID map is required to get the lineage of a node');
209
+ const start = graph.getVertex(id);
210
+ (0, assert_1.guard)(start !== undefined, 'Unable to find start for alias tracking');
211
+ const queue = new visiting_queue_1.VisitingQueue(25);
212
+ const clean = (0, environment_1.initializeCleanEnvironments)();
213
+ const cleanFingerprint = (0, fingerprint_1.envFingerprint)(clean);
214
+ queue.add(id, clean, cleanFingerprint, false);
215
+ let forceBot = false;
216
+ const resultIds = [];
217
+ while (queue.nonEmpty()) {
218
+ const { id, baseEnvironment } = queue.next();
219
+ const res = graph.get(id);
220
+ if (!res) {
221
+ continue;
222
+ }
223
+ const [vertex, outgoingEdges] = res;
224
+ const cds = vertex.controlDependencies;
225
+ for (const cd of cds ?? []) {
226
+ const target = graph.idMap?.get(cd.id);
227
+ if (target === undefined) {
228
+ continue;
229
+ }
230
+ if (target.type === type_1.RType.WhileLoop || target.type === type_1.RType.RepeatLoop) {
231
+ forceBot = true;
232
+ break;
233
+ }
234
+ }
235
+ if (!forceBot && (cds?.length === 0 && isNestedInLoop(idMap.get(id), idMap))) {
236
+ forceBot = true;
237
+ }
238
+ if (forceBot) {
239
+ break;
240
+ }
241
+ if (vertex.tag === vertex_1.VertexType.Value) {
242
+ resultIds.push(id);
243
+ continue;
244
+ }
245
+ // travel all read and defined-by edges
246
+ for (const [targetId, edge] of outgoingEdges) {
247
+ // currently, they have to be exact!
248
+ if (edge.types === edge_1.EdgeType.Reads || edge.types === edge_1.EdgeType.DefinedBy || edge.types === edge_1.EdgeType.DefinedByOnCall) {
249
+ queue.add(targetId, baseEnvironment, cleanFingerprint, false);
250
+ }
251
+ }
252
+ }
253
+ if (forceBot || resultIds.length === 0) {
254
+ return undefined;
255
+ }
256
+ const values = [];
257
+ for (const id of resultIds) {
258
+ const node = idMap.get(id);
259
+ if (node !== undefined) {
260
+ values.push(node.content);
261
+ }
262
+ }
263
+ return values;
264
+ }
186
265
  /**
187
266
  * Convenience function using the variable resolver as specified within the configuration file
188
267
  * In the future we may want to have this set once at the start of the analysis
268
+ *
269
+ * @see {@link resolveIdToValue} - for a more general approach which "evaluates" a node based on value resolve
189
270
  */
190
271
  function resolveValueOfVariable(identifier, environment, idMap) {
191
272
  const resolve = (0, config_1.getConfig)().solver.variables;
192
273
  switch (resolve) {
193
- case config_1.VariableResolve.Alias: return resolveToValues(identifier, environment, idMap);
274
+ case config_1.VariableResolve.Alias: return trackAliasInEnvironments(identifier, environment, idMap);
194
275
  case config_1.VariableResolve.Builtin: return resolveToConstants(identifier, environment);
195
276
  case config_1.VariableResolve.Disabled: return [];
196
277
  default: (0, assert_1.assertUnreachable)(resolve);
197
278
  }
198
279
  }
280
+ /**
281
+ * Generalized {@link resolveValueOfVariable} function which evaluates a node based on the value resolve
282
+ *
283
+ * @param id - The node id or node to resolve
284
+ * @param environment - The current environment used for name resolution
285
+ * @param graph - The graph to resolve in
286
+ * @param idMap - The id map to resolve the node if given as an id
287
+ * @param full - Whether to track variables
288
+ */
289
+ function resolveIdToValue(id, { environment, graph, idMap, full }) {
290
+ idMap ??= graph?.idMap;
291
+ const node = typeof id === 'object' ? id : idMap?.get(id);
292
+ if (node === undefined) {
293
+ return undefined;
294
+ }
295
+ switch (node.type) {
296
+ case type_1.RType.Symbol:
297
+ if (environment) {
298
+ return full ? resolveValueOfVariable(node.lexeme, environment, idMap) : undefined;
299
+ }
300
+ else if (graph && (0, config_1.getConfig)().solver.variables === config_1.VariableResolve.Alias) {
301
+ return full ? trackAliasesInGraph(node.info.id, graph, idMap) : undefined;
302
+ }
303
+ else {
304
+ return undefined;
305
+ }
306
+ case type_1.RType.String:
307
+ case type_1.RType.Number:
308
+ case type_1.RType.Logical:
309
+ return [node.content];
310
+ default:
311
+ return undefined;
312
+ }
313
+ }
199
314
  //# sourceMappingURL=resolve-by-name.js.map
@@ -208,9 +208,10 @@ ${await (0, doc_query_1.showQuery)(shell, example_query_code_1.exampleQueryCode,
208
208
  buildExplanation: async (shell) => {
209
209
  const exampleCode = 'x <- 1\nprint(x)';
210
210
  return `
211
- With this query you can use flowR's value-tracking capabilities to resolve identifiers to all potential values they may have at runtime (if possible). The extend to which flowR traces values (e.g. built-ins vs. constants) can be configured in flowR's Configuration file (see the [Interface](${doc_files_1.FlowrWikiBaseRef}/Interface) wiki page for more information).
211
+ With this query you can use flowR's value-tracking capabilities to resolve identifiers to all potential values they may have at runtime (if possible).
212
+ The extent to which flowR traces values (e.g., built-ins vs. constants) can be configured in flowR's Configuration file (see the [Interface](${doc_files_1.FlowrWikiBaseRef}/Interface) wiki page for more information).
212
213
 
213
- Using the example code \`${exampleCode}\`, the following query returns all values of 'x' in the code:
214
+ Using the example code \`${exampleCode}\` (with the \`print(x)\` in the second line), the following query returns all values of \`x\` in the code:
214
215
  ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
215
216
  type: 'resolve-value',
216
217
  criteria: ['2@x']
@@ -419,7 +420,7 @@ ${await (0, doc_query_1.showQuery)(shell, longerCode, [{
419
420
  type: 'dependencies'
420
421
  }], { showCode: false, collapseQuery: true, collapseResult: true })}
421
422
 
422
- Currently the dependency extraction may fail as it is essentially a set of heuristics guessing the dependencies.
423
+ Currently, the dependency extraction may fail as it is essentially a set of heuristics guessing the dependencies.
423
424
  We welcome any feedback on this (consider opening a [new issue](${doc_issue_1.NewIssueUrl})).
424
425
 
425
426
  In the meantime we offer several properties to overwrite the default behavior (e.g., function names that should be collected)
@@ -427,12 +428,13 @@ In the meantime we offer several properties to overwrite the default behavior (e
427
428
  ${await (0, doc_query_1.showQuery)(shell, longerCode, [{
428
429
  type: 'dependencies',
429
430
  ignoreDefaultFunctions: true,
430
- libraryFunctions: [{ name: 'print', argIdx: 0, argName: 'library' }],
431
+ libraryFunctions: [{ name: 'print', argIdx: 0, argName: 'library', resolveValue: true }],
431
432
  sourceFunctions: [],
432
433
  readFunctions: [],
433
434
  writeFunctions: []
434
435
  }], { showCode: false, collapseQuery: false, collapseResult: true })}
435
436
 
437
+ Here, \`resolveValue\` tells the dependency query to resolve the value of this argument in case it is not a constant.
436
438
  `;
437
439
  }
438
440
  });
@@ -64,8 +64,8 @@ The following showcases the dependency view of the [Visual Studio Code extension
64
64
 
65
65
  `), ' ')}
66
66
 
67
- * 🚀 **fast data and control-flow graphs**\\
68
- Within just ${'<i>' + (0, html_hover_over_1.textWithTooltip)((0, numbers_1.roundToDecimals)(await (0, doc_benchmarks_1.getLatestDfAnalysisTime)('"social-science" Benchmark Suite (tree-sitter)'), 1) + ' ms</i>', 'This measurement is automatically fetched from the latest benchmarks!')} (as of ${new Date(await (0, doc_benchmarks_1.getLastBenchmarkUpdate)()).toLocaleDateString('en-US', dateOptions)}),
67
+ * 🚀 **fast data- and control-flow graphs**\\
68
+ Within just ${'<i>' + (0, html_hover_over_1.textWithTooltip)((0, numbers_1.roundToDecimals)(await (0, doc_benchmarks_1.getLatestDfAnalysisTime)('"social-science" Benchmark Suite (tree-sitter)'), 1) + ' ms</i>', 'This measurement is automatically fetched from the latest benchmark!')} (as of ${new Date(await (0, doc_benchmarks_1.getLastBenchmarkUpdate)()).toLocaleDateString('en-US', dateOptions)}),
69
69
  _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,
70
70
  and consult the [wiki pages](${doc_files_1.FlowrWikiBaseRef}/Dataflow-Graph) for more details on the dataflow graph.
71
71
 
@@ -90,11 +90,11 @@ ${await (0, doc_dfg_1.printDfGraphForCode)(shell, (0, doc_files_1.getFileContent
90
90
 
91
91
  If you want to use flowR and the features it provides, feel free to check out the:
92
92
 
93
- - [Visual Studio Code extension](${doc_files_1.FlowrVsCode}): provides access to flowR's capabilities directly in VS Code (also works in [vscode.dev](https://vscode.dev/))
93
+ - [Visual Studio Code extension](${doc_files_1.FlowrVsCode}): provides access to flowR directly in VS Code (or [vscode.dev](https://vscode.dev/))
94
94
  - [RStudio Addin](${doc_files_1.FlowrGithubBaseRef}/rstudio-addin-flowr): integrates flowR into [RStudio](https://posit.co/downloads/)
95
- - [R package](${doc_files_1.FlowrGithubBaseRef}/flowr-r-adapter): allows you to use flowR in your R scripts
95
+ - [R package](${doc_files_1.FlowrGithubBaseRef}/flowr-r-adapter): use flowR in your R scripts
96
96
  - [Docker image](${doc_files_1.FlowrDockerRef}): run flowR in a container, this also includes [flowR's server](${doc_files_1.FlowrWikiBaseRef}/Interface#communicating-with-the-server)
97
- - [NPM package](${doc_files_1.FlowrNpmRef}): include flowR in your TypeScript and JavaScript projects (e.g., used for the VS Code extension)
97
+ - [NPM package](${doc_files_1.FlowrNpmRef}): include flowR in your TypeScript and JavaScript projects
98
98
 
99
99
  ## ⭐ Getting Started
100
100
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.2.5",
3
+ "version": "2.2.7",
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": {
@@ -55,7 +55,10 @@ function promoteQueryCallNames(queries) {
55
55
  ...q.fileFilter,
56
56
  filter: new RegExp(q.fileFilter.filter)
57
57
  },
58
- linkTo: {
58
+ linkTo: Array.isArray(q.linkTo) ? q.linkTo.map(l => ({
59
+ ...l,
60
+ callName: new RegExp(l.callName)
61
+ })) : {
59
62
  ...q.linkTo,
60
63
  /* we have to add another promotion layer whenever we add something without this call name */
61
64
  callName: new RegExp(q.linkTo.callName)
@@ -208,13 +211,24 @@ function executeCallContextQueries({ dataflow: { graph }, ast }, queries) {
208
211
  }
209
212
  let linkedIds = undefined;
210
213
  if (cfg && isSubCallQuery(query)) {
211
- /* if we have a linkTo query, we have to find the last call */
212
- const lastCall = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(nodeId, cfg.graph, graph, query.linkTo);
213
- if (lastCall) {
214
- linkedIds = lastCall;
214
+ const linked = Array.isArray(query.linkTo) ? query.linkTo : [query.linkTo];
215
+ for (const link of linked) {
216
+ /* if we have a linkTo query, we have to find the last call */
217
+ const lastCall = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(nodeId, cfg.graph, graph, link);
218
+ if (lastCall) {
219
+ linkedIds ??= new Set();
220
+ for (const l of lastCall) {
221
+ if (link.attachLinkInfo) {
222
+ linkedIds.add({ id: l, info: link.attachLinkInfo });
223
+ }
224
+ else {
225
+ linkedIds.add(l);
226
+ }
227
+ }
228
+ }
215
229
  }
216
230
  }
217
- initialIdCollector.add(query.kind ?? '.', query.subkind ?? '.', (0, objects_1.compactRecord)({ id: nodeId, name: info.name, calls: targets, linkedIds }));
231
+ initialIdCollector.add(query.kind ?? '.', query.subkind ?? '.', (0, objects_1.compactRecord)({ id: nodeId, name: info.name, calls: targets, linkedIds: linkedIds ? [...linkedIds] : undefined }));
218
232
  }
219
233
  }
220
234
  removeIdenticalDuplicates(initialIdCollector);
@@ -9,6 +9,7 @@ import { CallTargets } from './identify-link-to-last-call-relation';
9
9
  import type { DataflowGraph } from '../../../dataflow/graph/graph';
10
10
  import type { DataflowGraphVertexInfo } from '../../../dataflow/graph/vertex';
11
11
  import type { CascadeAction } from './cascade-action';
12
+ import type { NoInfo } from '../../../r-bridge/lang-4.x/ast/model/model';
12
13
  export interface FileFilter<FilterType> {
13
14
  /**
14
15
  * Regex that a node's file attribute must match to be considered
@@ -66,9 +67,11 @@ interface LinkToLastCall<CallName extends RegExp | string = RegExp | string> ext
66
67
  */
67
68
  readonly cascadeIf?: (target: DataflowGraphVertexInfo, from: NodeId, graph: DataflowGraph) => CascadeAction;
68
69
  }
69
- export type LinkTo<CallName extends RegExp | string> = LinkToLastCall<CallName>;
70
- export interface SubCallContextQueryFormat<CallName extends RegExp | string = RegExp | string> extends DefaultCallContextQueryFormat<CallName> {
71
- readonly linkTo: LinkTo<CallName>;
70
+ export type LinkTo<CallName extends RegExp | string = RegExp | string, AttachLinkInfo = NoInfo> = (LinkToLastCall<CallName>) & {
71
+ attachLinkInfo?: AttachLinkInfo;
72
+ };
73
+ export interface SubCallContextQueryFormat<CallName extends RegExp | string = RegExp | string, AttachLinkInfo = NoInfo> extends DefaultCallContextQueryFormat<CallName> {
74
+ readonly linkTo: LinkTo<CallName, AttachLinkInfo> | LinkTo<CallName, AttachLinkInfo>[];
72
75
  }
73
76
  export interface CallContextQuerySubKindResult {
74
77
  /** The id of the call vertex identified within the supplied dataflow graph */
@@ -81,8 +84,11 @@ export interface CallContextQuerySubKindResult {
81
84
  * An empty array means that the call targets only non-local functions.
82
85
  */
83
86
  readonly calls?: readonly NodeId[];
84
- /** ids attached by the linkTo query */
85
- readonly linkedIds?: readonly NodeId[];
87
+ /** ids attached by the linkTo query, if you attached information with the `attachLinkInfo` field you can find it here */
88
+ readonly linkedIds?: readonly (NodeId | {
89
+ id: NodeId;
90
+ info: object;
91
+ })[];
86
92
  /**
87
93
  * (Direct) alias locations this call stems from
88
94
  */
@@ -95,7 +101,7 @@ export type CallContextQueryKindResult = Record<string, {
95
101
  export interface CallContextQueryResult extends BaseQueryResult {
96
102
  readonly kinds: CallContextQueryKindResult;
97
103
  }
98
- export type CallContextQuery<CallName extends RegExp | string = RegExp | string> = DefaultCallContextQueryFormat<CallName> | SubCallContextQueryFormat<CallName>;
104
+ export type CallContextQuery<CallName extends RegExp | string = RegExp | string, AttachLinkInfo = NoInfo> = DefaultCallContextQueryFormat<CallName> | SubCallContextQueryFormat<CallName, AttachLinkInfo>;
99
105
  export declare const CallContextQueryDefinition: {
100
106
  readonly executor: typeof executeCallContextQueries;
101
107
  readonly asciiSummarizer: (formatter: OutputFormatter, processed: PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>, queryResults: BaseQueryResult, result: string[]) => boolean;
@@ -10,6 +10,13 @@ const time_1 = require("../../../util/time");
10
10
  const joi_1 = __importDefault(require("joi"));
11
11
  const query_print_1 = require("../../query-print");
12
12
  const identify_link_to_last_call_relation_1 = require("./identify-link-to-last-call-relation");
13
+ const CallContextQueryLinkTo = joi_1.default.object({
14
+ type: joi_1.default.string().valid('link-to-last-call').required().description('The type of the linkTo sub-query.'),
15
+ callName: joi_1.default.string().required().description('Regex regarding the function name of the last call. Similar to `callName`, strings are interpreted as a regular expression.'),
16
+ ignoreIf: joi_1.default.function().optional().description('Should we ignore this (source) call? Currently, there is no well working serialization for this.'),
17
+ cascadeIf: joi_1.default.function().optional().description('Should we continue searching after the link was created? Currently, there is no well working serialization for this.'),
18
+ attachLinkInfo: joi_1.default.object().optional().description('Additional information to attach to the link.')
19
+ });
13
20
  exports.CallContextQueryDefinition = {
14
21
  executor: call_context_query_executor_1.executeCallContextQueries,
15
22
  asciiSummarizer: (formatter, processed, queryResults, result) => {
@@ -30,12 +37,7 @@ exports.CallContextQueryDefinition = {
30
37
  fileFilter: joi_1.default.string().required().description('Regex that a node\'s file attribute must match to be considered'),
31
38
  includeUndefinedFiles: joi_1.default.boolean().optional().description('If `fileFilter` is set, but a nodes `file` attribute is `undefined`, should we include it in the results? Defaults to `true`.')
32
39
  }).optional().description('Filter that, when set, a node\'s file attribute must match to be considered'),
33
- linkTo: joi_1.default.object({
34
- type: joi_1.default.string().valid('link-to-last-call').required().description('The type of the linkTo sub-query.'),
35
- callName: joi_1.default.string().required().description('Regex regarding the function name of the last call. Similar to `callName`, strings are interpreted as a regular expression.'),
36
- ignoreIf: joi_1.default.function().optional().description('Should we ignore this (source) call? Currently, there is no well working serialization for this.'),
37
- cascadeIf: joi_1.default.function().optional().description('Should we continue searching after the link was created? Currently, there is no well working serialization for this.')
38
- }).optional().description('Links the current call to the last call of the given kind. This way, you can link a call like `points` to the latest graphics plot etc.')
40
+ linkTo: joi_1.default.alternatives(CallContextQueryLinkTo, joi_1.default.array().items(CallContextQueryLinkTo)).optional().description('Links the current call to the last call of the given kind. This way, you can link a call like `points` to the latest graphics plot etc.')
39
41
  }).description('Call context query used to find calls in the dataflow graph')
40
42
  };
41
43
  //# sourceMappingURL=call-context-query-format.js.map