@eagleoutice/flowr 2.1.2 → 2.1.4

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 (108) hide show
  1. package/benchmark/slicer.js +1 -1
  2. package/cli/repl/commands/repl-parse.js +1 -1
  3. package/cli/repl/commands/repl-query.d.ts +0 -5
  4. package/cli/repl/commands/repl-query.js +4 -145
  5. package/cli/repl/server/connection.js +6 -1
  6. package/cli/repl/server/messages/message-query.js +2 -2
  7. package/cli/repl/server/net.js +1 -1
  8. package/cli/repl/server/send.js +3 -6
  9. package/cli/repl/server/server.d.ts +2 -2
  10. package/cli/repl/server/server.js +1 -1
  11. package/config.js +1 -1
  12. package/core/pipeline-executor.js +2 -1
  13. package/core/steps/all/core/00-parse.d.ts +11 -4
  14. package/core/steps/all/core/00-parse.js +5 -5
  15. package/core/steps/all/core/10-normalize.d.ts +2 -1
  16. package/core/steps/all/core/20-dataflow.d.ts +2 -2
  17. package/core/steps/all/core/20-dataflow.js +2 -2
  18. package/core/steps/pipeline/default-pipelines.d.ts +41 -23
  19. package/core/steps/pipeline/pipeline.d.ts +15 -3
  20. package/core/steps/pipeline/pipeline.js +2 -2
  21. package/dataflow/environments/built-in.d.ts +8 -6
  22. package/dataflow/environments/built-in.js +6 -1
  23. package/dataflow/environments/default-builtin-config.js +21 -5
  24. package/dataflow/environments/environment.d.ts +1 -0
  25. package/dataflow/environments/environment.js +5 -5
  26. package/dataflow/extractor.js +23 -0
  27. package/dataflow/graph/dataflowgraph-builder.d.ts +2 -0
  28. package/dataflow/graph/dataflowgraph-builder.js +9 -0
  29. package/dataflow/graph/diff.js +1 -1
  30. package/dataflow/graph/graph.d.ts +7 -2
  31. package/dataflow/graph/graph.js +10 -2
  32. package/dataflow/internal/process/functions/call/argument/unpack-argument.d.ts +1 -1
  33. package/dataflow/internal/process/functions/call/argument/unpack-argument.js +2 -2
  34. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +2 -2
  35. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +1 -1
  36. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +1 -1
  37. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +1 -1
  38. package/dataflow/internal/process/functions/call/built-in/built-in-pipe.js +1 -1
  39. package/dataflow/internal/process/functions/call/built-in/built-in-source.js +13 -1
  40. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +1 -1
  41. package/dataflow/internal/process/functions/call/named-call-handling.js +1 -1
  42. package/dataflow/processor.d.ts +3 -3
  43. package/documentation/data/server/doc-data-server-messages.js +9 -10
  44. package/documentation/doc-util/doc-cli-option.js +4 -4
  45. package/documentation/doc-util/doc-query.d.ts +3 -1
  46. package/documentation/doc-util/doc-query.js +16 -9
  47. package/documentation/doc-util/doc-repl.js +2 -2
  48. package/documentation/print-dataflow-graph-wiki.js +2 -1
  49. package/documentation/print-interface-wiki.js +8 -3
  50. package/documentation/print-query-wiki.js +139 -16
  51. package/package.json +3 -2
  52. package/queries/base-query-format.d.ts +6 -0
  53. package/queries/catalog/call-context-query/call-context-query-executor.d.ts +1 -1
  54. package/queries/catalog/call-context-query/call-context-query-executor.js +27 -81
  55. package/queries/catalog/call-context-query/call-context-query-format.d.ts +14 -13
  56. package/queries/catalog/call-context-query/call-context-query-format.js +32 -14
  57. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +17 -0
  58. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +99 -0
  59. package/queries/catalog/cluster-query/cluster-query-executor.d.ts +1 -1
  60. package/queries/catalog/cluster-query/cluster-query-format.d.ts +59 -0
  61. package/queries/catalog/cluster-query/cluster-query-format.js +29 -0
  62. package/queries/catalog/dataflow-query/dataflow-query-executor.d.ts +1 -1
  63. package/queries/catalog/dataflow-query/dataflow-query-format.d.ts +59 -0
  64. package/queries/catalog/dataflow-query/dataflow-query-format.js +21 -0
  65. package/queries/catalog/dependencies-query/dependencies-query-executor.d.ts +3 -0
  66. package/queries/catalog/dependencies-query/dependencies-query-executor.js +144 -0
  67. package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +102 -0
  68. package/queries/catalog/dependencies-query/dependencies-query-format.js +187 -0
  69. package/queries/catalog/id-map-query/id-map-query-executor.d.ts +1 -1
  70. package/queries/catalog/id-map-query/id-map-query-format.d.ts +59 -0
  71. package/queries/catalog/id-map-query/id-map-query-format.js +21 -0
  72. package/queries/catalog/lineage-query/lineage-query-executor.d.ts +3 -0
  73. package/queries/catalog/lineage-query/lineage-query-executor.js +22 -0
  74. package/queries/catalog/lineage-query/lineage-query-format.d.ts +73 -0
  75. package/queries/catalog/lineage-query/lineage-query-format.js +27 -0
  76. package/queries/catalog/location-map-query/location-map-query-executor.d.ts +3 -0
  77. package/queries/catalog/location-map-query/location-map-query-executor.js +21 -0
  78. package/queries/catalog/location-map-query/location-map-query-format.d.ts +17 -0
  79. package/queries/catalog/location-map-query/location-map-query-format.js +24 -0
  80. package/queries/catalog/normalized-ast-query/normalized-ast-query-executor.d.ts +1 -1
  81. package/queries/catalog/normalized-ast-query/normalized-ast-query-format.d.ts +59 -0
  82. package/queries/catalog/normalized-ast-query/normalized-ast-query-format.js +21 -0
  83. package/queries/catalog/static-slice-query/static-slice-query-executor.d.ts +1 -1
  84. package/queries/catalog/static-slice-query/static-slice-query-executor.js +8 -3
  85. package/queries/catalog/static-slice-query/static-slice-query-format.d.ts +59 -0
  86. package/queries/catalog/static-slice-query/static-slice-query-format.js +40 -0
  87. package/queries/query-print.d.ts +8 -0
  88. package/queries/query-print.js +94 -0
  89. package/queries/query.d.ts +431 -23
  90. package/queries/query.js +36 -16
  91. package/r-bridge/lang-4.x/ast/parser/json/parser.d.ts +2 -1
  92. package/r-bridge/lang-4.x/ast/parser/json/parser.js +2 -2
  93. package/r-bridge/lang-4.x/ast/parser/main/internal/functions/normalize-argument.js +2 -1
  94. package/r-bridge/retriever.js +1 -1
  95. package/r-bridge/shell-executor.js +1 -1
  96. package/r-bridge/shell.d.ts +1 -2
  97. package/r-bridge/shell.js +22 -18
  98. package/slicing/static/static-slicer.js +3 -1
  99. package/statistics/features/supported/used-functions/used-functions.js +1 -1
  100. package/{documentation/doc-util/doc-hover-over.js → util/html-hover-over.js} +1 -1
  101. package/util/json.d.ts +2 -1
  102. package/util/json.js +101 -3
  103. package/util/objects.d.ts +2 -1
  104. package/util/objects.js +3 -0
  105. package/util/version.js +1 -1
  106. package/queries/query-schema.d.ts +0 -12
  107. package/queries/query-schema.js +0 -50
  108. /package/{documentation/doc-util/doc-hover-over.d.ts → util/html-hover-over.d.ts} +0 -0
@@ -5,8 +5,9 @@ import type { VirtualCompoundConstraint } from '../../queries/virtual-query/comp
5
5
  export interface ShowQueryOptions {
6
6
  readonly showCode?: boolean;
7
7
  readonly collapseResult?: boolean;
8
+ readonly collapseQuery?: boolean;
8
9
  }
9
- export declare function showQuery<Base extends SupportedQueryTypes, VirtualArguments extends VirtualCompoundConstraint<Base> = VirtualCompoundConstraint<Base>>(shell: RShell, code: string, queries: Queries<Base, VirtualArguments>, { showCode, collapseResult }?: ShowQueryOptions): Promise<string>;
10
+ export declare function showQuery<Base extends SupportedQueryTypes, VirtualArguments extends VirtualCompoundConstraint<Base> = VirtualCompoundConstraint<Base>>(shell: RShell, code: string, queries: Queries<Base, VirtualArguments>, { showCode, collapseResult, collapseQuery }?: ShowQueryOptions): Promise<string>;
10
11
  export interface QueryDocumentation {
11
12
  readonly name: string;
12
13
  readonly type: 'virtual' | 'active';
@@ -20,5 +21,6 @@ export declare const RegisteredQueries: {
20
21
  virtual: Map<string, QueryDocumentation>;
21
22
  };
22
23
  export declare function registerQueryDocumentation(query: SupportedQueryTypes | SupportedVirtualQueryTypes, doc: QueryDocumentation): void;
24
+ export declare function linkToQueryOfName(id: SupportedQueryTypes | SupportedVirtualQueryTypes): string;
23
25
  export declare function tocForQueryType(type: 'active' | 'virtual'): string;
24
26
  export declare function explainQueries(shell: RShell, type: 'active' | 'virtual'): Promise<string>;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RegisteredQueries = void 0;
4
4
  exports.showQuery = showQuery;
5
5
  exports.registerQueryDocumentation = registerQueryDocumentation;
6
+ exports.linkToQueryOfName = linkToQueryOfName;
6
7
  exports.tocForQueryType = tocForQueryType;
7
8
  exports.explainQueries = explainQueries;
8
9
  const query_1 = require("../../queries/query");
@@ -11,12 +12,12 @@ const default_pipelines_1 = require("../../core/steps/pipeline/default-pipelines
11
12
  const retriever_1 = require("../../r-bridge/retriever");
12
13
  const json_1 = require("../../util/json");
13
14
  const ansi_1 = require("../../util/ansi");
14
- const repl_query_1 = require("../../cli/repl/commands/repl-query");
15
15
  const doc_files_1 = require("./doc-files");
16
16
  const doc_dfg_1 = require("./doc-dfg");
17
17
  const doc_code_1 = require("./doc-code");
18
18
  const time_1 = require("../../util/time");
19
- async function showQuery(shell, code, queries, { showCode, collapseResult } = {}) {
19
+ const query_print_1 = require("../../queries/query-print");
20
+ async function showQuery(shell, code, queries, { showCode, collapseResult, collapseQuery } = {}) {
20
21
  const now = performance.now();
21
22
  const analysis = await new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_DATAFLOW_PIPELINE, {
22
23
  shell,
@@ -27,17 +28,16 @@ async function showQuery(shell, code, queries, { showCode, collapseResult } = {}
27
28
  const metaInfo = `
28
29
  The analysis required _${(0, time_1.printAsMs)(duration)}_ (including parsing and normalization and the query) within the generation environment.
29
30
  `.trim();
31
+ const str = JSON.stringify(queries, json_1.jsonReplacer, collapseQuery ? ' ' : 2);
30
32
  return `
31
33
 
32
- \`\`\`json
33
- ${JSON.stringify(queries, json_1.jsonReplacer, 2)}
34
- \`\`\`
34
+ ${(0, doc_code_1.codeBlock)('json', collapseQuery ? str.split('\n').join(' ').replace(/([{[])\s{2,}/g, '$1 ').replace(/\s{2,}([\]}])/g, ' $1') : str)}
35
35
 
36
36
  ${collapseResult ? ' <details> <summary style="color:gray">Show Results</summary>' : ''}
37
37
 
38
38
  _Results (prettified and summarized):_
39
39
 
40
- ${(0, repl_query_1.asciiSummaryOfQueryResult)(ansi_1.markdownFormatter, duration, results, analysis)}
40
+ ${(0, query_print_1.asciiSummaryOfQueryResult)(ansi_1.markdownFormatter, duration, results, analysis)}
41
41
 
42
42
  <details> <summary style="color:gray">Show Detailed Results as Json</summary>
43
43
 
@@ -76,6 +76,13 @@ function registerQueryDocumentation(query, doc) {
76
76
  function linkify(name) {
77
77
  return name.toLowerCase().replace(/ /g, '-');
78
78
  }
79
+ function linkToQueryOfName(id) {
80
+ const query = exports.RegisteredQueries.active.get(id) ?? exports.RegisteredQueries.virtual.get(id);
81
+ if (!query) {
82
+ throw new Error(`Query ${id} not found`);
83
+ }
84
+ return `[${query.name}](#${linkify(query.name)})`;
85
+ }
79
86
  function tocForQueryType(type) {
80
87
  const queries = [...exports.RegisteredQueries[type].entries()].sort(([, { name: a }], [, { name: b }]) => a.localeCompare(b));
81
88
  const result = [];
@@ -101,11 +108,11 @@ Responsible for the execution of the ${name} query is \`${functionName}\` in ${(
101
108
  `;
102
109
  }
103
110
  async function explainQueries(shell, type) {
104
- const queries = exports.RegisteredQueries[type];
111
+ const queries = [...exports.RegisteredQueries[type].entries()].sort(([, { name: a }], [, { name: b }]) => a.localeCompare(b));
105
112
  const result = [];
106
- for (const doc of queries.values()) {
113
+ for (const [, doc] of queries) {
107
114
  result.push(await explainQuery(shell, doc));
108
115
  }
109
- return result.join('\n\n\n');
116
+ return result.join(`\n${'-'.repeat(5)}\n\n`);
110
117
  }
111
118
  //# sourceMappingURL=doc-query.js.map
@@ -4,7 +4,7 @@ exports.printReplHelpAsMarkdownTable = printReplHelpAsMarkdownTable;
4
4
  exports.documentReplSession = documentReplSession;
5
5
  const repl_commands_1 = require("../../cli/repl/commands/repl-commands");
6
6
  const doc_cli_option_1 = require("./doc-cli-option");
7
- const doc_hover_over_1 = require("./doc-hover-over");
7
+ const html_hover_over_1 = require("../../util/html-hover-over");
8
8
  const core_1 = require("../../cli/repl/core");
9
9
  const ansi_1 = require("../../util/ansi");
10
10
  const doc_docker_1 = require("./doc-docker");
@@ -19,7 +19,7 @@ function printHelpForScript(script, starredVersion) {
19
19
  return base;
20
20
  }
21
21
  const aliases = script[1].aliases;
22
- return `${base} (alias${aliases.length > 1 ? 'es' : ''}: ${aliases.map(a => '**:' + (0, doc_hover_over_1.textWithTooltip)(a, `Alias of ':${script[0]}'. ${script[1].description}`) + '**').join(', ')}) |`;
22
+ return `${base} (alias${aliases.length > 1 ? 'es' : ''}: ${aliases.map(a => '**:' + (0, html_hover_over_1.textWithTooltip)(a, `Alias of ':${script[0]}'. ${script[1].description}`) + '**').join(', ')}) |`;
23
23
  }
24
24
  function printReplHelpAsMarkdownTable() {
25
25
  const scriptHelp = [];
@@ -807,7 +807,8 @@ ${(0, doc_types_1.printHierarchy)({ program: vertexType.program, hierarchy: vert
807
807
  Let's start by looking at the properties of the dataflow information object: ${Object.keys(result.dataflow).map(k => `\`${k}\``).join(', ')}.
808
808
 
809
809
  ${(() => {
810
- (0, assert_1.guard)(Object.keys(result.dataflow).length === 7, () => 'Update Dataflow Documentation!');
810
+ /* this includes the meta field for timing */
811
+ (0, assert_1.guard)(Object.keys(result.dataflow).length === 8, () => 'Update Dataflow Documentation!');
811
812
  return '';
812
813
  })()}
813
814
 
@@ -18,6 +18,7 @@ const ansi_1 = require("../util/ansi");
18
18
  const flowr_main_options_1 = require("../cli/flowr-main-options");
19
19
  const doc_issue_1 = require("./doc-util/doc-issue");
20
20
  const pipeline_executor_1 = require("../core/pipeline-executor");
21
+ const doc_structure_1 = require("./doc-util/doc-structure");
21
22
  async function explainServer(shell) {
22
23
  (0, doc_data_server_messages_1.documentAllServerMessages)();
23
24
  return `
@@ -27,9 +28,13 @@ Additionally, the server is not well guarded against attacks (e.g., you can theo
27
28
 
28
29
  Every message has to be given in a single line (i.e., without a newline in-between) and end with a newline character. Nevertheless, we will pretty-print example given in the following segments for the ease of reading.
29
30
 
30
- > [!NOTE]
31
- > The default ${(0, doc_cli_option_1.getCliLongOptionOf)('flowr', 'server', false)} uses a simple [TCP](https://de.wikipedia.org/wiki/Transmission_Control_Protocol)
32
- > connection. If you want _flowR_ to expose a [WebSocket](https://de.wikipedia.org/wiki/WebSocket) server instead, add the ${(0, doc_cli_option_1.getCliLongOptionOf)('flowr', 'server', false)} flag (i.e., ${(0, doc_cli_option_1.multipleCliOptions)('flowr', 'server', 'ws')}) when starting _flowR_ from the command line.
31
+ ${(0, doc_structure_1.block)({
32
+ type: 'NOTE',
33
+ content: `
34
+ The default ${(0, doc_cli_option_1.getCliLongOptionOf)('flowr', 'server', false)} uses a simple [TCP](https://de.wikipedia.org/wiki/Transmission_Control_Protocol)
35
+ connection. If you want _flowR_ to expose a [WebSocket](https://de.wikipedia.org/wiki/WebSocket) server instead, add the ${(0, doc_cli_option_1.getCliLongOptionOf)('flowr', 'ws', false)} flag (i.e., ${(0, doc_cli_option_1.multipleCliOptions)('flowr', 'server', 'ws')}) when starting _flowR_ from the command line.
36
+ `
37
+ })}
33
38
 
34
39
  ${await (0, doc_server_message_1.printServerMessages)(shell)}
35
40
 
@@ -6,9 +6,7 @@ const log_1 = require("../../test/functionality/_helper/log");
6
6
  const query_1 = require("../queries/query");
7
7
  const doc_files_1 = require("./doc-util/doc-files");
8
8
  const doc_query_1 = require("./doc-util/doc-query");
9
- const call_context_query_format_1 = require("../queries/catalog/call-context-query/call-context-query-format");
10
9
  const schema_1 = require("../util/schema");
11
- const query_schema_1 = require("../queries/query-schema");
12
10
  const ansi_1 = require("../util/ansi");
13
11
  const call_context_query_executor_1 = require("../queries/catalog/call-context-query/call-context-query-executor");
14
12
  const compound_query_1 = require("../queries/virtual-query/compound-query");
@@ -21,6 +19,12 @@ const id_map_query_executor_1 = require("../queries/catalog/id-map-query/id-map-
21
19
  const normalized_ast_query_executor_1 = require("../queries/catalog/normalized-ast-query/normalized-ast-query-executor");
22
20
  const cluster_query_executor_1 = require("../queries/catalog/cluster-query/cluster-query-executor");
23
21
  const static_slice_query_executor_1 = require("../queries/catalog/static-slice-query/static-slice-query-executor");
22
+ const lineage_query_executor_1 = require("../queries/catalog/lineage-query/lineage-query-executor");
23
+ const dependencies_query_executor_1 = require("../queries/catalog/dependencies-query/dependencies-query-executor");
24
+ const doc_cli_option_1 = require("./doc-util/doc-cli-option");
25
+ const doc_issue_1 = require("./doc-util/doc-issue");
26
+ const location_map_query_executor_1 = require("../queries/catalog/location-map-query/location-map-query-executor");
27
+ const identify_link_to_last_call_relation_1 = require("../queries/catalog/call-context-query/identify-link-to-last-call-relation");
24
28
  (0, doc_query_1.registerQueryDocumentation)('call-context', {
25
29
  name: 'Call-Context Query',
26
30
  type: 'active',
@@ -53,7 +57,7 @@ ${await (0, doc_query_1.showQuery)(shell, example_query_code_1.exampleQueryCode,
53
57
  callName: '^read_',
54
58
  kind: 'input',
55
59
  subkind: 'csv-file',
56
- callTargets: call_context_query_format_1.CallTargets.OnlyGlobal
60
+ callTargets: identify_link_to_last_call_relation_1.CallTargets.OnlyGlobal
57
61
  },
58
62
  {
59
63
  type: 'call-context',
@@ -66,7 +70,7 @@ ${await (0, doc_query_1.showQuery)(shell, example_query_code_1.exampleQueryCode,
66
70
 
67
71
  As you can see, all kinds and subkinds with the same name are grouped together.
68
72
  Yet, re-stating common arguments and kinds may be cumbersome (although you can already use clever regex patterns).
69
- See the [Compound Query](#compound-query) for a way to structure your queries more compactly if you think it gets too verbose.
73
+ See the ${(0, doc_query_1.linkToQueryOfName)('compound')} for a way to structure your queries more compactly if you think it gets too verbose.
70
74
 
71
75
  ${await (async () => {
72
76
  const code = `
@@ -98,7 +102,7 @@ This query type does exactly that!
98
102
  Using the example code \`${exampleCode}\`, the following query returns the dataflow graph of the code:
99
103
  ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
100
104
  type: 'dataflow'
101
- }], { showCode: true })}
105
+ }], { showCode: true, collapseQuery: true })}
102
106
  `;
103
107
  }
104
108
  });
@@ -117,7 +121,38 @@ This query type does exactly that!
117
121
  Using the example code \`${exampleCode}\`, the following query returns the normalized AST of the code:
118
122
  ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
119
123
  type: 'normalized-ast'
120
- }], { showCode: true })}
124
+ }], { showCode: true, collapseQuery: true })}
125
+ `;
126
+ }
127
+ });
128
+ (0, doc_query_1.registerQueryDocumentation)('lineage', {
129
+ name: 'Lineage Query',
130
+ type: 'active',
131
+ shortDescription: 'Returns lineage of a criteria.',
132
+ functionName: lineage_query_executor_1.executeLineageQuery.name,
133
+ functionFile: '../queries/catalog/lineage-query/lineage-query-executor.ts',
134
+ buildExplanation: async (shell) => {
135
+ const exampleCode = 'x <- 1\nx';
136
+ return `
137
+ This query calculates the _lineage_ of a given slicing criterion. The lineage traces back all parts that the
138
+ respective variables stems from given the reads, definitions, and returns in the dataflow graph.
139
+
140
+ To understand this, let's start with a simple example query, to get the lineage of the second use of \`x\` in the following code:
141
+ ${(0, doc_code_1.codeBlock)('r', exampleCode)}
142
+
143
+ For this, we use the criterion \`2@x\` (which is the first use of \`x\` in the second line).
144
+
145
+ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
146
+ type: 'lineage',
147
+ criterion: '2@x'
148
+ }], { showCode: false })}
149
+
150
+ In this simple scenario, the _lineage_ is equivalent to the slice (and in-fact the complete code).
151
+ In general the lineage is smaller and makes no executability guarantees.
152
+ It is just a quick and neither complete nor sound way to get information on where the variable originates from.
153
+
154
+ This query replaces the old [\`request-lineage\`](${doc_files_1.FlowrWikiBaseRef}/Interface#message-request-lineage) message.
155
+
121
156
  `;
122
157
  }
123
158
  });
@@ -144,7 +179,7 @@ ${(0, doc_structure_1.details)('Example <code>' + exampleB + '</code>', await (0
144
179
  Using the example code from above, the following query returns all clusters:
145
180
  ${await (0, doc_query_1.showQuery)(shell, example_query_code_1.exampleQueryCode, [{
146
181
  type: 'dataflow-cluster'
147
- }], { showCode: false })}
182
+ }], { showCode: false, collapseQuery: true })}
148
183
  `;
149
184
  }
150
185
  });
@@ -162,7 +197,7 @@ This query provides access to all nodes in the [normalized AST](${doc_files_1.Fl
162
197
  Using the example code \`${exampleCode}\`, the following query returns all nodes from the code:
163
198
  ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
164
199
  type: 'id-map'
165
- }], { showCode: true })}
200
+ }], { showCode: true, collapseQuery: true })}
166
201
  `;
167
202
  }
168
203
  });
@@ -208,10 +243,10 @@ want to resolve to a local definition:
208
243
  ${await (0, doc_query_1.showQuery)(shell, example_query_code_1.exampleQueryCode, [{
209
244
  type: 'compound',
210
245
  query: 'call-context',
211
- commonArguments: { kind: 'visualize', subkind: 'text', callTargets: call_context_query_format_1.CallTargets.OnlyGlobal },
246
+ commonArguments: { kind: 'visualize', subkind: 'text', callTargets: identify_link_to_last_call_relation_1.CallTargets.OnlyGlobal },
212
247
  arguments: [
213
248
  { callName: '^mean$' },
214
- { callName: '^print$', callTargets: call_context_query_format_1.CallTargets.OnlyLocal }
249
+ { callName: '^print$', callTargets: identify_link_to_last_call_relation_1.CallTargets.OnlyLocal }
215
250
  ]
216
251
  }], { showCode: false })}
217
252
 
@@ -257,6 +292,85 @@ This query replaces the old [\`request-slice\`](${doc_files_1.FlowrWikiBaseRef}/
257
292
  `;
258
293
  }
259
294
  });
295
+ (0, doc_query_1.registerQueryDocumentation)('dependencies', {
296
+ name: 'Dependencies Query',
297
+ type: 'active',
298
+ shortDescription: 'Returns all direct dependencies (in- and outputs) of a given R script',
299
+ functionName: dependencies_query_executor_1.executeDependenciesQuery.name,
300
+ functionFile: '../queries/catalog/dependencies-query/dependencies-query-executor.ts',
301
+ buildExplanation: async (shell) => {
302
+ const exampleCode = 'library(x)';
303
+ const longerCode = `
304
+ source("sample.R")
305
+ foo <- loadNamespace("bar")
306
+
307
+ data <- read.csv("data.csv")
308
+
309
+ #' @importFrom ggplot2 ggplot geom_point aes
310
+ ggplot(data, aes(x=x, y=y)) + geom_point()
311
+
312
+ better::write.csv(data, "data2.csv")
313
+ print("hello world!")
314
+ `;
315
+ return `
316
+ This query extracts all dependencies from an R script, using a combination of a ${(0, doc_query_1.linkToQueryOfName)('call-context')}
317
+ and more advanced tracking in the [Dataflow Graph](${doc_files_1.FlowrWikiBaseRef}/Dataflow%20Graph).
318
+
319
+ In other words, if you have a script simply reading: \`${exampleCode}\`, the following query returns the loaded library:
320
+ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
321
+ type: 'dependencies'
322
+ }], { showCode: false, collapseQuery: true })}
323
+
324
+ Of course, this works for more complicated scripts too. The query offers information on the loaded _libraries_, _sourced_ files, data which is _read_ and data which is _written_.
325
+ For example, consider the following script:
326
+ ${(0, doc_code_1.codeBlock)('r', longerCode)}
327
+ The following query returns the dependencies of the script.
328
+ ${await (0, doc_query_1.showQuery)(shell, longerCode, [{
329
+ type: 'dependencies'
330
+ }], { showCode: false, collapseQuery: true, collapseResult: true })}
331
+
332
+ Currently the dependency extraction may fail as it is essentially a set of heuristics guessing the dependencies.
333
+ We welcome any feedback on this (consider opening a [new issue](${doc_issue_1.NewIssueUrl})).
334
+
335
+ In the meantime we offer several properties to overwrite the default behavior (e.g., function names that should be collected)
336
+
337
+ ${await (0, doc_query_1.showQuery)(shell, longerCode, [{
338
+ type: 'dependencies',
339
+ ignoreDefaultFunctions: true,
340
+ libraryFunctions: [{ name: 'print', argIdx: 0, argName: 'library' }],
341
+ sourceFunctions: [],
342
+ readFunctions: [],
343
+ writeFunctions: []
344
+ }], { showCode: false, collapseQuery: false, collapseResult: true })}
345
+
346
+ `;
347
+ }
348
+ });
349
+ (0, doc_query_1.registerQueryDocumentation)('location-map', {
350
+ name: 'Location Map Query',
351
+ type: 'active',
352
+ shortDescription: 'Returns a simple mapping of ids to their location in the source file',
353
+ functionName: location_map_query_executor_1.executeLocationMapQuery.name,
354
+ functionFile: '../queries/catalog/location-map-query/location-map-query-executor.ts',
355
+ buildExplanation: async (shell) => {
356
+ const exampleCode = 'x + 1\nx * 2';
357
+ return `
358
+ A query like the ${(0, doc_query_1.linkToQueryOfName)('id-map')} query can return a really big result, especially for larger scripts.
359
+ If you are not interested in all of the information contained within the full map, you can use the location map query to get a simple mapping of ids to their location in the source file.
360
+
361
+ Consider you have the following code:
362
+
363
+ ${(0, doc_code_1.codeBlock)('r', exampleCode)}
364
+
365
+ The following query then gives you the aforementioned mapping:
366
+
367
+ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
368
+ type: 'location-map'
369
+ }], { showCode: false, collapseQuery: true })}
370
+
371
+ `;
372
+ }
373
+ });
260
374
  async function getText(shell) {
261
375
  const rversion = (await shell.usedRVersion())?.format() ?? 'unknown';
262
376
  return `${(0, doc_auto_gen_1.autoGenHeader)({ filename: module.filename, purpose: 'query API', rVersion: rversion })}
@@ -264,13 +378,22 @@ async function getText(shell) {
264
378
  This page briefly summarizes flowR's query API, represented by the ${query_1.executeQueries.name} function in ${(0, doc_files_1.getFilePathMd)('../queries/query.ts')}.
265
379
  Please see the [Interface](${doc_files_1.FlowrWikiBaseRef}/Interface) wiki page for more information on how to access this API.
266
380
 
381
+ ${(0, doc_structure_1.block)({
382
+ type: 'NOTE',
383
+ content: `
384
+ There are many ways to query a dataflow graph created by flowR.
385
+ For example, you can use the [\`request-query\`](${doc_files_1.FlowrWikiBaseRef}/Interface#message-request-query) message
386
+ with a running flowR server, or the ${(0, doc_cli_option_1.getReplCommand)('query')} command in the flowR [REPL](${doc_files_1.FlowrWikiBaseRef}/Interface#repl).
387
+ `.trim()
388
+ })}
389
+
267
390
  ## The Query Format
268
391
 
269
392
  Queries are JSON arrays of query objects, each of which uses a \`type\` property to specify the query type.
270
393
  In general, we separate two types of queries:
271
394
 
272
- 1. **Active Queries**: Are exactly what you would expect from a query (e.g., the [Call-Context Query](#call-context-query)). They fetch information from the dataflow graph.
273
- 2. **Virtual Queries**: Are used to structure your queries (e.g., the [Compound Query](#compound-query)).
395
+ 1. **Active Queries**: Are exactly what you would expect from a query (e.g., the ${(0, doc_query_1.linkToQueryOfName)('call-context')}). They fetch information from the dataflow graph.
396
+ 2. **Virtual Queries**: Are used to structure your queries (e.g., the ${(0, doc_query_1.linkToQueryOfName)('compound')}).
274
397
 
275
398
  We separate these from a concept perspective.
276
399
  For now, we support the following **active** queries (which we will refer to simply as a \`query\`):
@@ -288,7 +411,7 @@ ${(0, doc_query_1.tocForQueryType)('virtual')}
288
411
 
289
412
  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:
290
413
 
291
- ${(0, schema_1.describeSchema)(query_schema_1.QueriesSchema, ansi_1.markdownFormatter)}
414
+ ${(0, schema_1.describeSchema)(query_1.QueriesSchema, ansi_1.markdownFormatter)}
292
415
 
293
416
  </details>
294
417
 
@@ -317,11 +440,11 @@ However, this fails to incorporate
317
440
  3. Context information (e.g., calls like \`points\` may link to the current plot)
318
441
 
319
442
  To solve this, flowR provides a query API which allows you to specify queries on the dataflow graph.
320
- For the specific use-case stated, you could use the [Call-Context Query](#call-context-query) to find all calls to \`read_csv\` which refer functions that are not overwritten.
443
+ For the specific use-case stated, you could use the ${(0, doc_query_1.linkToQueryOfName)('call-context')} to find all calls to \`read_csv\` which refer functions that are not overwritten.
321
444
 
322
- Just as an example, the following [Call-Context Query](#call-context-query) finds all calls to \`read_csv\` that are not overwritten:
445
+ Just as an example, the following ${(0, doc_query_1.linkToQueryOfName)('call-context')} finds all calls to \`read_csv\` that are not overwritten:
323
446
 
324
- ${await (0, doc_query_1.showQuery)(shell, example_query_code_1.exampleQueryCode, [{ type: 'call-context', callName: '^read_csv$', callTargets: call_context_query_format_1.CallTargets.OnlyGlobal, kind: 'input', subkind: 'csv-file' }], { showCode: false })}
447
+ ${await (0, doc_query_1.showQuery)(shell, example_query_code_1.exampleQueryCode, [{ type: 'call-context', callName: '^read_csv$', callTargets: identify_link_to_last_call_relation_1.CallTargets.OnlyGlobal, kind: 'input', subkind: 'csv-file' }], { showCode: false })}
325
448
 
326
449
  ${await (0, doc_query_1.explainQueries)(shell, 'active')}
327
450
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
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": {
@@ -38,7 +38,8 @@
38
38
  "test": "nyc --no-clean mocha",
39
39
  "performance-test": "func() { cd test/performance/ && bash run-all-suites.sh $1 $2 $3; cd ../../; }; func",
40
40
  "test-full": "npm run test -- --test-installation",
41
- "detect-circular-deps": "npx madge --extensions ts,tsx --circular src/"
41
+ "detect-circular-deps": "npx madge --extensions ts,tsx --circular src/",
42
+ "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:*"
42
43
  },
43
44
  "keywords": [
44
45
  "static code analysis",
@@ -1,3 +1,5 @@
1
+ import type { NormalizedAst } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
2
+ import type { DataflowGraph } from '../dataflow/graph/graph';
1
3
  export interface BaseQueryFormat {
2
4
  /** used to select the query type :) */
3
5
  readonly type: string;
@@ -9,3 +11,7 @@ export interface BaseQueryMeta {
9
11
  export interface BaseQueryResult {
10
12
  readonly '.meta': BaseQueryMeta;
11
13
  }
14
+ export interface BasicQueryData {
15
+ readonly ast: NormalizedAst;
16
+ readonly graph: DataflowGraph;
17
+ }
@@ -1,5 +1,5 @@
1
1
  import type { CallContextQuery, CallContextQueryResult } from './call-context-query-format';
2
- import type { BasicQueryData } from '../../query';
2
+ import type { BasicQueryData } from '../../base-query-format';
3
3
  /**
4
4
  * Multi-stage call context query resolve.
5
5
  *
@@ -1,69 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.executeCallContextQueries = executeCallContextQueries;
4
- const call_context_query_format_1 = require("./call-context-query-format");
5
4
  const node_id_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/node-id");
6
5
  const vertex_1 = require("../../../dataflow/graph/vertex");
7
- const assert_1 = require("../../../util/assert");
8
6
  const edge_1 = require("../../../dataflow/graph/edge");
9
- const resolve_by_name_1 = require("../../../dataflow/environments/resolve-by-name");
10
- const built_in_1 = require("../../../dataflow/environments/built-in");
11
7
  const cfg_1 = require("../../../util/cfg/cfg");
12
8
  const two_layer_collector_1 = require("../../two-layer-collector");
13
9
  const objects_1 = require("../../../util/objects");
14
- const visitor_1 = require("../../../util/cfg/visitor");
15
- const identifier_1 = require("../../../dataflow/environments/identifier");
16
- function satisfiesCallTargets(id, graph, callTarget) {
17
- const callVertex = graph.get(id);
18
- if (callVertex === undefined || callVertex[0].tag !== vertex_1.VertexType.FunctionCall) {
19
- return 'no';
20
- }
21
- const [info, outgoing] = callVertex;
22
- const callTargets = [...outgoing]
23
- .filter(([, e]) => (0, edge_1.edgeIncludesType)(e.types, edge_1.EdgeType.Calls))
24
- .map(([t]) => t);
25
- let builtIn = false;
26
- if (info.environment === undefined) {
27
- /* if we have a call with an unbound environment,
28
- * this only happens if we are sure of built-in relations and want to save references
29
- */
30
- builtIn = true;
31
- }
32
- else {
33
- /*
34
- * for performance and scoping reasons, flowR will not identify the global linkage,
35
- * including any potential built-in mapping.
36
- */
37
- const reResolved = (0, resolve_by_name_1.resolveByName)(info.name, info.environment, identifier_1.ReferenceType.Unknown);
38
- if (reResolved?.some(t => t.definedAt === built_in_1.BuiltIn)) {
39
- builtIn = true;
40
- }
41
- }
42
- switch (callTarget) {
43
- case call_context_query_format_1.CallTargets.Any:
44
- return callTargets;
45
- case call_context_query_format_1.CallTargets.OnlyGlobal:
46
- if (callTargets.length === 0) {
47
- return builtIn ? [built_in_1.BuiltIn] : [];
48
- }
49
- else {
50
- return 'no';
51
- }
52
- case call_context_query_format_1.CallTargets.MustIncludeGlobal:
53
- return builtIn || callTargets.length === 0 ? [...callTargets, built_in_1.BuiltIn] : 'no';
54
- case call_context_query_format_1.CallTargets.OnlyLocal:
55
- return !builtIn && callTargets.length > 0 ? callTargets : 'no';
56
- case call_context_query_format_1.CallTargets.MustIncludeLocal:
57
- if (callTargets.length > 0) {
58
- return builtIn ? [...callTargets, built_in_1.BuiltIn] : callTargets;
59
- }
60
- else {
61
- return 'no';
62
- }
63
- default:
64
- (0, assert_1.assertUnreachable)(callTarget);
65
- }
66
- }
10
+ const identify_link_to_last_call_relation_1 = require("./identify-link-to-last-call-relation");
67
11
  /* if the node is effected by nse, we have an ingoing nse edge */
68
12
  function isQuoted(node, graph) {
69
13
  const vertex = graph.ingoingEdges(node);
@@ -77,6 +21,9 @@ function makeReport(collector) {
77
21
  for (const [kind, collected] of collector.store) {
78
22
  const subkinds = {};
79
23
  for (const [subkind, values] of collected) {
24
+ if (!Array.isArray(subkinds[subkind])) {
25
+ subkinds[subkind] = [];
26
+ }
80
27
  subkinds[subkind] ??= [];
81
28
  const collectIn = subkinds[subkind];
82
29
  for (const value of values) {
@@ -90,10 +37,10 @@ function makeReport(collector) {
90
37
  return result;
91
38
  }
92
39
  function isSubCallQuery(query) {
93
- return 'linkTo' in query;
40
+ return 'linkTo' in query && query.linkTo !== undefined;
94
41
  }
95
42
  function exactCallNameRegex(name) {
96
- return new RegExp(`^${name}$`);
43
+ return new RegExp(`^(${name})$`);
97
44
  }
98
45
  function promoteQueryCallNames(queries) {
99
46
  let requiresCfg = false;
@@ -121,24 +68,6 @@ function promoteQueryCallNames(queries) {
121
68
  });
122
69
  return { promotedQueries, requiresCfg };
123
70
  }
124
- function identifyLinkToLastCallRelation(from, cfg, graph, linkTo) {
125
- const found = [];
126
- (0, visitor_1.visitInReverseOrder)(cfg, from, node => {
127
- /* we ignore the start id as it cannot be the last call */
128
- if (node === from) {
129
- return;
130
- }
131
- const vertex = graph.getVertex(node);
132
- if (vertex === undefined || vertex.tag !== vertex_1.VertexType.FunctionCall) {
133
- return;
134
- }
135
- if (linkTo.test(vertex.name)) {
136
- found.push(node);
137
- return true;
138
- }
139
- });
140
- return found;
141
- }
142
71
  /* maybe we want to add caches to this */
143
72
  function retrieveAllCallAliases(nodeId, graph) {
144
73
  /* we want the names of all functions called at the source id, including synonyms and returns */
@@ -187,6 +116,22 @@ function retrieveAllCallAliases(nodeId, graph) {
187
116
  }
188
117
  return aliases;
189
118
  }
119
+ function removeIdenticalDuplicates(collector) {
120
+ for (const [, collected] of collector.store) {
121
+ for (const [subkind, values] of collected) {
122
+ const seen = new Set();
123
+ const newValues = values.filter(v => {
124
+ const str = JSON.stringify(v);
125
+ if (seen.has(str)) {
126
+ return false;
127
+ }
128
+ seen.add(str);
129
+ return true;
130
+ });
131
+ collected.set(subkind, newValues);
132
+ }
133
+ }
134
+ }
190
135
  /**
191
136
  * Multi-stage call context query resolve.
192
137
  *
@@ -223,7 +168,7 @@ function executeCallContextQueries({ graph, ast }, queries) {
223
168
  for (const [l, ids] of targets.entries()) {
224
169
  for (const query of queriesWhichWantAliases) {
225
170
  if (query.callName.test(l)) {
226
- initialIdCollector.add(query.kind ?? '.', query.subkind ?? '.', (0, objects_1.compactRecord)({ id: nodeId, aliasRoots: ids }));
171
+ initialIdCollector.add(query.kind ?? '.', query.subkind ?? '.', (0, objects_1.compactRecord)({ id: nodeId, name: info.name, aliasRoots: ids }));
227
172
  }
228
173
  }
229
174
  }
@@ -231,7 +176,7 @@ function executeCallContextQueries({ graph, ast }, queries) {
231
176
  for (const query of promotedQueries.filter(q => q.callName.test(info.name))) {
232
177
  let targets = undefined;
233
178
  if (query.callTargets) {
234
- targets = satisfiesCallTargets(nodeId, graph, query.callTargets);
179
+ targets = (0, identify_link_to_last_call_relation_1.satisfiesCallTargets)(nodeId, graph, query.callTargets);
235
180
  if (targets === 'no') {
236
181
  continue;
237
182
  }
@@ -243,14 +188,15 @@ function executeCallContextQueries({ graph, ast }, queries) {
243
188
  let linkedIds = undefined;
244
189
  if (cfg && isSubCallQuery(query)) {
245
190
  /* if we have a linkTo query, we have to find the last call */
246
- const lastCall = identifyLinkToLastCallRelation(nodeId, cfg.graph, graph, query.linkTo.callName);
191
+ const lastCall = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(nodeId, cfg.graph, graph, query.linkTo.callName);
247
192
  if (lastCall) {
248
193
  linkedIds = lastCall;
249
194
  }
250
195
  }
251
- initialIdCollector.add(query.kind ?? '.', query.subkind ?? '.', (0, objects_1.compactRecord)({ id: nodeId, calls: targets, linkedIds }));
196
+ initialIdCollector.add(query.kind ?? '.', query.subkind ?? '.', (0, objects_1.compactRecord)({ id: nodeId, name: info.name, calls: targets, linkedIds }));
252
197
  }
253
198
  }
199
+ removeIdenticalDuplicates(initialIdCollector);
254
200
  return {
255
201
  '.meta': {
256
202
  timing: Date.now() - now,