@eagleoutice/flowr 2.1.7 → 2.1.9

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.
Files changed (84) hide show
  1. package/README.md +2 -1
  2. package/abstract-interpretation/normalized-ast-fold.d.ts +124 -0
  3. package/abstract-interpretation/normalized-ast-fold.js +178 -0
  4. package/benchmark/summarizer/first-phase/process.js +6 -5
  5. package/cli/repl/commands/repl-dataflow.js +5 -2
  6. package/cli/repl/commands/repl-normalize.js +5 -2
  7. package/cli/repl/commands/repl-query.js +2 -2
  8. package/cli/repl/server/messages/message-query.js +1 -1
  9. package/cli/slicer-app.js +1 -1
  10. package/core/steps/pipeline/pipeline.d.ts +63 -0
  11. package/dataflow/environments/default-builtin-config.js +45 -6
  12. package/dataflow/environments/environment.d.ts +46 -8
  13. package/dataflow/environments/environment.js +24 -1
  14. package/dataflow/environments/identifier.d.ts +49 -7
  15. package/dataflow/environments/identifier.js +11 -2
  16. package/dataflow/environments/resolve-by-name.d.ts +5 -0
  17. package/dataflow/environments/resolve-by-name.js +14 -0
  18. package/dataflow/extractor.js +5 -4
  19. package/dataflow/graph/dataflowgraph-builder.d.ts +6 -0
  20. package/dataflow/graph/dataflowgraph-builder.js +8 -0
  21. package/dataflow/graph/edge.d.ts +10 -4
  22. package/dataflow/graph/edge.js +12 -5
  23. package/dataflow/graph/graph.d.ts +41 -3
  24. package/dataflow/graph/graph.js +39 -34
  25. package/dataflow/graph/vertex.d.ts +66 -7
  26. package/dataflow/graph/vertex.js +15 -0
  27. package/dataflow/info.d.ts +79 -11
  28. package/dataflow/info.js +20 -0
  29. package/dataflow/internal/linker.d.ts +4 -2
  30. package/dataflow/internal/linker.js +12 -5
  31. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +2 -0
  32. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +5 -3
  33. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +1 -1
  34. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.d.ts +16 -0
  35. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +83 -6
  36. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +17 -7
  37. package/dataflow/internal/process/functions/call/common.js +1 -1
  38. package/documentation/doc-util/doc-dfg.d.ts +2 -2
  39. package/documentation/doc-util/doc-dfg.js +11 -16
  40. package/documentation/doc-util/doc-normalized-ast.js +1 -1
  41. package/documentation/doc-util/doc-types.d.ts +1 -1
  42. package/documentation/doc-util/doc-types.js +21 -0
  43. package/documentation/print-capabilities-markdown.js +1 -1
  44. package/documentation/print-dataflow-graph-wiki.js +44 -7
  45. package/documentation/print-linting-and-testing-wiki.js +60 -26
  46. package/documentation/print-normalized-ast-wiki.js +107 -5
  47. package/documentation/print-query-wiki.js +8 -1
  48. package/package.json +17 -3
  49. package/queries/catalog/call-context-query/call-context-query-executor.js +23 -2
  50. package/queries/catalog/call-context-query/call-context-query-format.d.ts +29 -2
  51. package/queries/catalog/call-context-query/call-context-query-format.js +7 -1
  52. package/queries/catalog/call-context-query/cascade-action.d.ts +8 -0
  53. package/queries/catalog/call-context-query/cascade-action.js +13 -0
  54. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +11 -1
  55. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +41 -4
  56. package/queries/catalog/dependencies-query/dependencies-query-format.js +4 -0
  57. package/queries/query.d.ts +4 -4
  58. package/queries/query.js +17 -5
  59. package/r-bridge/lang-4.x/ast/model/model.d.ts +3 -0
  60. package/r-bridge/lang-4.x/ast/model/nodes/r-number.d.ts +5 -1
  61. package/r-bridge/lang-4.x/ast/model/processing/node-id.d.ts +6 -1
  62. package/r-bridge/lang-4.x/ast/model/processing/node-id.js +6 -1
  63. package/r-bridge/lang-4.x/ast/model/processing/visitor.d.ts +1 -1
  64. package/r-bridge/lang-4.x/ast/model/processing/visitor.js +1 -1
  65. package/r-bridge/lang-4.x/ast/parser/json/format.js +2 -2
  66. package/reconstruct/reconstruct.js +1 -1
  67. package/slicing/static/slice-call.d.ts +7 -2
  68. package/slicing/static/slice-call.js +33 -44
  69. package/slicing/static/static-slicer.d.ts +5 -1
  70. package/slicing/static/static-slicer.js +22 -8
  71. package/slicing/static/visiting-queue.d.ts +4 -4
  72. package/slicing/static/visiting-queue.js +5 -3
  73. package/statistics/output/print-stats.js +2 -1
  74. package/statistics/summarizer/post-process/histogram.js +2 -1
  75. package/statistics/summarizer/post-process/post-process-output.js +2 -1
  76. package/statistics/summarizer/second-phase/process.js +3 -3
  77. package/util/arrays.d.ts +1 -1
  78. package/util/arrays.js +3 -3
  79. package/util/assert.d.ts +1 -1
  80. package/util/assert.js +3 -2
  81. package/util/cfg/cfg.js +4 -2
  82. package/util/mermaid/cfg.js +1 -1
  83. package/util/summarizer.js +2 -2
  84. package/util/version.js +1 -1
@@ -13,11 +13,18 @@ const path_1 = __importDefault(require("path"));
13
13
  const doc_files_1 = require("./doc-util/doc-files");
14
14
  const doc_cli_option_1 = require("./doc-util/doc-cli-option");
15
15
  const time_1 = require("../util/time");
16
+ const doc_structure_1 = require("./doc-util/doc-structure");
17
+ const pipeline_executor_1 = require("../core/pipeline-executor");
18
+ const retriever_1 = require("../r-bridge/retriever");
19
+ const visitor_1 = require("../r-bridge/lang-4.x/ast/model/processing/visitor");
20
+ const collect_1 = require("../r-bridge/lang-4.x/ast/model/collect");
21
+ const normalized_ast_fold_1 = require("../abstract-interpretation/normalized-ast-fold");
16
22
  async function getText(shell) {
17
23
  const rversion = (await shell.usedRVersion())?.format() ?? 'unknown';
18
24
  const now = performance.now();
19
25
  const types = (0, doc_types_1.getTypesFromFolderAsMermaid)({
20
26
  rootFolder: path_1.default.resolve('./src/r-bridge/lang-4.x/ast/model/'),
27
+ files: [path_1.default.resolve('./src/abstract-interpretation/normalized-ast-fold.ts')],
21
28
  typeName: 'RNode',
22
29
  inlineTypes: doc_types_1.mermaidHide
23
30
  });
@@ -40,7 +47,7 @@ ${(0, doc_code_1.codeBlock)('r', 'x <- 2 * 3 + 1')}
40
47
  Each node in the AST contains the type, the id, and the lexeme (if applicable).
41
48
  Each edge is labeled with the type of the parent-child relationship (the "role").
42
49
 
43
- ${await (0, doc_normalized_ast_1.printNormalizedAstForCode)(shell, 'x <- 2 * 3 + 1')}
50
+ ${await (0, doc_normalized_ast_1.printNormalizedAstForCode)(shell, 'x <- 2 * 3 + 1', { showCode: false, prefix: 'flowchart LR\n' })}
44
51
 
45
52
  &nbsp;
46
53
 
@@ -49,7 +56,7 @@ ${await (0, doc_normalized_ast_1.printNormalizedAstForCode)(shell, 'x <- 2 * 3 +
49
56
  > you can either use the [Visual Studio Code extension](${doc_files_1.FlowrGithubBaseRef}/vscode-flowr) or the ${(0, doc_cli_option_1.getReplCommand)('normalize*')}
50
57
  > command in the REPL (see the [Interface wiki page](${doc_files_1.FlowrWikiBaseRef}/Interface) for more information).
51
58
 
52
- Indicative is the root expression list node, which is present in every normalized AST.
59
+ Indicative of the normalization is the root expression list node, which is present in every normalized AST.
53
60
  In general, we provide node types for:
54
61
 
55
62
  1. literals (e.g., numbers and strings)
@@ -77,11 +84,106 @@ Most notably, the \`info\` field holds the \`id\` of the node, which is used to
77
84
 
78
85
  In summary, we have the following types:
79
86
 
80
- ${(0, doc_types_1.printHierarchy)({ program: types.program, hierarchy: types.info, root: 'RNode', collapseFromNesting: Number.MAX_VALUE })}
87
+ ${(0, doc_structure_1.details)('Normalized AST Node Types', (0, doc_types_1.printHierarchy)({ program: types.program, hierarchy: types.info, root: 'RNode', collapseFromNesting: Number.MAX_VALUE }))}
81
88
 
82
- With this, the example file produced the following AST (shown from left to right for space reasons):
89
+ The following segments intend to give you an overview of how to work with the normalized AST:
83
90
 
84
- ${await (0, doc_normalized_ast_1.printNormalizedAstForCode)(shell, (0, doc_files_1.getFileContentFromRoot)('test/testfiles/example.R'), { prefix: 'flowchart LR\n' })}
91
+ * [How to get a normalized AST](#how-get-a-normalized-ast)
92
+ * [Visitors and Folds](#visitors-and-folds)
93
+
94
+ ## How Get a Normalized AST
95
+
96
+ As explained alongside the [Interface](${doc_files_1.FlowrWikiBaseRef}/Interface#the-pipeline-executor) wiki page, you can use the
97
+ \`${pipeline_executor_1.PipelineExecutor.name}\` to get the normalized AST. If you are only interested in the normalization,
98
+ a pipeline like the \`DEFAULT_NORMALIZE_PIPELINE\` suffices:
99
+
100
+ ${(0, doc_code_1.codeBlock)('ts', `
101
+ async function getAst(code: string): Promise<RNode> {
102
+ const result = await new ${pipeline_executor_1.PipelineExecutor.name}(DEFAULT_NORMALIZE_PIPELINE, {
103
+ shell: new ${shell_1.RShell.name}(),
104
+ request: ${retriever_1.requestFromInput.name}(code.trim())
105
+ }).allRemainingSteps();
106
+ return result.normalize.ast;
107
+ }`)}
108
+
109
+ From the REPL, you can use the ${(0, doc_cli_option_1.getReplCommand)('normalize')} command.
110
+
111
+ ## Traversing the Normalized AST
112
+
113
+ We provide two ways to traverse the normalized AST: [Visitors](#visitors) and [Folds](#folds).
114
+
115
+ ### Visitors
116
+
117
+ If you want a simple visitor which traverses the AST, the \`${visitor_1.visitAst.name}\` function from
118
+ ${(0, doc_files_1.getFilePathMd)('../r-bridge/lang-4.x/ast/model/processing/visitor.ts')} is a good starting point.
119
+ You may specify functions to be called whenever you enter and exit a node during the traversal, and any
120
+ computation is to be done by side effects.
121
+ For example, if you want to collect all the \`id\`s present within a normalized (sub-)ast,
122
+ as it is done by the ${collect_1.collectAllIds.name} function, you can use the following visitor:
123
+
124
+ ${(0, doc_code_1.codeBlock)('ts', `
125
+ const ids = new Set<NodeId>();
126
+ visitAst(nodes, node => {
127
+ ids.add(node.info.id);
128
+ });
129
+ return ids;
130
+ `)}
131
+
132
+ ### Folds
133
+
134
+ We formulate a fold with the base class \`${normalized_ast_fold_1.DefaultNormalizedAstFold.name}\` in ${(0, doc_files_1.getFilePathMd)('../abstract-interpretation/normalized-ast-fold.ts')}.
135
+ Using this class, you can create your own fold behavior by overwriting the default methods.
136
+ By default, the class provides a monoid abstraction using the _empty_ from the constructor and the _concat_ method.
137
+
138
+
139
+ ${(0, doc_types_1.printHierarchy)({ program: types.program, hierarchy: types.info, root: 'DefaultNormalizedAstFold' })}
140
+
141
+ Now, of course, we could provide hundreds of examples here, but we use tests to verify that the fold behaves as expected
142
+ and happily point to them at ${(0, doc_files_1.getFilePathMd)('../../test/functionality/r-bridge/normalize-ast-fold.test.ts')}.
143
+
144
+ As a simple showcase, we want to use the fold to evaluate numeric expressions containing numbers, \`+\`, and \`*\` operators.
145
+
146
+ ${(0, doc_code_1.codeBlock)('ts', `
147
+ class MyMathFold<Info> extends ${normalized_ast_fold_1.DefaultNormalizedAstFold.name}<number, Info> {
148
+ constructor() {
149
+ /* use \`0\` as a placeholder empty for the monoid */
150
+ super(0);
151
+ }
152
+
153
+ protected override concat(a: number, b: number): number {
154
+ /* for this example, we ignore cases that we cannot handle */
155
+ return b;
156
+ }
157
+
158
+ override foldRNumber(node: RNumber<Info>) {
159
+ /* return the value of the number */
160
+ return node.content.num;
161
+ }
162
+
163
+ override foldRBinaryOp(node: RBinaryOp<Info>) {
164
+ if(node.operator === '+') {
165
+ return this.fold(node.lhs) + this.fold(node.rhs);
166
+ } else if(node.operator === '*') {
167
+ return this.fold(node.lhs) * this.fold(node.rhs);
168
+ } else {
169
+ /* in case we cannot handle the operator we could throw an error, or just use the default behavior: */
170
+ return super.foldRBinaryOp(node);
171
+ }
172
+ }
173
+ }
174
+ `)}
175
+
176
+ Now, we can use the \`${pipeline_executor_1.PipelineExecutor.name}\` to get the normalized AST and apply the fold:
177
+
178
+ ${(0, doc_code_1.codeBlock)('ts', `
179
+ const shell = new ${shell_1.RShell.name}();
180
+ const ast = (await new ${pipeline_executor_1.PipelineExecutor.name}(DEFAULT_NORMALIZE_PIPELINE, {
181
+ shell, request: retrieveNormalizedAst(${shell_1.RShell.name}, '1 + 3 * 2')
182
+ }).allRemainingSteps()).normalize.ast;
183
+
184
+ const result = new MyMathFold().fold(ast);
185
+ console.log(result); // -> 7
186
+ `)}
85
187
 
86
188
  `;
87
189
  }
@@ -47,6 +47,13 @@ Besides this, we provide the following ways to automatically categorize and link
47
47
  For now, we _only_ offer support for linking to the last call, as the current flow dependency over-approximation is not stable.
48
48
  4. **Aliases** (\`includeAliases\`): Consider a case like \`f <- function_of_interest\`, do you want calls to \`f\` to be included in the results? There is probably no need to combine this with a global call target!
49
49
 
50
+ It's also possible to filter the results based on the following properties:
51
+
52
+ 1. **File** (\`fileFilter\`): This allows you to filter the results based on the file in which the call is located. This can be useful if you are only interested in calls in, e.g., specific folders.
53
+ The \`fileFilter\` property is an object made up of two properties:
54
+ - **Filter** (\`filter\`): A regular expression that a node's file attribute must match to be considered.
55
+ - **Include Undefined Files** (\`includeUndefinedFiles\`): If \`fileFilter\` is set, but a node's file attribute is not present, should we include it in the results? Defaults to \`true\`.
56
+
50
57
  Re-using the example code from above, the following query attaches all calls to \`mean\` to the kind \`visualize\` and the subkind \`text\`,
51
58
  all calls that start with \`read_\` to the kind \`input\` but only if they are not locally overwritten, and the subkind \`csv-file\`, and links all calls to \`points\` to the last call to \`plot\`:
52
59
 
@@ -411,7 +418,7 @@ ${(0, doc_query_1.tocForQueryType)('virtual')}
411
418
 
412
419
  Although it is probably better to consult the detailed explanations below, if you want to have a look at the scehma, here is its description:
413
420
 
414
- ${(0, schema_1.describeSchema)(query_1.QueriesSchema, ansi_1.markdownFormatter)}
421
+ ${(0, schema_1.describeSchema)((0, query_1.QueriesSchema)(), ansi_1.markdownFormatter)}
415
422
 
416
423
  </details>
417
424
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.1.7",
3
+ "version": "2.1.9",
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": {
@@ -36,12 +36,13 @@
36
36
  "lint": "npm run license-compat -- --summary && npx eslint --version && npx eslint src/ test/",
37
37
  "license-compat": "license-checker --onlyAllow 'MIT;MIT OR X11;GPLv2;LGPL;GNUGPL;ISC;Apache-2.0;FreeBSD;BSD-2-Clause;clearbsd;ModifiedBSD;BSD-3-Clause;Python-2.0;Unlicense;WTFPL;BlueOak-1.0.0;CC-BY-4.0;CC-BY-3.0;CC0-1.0;0BSD'",
38
38
  "doc": "typedoc",
39
- "test": "vitest --config test/vitest.config.mts",
39
+ "test": "vitest --exclude \"test/system-tests/**\" --config test/vitest.config.mts",
40
+ "test:system": "vitest --dir test/system-tests --config test/system-tests/vitest.config.mts",
40
41
  "test:coverage": "npm run test -- --coverage",
41
42
  "performance-test": "func() { cd test/performance/ && bash run-all-suites.sh $1 $2 $3; cd ../../; }; func",
42
43
  "test-full": "npm run test:coverage -- --no-watch -- --make-summary --test-installation",
43
44
  "detect-circular-deps": "npx madge --extensions ts,tsx --circular src/",
44
- "checkup": "npm run flowr -- --execute \":version\" && npm run lint && npm run test-full -- --forbid-only && docker build -t test-flowr -f scripts/Dockerfile . && npm run doc && npm-run-all wiki:*"
45
+ "checkup": "npm run flowr -- --execute \":version\" && npm run lint && npm run test-full -- --allowOnly=false && npm run test:system -- --no-watch && docker build -t test-flowr -f scripts/Dockerfile . && npm run doc && npm-run-all wiki:*"
45
46
  },
46
47
  "keywords": [
47
48
  "static code analysis",
@@ -84,6 +85,19 @@
84
85
  "**/node_modules/**/*",
85
86
  "**/index.ts"
86
87
  ],
88
+ "highlightLanguages": [
89
+ "bash",
90
+ "console",
91
+ "css",
92
+ "html",
93
+ "javascript",
94
+ "json",
95
+ "jsonc",
96
+ "json5",
97
+ "tsx",
98
+ "typescript",
99
+ "r"
100
+ ],
87
101
  "theme": "hierarchy",
88
102
  "out": "doc",
89
103
  "readme": "README.md",
@@ -51,6 +51,10 @@ function promoteQueryCallNames(queries) {
51
51
  ...q,
52
52
  callName: q.callNameExact ? exactCallNameRegex(q.callName)
53
53
  : new RegExp(q.callName),
54
+ fileFilter: q.fileFilter && {
55
+ ...q.fileFilter,
56
+ filter: new RegExp(q.fileFilter.filter)
57
+ },
54
58
  linkTo: {
55
59
  ...q.linkTo,
56
60
  /* we have to add another promotion layer whenever we add something without this call name */
@@ -62,7 +66,11 @@ function promoteQueryCallNames(queries) {
62
66
  return {
63
67
  ...q,
64
68
  callName: q.callNameExact ? exactCallNameRegex(q.callName)
65
- : new RegExp(q.callName)
69
+ : new RegExp(q.callName),
70
+ fileFilter: q.fileFilter && {
71
+ ...q.fileFilter,
72
+ filter: new RegExp(q.fileFilter.filter)
73
+ }
66
74
  };
67
75
  }
68
76
  });
@@ -132,6 +140,15 @@ function removeIdenticalDuplicates(collector) {
132
140
  }
133
141
  }
134
142
  }
143
+ function doesFilepathMatch(file, filter) {
144
+ if (filter === undefined) {
145
+ return true;
146
+ }
147
+ if (file === undefined) {
148
+ return filter.includeUndefinedFiles ?? true;
149
+ }
150
+ return filter.filter.test(file);
151
+ }
135
152
  /**
136
153
  * Multi-stage call context query resolve.
137
154
  *
@@ -174,6 +191,10 @@ function executeCallContextQueries({ graph, ast }, queries) {
174
191
  }
175
192
  }
176
193
  for (const query of promotedQueries.filter(q => q.callName.test(info.name))) {
194
+ const file = ast.idMap.get(nodeId)?.info.file;
195
+ if (!doesFilepathMatch(file, query.fileFilter)) {
196
+ continue;
197
+ }
177
198
  let targets = undefined;
178
199
  if (query.callTargets) {
179
200
  targets = (0, identify_link_to_last_call_relation_1.satisfiesCallTargets)(nodeId, graph, query.callTargets);
@@ -188,7 +209,7 @@ function executeCallContextQueries({ graph, ast }, queries) {
188
209
  let linkedIds = undefined;
189
210
  if (cfg && isSubCallQuery(query)) {
190
211
  /* if we have a linkTo query, we have to find the last call */
191
- const lastCall = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(nodeId, cfg.graph, graph, query.linkTo.callName);
212
+ const lastCall = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(nodeId, cfg.graph, graph, query.linkTo);
192
213
  if (lastCall) {
193
214
  linkedIds = lastCall;
194
215
  }
@@ -6,10 +6,23 @@ import Joi from 'joi';
6
6
  import type { PipelineOutput } from '../../../core/steps/pipeline/pipeline';
7
7
  import type { DEFAULT_DATAFLOW_PIPELINE } from '../../../core/steps/pipeline/default-pipelines';
8
8
  import { CallTargets } from './identify-link-to-last-call-relation';
9
- export interface DefaultCallContextQueryFormat<CallName extends RegExp | string> extends BaseQueryFormat {
9
+ import type { DataflowGraph } from '../../../dataflow/graph/graph';
10
+ import type { DataflowGraphVertexInfo } from '../../../dataflow/graph/vertex';
11
+ import type { CascadeAction } from './cascade-action';
12
+ export interface FileFilter<FilterType> {
13
+ /**
14
+ * Regex that a node's file attribute must match to be considered
15
+ */
16
+ readonly filter: FilterType;
17
+ /**
18
+ * If `fileFilter` is set, but a nodes `file` attribute is `undefined`, should we include it in the results? Defaults to `true`.
19
+ */
20
+ readonly includeUndefinedFiles?: boolean;
21
+ }
22
+ export interface DefaultCallContextQueryFormat<RegexType extends RegExp | string> extends BaseQueryFormat {
10
23
  readonly type: 'call-context';
11
24
  /** Regex regarding the function name, please note that strings will be interpreted as regular expressions too! */
12
- readonly callName: CallName;
25
+ readonly callName: RegexType;
13
26
  /**
14
27
  * Should we automatically add the `^` and `$` anchors to the regex to make it an exact match?
15
28
  */
@@ -27,6 +40,10 @@ export interface DefaultCallContextQueryFormat<CallName extends RegExp | string>
27
40
  * Consider a case like `f <- function_of_interest`, do you want uses of `f` to be included in the results?
28
41
  */
29
42
  readonly includeAliases?: boolean;
43
+ /**
44
+ * Filter that, when set, a node's file attribute must match to be considered
45
+ */
46
+ readonly fileFilter?: FileFilter<RegexType>;
30
47
  }
31
48
  /**
32
49
  * Links the current call to the last call of the given kind.
@@ -38,6 +55,16 @@ interface LinkToLastCall<CallName extends RegExp | string = RegExp | string> ext
38
55
  readonly type: 'link-to-last-call';
39
56
  /** Regex regarding the function name of the last call. Similar to {@link DefaultCallContextQueryFormat#callName}, strings are interpreted as a `RegExp`. */
40
57
  readonly callName: CallName;
58
+ /**
59
+ * Should we ignore this (source) call?
60
+ * Currently, there is no well working serialization for this.
61
+ */
62
+ readonly ignoreIf?: (id: NodeId, graph: DataflowGraph) => boolean;
63
+ /**
64
+ * Should we continue searching after the link was created?
65
+ * Currently, there is no well working serialization for this.
66
+ */
67
+ readonly cascadeIf?: (target: DataflowGraphVertexInfo, from: NodeId, graph: DataflowGraph) => CascadeAction;
41
68
  }
42
69
  export type LinkTo<CallName extends RegExp | string> = LinkToLastCall<CallName>;
43
70
  export interface SubCallContextQueryFormat<CallName extends RegExp | string = RegExp | string> extends DefaultCallContextQueryFormat<CallName> {
@@ -26,9 +26,15 @@ exports.CallContextQueryDefinition = {
26
26
  subkind: joi_1.default.string().optional().description('The subkind of the call, this can be used to uniquely identify the respective call type when grouping the output (e.g., the normalized name, linking `ggplot` to `plot`). Defaults to `.`'),
27
27
  callTargets: joi_1.default.string().valid(...Object.values(identify_link_to_last_call_relation_1.CallTargets)).optional().description('Call targets the function may have. This defaults to `any`. Request this specifically to gain all call targets we can resolve.'),
28
28
  includeAliases: joi_1.default.boolean().optional().description('Consider a case like `f <- function_of_interest`, do you want uses of `f` to be included in the results?'),
29
+ fileFilter: joi_1.default.object({
30
+ fileFilter: joi_1.default.string().required().description('Regex that a node\'s file attribute must match to be considered'),
31
+ 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
+ }).optional().description('Filter that, when set, a node\'s file attribute must match to be considered'),
29
33
  linkTo: joi_1.default.object({
30
34
  type: joi_1.default.string().valid('link-to-last-call').required().description('The type of the linkTo sub-query.'),
31
- 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.')
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.')
32
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.')
33
39
  }).description('Call context query used to find calls in the dataflow graph')
34
40
  };
@@ -0,0 +1,8 @@
1
+ export declare enum CascadeAction {
2
+ /** The action is to start the cascade */
3
+ Stop = "stop",
4
+ /** The action is to continue the cascade */
5
+ Continue = "continue",
6
+ /** The action is to skip the current node */
7
+ Skip = "skip"
8
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CascadeAction = void 0;
4
+ var CascadeAction;
5
+ (function (CascadeAction) {
6
+ /** The action is to start the cascade */
7
+ CascadeAction["Stop"] = "stop";
8
+ /** The action is to continue the cascade */
9
+ CascadeAction["Continue"] = "continue";
10
+ /** The action is to skip the current node */
11
+ CascadeAction["Skip"] = "skip";
12
+ })(CascadeAction || (exports.CascadeAction = CascadeAction = {}));
13
+ //# sourceMappingURL=cascade-action.js.map
@@ -1,6 +1,10 @@
1
1
  import type { NodeId } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
2
2
  import type { ControlFlowGraph } from '../../../util/cfg/cfg';
3
3
  import type { DataflowGraph } from '../../../dataflow/graph/graph';
4
+ import type { DataflowGraphVertexFunctionCall } from '../../../dataflow/graph/vertex';
5
+ import { RType } from '../../../r-bridge/lang-4.x/ast/model/type';
6
+ import type { RNodeWithParent } from '../../../r-bridge/lang-4.x/ast/model/processing/decorate';
7
+ import type { LinkTo } from './call-context-query-format';
4
8
  export declare enum CallTargets {
5
9
  /** call targets a function that is not defined locally (e.g., the call targets a library function) */
6
10
  OnlyGlobal = "global",
@@ -14,4 +18,10 @@ export declare enum CallTargets {
14
18
  Any = "any"
15
19
  }
16
20
  export declare function satisfiesCallTargets(id: NodeId, graph: DataflowGraph, callTarget: CallTargets): NodeId[] | 'no';
17
- export declare function identifyLinkToLastCallRelation(from: NodeId, cfg: ControlFlowGraph, graph: DataflowGraph, linkTo: RegExp): NodeId[];
21
+ export declare function getValueOfArgument<Types extends readonly RType[] = readonly RType[]>(graph: DataflowGraph, call: DataflowGraphVertexFunctionCall | undefined, argument: {
22
+ name?: string;
23
+ index: number;
24
+ }, additionalAllowedTypes?: Types): (RNodeWithParent & {
25
+ type: Types[number];
26
+ }) | undefined;
27
+ export declare function identifyLinkToLastCallRelation(from: NodeId, cfg: ControlFlowGraph, graph: DataflowGraph, { callName, ignoreIf, cascadeIf }: LinkTo<RegExp>): NodeId[];
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CallTargets = void 0;
4
4
  exports.satisfiesCallTargets = satisfiesCallTargets;
5
+ exports.getValueOfArgument = getValueOfArgument;
5
6
  exports.identifyLinkToLastCallRelation = identifyLinkToLastCallRelation;
7
+ const graph_1 = require("../../../dataflow/graph/graph");
6
8
  const visitor_1 = require("../../../util/cfg/visitor");
7
9
  const vertex_1 = require("../../../dataflow/graph/vertex");
8
10
  const edge_1 = require("../../../dataflow/graph/edge");
@@ -10,6 +12,9 @@ const resolve_by_name_1 = require("../../../dataflow/environments/resolve-by-nam
10
12
  const identifier_1 = require("../../../dataflow/environments/identifier");
11
13
  const built_in_1 = require("../../../dataflow/environments/built-in");
12
14
  const assert_1 = require("../../../util/assert");
15
+ const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type");
16
+ const r_function_call_1 = require("../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
17
+ const cascade_action_1 = require("./cascade-action");
13
18
  var CallTargets;
14
19
  (function (CallTargets) {
15
20
  /** call targets a function that is not defined locally (e.g., the call targets a library function) */
@@ -74,8 +79,36 @@ function satisfiesCallTargets(id, graph, callTarget) {
74
79
  (0, assert_1.assertUnreachable)(callTarget);
75
80
  }
76
81
  }
77
- function identifyLinkToLastCallRelation(from, cfg, graph, linkTo) {
82
+ function getValueOfArgument(graph, call, argument, additionalAllowedTypes) {
83
+ if (!call) {
84
+ return undefined;
85
+ }
86
+ const totalIndex = argument.name ? call.args.findIndex(arg => arg !== r_function_call_1.EmptyArgument && arg.name === argument.name) : -1;
87
+ let refAtIndex;
88
+ if (totalIndex < 0) {
89
+ const references = call.args.filter(arg => arg !== r_function_call_1.EmptyArgument && !arg.name).map(graph_1.getReferenceOfArgument);
90
+ refAtIndex = references[argument.index];
91
+ }
92
+ else {
93
+ const arg = call.args[totalIndex];
94
+ refAtIndex = (0, graph_1.getReferenceOfArgument)(arg);
95
+ }
96
+ if (refAtIndex === undefined) {
97
+ return undefined;
98
+ }
99
+ let valueNode = graph.idMap?.get(refAtIndex);
100
+ if (valueNode?.type === type_1.RType.Argument) {
101
+ valueNode = valueNode.value;
102
+ }
103
+ if (valueNode) {
104
+ return !additionalAllowedTypes || additionalAllowedTypes.includes(valueNode.type) ? valueNode : undefined;
105
+ }
106
+ }
107
+ function identifyLinkToLastCallRelation(from, cfg, graph, { callName, ignoreIf, cascadeIf }) {
78
108
  const found = [];
109
+ if (ignoreIf && ignoreIf(from, graph)) {
110
+ return found;
111
+ }
79
112
  (0, visitor_1.visitInReverseOrder)(cfg, from, node => {
80
113
  /* we ignore the start id as it cannot be the last call */
81
114
  if (node === from) {
@@ -85,13 +118,17 @@ function identifyLinkToLastCallRelation(from, cfg, graph, linkTo) {
85
118
  if (vertex === undefined || vertex[0].tag !== vertex_1.VertexType.FunctionCall) {
86
119
  return;
87
120
  }
88
- if (linkTo.test(vertex[0].name)) {
121
+ if (callName.test(vertex[0].name)) {
122
+ const act = cascadeIf ? cascadeIf(vertex[0], from, graph) : cascade_action_1.CascadeAction.Stop;
123
+ if (act === cascade_action_1.CascadeAction.Skip) {
124
+ return;
125
+ }
89
126
  const tar = satisfiesCallTargets(vertex[0].id, graph, CallTargets.MustIncludeGlobal);
90
127
  if (tar === 'no') {
91
- return true;
128
+ return act === cascade_action_1.CascadeAction.Stop;
92
129
  }
93
130
  found.push(node);
94
- return true;
131
+ return act === cascade_action_1.CascadeAction.Stop;
95
132
  }
96
133
  });
97
134
  return found;
@@ -83,6 +83,8 @@ exports.ReadFunctions = [
83
83
  { name: 'read.ssd', argIdx: 0, argName: 'file' },
84
84
  { name: 'read.systat', argIdx: 0, argName: 'file' },
85
85
  { name: 'read.xport', argIdx: 0, argName: 'file' },
86
+ // car
87
+ { name: 'Import', argIdx: 0, argName: 'file' },
86
88
  ];
87
89
  exports.WriteFunctions = [
88
90
  { name: 'save', argIdx: 0, argName: '...' },
@@ -138,6 +140,8 @@ exports.WriteFunctions = [
138
140
  { name: 'tiff', argIdx: 0, argName: 'file' },
139
141
  { name: 'X11', argIdx: 0, argName: 'file' },
140
142
  { name: 'quartz', argIdx: 0, argName: 'file' },
143
+ // car
144
+ { name: 'Export', argIdx: 0, argName: 'file' },
141
145
  ];
142
146
  function printResultSection(title, infos, result, sectionSpecifics) {
143
147
  if (infos.length <= 0) {
@@ -450,9 +450,9 @@ type OmitFromValues<T, K extends string | number | symbol> = {
450
450
  export type QueryResultsWithoutMeta<Queries extends Query> = OmitFromValues<Omit<QueryResults<Queries['type']>, '.meta'>, '.meta'>;
451
451
  export type Queries<Base extends SupportedQueryTypes, VirtualArguments extends VirtualCompoundConstraint<Base> = VirtualCompoundConstraint<Base>> = readonly (QueryArgumentsWithType<Base> | VirtualQueryArgumentsWithType<Base, VirtualArguments>)[];
452
452
  export declare function executeQueries<Base extends SupportedQueryTypes, VirtualArguments extends VirtualCompoundConstraint<Base> = VirtualCompoundConstraint<Base>>(data: BasicQueryData, queries: Queries<Base, VirtualArguments>): QueryResults<Base>;
453
- export declare const SupportedQueriesSchema: Joi.AlternativesSchema<any>;
453
+ export declare function SupportedQueriesSchema(): Joi.AlternativesSchema<any>;
454
454
  export declare const CompoundQuerySchema: Joi.ObjectSchema<any>;
455
- export declare const VirtualQuerySchema: Joi.AlternativesSchema<any>;
456
- export declare const AnyQuerySchema: Joi.AlternativesSchema<any>;
457
- export declare const QueriesSchema: Joi.ArraySchema<any[]>;
455
+ export declare function VirtualQuerySchema(): Joi.AlternativesSchema<any>;
456
+ export declare function AnyQuerySchema(): Joi.AlternativesSchema<any>;
457
+ export declare function QueriesSchema(): Joi.ArraySchema<any[]>;
458
458
  export {};
package/queries/query.js CHANGED
@@ -3,9 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.QueriesSchema = exports.AnyQuerySchema = exports.VirtualQuerySchema = exports.CompoundQuerySchema = exports.SupportedQueriesSchema = exports.SupportedQueries = void 0;
6
+ exports.CompoundQuerySchema = exports.SupportedQueries = void 0;
7
7
  exports.executeQueriesOfSameType = executeQueriesOfSameType;
8
8
  exports.executeQueries = executeQueries;
9
+ exports.SupportedQueriesSchema = SupportedQueriesSchema;
10
+ exports.VirtualQuerySchema = VirtualQuerySchema;
11
+ exports.AnyQuerySchema = AnyQuerySchema;
12
+ exports.QueriesSchema = QueriesSchema;
9
13
  const call_context_query_format_1 = require("./catalog/call-context-query/call-context-query-format");
10
14
  const assert_1 = require("../util/assert");
11
15
  const virtual_queries_1 = require("./virtual-query/virtual-queries");
@@ -74,14 +78,22 @@ function executeQueries(data, queries) {
74
78
  };
75
79
  return results;
76
80
  }
77
- exports.SupportedQueriesSchema = joi_1.default.alternatives(Object.values(exports.SupportedQueries).map(q => q.schema)).description('Supported queries');
81
+ function SupportedQueriesSchema() {
82
+ return joi_1.default.alternatives(Object.values(exports.SupportedQueries).map(q => q.schema)).description('Supported queries');
83
+ }
78
84
  exports.CompoundQuerySchema = joi_1.default.object({
79
85
  type: joi_1.default.string().valid('compound').required().description('The type of the query.'),
80
86
  query: joi_1.default.string().required().description('The query to run on the file analysis information.'),
81
87
  commonArguments: joi_1.default.object().required().description('Common arguments for all queries.'),
82
88
  arguments: joi_1.default.array().items(joi_1.default.object()).required().description('Arguments for each query.')
83
89
  }).description('Compound query used to combine queries of the same type');
84
- exports.VirtualQuerySchema = joi_1.default.alternatives(exports.CompoundQuerySchema).description('Virtual queries (used for structure)');
85
- exports.AnyQuerySchema = joi_1.default.alternatives(exports.SupportedQueriesSchema, exports.VirtualQuerySchema).description('Any query');
86
- exports.QueriesSchema = joi_1.default.array().items(exports.AnyQuerySchema).description('Queries to run on the file analysis information (in the form of an array)');
90
+ function VirtualQuerySchema() {
91
+ return joi_1.default.alternatives(exports.CompoundQuerySchema).description('Virtual queries (used for structure)');
92
+ }
93
+ function AnyQuerySchema() {
94
+ return joi_1.default.alternatives(SupportedQueriesSchema(), VirtualQuerySchema()).description('Any query');
95
+ }
96
+ function QueriesSchema() {
97
+ return joi_1.default.array().items(AnyQuerySchema()).description('Queries to run on the file analysis information (in the form of an array)');
98
+ }
87
99
  //# sourceMappingURL=query.js.map
@@ -145,6 +145,9 @@ export type ROther<Info> = RComment<Info> | RLineDirective<Info>;
145
145
  * All other subtypes (like {@link RLoopConstructs}) listed above
146
146
  * can be used to restrict the kind of node. They do not have to be
147
147
  * exclusive, some nodes can appear in multiple subtypes.
148
+ *
149
+ * @see {@link recoverName} - to receive the name/lexeme from such a node
150
+ * @see {@link recoverContent} - for a more rigorous approach to get the content of a node within a {@link DataflowGraph|dataflow graph}
148
151
  */
149
152
  export type RNode<Info = NoInfo> = RExpressionList<Info> | RFunctions<Info> | ROther<Info> | RConstructs<Info> | RNamedAccess<Info> | RIndexAccess<Info> | RUnaryOp<Info> | RBinaryOp<Info> | RSingleNode<Info> | RPipe<Info>;
150
153
  export type OtherInfoNode = RNode | RDelimiter;
@@ -1,7 +1,11 @@
1
1
  import type { Leaf, Location, NoInfo } from '../model';
2
2
  import type { RType } from '../type';
3
3
  import type { RNumberValue } from '../../../convert-values';
4
- /** includes numeric, integer, and complex */
4
+ /**
5
+ * A number like `3`, `-2.14`, `1L`, or `2i`.
6
+ * Includes numeric, integer, and complex.
7
+ * See {@link RNumberValue} for more information.
8
+ */
5
9
  export interface RNumber<Info = NoInfo> extends Leaf<Info>, Location {
6
10
  readonly type: RType.Number;
7
11
  content: RNumberValue;
@@ -7,7 +7,12 @@ export type NodeId<T extends string | number = string | number> = T & {
7
7
  /** used so that we do not have to store strings for the default numeric ids */
8
8
  export declare function normalizeIdToNumberIfPossible(id: NodeId): NodeId;
9
9
  /**
10
- * Recovers the lexeme of a node from its id in the idmap.
10
+ * Recovers the lexeme of a {@link RNode|node} from its id in the {@link AstIdMap|id map}.
11
+ *
12
+ * @see {@link recoverContent} - to recover the content of a node
11
13
  */
12
14
  export declare function recoverName(id: NodeId, idMap?: AstIdMap): string | undefined;
15
+ /**
16
+ * Recovers the content of a {@link RNode|node} from its id in the {@link DataflowGraph|dataflow graph}.
17
+ */
13
18
  export declare function recoverContent(id: NodeId, graph: DataflowGraph): string | undefined;
@@ -15,11 +15,16 @@ function normalizeIdToNumberIfPossible(id) {
15
15
  return id;
16
16
  }
17
17
  /**
18
- * Recovers the lexeme of a node from its id in the idmap.
18
+ * Recovers the lexeme of a {@link RNode|node} from its id in the {@link AstIdMap|id map}.
19
+ *
20
+ * @see {@link recoverContent} - to recover the content of a node
19
21
  */
20
22
  function recoverName(id, idMap) {
21
23
  return idMap?.get(id)?.lexeme;
22
24
  }
25
+ /**
26
+ * Recovers the content of a {@link RNode|node} from its id in the {@link DataflowGraph|dataflow graph}.
27
+ */
23
28
  function recoverContent(id, graph) {
24
29
  const vertex = graph.getVertex(id);
25
30
  if (vertex === undefined) {
@@ -4,7 +4,7 @@ export type OnEnter<OtherInfo> = (node: RNode<OtherInfo>) => (boolean | void);
4
4
  /** Similar to {@link OnEnter} but called when leaving a node. Can't stop exploration as the subtree is already visited! */
5
5
  export type OnExit<OtherInfo> = (node: RNode<OtherInfo>) => void;
6
6
  /**
7
- * Collects all node ids within a tree given by a respective root node
7
+ * Visits all node ids within a tree given by a respective root node using a depth-first search with prefix order.
8
8
  *
9
9
  * @param nodes - The root id nodes to start collecting from
10
10
  * @param onVisit - Called before visiting the subtree of each node. Can be used to stop visiting the subtree starting with this node (return `true` stop)
@@ -100,7 +100,7 @@ class NodeVisitor {
100
100
  }
101
101
  }
102
102
  /**
103
- * Collects all node ids within a tree given by a respective root node
103
+ * Visits all node ids within a tree given by a respective root node using a depth-first search with prefix order.
104
104
  *
105
105
  * @param nodes - The root id nodes to start collecting from
106
106
  * @param onVisit - Called before visiting the subtree of each node. Can be used to stop visiting the subtree starting with this node (return `true` stop)