@eagleoutice/flowr 2.1.9 → 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.
- package/README.md +3 -0
- package/config.d.ts +21 -0
- package/config.js +19 -2
- package/dataflow/environments/built-in.d.ts +2 -0
- package/dataflow/environments/built-in.js +2 -0
- package/dataflow/environments/default-builtin-config.js +3 -2
- package/dataflow/environments/define.js +78 -0
- package/dataflow/environments/identifier.d.ts +11 -3
- package/dataflow/environments/resolve-by-name.d.ts +10 -5
- package/dataflow/environments/resolve-by-name.js +103 -5
- package/dataflow/graph/vertex.d.ts +56 -1
- package/dataflow/graph/vertex.js +4 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-access.d.ts +11 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-access.js +141 -49
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +6 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +35 -8
- package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +3 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-list.d.ts +15 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-list.js +50 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-replacement.d.ts +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +29 -1
- package/dataflow/internal/process/functions/call/common.js +15 -1
- package/dataflow/internal/process/functions/call/known-call-handling.d.ts +2 -1
- package/dataflow/internal/process/functions/call/known-call-handling.js +3 -2
- package/documentation/print-interface-wiki.js +6 -1
- package/package.json +1 -1
- package/util/list-access.d.ts +48 -0
- package/util/list-access.js +115 -0
- package/util/version.js +1 -1
|
@@ -11,10 +11,23 @@ const environment_1 = require("../../../../../environments/environment");
|
|
|
11
11
|
const built_in_1 = require("../../../../../environments/built-in");
|
|
12
12
|
const built_in_assignment_1 = require("./built-in-assignment");
|
|
13
13
|
const identifier_1 = require("../../../../../environments/identifier");
|
|
14
|
+
const vertex_1 = require("../../../../../graph/vertex");
|
|
15
|
+
const list_access_1 = require("../../../../../../util/list-access");
|
|
14
16
|
function tableAssignmentProcessor(name, args, rootId, data, outInfo) {
|
|
15
17
|
outInfo.definitionRootNodes.push(rootId);
|
|
16
18
|
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
|
|
17
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Processes different types of access operations.
|
|
22
|
+
*
|
|
23
|
+
* Example:
|
|
24
|
+
* ```r
|
|
25
|
+
* a[i]
|
|
26
|
+
* a$foo
|
|
27
|
+
* a[[i]]
|
|
28
|
+
* a@foo
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
18
31
|
function processAccess(name, args, rootId, data, config) {
|
|
19
32
|
if (args.length < 2) {
|
|
20
33
|
logger_1.dataflowLogger.warn(`Access ${name.content} has less than 2 arguments, skipping`);
|
|
@@ -26,47 +39,10 @@ function processAccess(name, args, rootId, data, config) {
|
|
|
26
39
|
if (!config.treatIndicesAsString) {
|
|
27
40
|
/* within an access operation which treats its fields, we redefine the table assignment ':=' as a trigger if this is to be treated as a definition */
|
|
28
41
|
// do we have a local definition that needs to be recovered?
|
|
29
|
-
|
|
30
|
-
const outInfo = { definitionRootNodes: [] };
|
|
31
|
-
data.environment.current.memory.set(':=', [{
|
|
32
|
-
type: identifier_1.ReferenceType.BuiltInFunction,
|
|
33
|
-
definedAt: built_in_1.BuiltIn,
|
|
34
|
-
controlDependencies: undefined,
|
|
35
|
-
processor: (name, args, rootId, data) => tableAssignmentProcessor(name, args, rootId, data, outInfo),
|
|
36
|
-
name: ':=',
|
|
37
|
-
nodeId: built_in_1.BuiltIn
|
|
38
|
-
}]);
|
|
39
|
-
fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs });
|
|
40
|
-
/* recover the environment */
|
|
41
|
-
if (existing !== undefined) {
|
|
42
|
-
data.environment.current.memory.set(':=', existing);
|
|
43
|
-
}
|
|
44
|
-
if (head.value && outInfo.definitionRootNodes.length > 0) {
|
|
45
|
-
(0, built_in_assignment_1.markAsAssignment)(fnCall.information, { type: identifier_1.ReferenceType.Variable, name: head.value.lexeme ?? '', nodeId: head.value.info.id, definedAt: rootId, controlDependencies: [] }, outInfo.definitionRootNodes, rootId);
|
|
46
|
-
}
|
|
42
|
+
fnCall = processNumberBasedAccess(data, name, args, rootId, config, head);
|
|
47
43
|
}
|
|
48
44
|
else {
|
|
49
|
-
|
|
50
|
-
// if the argument is a symbol, we convert it to a string for this perspective
|
|
51
|
-
for (let i = 1; i < newArgs.length; i++) {
|
|
52
|
-
const arg = newArgs[i];
|
|
53
|
-
if (arg !== r_function_call_1.EmptyArgument && arg.value?.type === type_1.RType.Symbol) {
|
|
54
|
-
newArgs[i] = {
|
|
55
|
-
...arg,
|
|
56
|
-
value: {
|
|
57
|
-
type: type_1.RType.String,
|
|
58
|
-
info: arg.value.info,
|
|
59
|
-
lexeme: arg.value.lexeme,
|
|
60
|
-
location: arg.value.location,
|
|
61
|
-
content: {
|
|
62
|
-
quotes: 'none',
|
|
63
|
-
str: arg.value.lexeme
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args: newArgs, rootId, data, forceArgs: config.forceArgs });
|
|
45
|
+
fnCall = processStringBasedAccess(args, data, name, rootId, config);
|
|
70
46
|
}
|
|
71
47
|
const info = fnCall.information;
|
|
72
48
|
info.graph.addEdge(name.info.id, fnCall.processedArguments[0]?.entryPoint ?? head.info.id, edge_1.EdgeType.Returns);
|
|
@@ -80,16 +56,16 @@ function processAccess(name, args, rootId, data, config) {
|
|
|
80
56
|
return {
|
|
81
57
|
...info,
|
|
82
58
|
/*
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
59
|
+
* Keep active nodes in case of assignments etc.
|
|
60
|
+
* We make them maybe as a kind of hack.
|
|
61
|
+
* This way when using
|
|
62
|
+
* ```ts
|
|
63
|
+
* a[[1]] <- 3
|
|
64
|
+
* a[[2]] <- 4
|
|
65
|
+
* a
|
|
66
|
+
* ```
|
|
67
|
+
* the read for a will use both accesses as potential definitions and not just the last one!
|
|
68
|
+
*/
|
|
93
69
|
unknownReferences: (0, environment_1.makeAllMaybe)(info.unknownReferences, info.graph, info.environment, false),
|
|
94
70
|
entryPoint: rootId,
|
|
95
71
|
/** it is, to be precise, the accessed element we want to map to maybe */
|
|
@@ -103,4 +79,120 @@ function processAccess(name, args, rootId, data, config) {
|
|
|
103
79
|
})
|
|
104
80
|
};
|
|
105
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Processes different types of number-based access operations.
|
|
84
|
+
*
|
|
85
|
+
* Example:
|
|
86
|
+
* ```r
|
|
87
|
+
* a[i]
|
|
88
|
+
* a[[i]]
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
function processNumberBasedAccess(data, name, args, rootId, config, head) {
|
|
92
|
+
const existing = data.environment.current.memory.get(':=');
|
|
93
|
+
const outInfo = { definitionRootNodes: [] };
|
|
94
|
+
data.environment.current.memory.set(':=', [{
|
|
95
|
+
type: identifier_1.ReferenceType.BuiltInFunction,
|
|
96
|
+
definedAt: built_in_1.BuiltIn,
|
|
97
|
+
controlDependencies: undefined,
|
|
98
|
+
processor: (name, args, rootId, data) => tableAssignmentProcessor(name, args, rootId, data, outInfo),
|
|
99
|
+
name: ':=',
|
|
100
|
+
nodeId: built_in_1.BuiltIn,
|
|
101
|
+
}]);
|
|
102
|
+
const fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs });
|
|
103
|
+
/* recover the environment */
|
|
104
|
+
if (existing !== undefined) {
|
|
105
|
+
data.environment.current.memory.set(':=', existing);
|
|
106
|
+
}
|
|
107
|
+
if (head.value && outInfo.definitionRootNodes.length > 0) {
|
|
108
|
+
(0, built_in_assignment_1.markAsAssignment)(fnCall.information, { type: identifier_1.ReferenceType.Variable, name: head.value.lexeme ?? '', nodeId: head.value.info.id, definedAt: rootId, controlDependencies: [] }, outInfo.definitionRootNodes, rootId);
|
|
109
|
+
}
|
|
110
|
+
return fnCall;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Processes different types of string-based access operations.
|
|
114
|
+
*
|
|
115
|
+
* Example:
|
|
116
|
+
* ```r
|
|
117
|
+
* a$foo
|
|
118
|
+
* a@foo
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
function processStringBasedAccess(args, data, name, rootId, config) {
|
|
122
|
+
const newArgs = [...args];
|
|
123
|
+
// if the argument is a symbol, we convert it to a string for this perspective
|
|
124
|
+
for (let i = 1; i < newArgs.length; i++) {
|
|
125
|
+
const arg = newArgs[i];
|
|
126
|
+
if (arg !== r_function_call_1.EmptyArgument && arg.value?.type === type_1.RType.Symbol) {
|
|
127
|
+
newArgs[i] = {
|
|
128
|
+
...arg,
|
|
129
|
+
value: {
|
|
130
|
+
type: type_1.RType.String,
|
|
131
|
+
info: arg.value.info,
|
|
132
|
+
lexeme: arg.value.lexeme,
|
|
133
|
+
location: arg.value.location,
|
|
134
|
+
content: {
|
|
135
|
+
quotes: 'none',
|
|
136
|
+
str: arg.value.lexeme
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args: newArgs, rootId, data, forceArgs: config.forceArgs });
|
|
143
|
+
// Resolve access on the way up the fold
|
|
144
|
+
const nonEmptyArgs = newArgs.filter(arg => arg !== r_function_call_1.EmptyArgument);
|
|
145
|
+
const accessedArg = nonEmptyArgs.find(arg => arg.info.role === "accessed" /* RoleInParent.Accessed */);
|
|
146
|
+
const accessArg = nonEmptyArgs.find(arg => arg.info.role === "index-access" /* RoleInParent.IndexAccess */);
|
|
147
|
+
if (accessedArg === undefined || accessArg === undefined) {
|
|
148
|
+
return fnCall;
|
|
149
|
+
}
|
|
150
|
+
let accessedIndicesCollection;
|
|
151
|
+
// If the accessedArg is a symbol, it's either a simple access or the base case of a nested access
|
|
152
|
+
if (accessedArg.value?.type === type_1.RType.Symbol) {
|
|
153
|
+
accessedIndicesCollection = (0, list_access_1.resolveSingleIndex)(accessedArg, accessArg, data.environment);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
// Higher access call
|
|
157
|
+
const underlyingAccessId = accessedArg.value?.info.id ?? -1;
|
|
158
|
+
const vertex = fnCall.information.graph.getVertex(underlyingAccessId);
|
|
159
|
+
const subIndices = vertex?.indicesCollection
|
|
160
|
+
?.flatMap(indices => indices.indices)
|
|
161
|
+
?.flatMap(index => index?.subIndices ?? []);
|
|
162
|
+
if (subIndices) {
|
|
163
|
+
accessedIndicesCollection = (0, list_access_1.filterIndices)(subIndices, accessArg);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Add indices to vertex afterward
|
|
167
|
+
if (accessedIndicesCollection) {
|
|
168
|
+
const vertex = fnCall.information.graph.getVertex(rootId);
|
|
169
|
+
if (vertex) {
|
|
170
|
+
vertex.indicesCollection = accessedIndicesCollection;
|
|
171
|
+
}
|
|
172
|
+
// When access has no access as parent, it's the top most
|
|
173
|
+
const rootNode = data.completeAst.idMap.get(rootId);
|
|
174
|
+
const parentNode = data.completeAst.idMap.get(rootNode?.info.parent ?? -1);
|
|
175
|
+
if (parentNode?.type !== type_1.RType.Access) {
|
|
176
|
+
// Only reference indices in top most access
|
|
177
|
+
referenceIndices(accessedIndicesCollection, fnCall, name.info.id);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return fnCall;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Creates edges of type {@link EdgeType.Reads} to the accessed Indices and their sub-indices starting from
|
|
184
|
+
* the node with {@link parentNodeId}.
|
|
185
|
+
*
|
|
186
|
+
* @param accessedIndicesCollection - All indices that were accessed by the access operation
|
|
187
|
+
* @param fnCall - The {@link ProcessKnownFunctionCallResult} of the access operation
|
|
188
|
+
* @param parentNodeId - {@link NodeId} of the parent from which the edge starts
|
|
189
|
+
*/
|
|
190
|
+
function referenceIndices(accessedIndicesCollection, fnCall, parentNodeId) {
|
|
191
|
+
const accessedIndices = accessedIndicesCollection?.flatMap(indices => indices.indices);
|
|
192
|
+
for (const accessedIndex of accessedIndices ?? []) {
|
|
193
|
+
fnCall.information.graph.addEdge(parentNodeId, accessedIndex.nodeId, edge_1.EdgeType.Reads);
|
|
194
|
+
const accessedSubIndices = (0, vertex_1.isParentContainerIndex)(accessedIndex) ? accessedIndex.subIndices : undefined;
|
|
195
|
+
referenceIndices(accessedSubIndices, fnCall, accessedIndex.nodeId);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
106
198
|
//# sourceMappingURL=built-in-access.js.map
|
|
@@ -4,19 +4,22 @@ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/
|
|
|
4
4
|
import type { RNode } from '../../../../../../r-bridge/lang-4.x/ast/model/model';
|
|
5
5
|
import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
|
|
6
6
|
import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
|
|
7
|
-
import type
|
|
8
|
-
import type {
|
|
7
|
+
import { type NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
8
|
+
import type { InGraphIdentifierDefinition } from '../../../../../environments/identifier';
|
|
9
|
+
import type { ContainerIndicesCollection } from '../../../../../graph/vertex';
|
|
9
10
|
import type { ForceArguments } from '../common';
|
|
10
11
|
import type { REnvironmentInformation } from '../../../../../environments/environment';
|
|
11
12
|
import type { DataflowGraph } from '../../../../../graph/graph';
|
|
12
13
|
export interface AssignmentConfiguration extends ForceArguments {
|
|
13
14
|
readonly superAssignment?: boolean;
|
|
14
15
|
readonly swapSourceAndTarget?: boolean;
|
|
16
|
+
/** Make maybe if assigned to symbol */
|
|
15
17
|
readonly makeMaybe?: boolean;
|
|
16
18
|
readonly quoteSource?: boolean;
|
|
17
19
|
readonly canBeReplacement?: boolean;
|
|
18
20
|
/** is the target a variable pointing at the actual name? */
|
|
19
21
|
readonly targetVariable?: boolean;
|
|
22
|
+
readonly indicesCollection?: ContainerIndicesCollection;
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
22
25
|
* Processes an assignment, i.e., `<target> <- <source>`.
|
|
@@ -45,4 +48,4 @@ export interface AssignmentToSymbolParameters<OtherInfo> extends AssignmentConfi
|
|
|
45
48
|
export declare function markAsAssignment(information: {
|
|
46
49
|
environment: REnvironmentInformation;
|
|
47
50
|
graph: DataflowGraph;
|
|
48
|
-
}, nodeToDefine:
|
|
51
|
+
}, nodeToDefine: InGraphIdentifierDefinition, sourceIds: readonly NodeId[], rootIdOfAssignment: NodeId, config?: AssignmentConfiguration | undefined): void;
|
|
@@ -16,6 +16,8 @@ const retriever_1 = require("../../../../../../r-bridge/retriever");
|
|
|
16
16
|
const vertex_1 = require("../../../../../graph/vertex");
|
|
17
17
|
const define_1 = require("../../../../../environments/define");
|
|
18
18
|
const edge_1 = require("../../../../../graph/edge");
|
|
19
|
+
const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
|
|
20
|
+
const list_access_1 = require("../../../../../../util/list-access");
|
|
19
21
|
function toReplacementSymbol(target, prefix, superAssignment) {
|
|
20
22
|
return {
|
|
21
23
|
type: type_1.RType.Symbol,
|
|
@@ -119,12 +121,16 @@ function extractSourceAndTarget(args, name) {
|
|
|
119
121
|
(0, assert_1.guard)(target !== undefined, () => `Assignment ${name.content} has no target, impossible!`);
|
|
120
122
|
return { source, target };
|
|
121
123
|
}
|
|
122
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Promotes the ingoing/unknown references of target (an assignment) to definitions
|
|
126
|
+
*/
|
|
127
|
+
function produceWrittenNodes(rootId, target, referenceType, data, makeMaybe, value) {
|
|
123
128
|
return [...target.in, ...target.unknownReferences].map(ref => ({
|
|
124
129
|
...ref,
|
|
125
130
|
type: referenceType,
|
|
126
131
|
definedAt: rootId,
|
|
127
|
-
controlDependencies: data.controlDependencies ?? (makeMaybe ? [] : undefined)
|
|
132
|
+
controlDependencies: data.controlDependencies ?? (makeMaybe ? [] : undefined),
|
|
133
|
+
value: value
|
|
128
134
|
}));
|
|
129
135
|
}
|
|
130
136
|
function processAssignmentToString(target, args, name, rootId, data, config, source) {
|
|
@@ -181,10 +187,29 @@ function checkTargetReferenceType(source, sourceInfo) {
|
|
|
181
187
|
* @param quoteSource - whether to quote the source (i.e., define `x` without a direct reference to `v`)
|
|
182
188
|
* @param superAssignment - whether this is a super assignment (i.e., `<<-`)
|
|
183
189
|
*/
|
|
184
|
-
function markAsAssignment(information, nodeToDefine, sourceIds, rootIdOfAssignment,
|
|
185
|
-
|
|
190
|
+
function markAsAssignment(information, nodeToDefine, sourceIds, rootIdOfAssignment, config) {
|
|
191
|
+
let indicesCollection = undefined;
|
|
192
|
+
if (sourceIds.length === 1) {
|
|
193
|
+
// support for tracking indices
|
|
194
|
+
// Indices were defined for the vertex e.g. a <- list(c = 1) or a$b <- list(c = 1)
|
|
195
|
+
indicesCollection = information.graph.getVertex(sourceIds[0])?.indicesCollection;
|
|
196
|
+
}
|
|
197
|
+
// Indices defined by replacement operation e.g. $<-
|
|
198
|
+
if (config?.indicesCollection !== undefined) {
|
|
199
|
+
// If there were indices stored in the vertex, then a container was defined
|
|
200
|
+
// and assigned to the index of another container e.g. a$b <- list(c = 1)
|
|
201
|
+
if (indicesCollection) {
|
|
202
|
+
indicesCollection = (0, list_access_1.addSubIndicesToLeafIndices)(config.indicesCollection, indicesCollection);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// No indices were defined for the vertex e.g. a$b <- 2
|
|
206
|
+
indicesCollection = config.indicesCollection;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
nodeToDefine.indicesCollection ??= indicesCollection;
|
|
210
|
+
information.environment = (0, define_1.define)(nodeToDefine, config?.superAssignment, information.environment);
|
|
186
211
|
information.graph.setDefinitionOfVertex(nodeToDefine);
|
|
187
|
-
if (!quoteSource) {
|
|
212
|
+
if (!config?.quoteSource) {
|
|
188
213
|
for (const sourceId of sourceIds) {
|
|
189
214
|
information.graph.addEdge(nodeToDefine, sourceId, edge_1.EdgeType.DefinedBy);
|
|
190
215
|
}
|
|
@@ -202,9 +227,11 @@ function markAsAssignment(information, nodeToDefine, sourceIds, rootIdOfAssignme
|
|
|
202
227
|
/**
|
|
203
228
|
* Helper function whenever it is known that the _target_ of an assignment is a (single) symbol (i.e. `x <- ...`, but not `names(x) <- ...`).
|
|
204
229
|
*/
|
|
205
|
-
function processAssignmentToSymbol(
|
|
230
|
+
function processAssignmentToSymbol(config) {
|
|
231
|
+
const { nameOfAssignmentFunction, source, args: [targetArg, sourceArg], target, rootId, data, information, makeMaybe, quoteSource } = config;
|
|
206
232
|
const referenceType = checkTargetReferenceType(source, sourceArg);
|
|
207
|
-
const
|
|
233
|
+
const aliases = (0, resolve_by_name_1.getAliases)([source.info.id], information.graph, information.environment);
|
|
234
|
+
const writeNodes = produceWrittenNodes(rootId, targetArg, referenceType, data, makeMaybe ?? false, aliases);
|
|
208
235
|
if (writeNodes.length !== 1 && log_1.log.settings.minLevel <= 4 /* LogLevel.Warn */) {
|
|
209
236
|
log_1.log.warn(`Unexpected write number in assignment: ${JSON.stringify(writeNodes)}`);
|
|
210
237
|
}
|
|
@@ -217,7 +244,7 @@ function processAssignmentToSymbol({ nameOfAssignmentFunction, source, args: [ta
|
|
|
217
244
|
information.environment = (0, overwrite_1.overwriteEnvironment)(targetArg.environment, sourceArg.environment);
|
|
218
245
|
// install assigned variables in environment
|
|
219
246
|
for (const write of writeNodes) {
|
|
220
|
-
markAsAssignment(information, write, [source.info.id], rootId,
|
|
247
|
+
markAsAssignment(information, write, [source.info.id], rootId, config);
|
|
221
248
|
}
|
|
222
249
|
information.graph.addEdge(rootId, targetArg.entryPoint, edge_1.EdgeType.Returns);
|
|
223
250
|
if (quoteSource) {
|
|
@@ -33,9 +33,9 @@ function processIfThenElse(name, args, rootId, data) {
|
|
|
33
33
|
let then;
|
|
34
34
|
let makeThenMaybe = false;
|
|
35
35
|
// we should defer this to the abstract interpretation
|
|
36
|
-
const
|
|
37
|
-
const conditionIsAlwaysFalse =
|
|
38
|
-
const conditionIsAlwaysTrue =
|
|
36
|
+
const values = (0, resolve_by_name_1.resolveValueOfVariable)(condArg?.lexeme, data.environment, cond.graph);
|
|
37
|
+
const conditionIsAlwaysFalse = values?.every(d => d === false) ?? false;
|
|
38
|
+
const conditionIsAlwaysTrue = values?.every(d => d === true) ?? false;
|
|
39
39
|
if (!conditionIsAlwaysFalse) {
|
|
40
40
|
then = (0, processor_1.processDataflowFor)(thenArg, data);
|
|
41
41
|
if (then.entryPoint) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
|
|
2
|
+
import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
|
|
3
|
+
import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
4
|
+
import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
5
|
+
import type { DataflowInformation } from '../../../../../info';
|
|
6
|
+
import type { DataflowProcessorInformation } from '../../../../../processor';
|
|
7
|
+
/**
|
|
8
|
+
* Process a list call.
|
|
9
|
+
*
|
|
10
|
+
* Example:
|
|
11
|
+
* ```r
|
|
12
|
+
* list(a = 1, b = 2)
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function processList<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>): DataflowInformation;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processList = processList;
|
|
4
|
+
const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
5
|
+
const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
|
|
6
|
+
const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
|
|
7
|
+
const known_call_handling_1 = require("../known-call-handling");
|
|
8
|
+
const config_1 = require("../../../../../../config");
|
|
9
|
+
/**
|
|
10
|
+
* Process a list call.
|
|
11
|
+
*
|
|
12
|
+
* Example:
|
|
13
|
+
* ```r
|
|
14
|
+
* list(a = 1, b = 2)
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
function processList(name, args, rootId, data) {
|
|
18
|
+
if (!(0, config_1.getConfig)().solver.pointerTracking) {
|
|
19
|
+
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
|
|
20
|
+
}
|
|
21
|
+
const namedArguments = [];
|
|
22
|
+
for (const arg of args) {
|
|
23
|
+
// Skip non named arguments
|
|
24
|
+
if (arg === r_function_call_1.EmptyArgument || arg.type !== type_1.RType.Argument || arg.name === undefined) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
let newIndex = {
|
|
28
|
+
lexeme: arg.name.content,
|
|
29
|
+
nodeId: arg.info.id,
|
|
30
|
+
};
|
|
31
|
+
// Check whether argument value is non-primitive
|
|
32
|
+
if (arg.value?.type === type_1.RType.Symbol) {
|
|
33
|
+
const defs = (0, resolve_by_name_1.resolveByName)(arg.value.lexeme, data.environment);
|
|
34
|
+
const indices = defs?.flatMap(index => index.indicesCollection ?? []);
|
|
35
|
+
if (indices) {
|
|
36
|
+
newIndex = {
|
|
37
|
+
...newIndex,
|
|
38
|
+
subIndices: indices,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
namedArguments.push(newIndex);
|
|
43
|
+
}
|
|
44
|
+
const indices = {
|
|
45
|
+
indices: namedArguments,
|
|
46
|
+
isContainer: true,
|
|
47
|
+
};
|
|
48
|
+
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }, [indices]).information;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=built-in-list.js.map
|
|
@@ -3,7 +3,7 @@ import type { DataflowInformation } from '../../../../../info';
|
|
|
3
3
|
import type { ForceArguments } from '../common';
|
|
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
|
-
import type
|
|
6
|
+
import { type RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
|
|
7
7
|
import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
8
8
|
export declare function processReplacementFunction<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>,
|
|
9
9
|
/** The last one has to be the value */
|
|
@@ -7,11 +7,14 @@ const log_1 = require("../../../../../../util/log");
|
|
|
7
7
|
const built_in_assignment_1 = require("./built-in-assignment");
|
|
8
8
|
const common_1 = require("../common");
|
|
9
9
|
const assert_1 = require("../../../../../../util/assert");
|
|
10
|
+
const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
10
11
|
const logger_1 = require("../../../../../logger");
|
|
11
12
|
const vertex_1 = require("../../../../../graph/vertex");
|
|
12
13
|
const graph_1 = require("../../../../../graph/graph");
|
|
13
14
|
const edge_1 = require("../../../../../graph/edge");
|
|
14
15
|
const dfg_1 = require("../../../../../../util/mermaid/dfg");
|
|
16
|
+
const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
|
|
17
|
+
const list_access_1 = require("../../../../../../util/list-access");
|
|
15
18
|
function processReplacementFunction(name,
|
|
16
19
|
/** The last one has to be the value */
|
|
17
20
|
args, rootId, data, config) {
|
|
@@ -21,8 +24,33 @@ args, rootId, data, config) {
|
|
|
21
24
|
}
|
|
22
25
|
/* we only get here if <-, <<-, ... or whatever is part of the replacement is not overwritten */
|
|
23
26
|
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Replacement ${name.content} with ${JSON.stringify(args)}, processing`);
|
|
27
|
+
let indices = undefined;
|
|
28
|
+
if (name.content === '$<-') {
|
|
29
|
+
const nonEmptyArgs = args.filter(arg => arg !== r_function_call_1.EmptyArgument);
|
|
30
|
+
const accessedArg = nonEmptyArgs.find(arg => arg.info.role === "accessed" /* RoleInParent.Accessed */);
|
|
31
|
+
const accessArg = nonEmptyArgs.find(arg => arg.info.role === "index-access" /* RoleInParent.IndexAccess */);
|
|
32
|
+
if (accessArg !== undefined && accessedArg != undefined) {
|
|
33
|
+
const leafIndex = { lexeme: accessArg.lexeme, nodeId: accessedArg.info.parent ?? '' };
|
|
34
|
+
const accessIndices = {
|
|
35
|
+
indices: [leafIndex],
|
|
36
|
+
isContainer: false
|
|
37
|
+
};
|
|
38
|
+
// Check for nested access
|
|
39
|
+
if (accessedArg.value?.type === type_1.RType.Access) {
|
|
40
|
+
indices = (0, list_access_1.constructNestedAccess)(accessedArg.value, accessIndices);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// use access node as reference to get complete line in slice
|
|
44
|
+
indices = [accessIndices];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
24
48
|
/* we assign the first argument by the last for now and maybe mark as maybe!, we can keep the symbol as we now know we have an assignment */
|
|
25
|
-
const res = (0, built_in_assignment_1.processAssignment)(name, [args[0], args[args.length - 1]], rootId, data, {
|
|
49
|
+
const res = (0, built_in_assignment_1.processAssignment)(name, [args[0], args[args.length - 1]], rootId, data, {
|
|
50
|
+
superAssignment: config.assignmentOperator === '<<-',
|
|
51
|
+
makeMaybe: indices !== undefined ? false : config.makeMaybe,
|
|
52
|
+
indicesCollection: indices
|
|
53
|
+
});
|
|
26
54
|
/* now, we soft-inject other arguments, so that calls like `x[y] <- 3` are linked correctly */
|
|
27
55
|
const { callArgs } = (0, common_1.processAllArguments)({
|
|
28
56
|
functionName: (0, info_1.initializeCleanDataflowInformation)(rootId, data),
|
|
@@ -77,7 +77,12 @@ function processAllArguments({ functionName, args, data, finalGraph, functionRoo
|
|
|
77
77
|
if ((0, info_1.happensInEveryBranch)(resolved.controlDependencies)) {
|
|
78
78
|
assumeItMayHaveAHigherTarget = false;
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
// When only a single index is referenced, we don't need to reference the whole object
|
|
81
|
+
const resolvedInGraphDef = resolved;
|
|
82
|
+
const isContainer = checkForContainer(resolvedInGraphDef?.indicesCollection);
|
|
83
|
+
if (isContainer || isContainer === undefined) {
|
|
84
|
+
finalGraph.addEdge(ingoing.nodeId, resolved.nodeId, edge_1.EdgeType.Reads);
|
|
85
|
+
}
|
|
81
86
|
}
|
|
82
87
|
if (assumeItMayHaveAHigherTarget) {
|
|
83
88
|
remainingReadInArgs.push(ingoing);
|
|
@@ -113,4 +118,13 @@ function patchFunctionCall({ nextGraph, rootId, name, data, argumentProcessResul
|
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Check whether passed {@link indices} are containers or whether their sub-indices are containers.
|
|
123
|
+
*/
|
|
124
|
+
function checkForContainer(indices) {
|
|
125
|
+
return indices?.every((indices) => {
|
|
126
|
+
const areSubIndicesContainers = indices.indices.every(index => 'subIndices' in index && checkForContainer(index.subIndices));
|
|
127
|
+
return indices.isContainer || areSubIndicesContainers;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
116
130
|
//# sourceMappingURL=common.js.map
|
|
@@ -8,6 +8,7 @@ import type { NodeId } from '../../../../../r-bridge/lang-4.x/ast/model/processi
|
|
|
8
8
|
import type { RNode } from '../../../../../r-bridge/lang-4.x/ast/model/model';
|
|
9
9
|
import type { IdentifierReference } from '../../../../environments/identifier';
|
|
10
10
|
import { DataflowGraph } from '../../../../graph/graph';
|
|
11
|
+
import type { ContainerIndicesCollection } from '../../../../graph/vertex';
|
|
11
12
|
export interface ProcessKnownFunctionCallInput<OtherInfo> extends ForceArguments {
|
|
12
13
|
readonly name: RSymbol<OtherInfo & ParentInformation>;
|
|
13
14
|
readonly args: readonly (RNode<OtherInfo & ParentInformation> | RFunctionArgument<OtherInfo & ParentInformation>)[];
|
|
@@ -28,4 +29,4 @@ export interface ProcessKnownFunctionCallResult {
|
|
|
28
29
|
readonly fnRef: IdentifierReference;
|
|
29
30
|
}
|
|
30
31
|
export declare function markNonStandardEvaluationEdges(markAsNSE: readonly number[], callArgs: readonly (DataflowInformation | undefined)[], finalGraph: DataflowGraph, rootId: NodeId): void;
|
|
31
|
-
export declare function processKnownFunctionCall<OtherInfo>({ name, args, rootId, data, reverseOrder, markAsNSE, forceArgs, patchData, hasUnknownSideEffect }: ProcessKnownFunctionCallInput<OtherInfo
|
|
32
|
+
export declare function processKnownFunctionCall<OtherInfo>({ name, args, rootId, data, reverseOrder, markAsNSE, forceArgs, patchData, hasUnknownSideEffect }: ProcessKnownFunctionCallInput<OtherInfo>, indicesCollection?: ContainerIndicesCollection): ProcessKnownFunctionCallResult;
|
|
@@ -23,7 +23,7 @@ function markNonStandardEvaluationEdges(markAsNSE, callArgs, finalGraph, rootId)
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = false, markAsNSE = undefined, forceArgs, patchData = d => d, hasUnknownSideEffect }) {
|
|
26
|
+
function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = false, markAsNSE = undefined, forceArgs, patchData = d => d, hasUnknownSideEffect }, indicesCollection = undefined) {
|
|
27
27
|
const functionName = (0, processor_1.processDataflowFor)(name, data);
|
|
28
28
|
const finalGraph = new graph_1.DataflowGraph(data.completeAst.idMap);
|
|
29
29
|
const functionCallName = name.content;
|
|
@@ -41,7 +41,8 @@ function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = fal
|
|
|
41
41
|
/* will be overwritten accordingly */
|
|
42
42
|
onlyBuiltin: false,
|
|
43
43
|
controlDependencies: data.controlDependencies,
|
|
44
|
-
args: reverseOrder ? [...callArgs].reverse() : callArgs
|
|
44
|
+
args: reverseOrder ? [...callArgs].reverse() : callArgs,
|
|
45
|
+
indicesCollection: indicesCollection,
|
|
45
46
|
});
|
|
46
47
|
if (hasUnknownSideEffect) {
|
|
47
48
|
finalGraph.markIdForUnknownSideEffects(rootId);
|
|
@@ -171,6 +171,7 @@ The following summarizes the configuration options:
|
|
|
171
171
|
- \`semantics\`: allows to configure the way _flowR_ handles R, although we currently only support \`semantics/environment/overwriteBuiltIns\`.
|
|
172
172
|
You may use this to overwrite _flowR_'s handling of built-in function and even completely clear the preset definitions shipped with flowR.
|
|
173
173
|
See [Configure BuiltIn Semantics](#configure-builtin-semantics) for more information.
|
|
174
|
+
- \`solver\`: allows to configure how _flowR_ resolves variables and their values (currently we support: ${Object.values(config_1.VariableResolve).map(v => `\`${v}\``).join(', ')}), as well as if pointer analysis should be active.
|
|
174
175
|
|
|
175
176
|
So you can configure _flowR_ by adding a file like the following:
|
|
176
177
|
|
|
@@ -189,6 +190,10 @@ ${(0, doc_code_1.codeBlock)('json', JSON.stringify({
|
|
|
189
190
|
]
|
|
190
191
|
}
|
|
191
192
|
}
|
|
193
|
+
},
|
|
194
|
+
solver: {
|
|
195
|
+
variables: config_1.VariableResolve.Alias,
|
|
196
|
+
pointerTracking: true
|
|
192
197
|
}
|
|
193
198
|
}, null, 2))}
|
|
194
199
|
|
|
@@ -233,7 +238,7 @@ _flowR_ can be used as a [module](${doc_files_1.FlowrNpmRef}) and offers several
|
|
|
233
238
|
### Using the \`${shell_1.RShell.name}\` to Interact with R
|
|
234
239
|
|
|
235
240
|
The \`${shell_1.RShell.name}\` class allows to interface with the \`R\` ecosystem installed on the host system.
|
|
236
|
-
For now there are no (real) alternatives, although we plan on providing more flexible drop-in replacements.
|
|
241
|
+
For now, there are no (real) alternatives, although we plan on providing more flexible drop-in replacements.
|
|
237
242
|
|
|
238
243
|
> [!IMPORTANT]
|
|
239
244
|
> Each \`${shell_1.RShell.name}\` controls a new instance of the R interpreter, make sure to call \`${shell_1.RShell.name}::${shell.close.name}()\` when you’re done.
|
package/package.json
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { REnvironmentInformation } from '../dataflow/environments/environment';
|
|
2
|
+
import type { ContainerIndices, ContainerIndicesCollection } from '../dataflow/graph/vertex';
|
|
3
|
+
import type { RAccess } from '../r-bridge/lang-4.x/ast/model/nodes/r-access';
|
|
4
|
+
import type { ParentInformation } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
5
|
+
/**
|
|
6
|
+
* Resolves {@link accessedArg} in the {@link environment} and filters its indices according to {@link accessArg}.
|
|
7
|
+
*
|
|
8
|
+
* @param accessedArg - The argument to resolve
|
|
9
|
+
* @param accessArg - The argument which is used to filter the indices
|
|
10
|
+
* @param environment - The environment in which {@link accessedArg} is resolved
|
|
11
|
+
* @returns The filtered {@link ContainerIndicesCollection} of the resolved {@link accessedArg}
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveSingleIndex(accessedArg: {
|
|
14
|
+
lexeme: string;
|
|
15
|
+
}, accessArg: {
|
|
16
|
+
lexeme: string;
|
|
17
|
+
}, environment: REnvironmentInformation): ContainerIndicesCollection;
|
|
18
|
+
/**
|
|
19
|
+
* Filters the single indices of the {@link indicesCollection} according to the lexeme of the {@link accessArg}.
|
|
20
|
+
*
|
|
21
|
+
* @param indicesCollection - The {@link ContainerIndicesCollection} to filter
|
|
22
|
+
* @param accessArg - The argument which is used to filter {@link indicesCollection}
|
|
23
|
+
* @returns The filtered copy of {@link indicesCollection}
|
|
24
|
+
*/
|
|
25
|
+
export declare function filterIndices(indicesCollection: ContainerIndicesCollection, accessArg: {
|
|
26
|
+
lexeme: string;
|
|
27
|
+
}): ContainerIndicesCollection;
|
|
28
|
+
/**
|
|
29
|
+
* Constructs the definition of a nested access.
|
|
30
|
+
*
|
|
31
|
+
* Example:
|
|
32
|
+
* ```r
|
|
33
|
+
* person$credentials$username
|
|
34
|
+
* ```
|
|
35
|
+
* would result in a list with the index `credentials`, which has the subIndex `username`.
|
|
36
|
+
*
|
|
37
|
+
* @param accessedArg - The top level argument that is accessed
|
|
38
|
+
* @param leafIndices - The index at the end of the nested access i.e. `c` in `a$b$c`.
|
|
39
|
+
* @returns The constructed nested access
|
|
40
|
+
*/
|
|
41
|
+
export declare function constructNestedAccess<OtherInfo>(accessedArg: RAccess<OtherInfo & ParentInformation>, leafIndices: ContainerIndices): ContainerIndices[];
|
|
42
|
+
/**
|
|
43
|
+
* Adds the passed list of {@link leafSubIndices} to the leaf (sub-)indices of {@link indicesCollection}.
|
|
44
|
+
*
|
|
45
|
+
* @param indicesCollection - Indices where to add the sub indices.
|
|
46
|
+
* @param leafSubIndices - Indices that are added to the leaf indices.
|
|
47
|
+
*/
|
|
48
|
+
export declare function addSubIndicesToLeafIndices(indicesCollection: ContainerIndices[], leafSubIndices: ContainerIndices[]): ContainerIndices[];
|