@eagleoutice/flowr 2.1.8 → 2.1.10

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 (85) hide show
  1. package/README.md +3 -0
  2. package/benchmark/summarizer/first-phase/process.js +6 -5
  3. package/cli/repl/commands/repl-dataflow.js +5 -2
  4. package/cli/repl/commands/repl-normalize.js +5 -2
  5. package/cli/repl/commands/repl-query.js +2 -2
  6. package/cli/repl/server/messages/message-query.js +1 -1
  7. package/config.d.ts +21 -0
  8. package/config.js +19 -2
  9. package/dataflow/environments/built-in.d.ts +2 -0
  10. package/dataflow/environments/built-in.js +2 -0
  11. package/dataflow/environments/default-builtin-config.js +48 -8
  12. package/dataflow/environments/define.js +78 -0
  13. package/dataflow/environments/environment.d.ts +46 -8
  14. package/dataflow/environments/environment.js +24 -1
  15. package/dataflow/environments/identifier.d.ts +60 -10
  16. package/dataflow/environments/identifier.js +11 -2
  17. package/dataflow/environments/resolve-by-name.d.ts +10 -5
  18. package/dataflow/environments/resolve-by-name.js +103 -5
  19. package/dataflow/extractor.js +5 -4
  20. package/dataflow/graph/dataflowgraph-builder.d.ts +6 -0
  21. package/dataflow/graph/dataflowgraph-builder.js +8 -0
  22. package/dataflow/graph/edge.d.ts +10 -4
  23. package/dataflow/graph/edge.js +12 -5
  24. package/dataflow/graph/graph.d.ts +41 -3
  25. package/dataflow/graph/graph.js +39 -34
  26. package/dataflow/graph/vertex.d.ts +122 -8
  27. package/dataflow/graph/vertex.js +19 -0
  28. package/dataflow/info.d.ts +79 -11
  29. package/dataflow/info.js +20 -0
  30. package/dataflow/internal/linker.d.ts +4 -2
  31. package/dataflow/internal/linker.js +12 -5
  32. package/dataflow/internal/process/functions/call/built-in/built-in-access.d.ts +11 -0
  33. package/dataflow/internal/process/functions/call/built-in/built-in-access.js +141 -49
  34. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +8 -3
  35. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +40 -11
  36. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.d.ts +16 -0
  37. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +83 -6
  38. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +3 -3
  39. package/dataflow/internal/process/functions/call/built-in/built-in-list.d.ts +15 -0
  40. package/dataflow/internal/process/functions/call/built-in/built-in-list.js +50 -0
  41. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.d.ts +1 -1
  42. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +29 -1
  43. package/dataflow/internal/process/functions/call/common.js +16 -2
  44. package/dataflow/internal/process/functions/call/known-call-handling.d.ts +2 -1
  45. package/dataflow/internal/process/functions/call/known-call-handling.js +3 -2
  46. package/documentation/doc-util/doc-dfg.d.ts +0 -1
  47. package/documentation/doc-util/doc-dfg.js +1 -14
  48. package/documentation/print-capabilities-markdown.js +1 -1
  49. package/documentation/print-dataflow-graph-wiki.js +26 -7
  50. package/documentation/print-interface-wiki.js +6 -1
  51. package/documentation/print-linting-and-testing-wiki.js +60 -26
  52. package/documentation/print-query-wiki.js +1 -1
  53. package/package.json +17 -3
  54. package/queries/catalog/call-context-query/call-context-query-executor.js +1 -1
  55. package/queries/catalog/call-context-query/call-context-query-format.d.ts +13 -0
  56. package/queries/catalog/call-context-query/call-context-query-format.js +3 -1
  57. package/queries/catalog/call-context-query/cascade-action.d.ts +8 -0
  58. package/queries/catalog/call-context-query/cascade-action.js +13 -0
  59. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +11 -1
  60. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +41 -4
  61. package/queries/catalog/dependencies-query/dependencies-query-format.js +4 -0
  62. package/queries/query.d.ts +4 -4
  63. package/queries/query.js +17 -5
  64. package/r-bridge/lang-4.x/ast/model/model.d.ts +3 -0
  65. package/r-bridge/lang-4.x/ast/model/nodes/r-number.d.ts +5 -1
  66. package/r-bridge/lang-4.x/ast/model/processing/node-id.d.ts +6 -1
  67. package/r-bridge/lang-4.x/ast/model/processing/node-id.js +6 -1
  68. package/slicing/static/slice-call.d.ts +7 -2
  69. package/slicing/static/slice-call.js +33 -44
  70. package/slicing/static/static-slicer.d.ts +5 -1
  71. package/slicing/static/static-slicer.js +22 -8
  72. package/slicing/static/visiting-queue.d.ts +4 -4
  73. package/slicing/static/visiting-queue.js +5 -3
  74. package/statistics/output/print-stats.js +2 -1
  75. package/statistics/summarizer/post-process/histogram.js +2 -1
  76. package/statistics/summarizer/post-process/post-process-output.js +2 -1
  77. package/statistics/summarizer/second-phase/process.js +3 -3
  78. package/util/arrays.d.ts +1 -1
  79. package/util/arrays.js +3 -3
  80. package/util/cfg/cfg.js +4 -2
  81. package/util/list-access.d.ts +48 -0
  82. package/util/list-access.js +115 -0
  83. package/util/mermaid/cfg.js +1 -1
  84. package/util/summarizer.js +2 -2
  85. package/util/version.js +1 -1
package/README.md CHANGED
@@ -47,6 +47,9 @@ We welcome every contribution! Please check out the [contributing guidelines](ht
47
47
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/Core5563"><img src="https://avatars.githubusercontent.com/u/140061253?v=4?s=100" width="100px;" alt="Core5563"/><br /><sub><b>Core5563</b></sub></a><br /><a href="https://github.com/flowr-analysis/flowr/commits?author=Core5563" title="Code">💻</a> <a href="https://github.com/flowr-analysis/flowr/commits?author=Core5563" title="Tests">⚠️</a></td>
48
48
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/Ehcsan"><img src="https://avatars.githubusercontent.com/u/68707578?v=4?s=100" width="100px;" alt="Ehcsan"/><br /><sub><b>Ehcsan</b></sub></a><br /><a href="https://github.com/flowr-analysis/flowr/commits?author=Ehcsan" title="Code">💻</a> <a href="https://github.com/flowr-analysis/flowr/commits?author=Ehcsan" title="Tests">⚠️</a></td>
49
49
  </tr>
50
+ <tr>
51
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/Slartibartfass2"><img src="https://avatars.githubusercontent.com/u/40503329?v=4?s=100" width="100px;" alt="Felix Schlegel"/><br /><sub><b>Felix Schlegel</b></sub></a><br /><a href="https://github.com/flowr-analysis/flowr/commits?author=Slartibartfass2" title="Code">💻</a> <a href="https://github.com/flowr-analysis/flowr/commits?author=Slartibartfass2" title="Tests">⚠️</a></td>
52
+ </tr>
50
53
  </tbody>
51
54
  <tfoot>
52
55
  <tr>
@@ -42,6 +42,7 @@ const shell_1 = require("../../../r-bridge/shell");
42
42
  const retriever_1 = require("../../../r-bridge/retriever");
43
43
  const visitor_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/visitor");
44
44
  const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type");
45
+ const arrays_1 = require("../../../util/arrays");
45
46
  const tempfile = (() => {
46
47
  let _tempfile = undefined;
47
48
  return () => {
@@ -244,15 +245,15 @@ async function summarizeSlicerStats(stats, report = () => {
244
245
  }
245
246
  function summarizeSummarizedMeasurement(data) {
246
247
  data = data.filter(assert_1.isNotUndefined);
247
- const min = data.map(d => d.min).filter(assert_1.isNotUndefined).reduce((a, b) => Math.min(a, b), Infinity);
248
- const max = data.map(d => d.max).filter(assert_1.isNotUndefined).reduce((a, b) => Math.max(a, b), -Infinity);
248
+ const min = Math.min(...data.map(d => d.min).filter(assert_1.isNotUndefined));
249
+ const max = Math.max(...data.map(d => d.max).filter(assert_1.isNotUndefined));
249
250
  // calculate median of medians (don't just average the median!)
250
251
  const medians = data.map(d => d.median).filter(assert_1.isNotUndefined).sort((a, b) => a - b);
251
252
  const median = medians[Math.floor(medians.length / 2)];
252
- const mean = data.map(d => d.mean).filter(assert_1.isNotUndefined).reduce((a, b) => a + b, 0) / data.length;
253
+ const mean = (0, arrays_1.arraySum)(data.map(d => d.mean).filter(assert_1.isNotUndefined)) / data.length;
253
254
  // Method 1 of https://www.statology.org/averaging-standard-deviations/
254
- const std = Math.sqrt(data.map(d => d.std ** 2).filter(assert_1.isNotUndefined).reduce((a, b) => a + b, 0) / data.length);
255
- const total = data.map(d => d.total).filter(assert_1.isNotUndefined).reduce((a, b) => a + b, 0);
255
+ const std = Math.sqrt((0, arrays_1.arraySum)(data.map(d => d.std ** 2).filter(assert_1.isNotUndefined)) / data.length);
256
+ const total = (0, arrays_1.arraySum)(data.map(d => d.total).filter(assert_1.isNotUndefined));
256
257
  return { min, max, median, mean, std, total };
257
258
  }
258
259
  function summarizeSummarizedReductions(reductions) {
@@ -11,13 +11,16 @@ async function dataflow(shell, remainingLine) {
11
11
  request: (0, retriever_1.requestFromInput)(remainingLine.trim())
12
12
  }).allRemainingSteps();
13
13
  }
14
+ function handleString(code) {
15
+ return code.startsWith('"') ? JSON.parse(code) : code;
16
+ }
14
17
  exports.dataflowCommand = {
15
18
  description: `Get mermaid code for the dataflow graph of R code, start with '${retriever_1.fileProtocol}' to indicate a file`,
16
19
  usageExample: ':dataflow',
17
20
  aliases: ['d', 'df'],
18
21
  script: false,
19
22
  fn: async (output, shell, remainingLine) => {
20
- const result = await dataflow(shell, remainingLine);
23
+ const result = await dataflow(shell, handleString(remainingLine));
21
24
  output.stdout((0, dfg_1.graphToMermaid)({ graph: result.dataflow.graph, includeEnvironments: false }).string);
22
25
  }
23
26
  };
@@ -27,7 +30,7 @@ exports.dataflowStarCommand = {
27
30
  aliases: ['d*', 'df*'],
28
31
  script: false,
29
32
  fn: async (output, shell, remainingLine) => {
30
- const result = await dataflow(shell, remainingLine);
33
+ const result = await dataflow(shell, handleString(remainingLine));
31
34
  output.stdout((0, dfg_1.graphToMermaidUrl)(result.dataflow.graph, false));
32
35
  }
33
36
  };
@@ -11,13 +11,16 @@ async function normalize(shell, remainingLine) {
11
11
  request: (0, retriever_1.requestFromInput)(remainingLine.trim())
12
12
  }).allRemainingSteps();
13
13
  }
14
+ function handleString(code) {
15
+ return code.startsWith('"') ? JSON.parse(code) : code;
16
+ }
14
17
  exports.normalizeCommand = {
15
18
  description: `Get mermaid code for the normalized AST of R code, start with '${retriever_1.fileProtocol}' to indicate a file`,
16
19
  usageExample: ':normalize',
17
20
  aliases: ['n'],
18
21
  script: false,
19
22
  fn: async (output, shell, remainingLine) => {
20
- const result = await normalize(shell, remainingLine);
23
+ const result = await normalize(shell, handleString(remainingLine));
21
24
  output.stdout((0, ast_1.normalizedAstToMermaid)(result.normalize.ast));
22
25
  }
23
26
  };
@@ -27,7 +30,7 @@ exports.normalizeStarCommand = {
27
30
  aliases: ['n*'],
28
31
  script: false,
29
32
  fn: async (output, shell, remainingLine) => {
30
- const result = await normalize(shell, remainingLine);
33
+ const result = await normalize(shell, handleString(remainingLine));
31
34
  output.stdout((0, ast_1.normalizedAstToMermaidUrl)(result.normalize.ast));
32
35
  }
33
36
  };
@@ -19,7 +19,7 @@ async function getDataflow(shell, remainingLine) {
19
19
  function printHelp(output) {
20
20
  output.stderr(`Format: ${(0, ansi_1.italic)(':query "<query>" <code>', output.formatter)}`);
21
21
  output.stdout('The query is an array of query objects to represent multiple queries. Each query object may have the following properties:');
22
- output.stdout((0, schema_1.describeSchema)(query_1.AnyQuerySchema, output.formatter));
22
+ output.stdout((0, schema_1.describeSchema)((0, query_1.AnyQuerySchema)(), output.formatter));
23
23
  output.stdout(`\n\nThe example ${(0, ansi_1.italic)(':query "[{\\"type\\": \\"call-context\\", \\"callName\\": \\"mean\\" }]" mean(1:10)', output.formatter)} would return the call context of the mean function.`);
24
24
  output.stdout('As a convenience, we interpret any (non-help) string not starting with \'[\' as a regex for the simple call-context query.');
25
25
  output.stdout(`Hence, ${(0, ansi_1.italic)(':query "mean" mean(1:10)', output.formatter)} is equivalent to the above example.`);
@@ -38,7 +38,7 @@ async function processQueryArgs(line, shell, output) {
38
38
  let parsedQuery = [];
39
39
  if (query.startsWith('[')) {
40
40
  parsedQuery = JSON.parse(query);
41
- const validationResult = query_1.QueriesSchema.validate(parsedQuery);
41
+ const validationResult = (0, query_1.QueriesSchema)().validate(parsedQuery);
42
42
  if (validationResult.error) {
43
43
  output.stderr(`Invalid query: ${validationResult.error.message}`);
44
44
  printHelp(output);
@@ -12,7 +12,7 @@ exports.requestQueryMessage = {
12
12
  type: joi_1.default.string().valid('request-query').required().description('The type of the message.'),
13
13
  id: joi_1.default.string().optional().description('If you give the id, the response will be sent to the client with the same id.'),
14
14
  filetoken: joi_1.default.string().required().description('The filetoken of the file/data retrieved from the analysis request.'),
15
- query: query_1.QueriesSchema.required().description('The query to run on the file analysis information.')
15
+ query: (0, query_1.QueriesSchema)().required().description('The query to run on the file analysis information.')
16
16
  }).description('Request a query to be run on the file analysis information.')
17
17
  };
18
18
  exports.responseQueryMessage = {
package/config.d.ts CHANGED
@@ -1,6 +1,14 @@
1
1
  import type { MergeableRecord } from './util/objects';
2
2
  import Joi from 'joi';
3
3
  import type { BuiltInDefinitions } from './dataflow/environments/built-in-config';
4
+ export declare enum VariableResolve {
5
+ /** Don't resolve constants at all */
6
+ Disabled = "disabled",
7
+ /** Use alias tracking to resolve */
8
+ Alias = "alias",
9
+ /** Only resolve directly assigned builtin constants */
10
+ Builtin = "builtin"
11
+ }
4
12
  export interface FlowrConfigOptions extends MergeableRecord {
5
13
  /**
6
14
  * Whether source calls should be ignored, causing {@link processSourceCall}'s behavior to be skipped
@@ -23,6 +31,19 @@ export interface FlowrConfigOptions extends MergeableRecord {
23
31
  };
24
32
  };
25
33
  };
34
+ /** How to resolve constants, constraints, cells, ... */
35
+ readonly solver: {
36
+ /**
37
+ * How to resolve variables and their values
38
+ */
39
+ readonly variables: VariableResolve;
40
+ /**
41
+ * Whether to track pointers in the dataflow graph,
42
+ * if not, the graph will be over-approximated wrt.
43
+ * containers and accesses
44
+ */
45
+ readonly pointerTracking: boolean;
46
+ };
26
47
  }
27
48
  export declare const defaultConfigOptions: FlowrConfigOptions;
28
49
  export declare const flowrConfigFileSchema: Joi.ObjectSchema<any>;
package/config.js CHANGED
@@ -3,7 +3,7 @@ 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.flowrConfigFileSchema = exports.defaultConfigOptions = void 0;
6
+ exports.flowrConfigFileSchema = exports.defaultConfigOptions = exports.VariableResolve = void 0;
7
7
  exports.setConfigFile = setConfigFile;
8
8
  exports.parseConfig = parseConfig;
9
9
  exports.setConfig = setConfig;
@@ -14,6 +14,15 @@ const fs_1 = __importDefault(require("fs"));
14
14
  const log_1 = require("./util/log");
15
15
  const files_1 = require("./util/files");
16
16
  const joi_1 = __importDefault(require("joi"));
17
+ var VariableResolve;
18
+ (function (VariableResolve) {
19
+ /** Don't resolve constants at all */
20
+ VariableResolve["Disabled"] = "disabled";
21
+ /** Use alias tracking to resolve */
22
+ VariableResolve["Alias"] = "alias";
23
+ /** Only resolve directly assigned builtin constants */
24
+ VariableResolve["Builtin"] = "builtin";
25
+ })(VariableResolve || (exports.VariableResolve = VariableResolve = {}));
17
26
  exports.defaultConfigOptions = {
18
27
  ignoreSourceCalls: false,
19
28
  rPath: undefined,
@@ -24,6 +33,10 @@ exports.defaultConfigOptions = {
24
33
  definitions: []
25
34
  }
26
35
  }
36
+ },
37
+ solver: {
38
+ variables: VariableResolve.Alias,
39
+ pointerTracking: true
27
40
  }
28
41
  };
29
42
  exports.flowrConfigFileSchema = joi_1.default.object({
@@ -36,7 +49,11 @@ exports.flowrConfigFileSchema = joi_1.default.object({
36
49
  definitions: joi_1.default.array().items(joi_1.default.object()).optional().description('The definitions to load/overwrite.')
37
50
  }).optional().description('Do you want to overwrite (parts) of the builtin definition?')
38
51
  }).optional().description('Semantics regarding the handlings of the environment.')
39
- }).description('Configure language semantics and how flowR handles them.')
52
+ }).description('Configure language semantics and how flowR handles them.'),
53
+ solver: joi_1.default.object({
54
+ variables: joi_1.default.string().valid(...Object.values(VariableResolve)).description('How to resolve variables and their values.'),
55
+ pointerTracking: joi_1.default.boolean().description('Whether to track pointers in the dataflow graph, if not, the graph will be over-approximated wrt. containers and accesses.')
56
+ }).description('How to resolve constants, constraints, cells, ...')
40
57
  }).description('The configuration file format for flowR.');
41
58
  // we don't load from a config file at all by default unless setConfigFile is called
42
59
  let configFile = undefined;
@@ -24,6 +24,7 @@ import { processSourceCall } from '../internal/process/functions/call/built-in/b
24
24
  import type { ForceArguments } from '../internal/process/functions/call/common';
25
25
  import { processApply } from '../internal/process/functions/call/built-in/built-in-apply';
26
26
  import type { LinkTo } from '../../queries/catalog/call-context-query/call-context-query-format';
27
+ import { processList } from '../internal/process/functions/call/built-in/built-in-list';
27
28
  export declare const BuiltIn = "built-in";
28
29
  export type BuiltInIdentifierProcessor = <OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>) => DataflowInformation;
29
30
  export type BuiltInIdentifierProcessorWithConfig<Config> = <OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: Config) => DataflowInformation;
@@ -63,6 +64,7 @@ export declare const BuiltInProcessorMapper: {
63
64
  readonly 'builtin:repeat-loop': typeof processRepeatLoop;
64
65
  readonly 'builtin:while-loop': typeof processWhileLoop;
65
66
  readonly 'builtin:replacement': typeof processReplacementFunction;
67
+ readonly 'builtin:list': typeof processList;
66
68
  };
67
69
  export type BuiltInMappingName = keyof typeof BuiltInProcessorMapper;
68
70
  export type ConfigOfBuiltInMappingName<N extends BuiltInMappingName> = Parameters<typeof BuiltInProcessorMapper[N]>[4];
@@ -24,6 +24,7 @@ const built_in_source_1 = require("../internal/process/functions/call/built-in/b
24
24
  const built_in_apply_1 = require("../internal/process/functions/call/built-in/built-in-apply");
25
25
  const built_in_config_1 = require("./built-in-config");
26
26
  const default_builtin_config_1 = require("./default-builtin-config");
27
+ const built_in_list_1 = require("../internal/process/functions/call/built-in/built-in-list");
27
28
  exports.BuiltIn = 'built-in';
28
29
  function defaultBuiltInProcessor(name, args, rootId, data, config) {
29
30
  const { information: res, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs });
@@ -88,6 +89,7 @@ exports.BuiltInProcessorMapper = {
88
89
  'builtin:repeat-loop': built_in_repeat_loop_1.processRepeatLoop,
89
90
  'builtin:while-loop': built_in_while_loop_1.processWhileLoop,
90
91
  'builtin:replacement': built_in_replacement_1.processReplacementFunction,
92
+ 'builtin:list': built_in_list_1.processList,
91
93
  };
92
94
  exports.BuiltInMemory = new Map();
93
95
  exports.EmptyBuiltInMemory = new Map();
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DefaultBuiltinConfig = void 0;
4
+ const identify_link_to_last_call_relation_1 = require("../../queries/catalog/call-context-query/identify-link-to-last-call-relation");
5
+ const type_1 = require("../../r-bridge/lang-4.x/ast/model/type");
6
+ const cascade_action_1 = require("../../queries/catalog/call-context-query/cascade-action");
4
7
  /**
5
8
  * Contains the built-in definitions recognized by flowR
6
9
  */
@@ -11,7 +14,7 @@ exports.DefaultBuiltinConfig = [
11
14
  {
12
15
  type: 'function',
13
16
  names: [
14
- '~', '+', '-', '*', '/', '^', '!', '?', '**', '==', '!=', '>', '<', '>=', '<=', '%%', '%/%', '%*%', '%in%', ':', 'list',
17
+ '~', '+', '-', '*', '/', '^', '!', '?', '**', '==', '!=', '>', '<', '>=', '<=', '%%', '%/%', '%*%', '%in%', ':',
15
18
  'rep', 'seq', 'seq_len', 'seq_along', 'seq.int', 'gsub', 'which', 'class', 'dimnames', 'min', 'max',
16
19
  'intersect', 'subset', 'match', 'sqrt', 'abs', 'round', 'floor', 'ceiling', 'signif', 'trunc', 'log', 'log10', 'log2', 'sum', 'mean',
17
20
  'unique', 'paste', 'paste0', 'read.csv', 'stop', 'is.null', 'numeric', 'as.character', 'as.integer', 'as.logical', 'as.numeric', 'as.matrix',
@@ -26,7 +29,7 @@ exports.DefaultBuiltinConfig = [
26
29
  {
27
30
  type: 'function',
28
31
  names: [
29
- 'c', 't'
32
+ 'c', 't', 'aperm' /* vector construction, concatenation, transpose function, permutation generation */
30
33
  ],
31
34
  processor: 'builtin:default',
32
35
  config: { readAllArguments: true },
@@ -40,10 +43,45 @@ exports.DefaultBuiltinConfig = [
40
43
  { type: 'function', names: ['print', 'message', 'warning'], processor: 'builtin:default', config: { returnsNthArgument: 0, forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^sink$/ } }, assumePrimitive: false },
41
44
  // graphics base
42
45
  { type: 'function', names: ['plot', 'plot.new', 'curve', 'map', 'image', 'boxplot', 'dotchart', 'sunflowerplot', 'barplot', 'matplot', 'hist', 'stem', 'density', 'smoothScatter', 'contour', 'persp'],
43
- processor: 'builtin:default', config: { forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^pdf|jpeg|png|windows|postscript|xfig|bitmap|pictex|cairo_pdf|svg|bmp|tiff|X11|quartz$/ } }, assumePrimitive: true },
46
+ processor: 'builtin:default',
47
+ config: {
48
+ forceArgs: 'all',
49
+ hasUnknownSideEffects: {
50
+ type: 'link-to-last-call',
51
+ ignoreIf: (source, graph) => {
52
+ /* map with add = true appends to an existing plot */
53
+ return (source.name === 'map' && (0, identify_link_to_last_call_relation_1.getValueOfArgument)(graph, source, {
54
+ index: 11,
55
+ name: 'add'
56
+ }, [type_1.RType.Logical])?.content === true);
57
+ },
58
+ callName: /^(pdf|jpeg|png|windows|postscript|xfig|bitmap|pictex|cairo_pdf|svg|bmp|tiff|X11|quartz)$/
59
+ }
60
+ }, assumePrimitive: true },
44
61
  // graphics addons
45
- { type: 'function', names: ['points', 'abline', 'mtext', 'lines', 'text', 'legend', 'title', 'axis', 'polygon', 'polypath', 'pie', 'rect', 'segments', 'arrows', 'symbols', 'tiplabels'],
46
- processor: 'builtin:default', config: { forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^dev\.new|dev\.copy|plot\.new|xspline|sunflowerplot|dotchart|plot|map|image|curve|boxplot|barplot|matplot|hist|stem|density|smoothScatter|contour|persp$/ } }, assumePrimitive: true },
62
+ { type: 'function', names: ['points', 'abline', 'map', 'mtext', 'lines', 'text', 'legend', 'title', 'axis', 'polygon', 'polypath', 'pie', 'rect', 'segments', 'arrows', 'symbols', 'tiplabels'],
63
+ processor: 'builtin:default', config: {
64
+ forceArgs: 'all',
65
+ hasUnknownSideEffects: {
66
+ type: 'link-to-last-call',
67
+ callName: /^(dev\.new|dev\.copy|plot\.new|xspline|sunflowerplot|dotchart|plot|map|image|curve|boxplot|barplot|matplot|hist|stem|density|smoothScatter|contour|persp)$/,
68
+ ignoreIf: (source, graph) => {
69
+ const sourceVertex = graph.getVertex(source);
70
+ /* map with add = true appends to an existing plot */
71
+ return (sourceVertex?.name === 'map' && (0, identify_link_to_last_call_relation_1.getValueOfArgument)(graph, sourceVertex, {
72
+ index: 11,
73
+ name: 'add'
74
+ }, [type_1.RType.Logical])?.content !== true);
75
+ },
76
+ cascadeIf: (target, _, graph) => {
77
+ /* map with add = true appends to an existing plot */
78
+ return target.name === 'map' ? ((0, identify_link_to_last_call_relation_1.getValueOfArgument)(graph, target, {
79
+ index: 11,
80
+ name: 'add'
81
+ }, [type_1.RType.Logical])?.content === true ? cascade_action_1.CascadeAction.Continue : cascade_action_1.CascadeAction.Stop) : cascade_action_1.CascadeAction.Stop;
82
+ }
83
+ }
84
+ }, assumePrimitive: true },
47
85
  { type: 'function', names: ['('], processor: 'builtin:default', config: { returnsNthArgument: 0 }, assumePrimitive: true },
48
86
  { type: 'function', names: ['load', 'load_all', 'setwd', 'set.seed'], processor: 'builtin:default', config: { hasUnknownSideEffects: true, forceArgs: [true] }, assumePrimitive: false },
49
87
  { type: 'function', names: ['eval', 'body', 'formals', 'environment'], processor: 'builtin:default', config: { hasUnknownSideEffects: true, forceArgs: [true] }, assumePrimitive: false },
@@ -60,8 +98,9 @@ exports.DefaultBuiltinConfig = [
60
98
  { type: 'function', names: ['get'], processor: 'builtin:get', config: {}, assumePrimitive: false },
61
99
  { type: 'function', names: ['library', 'require'], processor: 'builtin:library', config: {}, assumePrimitive: false },
62
100
  { type: 'function', names: ['<-', '='], processor: 'builtin:assignment', config: { canBeReplacement: true }, assumePrimitive: true },
63
- { type: 'function', names: [':=', 'assign'], processor: 'builtin:assignment', config: {}, assumePrimitive: true },
64
- { type: 'function', names: ['delayedAssign'], processor: 'builtin:assignment', config: { quoteSource: true }, assumePrimitive: true },
101
+ { type: 'function', names: [':='], processor: 'builtin:assignment', config: {}, assumePrimitive: true },
102
+ { type: 'function', names: ['assign'], processor: 'builtin:assignment', config: { targetVariable: true }, assumePrimitive: true },
103
+ { type: 'function', names: ['delayedAssign'], processor: 'builtin:assignment', config: { quoteSource: true, targetVariable: true }, assumePrimitive: true },
65
104
  { type: 'function', names: ['<<-'], processor: 'builtin:assignment', config: { superAssignment: true, canBeReplacement: true }, assumePrimitive: true },
66
105
  { type: 'function', names: ['->'], processor: 'builtin:assignment', config: { swapSourceAndTarget: true, canBeReplacement: true }, assumePrimitive: true },
67
106
  { type: 'function', names: ['->>'], processor: 'builtin:assignment', config: { superAssignment: true, swapSourceAndTarget: true, canBeReplacement: true }, assumePrimitive: true },
@@ -74,6 +113,7 @@ exports.DefaultBuiltinConfig = [
74
113
  { type: 'function', names: ['repeat'], processor: 'builtin:repeat-loop', config: {}, assumePrimitive: true },
75
114
  { type: 'function', names: ['while'], processor: 'builtin:while-loop', config: {}, assumePrimitive: true },
76
115
  { type: 'function', names: ['do.call'], processor: 'builtin:apply', config: { indexOfFunction: 0, unquoteFunction: true }, assumePrimitive: true },
116
+ { type: 'function', names: ['list'], processor: 'builtin:list', config: {}, assumePrimitive: true },
77
117
  {
78
118
  type: 'function',
79
119
  names: [
@@ -83,7 +123,7 @@ exports.DefaultBuiltinConfig = [
83
123
  /* downloader and installer functions (R, devtools, BiocManager) */
84
124
  'library.dynam', 'install.packages', 'install', 'install_github', 'install_gitlab', 'install_bitbucket', 'install_url', 'install_git', 'install_svn', 'install_local', 'install_version', 'update_packages',
85
125
  /* weird env attachments */
86
- 'attach', 'unname'
126
+ 'attach', 'unname', 'data'
87
127
  ],
88
128
  processor: 'builtin:default',
89
129
  config: { hasUnknownSideEffects: true },
@@ -4,8 +4,15 @@ exports.define = define;
4
4
  const assert_1 = require("../../util/assert");
5
5
  const environment_1 = require("./environment");
6
6
  const clone_1 = require("./clone");
7
+ const vertex_1 = require("../graph/vertex");
7
8
  function defInEnv(newEnvironments, name, definition) {
8
9
  const existing = newEnvironments.memory.get(name);
10
+ // When there are defined indices, merge the definitions
11
+ const inGraphDefinition = definition;
12
+ if (existing !== undefined && inGraphDefinition.indicesCollection !== undefined && inGraphDefinition.controlDependencies === undefined) {
13
+ newEnvironments.memory.set(name, mergeDefinitions(existing, inGraphDefinition));
14
+ return;
15
+ }
9
16
  // check if it is maybe or not
10
17
  if (existing === undefined || definition.controlDependencies === undefined) {
11
18
  newEnvironments.memory.set(name, [definition]);
@@ -14,6 +21,77 @@ function defInEnv(newEnvironments, name, definition) {
14
21
  existing.push(definition);
15
22
  }
16
23
  }
24
+ function mergeDefinitions(existing, definition) {
25
+ // When new definition is not a single index, e.g., a list redefinition, then reset existing definition
26
+ if (definition.indicesCollection?.some(indices => indices.isContainer)) {
27
+ return [definition];
28
+ }
29
+ const existingDefs = existing.map((def) => def).filter((def) => def !== undefined);
30
+ const overwriteIndices = definition.indicesCollection?.flatMap(indices => indices.indices) ?? [];
31
+ // Compare existing and new definitions,
32
+ // add new definitions and remove existing definitions that are overwritten by new definition
33
+ const newExistingDefs = [];
34
+ for (const overwriteIndex of overwriteIndices) {
35
+ for (const existingDef of existingDefs) {
36
+ if (existingDef.indicesCollection === undefined) {
37
+ continue;
38
+ }
39
+ const newIndicesCollection = overwriteContainerIndices(existingDef.indicesCollection, overwriteIndex);
40
+ // if indices are now empty list, don't keep empty definition
41
+ if (newIndicesCollection.length > 0) {
42
+ newExistingDefs.push({
43
+ ...existingDef,
44
+ indicesCollection: newIndicesCollection,
45
+ });
46
+ }
47
+ }
48
+ }
49
+ // store changed existing definitions and add new one
50
+ return [...newExistingDefs, definition];
51
+ }
52
+ function overwriteContainerIndices(existingIndices, overwriteIndex) {
53
+ const newIndicesCollection = [];
54
+ for (const indices of existingIndices) {
55
+ let newIndices;
56
+ // When overwrite index is container itself, then only overwrite sub-index
57
+ if ((0, vertex_1.isParentContainerIndex)(overwriteIndex)) {
58
+ newIndices = [];
59
+ for (const index of indices.indices) {
60
+ if (index.lexeme === overwriteIndex.lexeme && (0, vertex_1.isParentContainerIndex)(index)) {
61
+ const overwriteSubIndices = overwriteIndex.subIndices.flatMap(a => a.indices);
62
+ let newSubIndices = index.subIndices;
63
+ for (const overwriteSubIndex of overwriteSubIndices) {
64
+ newSubIndices = overwriteContainerIndices(newSubIndices, overwriteSubIndex);
65
+ }
66
+ if (newSubIndices.length > 0) {
67
+ newIndices.push({
68
+ ...index,
69
+ subIndices: newSubIndices,
70
+ });
71
+ }
72
+ }
73
+ if (index.lexeme !== overwriteIndex.lexeme || !(0, vertex_1.isParentContainerIndex)(index)) {
74
+ newIndices.push(index);
75
+ }
76
+ }
77
+ }
78
+ else if (indices.isContainer) {
79
+ // If indices are not a single, e.g., a list, take the whole definition
80
+ newIndices = indices.indices;
81
+ }
82
+ else {
83
+ // Filter existing indices with the same name
84
+ newIndices = indices.indices.filter(def => def.lexeme !== overwriteIndex.lexeme);
85
+ }
86
+ if (indices.isContainer || newIndices.length > 0) {
87
+ newIndicesCollection.push({
88
+ ...indices,
89
+ indices: newIndices,
90
+ });
91
+ }
92
+ }
93
+ return newIndicesCollection;
94
+ }
17
95
  /**
18
96
  * Insert the given `definition` --- defined within the given scope --- into the passed along `environments` will take care of propagation.
19
97
  * Does not modify the passed along `environments` in-place! It returns the new reference.
@@ -7,19 +7,22 @@
7
7
  import type { Identifier, IdentifierDefinition, IdentifierReference } from './identifier';
8
8
  import type { DataflowGraph } from '../graph/graph';
9
9
  import type { ControlDependency } from '../info';
10
+ /**
11
+ * Marks the reference as maybe (i.e., as controlled by a set of {@link IdentifierReference#controlDependencies|control dependencies}).
12
+ */
10
13
  export declare function makeReferenceMaybe(ref: IdentifierReference, graph: DataflowGraph, environments: REnvironmentInformation, includeDefs: boolean, defaultCd?: ControlDependency | undefined): IdentifierReference;
11
14
  export declare function makeAllMaybe(references: readonly IdentifierReference[] | undefined, graph: DataflowGraph, environments: REnvironmentInformation, includeDefs: boolean, defaultCd?: ControlDependency | undefined): IdentifierReference[];
12
15
  export type EnvironmentMemory = Map<Identifier, IdentifierDefinition[]>;
16
+ /** A single entry/scope within an {@link REnvironmentInformation} */
13
17
  export interface IEnvironment {
14
- /** unique and internally generated identifier -- will not be used for comparison but assists debugging for tracking identities */
18
+ /** Unique and internally generated identifier -- will not be used for comparison but helps with debugging for tracking identities */
15
19
  readonly id: number;
16
20
  /** Lexical parent of the environment, if any (can be manipulated by R code) */
17
21
  parent: IEnvironment;
18
- /**
19
- * Maps to exactly one definition of an identifier if the source is known, otherwise to a list of all possible definitions
20
- */
22
+ /** Maps to exactly one definition of an identifier if the source is known, otherwise to a list of all possible definitions */
21
23
  memory: EnvironmentMemory;
22
24
  }
25
+ /** @see REnvironmentInformation */
23
26
  export declare class Environment implements IEnvironment {
24
27
  readonly id: number;
25
28
  parent: IEnvironment;
@@ -27,11 +30,26 @@ export declare class Environment implements IEnvironment {
27
30
  constructor(parent: IEnvironment);
28
31
  }
29
32
  /**
30
- * First of all, yes, R stores its environments differently, potentially even with a different differentiation between
31
- * the `baseenv`, the `emptyenv`and other default environments. Yet, during dataflow we want sometimes to know more (static
32
- * reference information) and sometimes know less (to be honest we do not want that,
33
+ * An environment describes a ({@link IEnvironment#parent|scoped}) mapping of names to their definitions ({@link EnvironmentMemory}).
34
+ *
35
+ * First, yes, R stores its environments differently, potentially even with another differentiation between
36
+ * the `baseenv`, the `emptyenv`, and other default environments (see https://adv-r.hadley.nz/environments.html).
37
+ * Yet, during the dataflow analysis, we want sometimes to know more (static {@link IdentifierDefinition|reference information})
38
+ * and sometimes know less (to be honest, we do not want that,
33
39
  * but statically determining all attached environments is theoretically impossible --- consider attachments by user input).
34
- * One example would be maps holding a potential list of all definitions of a variable, if we do not know the execution path (like with `if(x) A else B`).
40
+ *
41
+ * One important environment is the {@link BuiltInEnvironment} which contains the default definitions for R's built-in functions and constants.
42
+ * Please use {@link initializeCleanEnvironments} to initialize the environments (which includes the built-ins).
43
+ * During serialization, you may want to rely on the {@link builtInEnvJsonReplacer} to avoid the huge built-in environment.
44
+ *
45
+ *
46
+ * @see {@link define} - to define a new {@link IdentifierDefinition|identifier definition} within an environment
47
+ * @see {@link resolveByName} - to resolve an {@link Identifier|identifier/name} to its {@link IdentifierDefinition|definitions} within an environment
48
+ * @see {@link makeReferenceMaybe} - to attach control dependencies to a reference
49
+ * @see {@link pushLocalEnvironment} - to create a new local scope
50
+ * @see {@link popLocalEnvironment} - to remove the current local scope
51
+ * @see {@link appendEnvironment} - to append an environment to the current one
52
+ * @see {@link overwriteEnvironment} - to overwrite the definitions in the current environment with those of another one
35
53
  */
36
54
  export interface REnvironmentInformation {
37
55
  /** The currently active environment (the stack is represented by the currently active {@link IEnvironment#parent}). Environments are maintained within the dataflow graph. */
@@ -39,7 +57,27 @@ export interface REnvironmentInformation {
39
57
  /** nesting level of the environment, will be `0` for the global/root environment */
40
58
  readonly level: number;
41
59
  }
60
+ /**
61
+ * The built-in {@link REnvironmentInformation|environment} is the root of all environments.
62
+ *
63
+ * For its default content (when not overwritten by a flowR config),
64
+ * see the {@link DefaultBuiltinConfig}.
65
+ */
42
66
  export declare const BuiltInEnvironment: Environment;
67
+ /**
68
+ * The twin of the {@link BuiltInEnvironment} but with less built ins defined for
69
+ * cases in which we want some commonly overwritten variables to remain open.
70
+ * If you do not know if you need the empty environment, you do not need the empty environment (right now).
71
+ *
72
+ * @see {@link BuiltInEnvironment}
73
+ */
43
74
  export declare const EmptyBuiltInEnvironment: IEnvironment;
75
+ /**
76
+ * Initialize a new {@link REnvironmentInformation|environment} with the built-ins.
77
+ * See {@link EmptyBuiltInEnvironment} for the case `fullBuiltIns = false`.
78
+ */
44
79
  export declare function initializeCleanEnvironments(fullBuiltIns?: boolean): REnvironmentInformation;
80
+ /**
81
+ * Helps to serialize an environment, but replaces the built-in environment with a placeholder.
82
+ */
45
83
  export declare function builtInEnvJsonReplacer(k: unknown, v: unknown): unknown;
@@ -9,6 +9,9 @@ const identifier_1 = require("./identifier");
9
9
  const built_in_1 = require("./built-in");
10
10
  const resolve_by_name_1 = require("./resolve-by-name");
11
11
  const json_1 = require("../../util/json");
12
+ /**
13
+ * Marks the reference as maybe (i.e., as controlled by a set of {@link IdentifierReference#controlDependencies|control dependencies}).
14
+ */
12
15
  function makeReferenceMaybe(ref, graph, environments, includeDefs, defaultCd = undefined) {
13
16
  const node = graph.get(ref.nodeId, true);
14
17
  if (includeDefs) {
@@ -42,6 +45,7 @@ function makeAllMaybe(references, graph, environments, includeDefs, defaultCd =
42
45
  return references.map(ref => makeReferenceMaybe(ref, graph, environments, includeDefs, defaultCd));
43
46
  }
44
47
  let environmentIdCounter = 0;
48
+ /** @see REnvironmentInformation */
45
49
  class Environment {
46
50
  id = environmentIdCounter++;
47
51
  parent;
@@ -52,14 +56,30 @@ class Environment {
52
56
  }
53
57
  }
54
58
  exports.Environment = Environment;
55
- /* the built-in environment is the root of all environments */
59
+ /**
60
+ * The built-in {@link REnvironmentInformation|environment} is the root of all environments.
61
+ *
62
+ * For its default content (when not overwritten by a flowR config),
63
+ * see the {@link DefaultBuiltinConfig}.
64
+ */
56
65
  exports.BuiltInEnvironment = new Environment(undefined);
57
66
  exports.BuiltInEnvironment.memory = undefined;
67
+ /**
68
+ * The twin of the {@link BuiltInEnvironment} but with less built ins defined for
69
+ * cases in which we want some commonly overwritten variables to remain open.
70
+ * If you do not know if you need the empty environment, you do not need the empty environment (right now).
71
+ *
72
+ * @see {@link BuiltInEnvironment}
73
+ */
58
74
  exports.EmptyBuiltInEnvironment = {
59
75
  id: exports.BuiltInEnvironment.id,
60
76
  memory: undefined,
61
77
  parent: undefined
62
78
  };
79
+ /**
80
+ * Initialize a new {@link REnvironmentInformation|environment} with the built-ins.
81
+ * See {@link EmptyBuiltInEnvironment} for the case `fullBuiltIns = false`.
82
+ */
63
83
  function initializeCleanEnvironments(fullBuiltIns = true) {
64
84
  exports.BuiltInEnvironment.memory ??= built_in_1.BuiltInMemory;
65
85
  exports.EmptyBuiltInEnvironment.memory ??= built_in_1.EmptyBuiltInMemory;
@@ -68,6 +88,9 @@ function initializeCleanEnvironments(fullBuiltIns = true) {
68
88
  level: 0
69
89
  };
70
90
  }
91
+ /**
92
+ * Helps to serialize an environment, but replaces the built-in environment with a placeholder.
93
+ */
71
94
  function builtInEnvJsonReplacer(k, v) {
72
95
  if (v === exports.BuiltInEnvironment) {
73
96
  return '<BuiltInEnvironment>';