@eagleoutice/flowr 2.10.1 → 2.10.2

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 (123) hide show
  1. package/README.md +27 -16
  2. package/abstract-interpretation/absint-visitor.d.ts +13 -8
  3. package/abstract-interpretation/absint-visitor.js +35 -26
  4. package/abstract-interpretation/data-frame/dataframe-domain.d.ts +1 -2
  5. package/abstract-interpretation/data-frame/dataframe-domain.js +14 -15
  6. package/abstract-interpretation/data-frame/mappers/access-mapper.js +2 -15
  7. package/abstract-interpretation/data-frame/mappers/arguments.d.ts +11 -17
  8. package/abstract-interpretation/data-frame/mappers/arguments.js +18 -18
  9. package/abstract-interpretation/data-frame/mappers/function-mapper.d.ts +41 -15
  10. package/abstract-interpretation/data-frame/mappers/function-mapper.js +74 -48
  11. package/abstract-interpretation/data-frame/mappers/replacement-mapper.js +2 -1
  12. package/abstract-interpretation/data-frame/semantics.d.ts +1 -1
  13. package/abstract-interpretation/data-frame/semantics.js +31 -35
  14. package/abstract-interpretation/data-frame/shape-inference.js +1 -1
  15. package/abstract-interpretation/domains/interval-domain.d.ts +1 -0
  16. package/abstract-interpretation/domains/interval-domain.js +3 -0
  17. package/abstract-interpretation/domains/product-domain.d.ts +9 -0
  18. package/abstract-interpretation/domains/product-domain.js +26 -6
  19. package/abstract-interpretation/domains/state-abstract-domain.d.ts +36 -22
  20. package/abstract-interpretation/domains/state-abstract-domain.js +169 -62
  21. package/abstract-interpretation/unsupported-functions.d.ts +10 -0
  22. package/abstract-interpretation/unsupported-functions.js +45 -0
  23. package/benchmark/slicer.js +10 -13
  24. package/cli/flowr.js +1 -1
  25. package/control-flow/control-flow-graph.js +13 -9
  26. package/control-flow/semantic-cfg-guided-visitor.d.ts +6 -0
  27. package/control-flow/semantic-cfg-guided-visitor.js +6 -0
  28. package/dataflow/environments/built-in-proc-name.d.ts +6 -0
  29. package/dataflow/environments/built-in-proc-name.js +6 -0
  30. package/dataflow/environments/built-in.d.ts +7 -5
  31. package/dataflow/environments/built-in.js +2 -0
  32. package/dataflow/environments/default-builtin-config.d.ts +442 -6
  33. package/dataflow/environments/default-builtin-config.js +158 -3
  34. package/dataflow/environments/overwrite.js +2 -5
  35. package/dataflow/graph/df-helper.d.ts +14 -4
  36. package/dataflow/graph/df-helper.js +36 -6
  37. package/dataflow/graph/graph.d.ts +10 -0
  38. package/dataflow/graph/graph.js +12 -0
  39. package/dataflow/instrument/instrument-dataflow-count.d.ts +10 -0
  40. package/dataflow/instrument/instrument-dataflow-count.js +10 -0
  41. package/dataflow/internal/process/functions/call/argument/unpack-argument.d.ts +4 -4
  42. package/dataflow/internal/process/functions/call/built-in/built-in-access.d.ts +3 -3
  43. package/dataflow/internal/process/functions/call/built-in/built-in-apply.d.ts +2 -2
  44. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +3 -3
  45. package/dataflow/internal/process/functions/call/built-in/built-in-eval.d.ts +2 -2
  46. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.d.ts +2 -2
  47. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.d.ts +2 -2
  48. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.d.ts +2 -2
  49. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +6 -17
  50. package/dataflow/internal/process/functions/call/built-in/built-in-get.d.ts +2 -2
  51. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.d.ts +2 -2
  52. package/dataflow/internal/process/functions/call/built-in/built-in-library.d.ts +2 -2
  53. package/dataflow/internal/process/functions/call/built-in/built-in-list.d.ts +2 -2
  54. package/dataflow/internal/process/functions/call/built-in/built-in-local.d.ts +2 -2
  55. package/dataflow/internal/process/functions/call/built-in/built-in-pipe.d.ts +23 -3
  56. package/dataflow/internal/process/functions/call/built-in/built-in-pipe.js +80 -12
  57. package/dataflow/internal/process/functions/call/built-in/built-in-purrr-formula.d.ts +41 -0
  58. package/dataflow/internal/process/functions/call/built-in/built-in-purrr-formula.js +179 -0
  59. package/dataflow/internal/process/functions/call/built-in/built-in-quote.d.ts +7 -4
  60. package/dataflow/internal/process/functions/call/built-in/built-in-quote.js +62 -1
  61. package/dataflow/internal/process/functions/call/built-in/built-in-recall.d.ts +7 -2
  62. package/dataflow/internal/process/functions/call/built-in/built-in-recall.js +15 -1
  63. package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.d.ts +2 -2
  64. package/dataflow/internal/process/functions/call/built-in/built-in-repeat-loop.d.ts +2 -2
  65. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.d.ts +2 -2
  66. package/dataflow/internal/process/functions/call/built-in/built-in-rm.d.ts +2 -2
  67. package/dataflow/internal/process/functions/call/built-in/built-in-s-seven-dispatch.d.ts +2 -2
  68. package/dataflow/internal/process/functions/call/built-in/built-in-s-seven-new-generic.d.ts +2 -2
  69. package/dataflow/internal/process/functions/call/built-in/built-in-s-three-dispatch.d.ts +2 -2
  70. package/dataflow/internal/process/functions/call/built-in/built-in-source.d.ts +2 -2
  71. package/dataflow/internal/process/functions/call/built-in/built-in-special-bin-op.d.ts +2 -2
  72. package/dataflow/internal/process/functions/call/built-in/built-in-stop-if-not.d.ts +2 -2
  73. package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.d.ts +2 -2
  74. package/dataflow/internal/process/functions/call/built-in/built-in-vector.d.ts +2 -2
  75. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.d.ts +2 -2
  76. package/dataflow/internal/process/functions/call/common.d.ts +2 -2
  77. package/dataflow/internal/process/functions/call/common.js +4 -3
  78. package/dataflow/internal/process/functions/call/known-call-handling.d.ts +2 -2
  79. package/dataflow/internal/process/functions/call/named-call-handling.d.ts +2 -2
  80. package/documentation/wiki-absint.js +6 -5
  81. package/documentation/wiki-analyzer.js +0 -2
  82. package/documentation/wiki-linter.js +1 -0
  83. package/documentation/wiki-normalized-ast.js +7 -7
  84. package/linter/linter-rules.d.ts +24 -1
  85. package/linter/linter-rules.js +3 -1
  86. package/linter/rules/dataframe-access-validation.d.ts +1 -1
  87. package/linter/rules/dataframe-access-validation.js +3 -4
  88. package/linter/rules/roxygen-arguments.d.ts +35 -0
  89. package/linter/rules/roxygen-arguments.js +100 -0
  90. package/package.json +2 -3
  91. package/project/context/flowr-analyzer-context.d.ts +1 -8
  92. package/project/context/flowr-analyzer-context.js +1 -7
  93. package/project/context/flowr-analyzer-environment-context.d.ts +5 -0
  94. package/project/context/flowr-analyzer-environment-context.js +6 -0
  95. package/project/context/flowr-analyzer-files-context.d.ts +6 -0
  96. package/project/context/flowr-analyzer-files-context.js +4 -2
  97. package/project/flowr-analyzer-builder.js +1 -4
  98. package/queries/catalog/call-context-query/call-context-query-executor.d.ts +1 -1
  99. package/queries/catalog/call-context-query/call-context-query-executor.js +10 -5
  100. package/queries/catalog/call-context-query/call-context-query-format.d.ts +1 -1
  101. package/queries/catalog/dependencies-query/function-info/library-functions.js +2 -0
  102. package/queries/catalog/df-shape-query/df-shape-query-format.js +7 -2
  103. package/queries/catalog/files-query/files-query-executor.js +0 -1
  104. package/queries/catalog/input-sources-query/simple-input-classifier.d.ts +2 -0
  105. package/queries/catalog/input-sources-query/simple-input-classifier.js +2 -0
  106. package/r-bridge/data/data.d.ts +2 -2
  107. package/r-bridge/data/data.js +2 -2
  108. package/r-bridge/lang-4.x/ast/model/nodes/r-argument.d.ts +13 -5
  109. package/r-bridge/lang-4.x/ast/model/nodes/r-argument.js +14 -2
  110. package/r-bridge/lang-4.x/ast/model/nodes/r-function-call.d.ts +3 -3
  111. package/r-bridge/lang-4.x/ast/model/nodes/r-pipe.d.ts +9 -0
  112. package/r-bridge/lang-4.x/ast/model/nodes/r-pipe.js +13 -0
  113. package/r-bridge/lang-4.x/ast/model/versions.d.ts +2 -0
  114. package/r-bridge/lang-4.x/ast/model/versions.js +3 -1
  115. package/r-bridge/roxygen2/documentation-provider.js +15 -6
  116. package/r-bridge/roxygen2/roxygen-ast.d.ts +3 -1
  117. package/search/flowr-search-builder.js +3 -2
  118. package/util/mermaid/ast.js +2 -1
  119. package/util/record.d.ts +23 -0
  120. package/util/record.js +33 -0
  121. package/util/version.js +1 -1
  122. package/abstract-interpretation/domains/mapped-abstract-domain.d.ts +0 -41
  123. package/abstract-interpretation/domains/mapped-abstract-domain.js +0 -213
@@ -1,10 +1,10 @@
1
1
  import type { DataflowProcessorInformation } from '../../../../../processor';
2
2
  import type { DataflowInformation } from '../../../../../info';
3
3
  import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
4
- import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
4
+ import type { PotentiallyEmptyRArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
6
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
7
  /**
8
8
  * Processes a built-in 'get' function call.
9
9
  */
10
- export declare function processGet<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
10
+ export declare function processGet<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly PotentiallyEmptyRArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
@@ -2,7 +2,7 @@ import { type DataflowProcessorInformation } from '../../../../../processor';
2
2
  import { type DataflowInformation } from '../../../../../info';
3
3
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
4
4
  import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
5
- import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
5
+ import type { PotentiallyEmptyRArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
6
6
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
7
  /** `if(<cond>) <then> else <else>` built-in function configuration, make sure to not reuse indices */
8
8
  export interface IfThenElseConfig {
@@ -20,4 +20,4 @@ export interface IfThenElseConfig {
20
20
  * For example, `if(cond) thenExpr else elseExpr` and `if(cond) thenExpr`.
21
21
  * The arguments will be either `[cond, thenExpr]` or `[cond, thenExpr, elseExpr]`.
22
22
  */
23
- export declare function processIfThenElse<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config?: IfThenElseConfig): DataflowInformation;
23
+ export declare function processIfThenElse<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly PotentiallyEmptyRArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config?: IfThenElseConfig): DataflowInformation;
@@ -1,10 +1,10 @@
1
1
  import { type DataflowProcessorInformation } from '../../../../../processor';
2
2
  import type { DataflowInformation } from '../../../../../info';
3
3
  import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
4
- import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
4
+ import type { PotentiallyEmptyRArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
6
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
7
  /**
8
8
  * Process a library call like `library` or `require`
9
9
  */
10
- export declare function processLibrary<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
10
+ export declare function processLibrary<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly PotentiallyEmptyRArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
@@ -1,4 +1,4 @@
1
- import { type RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
1
+ import { type PotentiallyEmptyRArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
2
2
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
3
3
  import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
4
4
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
@@ -12,4 +12,4 @@ import type { DataflowProcessorInformation } from '../../../../../processor';
12
12
  * list(a = 1, b = 2)
13
13
  * ```
14
14
  */
15
- export declare function processList<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
15
+ export declare function processList<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly PotentiallyEmptyRArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
@@ -1,7 +1,7 @@
1
1
  import type { DataflowProcessorInformation } from '../../../../../processor';
2
2
  import { DataflowInformation } from '../../../../../info';
3
3
  import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
4
- import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
4
+ import type { PotentiallyEmptyRArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
6
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
7
  export interface LocalFunctionConfiguration {
@@ -15,4 +15,4 @@ export interface LocalFunctionConfiguration {
15
15
  /**
16
16
  * Processes a built-in 'local' function call.
17
17
  */
18
- export declare function processLocal<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: LocalFunctionConfiguration): DataflowInformation;
18
+ export declare function processLocal<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly PotentiallyEmptyRArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: LocalFunctionConfiguration): DataflowInformation;
@@ -1,10 +1,30 @@
1
1
  import type { DataflowProcessorInformation } from '../../../../../processor';
2
2
  import type { DataflowInformation } from '../../../../../info';
3
- import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
3
+ import type { PotentiallyEmptyRArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
4
4
  import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
6
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
+ import type { BrandedIdentifier } from '../../../../../environments/identifier';
7
8
  /**
8
- * Suport for R's pipe functions like `|>`.
9
+ * Configuration options for the basic R pipe
9
10
  */
10
- export declare function processPipe<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
11
+ interface PipeConfiguration {
12
+ pipePlaceholderName: BrandedIdentifier;
13
+ /**
14
+ * this is for a pipe like `%<>%` which assigns its lhs
15
+ */
16
+ assignLhs: boolean;
17
+ /**
18
+ * Whether to return the lhs (e.g., with the TPipe)
19
+ */
20
+ returnLhs: boolean;
21
+ /**
22
+ * If so, also allow a symbol instead of a function as rhs, if it is the case, this automatically converts the symbol on the rhs to a function call
23
+ */
24
+ rhsMightBeSymbol?: boolean;
25
+ }
26
+ /**
27
+ * Support for R's pipe functions like `|>` or magrittr's `%>%`
28
+ */
29
+ export declare function processPipe<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly PotentiallyEmptyRArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, { pipePlaceholderName, assignLhs, returnLhs, rhsMightBeSymbol }: PipeConfiguration): DataflowInformation;
30
+ export {};
@@ -4,41 +4,109 @@ exports.processPipe = processPipe;
4
4
  const known_call_handling_1 = require("../known-call-handling");
5
5
  const assert_1 = require("../../../../../../util/assert");
6
6
  const unpack_argument_1 = require("../argument/unpack-argument");
7
+ const model_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/model");
7
8
  const logger_1 = require("../../../../../logger");
8
9
  const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
9
10
  const vertex_1 = require("../../../../../graph/vertex");
10
11
  const edge_1 = require("../../../../../graph/edge");
11
12
  const identifier_1 = require("../../../../../environments/identifier");
13
+ const make_argument_1 = require("../argument/make-argument");
14
+ const built_in_assignment_1 = require("./built-in-assignment");
12
15
  const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name");
16
+ const log_1 = require("../../../../../../util/log");
13
17
  /**
14
- * Suport for R's pipe functions like `|>`.
18
+ * Support for R's pipe functions like `|>` or magrittr's `%>%`
15
19
  */
16
- function processPipe(name, args, rootId, data) {
17
- const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.Pipe });
20
+ function processPipe(name, args, rootId, data, { pipePlaceholderName, assignLhs, returnLhs, rhsMightBeSymbol = false }) {
21
+ const fCallInfo = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.Pipe });
22
+ const processedArguments = fCallInfo.processedArguments;
23
+ let information = fCallInfo.information;
18
24
  if (args.length !== 2) {
19
25
  logger_1.dataflowLogger.warn(`Pipe ${identifier_1.Identifier.toString(name.content)} has something else than 2 arguments, skipping`);
20
26
  return information;
21
27
  }
22
28
  const [lhs, rhs] = args.map(e => (0, unpack_argument_1.unpackNonameArg)(e));
23
29
  (0, assert_1.guard)(lhs !== undefined && rhs !== undefined, () => `lhs and rhs must be present, but ${JSON.stringify(lhs)} and ${JSON.stringify(rhs)} were found instead.`);
24
- if (rhs.type === type_1.RType.FunctionCall) {
30
+ // If this is an assigning pipe (e.g., %<>%), perform the assignment writeback using the built-in
31
+ // assignment processor: target <- source where target is the original lhs and source is the rhs call
32
+ if (assignLhs) {
33
+ // create unnamed args for target and source
34
+ const targetArg = (0, make_argument_1.toUnnamedArgument)(lhs, data.completeAst.idMap);
35
+ const sourceArg = (0, make_argument_1.toUnnamedArgument)(rhs, data.completeAst.idMap);
36
+ // construct a synthetic symbol for the assignment operator '<-'
37
+ const assignSym = {
38
+ type: type_1.RType.Symbol,
39
+ info: name.info,
40
+ content: identifier_1.Identifier.make('<-', 'base'),
41
+ lexeme: '<-',
42
+ location: name.location
43
+ };
44
+ information = (0, built_in_assignment_1.processAssignment)(assignSym, [targetArg, sourceArg], rootId, data, { canBeReplacement: true, mayHaveMoreArgs: true });
45
+ }
46
+ let treatedAsFunctionCall = false;
47
+ if (rhs.type === type_1.RType.Symbol && rhsMightBeSymbol) {
48
+ // convert a plain symbol on the RHS into a function-call vertex so we can treat it like `df %>% head`
49
+ const maybeVertex = information.graph.getVertex(rhs.info.id);
50
+ if (maybeVertex && maybeVertex.tag === vertex_1.VertexType.Use) {
51
+ information.graph.updateToFunctionCall({
52
+ tag: vertex_1.VertexType.FunctionCall,
53
+ id: rhs.info.id,
54
+ name: rhs.content,
55
+ args: [],
56
+ environment: data.environment,
57
+ onlyBuiltin: false,
58
+ cds: data.cds,
59
+ origin: [built_in_proc_name_1.BuiltInProcName.Function]
60
+ });
61
+ treatedAsFunctionCall = true;
62
+ }
63
+ }
64
+ if (treatedAsFunctionCall || rhs.type === type_1.RType.FunctionCall) {
25
65
  const functionCallNode = information.graph.getVertex(rhs.info.id);
26
66
  (0, assert_1.guard)(functionCallNode?.tag === vertex_1.VertexType.FunctionCall, () => `Expected function call node with id ${rhs.info.id} to be a function call node, but got ${functionCallNode?.tag} instead.`);
27
- // make the lhs an argument node:
67
+ // make the lhs an argument node (or link it to placeholders within the rhs call):
28
68
  const argId = lhs.info.id;
29
- logger_1.dataflowLogger.trace(`Linking pipe arg ${argId} as first argument of ${rhs.info.id}`);
30
- functionCallNode.args.unshift({
31
- name: undefined,
32
- nodeId: argId,
33
- cds: data.cds,
34
- type: identifier_1.ReferenceType.Function
69
+ // find all symbol occurrences inside the rhs function call AST that match the placeholder name
70
+ const occurrenceIds = [];
71
+ model_1.RNode.visitAst(rhs, (node) => {
72
+ if (node.type === type_1.RType.Symbol && node.content === pipePlaceholderName) {
73
+ occurrenceIds.push(node.info.id);
74
+ }
75
+ return false;
35
76
  });
36
- information.graph.addEdge(functionCallNode.id, argId, edge_1.EdgeType.Argument | edge_1.EdgeType.Reads);
77
+ if (occurrenceIds.length > 0) {
78
+ if (occurrenceIds.length !== 1) {
79
+ log_1.log.warn(`Expected exactly one occurrence of the pipe placeholder '${identifier_1.Identifier.toString(pipePlaceholderName)}' in the rhs of the pipe, but found ${occurrenceIds.length}. Linking all occurrences to the lhs.`);
80
+ }
81
+ for (const occId of occurrenceIds) {
82
+ information.graph.addEdge(occId, argId, edge_1.EdgeType.Reads);
83
+ }
84
+ }
85
+ else {
86
+ logger_1.dataflowLogger.trace(`Linking pipe arg ${argId} as first argument of ${rhs.info.id}`);
87
+ functionCallNode.args.unshift({
88
+ name: undefined,
89
+ nodeId: argId,
90
+ cds: data.cds,
91
+ type: identifier_1.ReferenceType.Function
92
+ });
93
+ information.graph.addEdge(functionCallNode.id, argId, edge_1.EdgeType.Argument | edge_1.EdgeType.Reads);
94
+ }
37
95
  }
38
96
  else {
39
97
  logger_1.dataflowLogger.warn(`Expected rhs of pipe to be a function call, but got ${rhs.type} instead.`);
40
98
  }
41
99
  const firstArgument = processedArguments[0];
100
+ // If requested, return the lhs value (tee/TPipe semantics): add a Returns edge to the lhs entry
101
+ if (firstArgument && returnLhs) {
102
+ information.graph.addEdge(rootId, firstArgument.entryPoint, edge_1.EdgeType.Returns);
103
+ }
104
+ else {
105
+ const secondArgument = processedArguments[1];
106
+ if (secondArgument && !returnLhs) {
107
+ information.graph.addEdge(rootId, secondArgument.entryPoint, edge_1.EdgeType.Returns);
108
+ }
109
+ }
42
110
  const uniqueIn = information.in.slice();
43
111
  for (const ing of (firstArgument?.in ?? [])) {
44
112
  if (!uniqueIn.some(e => e.nodeId === ing.nodeId)) {
@@ -0,0 +1,41 @@
1
+ import type { DataflowProcessorInformation } from '../../../../../processor';
2
+ import type { DataflowInformation } from '../../../../../info';
3
+ import type { PotentiallyEmptyRArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
4
+ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
5
+ import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
+ import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
+ interface BuiltInPurrrFormulaConfiguration {
8
+ /**
9
+ * Maps the created variable/variable to bind in the formula, to the argument that produces it.
10
+ * For example:
11
+ * ```ts
12
+ * {
13
+ * '.x': { index: 0, name: '.x' },
14
+ * '.y': { index: 1, name: '.y' }
15
+ * }
16
+ * ```
17
+ */
18
+ args: Record<string, {
19
+ index: number;
20
+ name: string;
21
+ }>;
22
+ /**
23
+ * This represents the special argument that represents the formula.
24
+ * We map all additional arguments that are not in `ignore` to this list.
25
+ */
26
+ '.f': {
27
+ index: number;
28
+ name: string;
29
+ };
30
+ /** arguments to additionally ignore when mapping the formulae */
31
+ ignore?: string[];
32
+ /**
33
+ * if given, this is a name that indexes into the 'args' map and indicates whatever this function is to return
34
+ */
35
+ returnArg?: string;
36
+ }
37
+ /**
38
+ * Support for R's purr formula: `map(df, ~ .x + 1)`.
39
+ */
40
+ export declare function processPurrrFormula<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly PotentiallyEmptyRArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: BuiltInPurrrFormulaConfiguration): DataflowInformation;
41
+ export {};
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processPurrrFormula = processPurrrFormula;
4
+ const known_call_handling_1 = require("../known-call-handling");
5
+ const linker_1 = require("../../../../linker");
6
+ const df_helper_1 = require("../../../../../graph/df-helper");
7
+ const vertex_1 = require("../../../../../graph/vertex");
8
+ const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
9
+ const identifier_1 = require("../../../../../environments/identifier");
10
+ const common_1 = require("../common");
11
+ const r_argument_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-argument");
12
+ const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name");
13
+ const model_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/model");
14
+ const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
15
+ const edge_1 = require("../../../../../graph/edge");
16
+ const unpack_argument_1 = require("../argument/unpack-argument");
17
+ const logger_1 = require("../../../../../logger");
18
+ const graph_1 = require("../../../../../graph/graph");
19
+ function linkOnSymbol(rootId, filteredArgs, node, graph, data) {
20
+ // If the formula is a symbol naming a function, try to resolve it in the call environment.
21
+ try {
22
+ const defs = (0, resolve_by_name_1.resolveByName)(node.content, data.environment, identifier_1.ReferenceType.Function) ?? [];
23
+ graph.addEdge(rootId, node.info.id, edge_1.EdgeType.Calls);
24
+ for (const def of defs) {
25
+ // Mark the call as calling this target
26
+ graph.addEdge(rootId, def.nodeId, edge_1.EdgeType.Calls);
27
+ // If the target directly maps to a function definition AST, try to link arguments to parameters
28
+ let linked = data.completeAst.idMap.get(def.nodeId);
29
+ if (linked?.type !== type_1.RType.FunctionDefinition) {
30
+ for (const vid of def.value) {
31
+ const candidate = data.completeAst.idMap.get(vid);
32
+ if (candidate && candidate.type === type_1.RType.FunctionDefinition) {
33
+ linked = candidate;
34
+ // also mark that we call the resolved function-definition node
35
+ graph.addEdge(rootId, vid, edge_1.EdgeType.Calls);
36
+ break;
37
+ }
38
+ }
39
+ }
40
+ // we may find a candidate in the first check
41
+ if (linked?.type === type_1.RType.FunctionDefinition) {
42
+ try {
43
+ return (0, linker_1.linkArgumentsOnCall)(filteredArgs, linked.parameters, graph);
44
+ }
45
+ catch (e) {
46
+ logger_1.dataflowLogger.warn('Failed to link arguments to parameters for purr formula (symbol target), some bindings may be missing', { error: e });
47
+ }
48
+ }
49
+ }
50
+ }
51
+ catch (e) {
52
+ logger_1.dataflowLogger.warn('Failed to resolve symbol for purrr formula .f', { error: e });
53
+ }
54
+ }
55
+ /**
56
+ * Support for R's purr formula: `map(df, ~ .x + 1)`.
57
+ */
58
+ function processPurrrFormula(name, args, rootId, data, config) {
59
+ const { information, callArgs } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.PurrrFormula });
60
+ const params = {};
61
+ for (const key of Object.keys(config.args)) {
62
+ params[config.args[key].name] = config.args[key].name;
63
+ }
64
+ // formula parameter
65
+ params[config['.f'].name] = config['.f'].name;
66
+ params['...'] = '...';
67
+ const argMaps = (0, linker_1.pMatch)((0, common_1.convertFnArguments)(args), params);
68
+ const formulaArgId = argMaps.get(config['.f'].name)?.[0];
69
+ if (!formulaArgId) {
70
+ // nothing to do if we couldn't find the formula argument
71
+ return information;
72
+ }
73
+ const formulaArg = r_argument_1.RArgument.getWithId(args, formulaArgId);
74
+ const formulaNode = formulaArg ? (0, unpack_argument_1.unpackNonameArg)(formulaArg) : undefined;
75
+ if (!formulaNode) {
76
+ return information;
77
+ }
78
+ let argToParamMap = new Map();
79
+ // Prepare the list of call arguments to consider for linking (exclude the formula argument and ignored args)
80
+ const filteredCallArgs = [];
81
+ for (const arg of callArgs) {
82
+ const aid = graph_1.FunctionArgument.getId(arg);
83
+ if (aid === formulaArgId || aid === formulaNode.info.id || config.ignore?.includes(graph_1.FunctionArgument.getName(arg) ?? '')) {
84
+ continue;
85
+ }
86
+ filteredCallArgs.push(arg);
87
+ }
88
+ if (formulaNode.type === type_1.RType.FunctionDefinition) {
89
+ const fdef = formulaNode;
90
+ information.graph.addEdge(rootId, fdef.info.id, edge_1.EdgeType.Calls);
91
+ try {
92
+ argToParamMap = (0, linker_1.linkArgumentsOnCall)(filteredCallArgs, fdef.parameters, information.graph);
93
+ }
94
+ catch (e) {
95
+ logger_1.dataflowLogger.warn('Failed to link arguments to parameters for purr formula, some bindings may be missing', { error: e });
96
+ }
97
+ }
98
+ else if (formulaNode.type === type_1.RType.Symbol) {
99
+ linkOnSymbol(rootId, filteredCallArgs, formulaNode, information.graph, data);
100
+ }
101
+ else {
102
+ try {
103
+ df_helper_1.Dataflow.visitDfg(information.graph, formulaNode.info.id, (vtx) => {
104
+ if (vtx.tag === vertex_1.VertexType.FunctionCall) {
105
+ information.graph.addEdge(rootId, vtx.id, edge_1.EdgeType.Calls);
106
+ const targets = (0, linker_1.getAllFunctionCallTargets)(vtx.id, information.graph, vtx.environment);
107
+ for (const t of targets) {
108
+ information.graph.addEdge(rootId, t, edge_1.EdgeType.Calls);
109
+ const linked = data.completeAst.idMap.get(t);
110
+ if (linked && linked.type === type_1.RType.FunctionDefinition) {
111
+ try {
112
+ const map = (0, linker_1.linkArgumentsOnCall)(filteredCallArgs, linked.parameters, information.graph);
113
+ for (const [argId, paramId] of map.entries()) {
114
+ if (!argToParamMap.has(argId)) {
115
+ argToParamMap.set(argId, paramId);
116
+ }
117
+ }
118
+ }
119
+ catch (e) {
120
+ logger_1.dataflowLogger.warn('Failed to link arguments to parameters for purrr formula (list target)', { error: e });
121
+ }
122
+ }
123
+ }
124
+ return !vtx.origin.includes(built_in_proc_name_1.BuiltInProcName.List);
125
+ }
126
+ else if (vtx.tag === vertex_1.VertexType.Use) {
127
+ const node = data.completeAst.idMap.get(vtx.id);
128
+ if (node?.type === type_1.RType.Symbol) {
129
+ linkOnSymbol(rootId, filteredCallArgs, node, information.graph, data);
130
+ }
131
+ }
132
+ return false;
133
+ });
134
+ }
135
+ catch (e) {
136
+ logger_1.dataflowLogger.warn('Failed to traverse sub-dfg for purrr formula .f', { error: e });
137
+ }
138
+ }
139
+ const ignore = new Set(config.ignore ?? []);
140
+ model_1.RNode.visitAst(formulaNode, (node) => {
141
+ if (node.type === type_1.RType.Symbol) {
142
+ const sym = node;
143
+ const name = sym.content;
144
+ if (ignore.has(name)) {
145
+ return false;
146
+ }
147
+ const mappingKey = config.args[name]?.name;
148
+ if (mappingKey) {
149
+ const pid = (argMaps.get(mappingKey) ?? [])[0];
150
+ if (!pid) {
151
+ return false;
152
+ }
153
+ const arg = r_argument_1.RArgument.getWithId(args, pid);
154
+ const producedNode = arg ? (0, unpack_argument_1.unpackNonameArg)(arg) : undefined;
155
+ if (!producedNode) {
156
+ return false;
157
+ }
158
+ // If this argument was linked to a parameter on the call, skip adding edge
159
+ const resultId = producedNode.info.id;
160
+ if (!argToParamMap.has(resultId)) {
161
+ information.graph.addEdge(node.info.id, resultId, edge_1.EdgeType.Reads);
162
+ }
163
+ }
164
+ }
165
+ return false;
166
+ });
167
+ if (config.returnArg) {
168
+ const returnArgId = argMaps.get(config.returnArg)?.[0];
169
+ if (returnArgId) {
170
+ const returnArg = r_argument_1.RArgument.getWithId(args, returnArgId);
171
+ const producedNode = returnArg ? (0, unpack_argument_1.unpackNonameArg)(returnArg) : undefined;
172
+ if (producedNode) {
173
+ information.graph.addEdge(rootId, producedNode.info.id, edge_1.EdgeType.Returns);
174
+ }
175
+ }
176
+ }
177
+ return information;
178
+ }
179
+ //# sourceMappingURL=built-in-purrr-formula.js.map
@@ -1,13 +1,16 @@
1
1
  import { type DataflowProcessorInformation } from '../../../../../processor';
2
2
  import type { DataflowInformation } from '../../../../../info';
3
3
  import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
4
- import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
4
+ import type { PotentiallyEmptyRArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
6
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
7
  import type { ForceArguments } from '../common';
8
+ interface QuoteConfig extends ForceArguments {
9
+ quoteArgumentsWithIndex: number;
10
+ envArgIndex?: number;
11
+ }
8
12
  /**
9
13
  * Process a call to `quote` or similar nse/substitution functions.
10
14
  */
11
- export declare function processQuote<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
12
- quoteArgumentsWithIndex?: number;
13
- } & ForceArguments): DataflowInformation;
15
+ export declare function processQuote<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly PotentiallyEmptyRArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: QuoteConfig): DataflowInformation;
16
+ export {};
@@ -2,14 +2,31 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.processQuote = processQuote;
4
4
  const known_call_handling_1 = require("../known-call-handling");
5
+ const node_id_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id");
6
+ const vertex_1 = require("../../../../../graph/vertex");
7
+ const logger_1 = require("../../../../../logger");
8
+ const df_helper_1 = require("../../../../../graph/df-helper");
5
9
  const edge_1 = require("../../../../../graph/edge");
6
10
  const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name");
11
+ const graph_1 = require("../../../../../graph/graph");
7
12
  /**
8
13
  * Process a call to `quote` or similar nse/substitution functions.
9
14
  */
10
15
  function processQuote(name, args, rootId, data, config) {
11
16
  const startEnv = data.environment;
12
- const { information, processedArguments, fnRef } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs, origin: built_in_proc_name_1.BuiltInProcName.Quote });
17
+ let lastEnv = startEnv;
18
+ const { information, processedArguments, fnRef } = (0, known_call_handling_1.processKnownFunctionCall)({
19
+ name, args, rootId, data, forceArgs: config.forceArgs, origin: built_in_proc_name_1.BuiltInProcName.Quote,
20
+ patchData(data, index) {
21
+ if (index === config.quoteArgumentsWithIndex) {
22
+ lastEnv = data.environment;
23
+ return { ...data, environment: data.ctx.env.makeCleanEnv() };
24
+ }
25
+ else {
26
+ return { ...data, environment: lastEnv };
27
+ }
28
+ }
29
+ });
13
30
  let inRefs = [fnRef];
14
31
  let outRefs = [];
15
32
  let unknownRefs = [];
@@ -28,6 +45,50 @@ function processQuote(name, args, rootId, data, config) {
28
45
  }
29
46
  }
30
47
  }
48
+ if (config?.envArgIndex !== undefined) {
49
+ const envProcessed = processedArguments[config.envArgIndex];
50
+ const exprProcessed = processedArguments[config.quoteArgumentsWithIndex];
51
+ if (envProcessed && exprProcessed) {
52
+ try {
53
+ // traverse the env processed DFG to find list calls (prefix-aware handling)
54
+ df_helper_1.Dataflow.visitDfg(envProcessed.graph, envProcessed.entryPoint, (vtx) => {
55
+ if (vtx.tag !== vertex_1.VertexType.FunctionCall) {
56
+ return;
57
+ }
58
+ if (!vtx.origin.includes(built_in_proc_name_1.BuiltInProcName.List)) {
59
+ return true;
60
+ }
61
+ const useMap = new Map();
62
+ for (const vType of [vertex_1.VertexType.Use, vertex_1.VertexType.FunctionCall]) {
63
+ for (const [useId] of exprProcessed.graph.verticesOfType(vType)) {
64
+ const rn = (0, node_id_1.recoverName)(useId, exprProcessed.graph.idMap);
65
+ if (rn) {
66
+ const arr = useMap.get(rn) ?? [];
67
+ arr.push(useId);
68
+ useMap.set(rn, arr);
69
+ }
70
+ }
71
+ }
72
+ if (useMap.size === 0) {
73
+ return true;
74
+ }
75
+ const listFields = vtx.args.filter(graph_1.FunctionArgument.isNamed);
76
+ for (const field of listFields) {
77
+ const uses = useMap.get(field.name);
78
+ if (uses) {
79
+ for (const useId of uses) {
80
+ information.graph.addEdge(useId, field.nodeId, edge_1.EdgeType.Reads);
81
+ }
82
+ }
83
+ }
84
+ return true;
85
+ });
86
+ }
87
+ catch (e) {
88
+ logger_1.dataflowLogger.warn('Failed to apply substitute-style env list replacement in quote-like call', { error: e });
89
+ }
90
+ }
91
+ }
31
92
  return {
32
93
  ...information,
33
94
  environment: startEnv,
@@ -1,11 +1,16 @@
1
1
  import type { DataflowProcessorInformation } from '../../../../../processor';
2
2
  import type { DataflowInformation } from '../../../../../info';
3
3
  import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
4
- import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
4
+ import { type PotentiallyEmptyRArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
5
5
  import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
6
6
  import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
7
+ interface RecallConfig {
8
+ /** whether to not handle as a recall function but rather as an unknown side effect if the argument is non-constant non-zero */
9
+ unknownOnNonZeroArg?: boolean;
10
+ }
7
11
  /**
8
12
  * Processes a built-in 'Recall' function call, linking
9
13
  * the recall to the enveloping function closure.
10
14
  */
11
- export declare function processRecall<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
15
+ export declare function processRecall<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly PotentiallyEmptyRArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: RecallConfig): DataflowInformation;
16
+ export {};
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.processRecall = processRecall;
4
4
  const known_call_handling_1 = require("../known-call-handling");
5
+ const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
6
+ const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
7
+ const unknown_side_effect_1 = require("../../../../../graph/unknown-side-effect");
5
8
  const log_1 = require("../../../../../../util/log");
6
9
  const edge_1 = require("../../../../../graph/edge");
7
10
  const vertex_1 = require("../../../../../graph/vertex");
@@ -12,7 +15,7 @@ const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-
12
15
  * Processes a built-in 'Recall' function call, linking
13
16
  * the recall to the enveloping function closure.
14
17
  */
15
- function processRecall(name, args, rootId, data) {
18
+ function processRecall(name, args, rootId, data, config) {
16
19
  const { information } = (0, known_call_handling_1.processKnownFunctionCall)({
17
20
  name,
18
21
  args,
@@ -20,6 +23,17 @@ function processRecall(name, args, rootId, data) {
20
23
  data,
21
24
  origin: built_in_proc_name_1.BuiltInProcName.Recall
22
25
  });
26
+ // If requested, treat Recall as an unknown side effect when a single argument is provided and it is not the numeric literal 0.
27
+ if (config?.unknownOnNonZeroArg) {
28
+ if (args.length === 1 && args[0] !== r_function_call_1.EmptyArgument && args[0].value) {
29
+ const v = args[0].value;
30
+ // only allow the normal recall handling if the single arg is the literal 0
31
+ if (!(v.type === type_1.RType.Number && v.content.num === 0)) {
32
+ (0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId);
33
+ return information;
34
+ }
35
+ }
36
+ }
23
37
  let cur = data.environment.current;
24
38
  let closure;
25
39
  while (cur) {